<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://shazwazza.com/rss/xslt"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>Shazwazza</title>
    <link>https://shazwazza.com/</link>
    <description>My blog which is pretty much just all about coding</description>
    <generator>Articulate, blogging built on Umbraco</generator>
    <image>
      <url>/media/0libq25y/frog.png?rmode=max&amp;v=1da0e911f4e6890</url>
      <title>Shazwazza</title>
      <link>https://shazwazza.com/</link>
    </image>
    <item>
      <guid isPermaLink="false">1334</guid>
      <link>https://shazwazza.com/post/an-examine-fix-for-umbraco-index-corruption/</link>
      <category>Examine</category>
      <title>An Examine fix for Umbraco index corruption</title>
      <description>&lt;p&gt;A new Examine version 3.3.0 has been released to address a long awaited &lt;a href="https://github.com/umbraco/Umbraco-CMS/issues/16163"&gt;bug fix for Umbraco websites&lt;/a&gt; that use the &lt;a href="https://github.com/Shazwazza/Examine/blob/release/3.0/src/Examine.Lucene/Directories/SyncedFileSystemDirectoryFactory.cs"&gt;&lt;code&gt;SyncedFileSystemDirectoryFactory&lt;/code&gt;&lt;/a&gt; which is the default setting for Umbraco CMS.&lt;/p&gt;

&lt;p&gt;The bug typically means that indexes cannot be used and log entries such as &lt;code&gt;Lucene.Net.Index.CorruptIndexException: invalid deletion count: 2 vs docCount=1&lt;/code&gt; are present.&lt;/p&gt;

&lt;h2&gt;Understanding the problem:&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;SyncedFileSystemDirectoryFactory&lt;/code&gt; directory is there to avoid performance implications of rebuilding indexes on startup when a site is moved to another worker in Azure. The reason an index rebuild would occur is because in Azure, Lucene files need to work off of the local 'fast drive' (C:\%temp%), not the default/shared network 'slow drive' (D:), and whenever a site is moved, or spawned on a new worker in Azure, the local 'fast drive' is empty, meaning no indexes exist. The &lt;code&gt;SyncedFileSystemDirectoryFactory&lt;/code&gt; attempts to work around this challenge by continually synchronizing a copy of the indexes from the 'fast drive' to the 'slow drive' so that when a site is moved to another worker, it can sync (restore) from the 'slow drive' back to the 'fast drive' in order to avoid the index rebuild overhead.&lt;/p&gt;

&lt;p&gt;The problem with &lt;code&gt;SyncedFileSystemDirectoryFactory&lt;/code&gt; is that this implementation doesn't take into account what happens if the index files in your main storage ('slow drive') become corrupted which can happen for a number of reasons - misconfiguration, network latency, process termination, etc... &lt;/p&gt;

&lt;h2&gt;Understanding the solution:&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;SyncedFileSystemDirectoryFactory&lt;/code&gt; has been updated to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check the health of the main index if it exists ('slow drive').&lt;/li&gt;
&lt;li&gt;Check the health of the local index if it exists ('fast drive').&lt;/li&gt;
&lt;li&gt;If the main index is unhealthy or doesn't exist and the local index is healthy, it will synchronize the local index to the main index. This can occur only if a site hasn't moved to a new worker.&lt;/li&gt;
&lt;li&gt;If the main index is unhealthy and the local index doesn't exist or is unhealthy, then it will delete the main (corrupted) index.&lt;/li&gt;
&lt;li&gt;Once health checks are done, the index from main is always synced to local. If the main index was deleted due to corruption, this will mean that the local index is empty and an index rebuild will occur.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This change will attempt to keep any healthy index that is available (main vs local), but if nothing can be read, the indexes will be deleted and an &lt;strong&gt;index rebuild will occur&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There's also a new option to fix a corrupted index but this is not enabled by default since it can mean a loss of documents.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Shazwazza/Examine/blob/release/3.0/src/Examine.Lucene/Directories/SyncedFileSystemDirectoryFactory.cs"&gt;Source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Shazwazza/Examine/blob/release/3.0/src/Examine.Test/Examine.Lucene/Directories/SyncedFileSystemDirectoryFactoryTests.cs"&gt;Tests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Understanding the rebuilding overhead&lt;/h2&gt;

&lt;p&gt;The performance overhead of index rebuilding is due to the Umbraco database queries that need to be executed in order to populate the indexes. The only reason &lt;code&gt;SyncedFileSystemDirectoryFactory&lt;/code&gt; exists is to prevent this overhead when hosting in &lt;a href="https://azure.microsoft.com/en-us/products/app-service"&gt;Azure App Service&lt;/a&gt; (which is what &lt;a href="https://umbraco.com/products/umbraco-cloud/"&gt;Umbraco Cloud&lt;/a&gt; uses), and it can only be used on your Umbraco primary node. It does not prevent index rebuilding overhead for non-primary nodes when load balancing or scaling out because the main network 'slow drive' is shared between all workers and an index can only be read/written to be a single process. &lt;/p&gt;

&lt;p&gt;This means that it's only useful if you are hosting in Azure App Service without any load balancing while keeping in mind that it does not always prevent index rebuilds (see above).&lt;/p&gt;

&lt;p&gt;The index rebuilding overhead can be dramatic when load balancing or scaling out, for example: If you scale out to +5 nodes in a load balancing setup, that means that 5x nodes will be performing index rebuilds around the same time, this means that your DB is going to get hammered by queries to build all of those new indexes. The performance hit isn't the index building - it is the DB queries and this can lead to DB locks and lead to the dreaded SQL Lock Timeout issue in the Umbraco back office. Plus, if search is critical to your front-end, than for a while after your site has started up, there won't be any index which means there won't be any search until the background processing is done. &lt;/p&gt;

&lt;p&gt;... Many of these reasons is why &lt;a href="https://examinex.online/"&gt;ExamineX&lt;/a&gt; was created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reliably and centrally persisted indexes means nothing is out of sync between nodes.&lt;/li&gt;
&lt;li&gt;No index rebuilding when your site is moved or scaled = no rebuilding overhead.&lt;/li&gt;
&lt;li&gt;Prevents SQL Timeout locks due to DB rebuilding queries.&lt;/li&gt;
&lt;li&gt;Very easy to setup and seamlessly changes your Lucene based indexes to &lt;a href="https://azure.microsoft.com/en-us/products/ai-services/ai-search"&gt;Azure&lt;/a&gt;/&lt;a href="https://www.elastic.co/"&gt;Elastic&lt;/a&gt; search indexes.&lt;/li&gt;
&lt;li&gt;Ideal when hosting Umbraco on Azure Web Apps (or Umbraco Cloud) and a perfect solution for load balancing and scaling.&lt;/li&gt;
&lt;li&gt;Automatically index Umbraco media file content without the need for additional indexes with support for PDFs, Microsoft Office documents and more.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(coming soon)&lt;/em&gt; Automatically generate Umbraco media image descriptions, tags, locations, and more using AI allowing your editors to quickly find the media/images they need.&lt;/li&gt;
&lt;/ul&gt;</description>
      <pubDate>Wed, 31 Jul 2024 17:49:54 Z</pubDate>
      <a10:updated>2024-07-31T17:49:54Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1332</guid>
      <link>https://shazwazza.com/post/configuring-a-suggester-with-examinex-and-azure-ai-search/</link>
      <category>Examine</category>
      <title>Configuring a Suggester with ExamineX and Azure AI Search</title>
      <description>&lt;p&gt;We recently had a customer ask about integrating the &lt;a href="https://learn.microsoft.com/en-us/azure/search/index-add-suggesters"&gt;Azure AI Search suggester&lt;/a&gt; API with &lt;a href="https://examinex.online/"&gt;ExamineX&lt;/a&gt; and this turns out to be quite straight forward :)&lt;/p&gt;

&lt;p&gt;First thing is to ensure that the fields you want to use the Suggester for are configured correctly:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cs
// Configuration for the External Index for the City and Country fields.
// The Azure AI Search Suggester requires fields to be 'string fields only'
// and using the Standard Analyzer (default on the External Index)
services.Configure&amp;lt;AzureSearchIndexOptions&amp;gt;(
    Constants.UmbracoIndexes.ExternalIndexName,
    options =&amp;gt;
    {
         options.FieldDefinitions = new FieldDefinitionCollection(
            new FieldDefinition("City", AzureSearchFieldDefinitionTypes.FullText),
            new FieldDefinition("Country", AzureSearchFieldDefinitionTypes.FulText));
    });
&lt;/code&gt;
Next up, is to create the Suggester which is done as part of the &lt;a href="https://examinex.online/customization#events"&gt;CreatingOrUpdatingIndex&lt;/a&gt; event:&lt;/p&gt;

&lt;p&gt;```cs
externalIndex.CreatingOrUpdatingIndex += AzureIndex&lt;em&gt;CreatingOrUpdatingIndex&lt;/em&gt;ScoringProfile;&lt;/p&gt;

&lt;p&gt;private void AzureIndex&lt;em&gt;CreatingOrUpdatingIndex&lt;/em&gt;Suggester(object sender, CreatingOrUpdatingIndexEventArgs e)
{
    switch (e.EventType)
    {
        case IndexModifiedEventType.Creating:
        case IndexModifiedEventType.Rebuilding:
            var index = e.AzureSearchIndexDefinition;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;        var suggester = new SearchSuggester(
            "sg",
            new[] { "Country", "City" });

        index.Suggesters.Add(suggester);

        break;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;}
```
When data has been indexed for these fields, using the Suggester API is simple. For example, lets assume that the following ValueSets were indexed:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cs
externalIndex.IndexItems(new[]
{
    ValueSet.FromObject(id1, new { City = "Calgary", Country = "Canada" }),
    ValueSet.FromObject(id2, new { City = "Sydney", Country = "Australia" }),
    ValueSet.FromObject(id3, new { City = "Copenhagen", Country = "Denmark" }),
    ValueSet.FromObject(id4, new { City = "Vienna", Country = "Austria" }),
});
&lt;/code&gt;
Then to get suggestions for "Au":&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cs
// cast to AzureSearchIndex to expose underlying Azure Search APIs
var azureSearchIndex = (AzureSearchIndex)externalIndex;
var result = await  azureSearchIndex.IndexClient.SuggestAsync&amp;lt;ExamineDocument&amp;gt;(
    "Au",
    "sg");
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The result will contain 2x entries, one for the Australia and one for the Austria documents. Super easy!&lt;/p&gt;

&lt;p&gt;One thing to watch out for is if your index is not configured to use the Standard Analyzer by default (i.e. like the Internal Index in Umbraco). In that case, you'll need to define a custom field type with that indexer:&lt;/p&gt;

&lt;p&gt;```cs
services.Configure&lt;AzureSearchIndexOptions&gt;(
    Constants.UmbracoIndexes.ExternalIndexName,
    options =&gt;
    {
        // This adds a custom field type that uses the Standard Analyzer
        options.IndexValueTypesFactory = new Dictionary&lt;string, IAzureSearchFieldValueTypeFactory&gt;
        {
            ["standard"] = new AzureSearchFieldValueTypeFactory(s =&gt;
                new AzureSearchFieldValueType(s, SearchFieldDataType.String, LexicalAnalyzerName.StandardLucene.ToString()))
        };&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    // Set these field types to the one defined above
    options.FieldDefinitions = new FieldDefinitionCollection(
        new FieldDefinition("City", "standard"),
        new FieldDefinition("Country", "standard"));
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;```
Happy Searching!&lt;/p&gt;</description>
      <pubDate>Wed, 01 May 2024 14:51:52 Z</pubDate>
      <a10:updated>2024-05-01T14:51:52Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1212</guid>
      <link>https://shazwazza.com/post/can-i-disable-examine-indexes-on-umbraco-front-end-servers/</link>
      <category>Umbraco</category>
      <category>Examine</category>
      <title>Can I disable Examine indexes on Umbraco front-end servers?</title>
      <description>&lt;p&gt;In Umbraco v8, Examine and Lucene are only used for the back office searches, unless you specifically use those APIs for your front-end pages. I recently had a request to know if it’s possible to disable Examine/Lucene for front-end servers since they didn’t use Examine/Lucene APIs at all on their front-end pages… here’s the answer&lt;/p&gt;
&lt;h2 id="why-would-you-want-this"&gt;Why would you want this?&lt;/h2&gt;
&lt;p&gt;If you are running a Load Balancing setup in Azure App Service then you have the option to scale out (and perhaps you do!). In this case, you need to have the Examine configuration option of:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;add key=&amp;quot;Umbraco.Examine.LuceneDirectoryFactory&amp;quot; 
          value=&amp;quot;Examine.LuceneEngine.Directories.TempEnvDirectoryFactory, Examine&amp;quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is because each scaled out worker is running from the same network share file system. Without this setting (or with the &lt;code&gt;SyncTempEnvDirectoryFactory&lt;/code&gt; setting) it would mean that each worker will be trying to write Lucene file based indexes to the same location which will result in corrupt indexes and locked files. Using the &lt;code&gt;TempEnvDirectoryFactory&lt;/code&gt; means that the indexes will only be stored on the worker's local 'fast drive' which is in it's %temp% folder on the local (non-network share) hard disk.&lt;/p&gt;
&lt;p&gt;When a site is moved or provisioned on a new worker the local %temp% location will be empty so Lucene indexes will be rebuilt on startup for that worker. This will occur when Azure moves a site or when a new worker comes online from a scale out action. When indexes are rebuilt, the worker will query the database for the data and depending on how much data you have in your Umbraco installation, this could take a few minutes which can be problematic. Why? Because Umbraco v8 uses distributed SQL locks to ensure data integrity and during these queries a content lock will be created which means other back office operations on content will need to wait. This can end up with SQL Lock timeout issues. An important thing to realize is that these rebuild queries will occur for all new workers, so if you scaled out from 1 to 10, that is 9 new workers coming online at the same time.&lt;/p&gt;
&lt;h2 id="how-to-avoid-this-problem"&gt;How to avoid this problem?&lt;/h2&gt;
&lt;p&gt;If you use Examine APIs on your front-end, then you cannot just disable Examine/Lucene so the only reasonable solution is to use an Examine implementation that uses a hosted search service like &lt;a href="https://examinex.online/"&gt;ExamineX&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you don't use Examine APIs on your front-ends then it is a reasonable solution to disable Examine/Lucene on the front-ends to avoid this issue.  To do that, you would change the default Umbraco indexes to use an in-memory only store and prohibit data from being put into the indexes. Then disable the queries that execute when Umbraco tries to re-populate the indexes.&lt;/p&gt;
&lt;h2 id="show-me-the-code"&gt;Show me the code&lt;/h2&gt;
&lt;p&gt;First thing is to replace the default index factory. This new one will change the underlying Lucene directory for each index to be a &lt;code&gt;RAMDirectory&lt;/code&gt; and will also disable the default Umbraco event handling that populates the indexes. This means Umbraco will not try to update the index based on content, media or member changes.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class InMemoryExamineIndexFactory : UmbracoIndexesCreator
{
    public InMemoryExamineIndexFactory(
        IProfilingLogger profilingLogger,
        ILocalizationService languageService,
        IPublicAccessService publicAccessService,
        IMemberService memberService,
        IUmbracoIndexConfig umbracoIndexConfig)
        : base(profilingLogger, languageService, publicAccessService, memberService, umbracoIndexConfig)
    {
    }

    public override IEnumerable&amp;lt;IIndex&amp;gt; Create()
    {
        return new[]
        {
            CreateInternalIndex(),
            CreateExternalIndex(),
            CreateMemberIndex()
        };
    }

    // all of the below is the same as Umbraco defaults, except
    // we are using an in-memory Lucene directory.

    private IIndex CreateInternalIndex()
        =&amp;gt; new UmbracoContentIndex(
            Constants.UmbracoIndexes.InternalIndexName,
            new RandomIdRamDirectory(), // in-memory dir
            new UmbracoFieldDefinitionCollection(),
            new CultureInvariantWhitespaceAnalyzer(),
            ProfilingLogger,
            LanguageService,
            UmbracoIndexConfig.GetContentValueSetValidator())
        {
            EnableDefaultEventHandler = false
        };

    private IIndex CreateExternalIndex()
        =&amp;gt; new UmbracoContentIndex(
            Constants.UmbracoIndexes.ExternalIndexName,
            new RandomIdRamDirectory(), // in-memory dir
            new UmbracoFieldDefinitionCollection(),
            new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30),
            ProfilingLogger,
            LanguageService,
            UmbracoIndexConfig.GetPublishedContentValueSetValidator())
        {
            EnableDefaultEventHandler = false
        };

    private IIndex CreateMemberIndex()
        =&amp;gt; new UmbracoMemberIndex(
            Constants.UmbracoIndexes.MembersIndexName,
            new UmbracoFieldDefinitionCollection(),
            new RandomIdRamDirectory(), // in-memory dir
            new CultureInvariantWhitespaceAnalyzer(),
            ProfilingLogger,
            UmbracoIndexConfig.GetMemberValueSetValidator())
        {
            EnableDefaultEventHandler = false
        };

    // required so that each ram dir has a different ID
    private class RandomIdRamDirectory : RAMDirectory
    {
        private readonly string _lockId = Guid.NewGuid().ToString();
        public override string GetLockId()
        {
            return _lockId;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next thing to do is to create no-op index populators to replace the Umbraco default ones. All these do is ensure they are not associated with any index and then just to be sure, does not execute any logic for population.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class DisabledMemberIndexPopulator : MemberIndexPopulator
{
    public DisabledMemberIndexPopulator(
        IMemberService memberService,
        IValueSetBuilder&amp;lt;IMember&amp;gt; valueSetBuilder)
        : base(memberService, valueSetBuilder)
    {
    }

    public override bool IsRegistered(IIndex index) =&amp;gt; false;
    public override bool IsRegistered(IUmbracoMemberIndex index) =&amp;gt; false;
    protected override void PopulateIndexes(IReadOnlyList&amp;lt;IIndex&amp;gt; indexes) { }
}

public class DisabledContentIndexPopulator : ContentIndexPopulator
{
    public DisabledContentIndexPopulator(
        IContentService contentService,
        ISqlContext sqlContext,
        IContentValueSetBuilder contentValueSetBuilder)
        : base(contentService, sqlContext, contentValueSetBuilder)
    {
    }

    public override bool IsRegistered(IIndex index) =&amp;gt; false;
    public override bool IsRegistered(IUmbracoContentIndex2 index) =&amp;gt; false;
    protected override void PopulateIndexes(IReadOnlyList&amp;lt;IIndex&amp;gt; indexes) { }
}

public class DisabledPublishedContentIndexPopulator : PublishedContentIndexPopulator
{
    public DisabledPublishedContentIndexPopulator(
        IContentService contentService,
        ISqlContext sqlContext,
        IPublishedContentValueSetBuilder contentValueSetBuilder)
        : base(contentService, sqlContext, contentValueSetBuilder)
    {
    }

    public override bool IsRegistered(IIndex index) =&amp;gt; false;
    public override bool IsRegistered(IUmbracoContentIndex2 index) =&amp;gt; false;
    protected override void PopulateIndexes(IReadOnlyList&amp;lt;IIndex&amp;gt; indexes) { }
}

public class DisabledMediaIndexPopulator : MediaIndexPopulator
{
    public DisabledMediaIndexPopulator(
        IMediaService mediaService,
        IValueSetBuilder&amp;lt;IMedia&amp;gt; mediaValueSetBuilder) : base(mediaService, mediaValueSetBuilder)
    {
    }

    public override bool IsRegistered(IIndex index) =&amp;gt; false;
    public override bool IsRegistered(IUmbracoContentIndex index) =&amp;gt; false;
    protected override void PopulateIndexes(IReadOnlyList&amp;lt;IIndex&amp;gt; indexes) { }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lastly, we just need to enable these services:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class DisabledExamineComposer : IUserComposer
{
    public void Compose(Composition composition)
    {
        // replace the default
        composition.RegisterUnique&amp;lt;IUmbracoIndexesCreator, InMemoryExamineIndexFactory&amp;gt;();

        // replace the default populators
        composition.Register&amp;lt;MemberIndexPopulator, DisabledMemberIndexPopulator&amp;gt;(Lifetime.Singleton);
        composition.Register&amp;lt;ContentIndexPopulator, DisabledContentIndexPopulator&amp;gt;(Lifetime.Singleton);
        composition.Register&amp;lt;PublishedContentIndexPopulator, DisabledPublishedContentIndexPopulator&amp;gt;(Lifetime.Singleton);
        composition.Register&amp;lt;MediaIndexPopulator, DisabledMediaIndexPopulator&amp;gt;(Lifetime.Singleton);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that all in place, it means that no data will ever be looked up to rebuild indexes and Umbraco will not send data to be indexed. There is nothing here preventing data from being indexed though. For example, if you use the Examine APIs to update the index directly, that data will be indexed in memory. If you wanted to absolutely make sure no data ever went into the index, you would have to override some methods on the &lt;code&gt;RAMDirectory&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="can-i-run-examine-with-ramdirectory-with-data"&gt;Can I run Examine with RAMDirectory with data?&lt;/h2&gt;
&lt;p&gt;You might have realized with the above that if you don't replace the populators, you will essentially have Examine indexes in Umbraco running from RAMDirectory. Is this ok? Yes absolutely, &lt;strong&gt;but&lt;/strong&gt; that entirely depends on your data set. If you have a large index, that means it will consume large amounts of memory which is typically not a good idea. But if you have a small data set, or you filter the index so that it remains small enough, then yes! You can certainly run Examine with an in-memory directory but this would still only be advised on your front-end/replica servers.&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:10:30 Z</pubDate>
      <a10:updated>2023-03-23T15:10:30Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1320</guid>
      <link>https://shazwazza.com/post/spatial-search-with-examine-and-lucene/</link>
      <category>Examine</category>
      <title>Spatial Search with Examine and Lucene</title>
      <description>&lt;p&gt;I was asked about how to do Spatial search with Examine recently which sparked my interest on how that should be done so here’s how it goes…&lt;/p&gt;
&lt;p&gt;Examine’s default implementation is Lucene so by default whatever you can do in Lucene you can achieve in Examine by exposing the underlying Lucene bits. If you want to jump straight to code, I’ve created a couple of &lt;a rel="noopener" href="https://github.com/Shazwazza/Examine/blob/master/src/Examine.Test/Extensions/SpatialSearch.cs" target="_blank"&gt;unit tests in the Examine project&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Source code as documentation&lt;/h2&gt;
&lt;p&gt;&lt;a rel="noopener" href="https://lucenenet.apache.org/" target="_blank"&gt;Lucene.Net&lt;/a&gt; and &lt;a rel="noopener" href="https://lucenenet.apache.org/" target="_blank"&gt;Lucene (Java)&lt;/a&gt; are more or less the same. There’s a few API and naming conventions differences but at the end of the day Lucene.Net is just a .NET port of Lucene. So pretty much any of the documentation you’ll find for Lucene will work with Lucene.Net just with a bit of tweaking. Same goes for code snippets in the source code and Lucene and Lucene.Net have tons of examples of how to do things. In fact for Spatial search there’s a &lt;a rel="noopener" href="https://github.com/apache/lucene-solr/blob/master/lucene/spatial-extras/src/test/org/apache/lucene/spatial/SpatialExample.java" target="_blank"&gt;specific test example for that&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So we ‘just’ need to take that example and go with it.&lt;/p&gt;
&lt;p&gt;Firstly we’ll need the Lucene.Net.Contrib package:&lt;/p&gt;
&lt;pre&gt;Install-Package Lucene.Net.Contrib -Version 3.0.3&lt;/pre&gt;
&lt;h2&gt;Indexing&lt;/h2&gt;
&lt;p&gt;The indexing part doesn't really need to do anything out of the ordinary from what you would normally do. You just need to get either latitude/longitude or x/y (numerical) values into your index. This can be done directly using a ValueSet when you index and having your &lt;a rel="noopener" href="https://shazwazza.github.io/Examine/configuration.html" target="_blank"&gt;field types set as numeric&lt;/a&gt; or it could be done with the DocumentWriting event which gives you direct access to the underlying Lucene document. &lt;/p&gt;
&lt;h2&gt;Strategies&lt;/h2&gt;
&lt;p&gt;For this example I’m just going to stick with simple Geo Spatial searching with simple x/y coordinates. There’s different “stategies” and you can configure these to handle different types of spatial search when it’s not just as simple as an x/y distance calculation. I was shown an example of a Spatial search that used the “PointVectorStrategy” but after looking into that it seems like this is a semi deprecated strategy and even one of it’s methods says: &lt;a rel="noopener" href="https://lucenenet.apache.org/docs/3.0.3/d0/d37/_point_vector_strategy_8cs_source.html#l00104" target="_blank"&gt;“//TODO this is basically old code that hasn't been verified well and should probably be removed”&lt;/a&gt; and then I found an SO article stating that “RecursivePrefixTreeStrategy” was what should be used instead anyways and as it turns out that’s exactly what the java example uses too.&lt;/p&gt;
&lt;p&gt;If you need some more advanced Spatial searching then I’d suggest researching some of the strategies available, reading the docs  and looking at the source examples. There’s unit tests for pretty much everything in Lucene and Lucene.Net.&lt;/p&gt;
&lt;h2&gt;Get the underlying Lucene Searcher instance&lt;/h2&gt;
&lt;p&gt;If you need to do some interesting Lucene things with Examine you need to gain access to the underlying Lucene bits. Namely you’d normally only need access to the &lt;em&gt;IndexWriter&lt;/em&gt; which you can get from &lt;em&gt;LuceneIndex.GetIndexWriter()&lt;/em&gt; and the Lucene Searcher which you can get from &lt;em&gt;LuceneSearcher.GetSearcher(). &lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;// Get an index from the IExamineManager
if (!examineMgr.TryGetIndex("MyIndex", out var index))
    throw new InvalidOperationException("No index found with name MyIndex");
            
// We are expecting this to be a LuceneIndex
if (!(index is LuceneIndex luceneIndex))
    throw new InvalidOperationException("Index MyIndex is not a LuceneIndex");

// If you wanted a LuceneWriter, here's how:
//var luceneWriter = luceneIndex.GetIndexWriter();

// Need to cast in order to expose the Lucene bits
var searcher = (LuceneSearcher)luceneIndex.GetSearcher();

// Get the underlying Lucene Searcher instance
var luceneSearcher = searcher.GetLuceneSearcher();&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Do the search&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Important! Latitude/Longitude != X/Y&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Lucene GEO Spatial APIs take an X/Y coordinates, not latitude/longitude and a common mistake is to just use them in place but that’s incorrect and they are actually opposite so be sure you swap tham. &lt;strong&gt;Latitude = Y, Longitude = X&lt;/strong&gt;. Here’s a simple function to swap them:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;private void GetXYFromCoords(double lat, double lng, out double x, out double y)
{
    // change to x/y coords, longitude = x, latitude = y
    x = lng;
    y = lat;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have the underlying Lucene Searcher instance we can search however we want:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;// Create the Geo Spatial lucene objects
SpatialContext ctx = SpatialContext.GEO;
int maxLevels = 11; //results in sub-meter precision for geohash
SpatialPrefixTree grid = new GeohashPrefixTree(ctx, maxLevels);
RecursivePrefixTreeStrategy strategy = new RecursivePrefixTreeStrategy(grid, GeoLocationFieldName);

// lat/lng of Sydney Australia
var latitudeSydney = -33.8688;
var longitudeSydney = 151.2093;
            
// search within 100 KM
var searchRadiusInKm = 100;

// convert to X/Y
GetXYFromCoords(latitudeSydney, longitudeSydney, out var x, out var y);

// Make a circle around the search point
var args = new SpatialArgs(
    SpatialOperation.Intersects,
    ctx.MakeCircle(x, y, DistanceUtils.Dist2Degrees(searchRadiusInKm, DistanceUtils.EARTH_MEAN_RADIUS_KM)));

// Create the Lucene Filter
var filter = strategy.MakeFilter(args);

// Create the Lucene Query
var query = strategy.MakeQuery(args);

// sort on ID
Sort idSort = new Sort(new SortField(LuceneIndex.ItemIdFieldName, SortField.INT));
TopDocs docs = luceneSearcher.Search(query, filter, MaxResultDocs, idSort);

// iterate raw lucene results
foreach(var doc in docs.ScoreDocs)
{
    // TODO: Do something with result
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Filter vs Query?&lt;/h2&gt;
&lt;p&gt;The above code creates both a Filter and a Query that is being used to get the results but the &lt;em&gt;SpatialExample&lt;/em&gt; just uses a “MatchAllDocsQuery” instead of what is done above. Both return the same results so what is happening with “strategy.MakeQuery”? It’s creating a ConstantScoreQuery which means that the resulting document “Score” will be empty/same for all results. That’s really all this does so it’s optional but really when searching on only locations with no other data Score doesn’t make a ton of sense. It is possible however to mix Spatial search filters with real queries.&lt;/p&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;You’ll see above that the ordering is by Id but probably in a lot of cases you’ll want to sort by distance. There’s examples of this in the Lucene &lt;em&gt;SpatialExample&lt;/em&gt; linked above and there’s a reference to that in &lt;a rel="noopener" href="https://stackoverflow.com/questions/28717528/lucene-spatial-sorting-by-distance" target="_blank"&gt;this SO article&lt;/a&gt; too, the only problem is those examples are for later Lucene versions than the current Lucene.Net 3.x. But if there’s a will there’s a way and I’m sure with some Googling, code researching and testing you’ll be able to figure it out :)&lt;/p&gt;
&lt;p&gt;The Examine docs pages need a little love and should probably include this info. The docs pages are just built in Jekyll and located in the &lt;a rel="noopener" href="https://github.com/Shazwazza/Examine/tree/master/docs" target="_blank"&gt;/docs folder of the Examine repository&lt;/a&gt;. I would love any help with Examine’s docs if you’ve got a bit of time :)&lt;/p&gt;
&lt;p&gt;As far as Examine goes though, there’s actually custom search method called “&lt;a rel="noopener" href="https://github.com/Shazwazza/Examine/blob/master/src/Examine.Test/Search/FluentApiTests.cs#L1774" target="_blank"&gt;LuceneQuery&lt;/a&gt;” on the “LuceneSearchQueryBase” which is the object created when creating normal Examine queries with &lt;em&gt;CreateQuery()&lt;/em&gt;. Using this method you can pass in a native Lucene Query instance like the one created above and it will manage all of the searching/paging/sorting/results/etc… for you so you don’t have to do some of the above manual work. However there is currently no method allowing a native Lucene Filter instances to be passed in like the one created above. Once that’s in place then some of the lucene APIs above wont be needed and this can be a bit nicer. Then it’s probably worthwhile adding another Nuget project like Examine.Extensions which can contain methods and functionality for this stuff, or maybe the community can do something like that just like &lt;a rel="noopener" href="https://github.com/callumbwhyte" target="_blank"&gt;Callum&lt;/a&gt; has done for &lt;a rel="noopener" href="https://github.com/callumbwhyte/examine-facets" target="_blank"&gt;Examine Facets&lt;/a&gt;.  What do you think?&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:10:06 Z</pubDate>
      <a10:updated>2023-03-23T15:10:06Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1248</guid>
      <link>https://shazwazza.com/post/searching-with-ipublishedcontentquery-in-umbraco/</link>
      <category>Umbraco</category>
      <category>Examine</category>
      <title>Searching with IPublishedContentQuery in Umbraco</title>
      <description>&lt;p&gt;I recently realized that I don’t think Umbraco’s APIs on &lt;em&gt;IPublishedContentQuery&lt;/em&gt; are documented so hopefully this post may inspire some docs to be written or at least guide some folks on some functionality they may not know about.&lt;/p&gt;
&lt;p&gt;A long while back even in Umbraco v7 &lt;em&gt;UmbracoHelper&lt;/em&gt; was split into different components and &lt;em&gt;UmbracoHelper&lt;/em&gt; just wrapped these. One of these components was called &lt;em&gt;ITypedPublishedContentQuery&lt;/em&gt; and in v8 is now called &lt;em&gt;IPublishedContentQuery&lt;/em&gt;, and this component is responsible for executing queries for content and media on the front-end in razor templates. In v8 a lot of methods were removed or obsoleted from &lt;em&gt;UmbracoHelper&lt;/em&gt; so that it wasn’t one gigantic object and tries to steer developers to use these sub components directly instead. For example if you try to access &lt;em&gt;UmbracoHelper.ContentQuery&lt;/em&gt; you’ll see that has been deprecated saying:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Inject and use an instance of IPublishedContentQuery in the constructor for using it in classes or get it from Current.PublishedContentQuery in views&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and the &lt;em&gt;UmbracoHelper.Search&lt;/em&gt; methods from v7 have been removed and now only exist on &lt;em&gt;IPublishedContentQuery&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;There are &lt;a rel="noopener" href="https://our.umbraco.com/apidocs/v8/csharp/api/Umbraco.Web.IPublishedContentQuery.html" target="_blank"&gt;API docs for IPublishedContentQuery&lt;/a&gt; which are a bit helpful, at least will tell you what all available methods and parameters are. The main one’s I wanted to point out are the &lt;strong&gt;Search&lt;/strong&gt; methods.&lt;/p&gt;
&lt;h2&gt;Strongly typed search responses&lt;/h2&gt;
&lt;p&gt;When you use Examine directly to search you will get an Examine &lt;em&gt;ISearchResults&lt;/em&gt; object back which is more or less raw data. It’s possible to work with that data but most people want to work with some strongly typed data and at the very least in Umbraco with &lt;em&gt;IPublishedContent&lt;/em&gt;. That is pretty much what &lt;a rel="noopener" href="https://our.umbraco.com/apidocs/v8/csharp/api/Umbraco.Web.IPublishedContentQuery.html#Umbraco_Web_IPublishedContentQuery_Search_System_String_System_String_System_String_" target="_blank"&gt;IPublishedContentQuery.Search&lt;/a&gt; methods are solving. Each of these methods will return an &lt;em&gt;IEnumerable&amp;lt;PublishedSearchResult&amp;gt;&lt;/em&gt; and each &lt;a rel="noopener" href="https://our.umbraco.com/apidocs/v8/csharp/api/Umbraco.Core.Models.PublishedContent.PublishedSearchResult.html" target="_blank"&gt;PublishedSearchResult&lt;/a&gt; contains an &lt;em&gt;IPublishedContent&lt;/em&gt; instance along with a &lt;em&gt;Score&lt;/em&gt; value. A quick example in razor:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;@inherits Umbraco.Web.Mvc.UmbracoViewPage
@using Current = Umbraco.Web.Composing.Current;
@{
    var search = Current.PublishedContentQuery.Search(Request.QueryString["query"]);
}

&amp;lt;div&amp;gt;
    &amp;lt;h3&amp;gt;Search Results&amp;lt;/h3&amp;gt;
    &amp;lt;ul&amp;gt;
        @foreach (var result in search)
        {
            &amp;lt;li&amp;gt;
                Id: @result.Content.Id
                &amp;lt;br/&amp;gt;
                Name: @result.Content.Name
                &amp;lt;br /&amp;gt;
                Score: @result.Score
            &amp;lt;/li&amp;gt;
        }
    &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ordering of this search is by Score so the highest score is first. This makes searching very easy while the underlying mechanism is still Examine. The &lt;em&gt;IPublishedContentQuery.Search &lt;/em&gt;methods make working with the results a bit nicer.&lt;/p&gt;
&lt;h2&gt;Paging results&lt;/h2&gt;
&lt;p&gt;You may have noticed that there’s a few overloads and optional parameters to these search methods too. 2 of the overloads support paging parameters and these take care of all of the quirks with Lucene paging for you. I wrote &lt;a rel="noopener" href="/post/paging-with-examine/" target="_blank"&gt;a previous post about paging with Examine&lt;/a&gt; and you need to make sure you do that correctly else you’ll end up iterating over possibly tons of search results which can have performance problems. To expand on the above example with paging is super easy:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;@inherits Umbraco.Web.Mvc.UmbracoViewPage
@using Current = Umbraco.Web.Composing.Current;
@{
    var pageSize = 10;
    var pageIndex = int.Parse(Request.QueryString["page"]);
    var search = Current.PublishedContentQuery.Search(
        Request.QueryString["query"],
        pageIndex * pageSize,   // skip
        pageSize,               // take
        out var totalRecords);
}

&amp;lt;div&amp;gt;
    &amp;lt;h3&amp;gt;Search Results&amp;lt;/h3&amp;gt;
    &amp;lt;ul&amp;gt;
        @foreach (var result in search)
        {
            &amp;lt;li&amp;gt;
                Id: @result.Content.Id
                &amp;lt;br/&amp;gt;
                Name: @result.Content.Name
                &amp;lt;br /&amp;gt;
                Score: @result.Score
            &amp;lt;/li&amp;gt;
        }
    &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Simple search with cultures&lt;/h2&gt;
&lt;p&gt;Another optional parameter you might have noticed is the culture parameter. The docs state this about the culture parameter:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When the &lt;code&gt;culture&lt;/code&gt; is not specified or is *, all cultures are searched. To search for only invariant documents and fields use null. When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents. While enumerating results, the ambient culture is changed to be the searched culture.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What this is saying is that if you aren’t using culture variants in Umbraco then don’t worry about it. But if you are, you will also generally not have to worry about it either! What?! By default the simple Search method will use the “ambient” (aka ‘Current’) culture to search and return data. So if you are currently browsing your “fr-FR” culture site this method will automatically only search for your data in your French culture but will also search on any invariant (non-culture) data. And as a bonus, the IPublishedContent returned also uses this ambient culture so any values you retrieve from the content item without specifying the culture will just be the ambient/default culture.&lt;/p&gt;
&lt;p&gt;So why is there a “culture” parameter? It’s just there in case you want to search on a specific culture instead of relying on the ambient/current one.&lt;/p&gt;
&lt;h2&gt;Search with IQueryExecutor&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;IQueryExecutor &lt;/em&gt;is the resulting object created when creating a query with the Examine fluent API. This means you can build up any complex Examine query you want, even with raw Lucene, and then pass this query to one of the &lt;em&gt;IPublishedContentQuery.Search&lt;/em&gt; overloads and you’ll get all the goodness of the above queries. There’s also paging overloads with &lt;em&gt;IQueryExecutor&lt;/em&gt; too. To further expand on the above example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-csharp"&gt;@inherits Umbraco.Web.Mvc.UmbracoViewPage
@using Current = Umbraco.Web.Composing.Current;
@{
    // Get the external index with error checking
    if (ExamineManager.Instance.TryGetIndex(
        Constants.UmbracoIndexes.ExternalIndexName, out var index))
    {
        throw new InvalidOperationException(
            $"No index found with name {Constants.UmbracoIndexes.ExternalIndexName}");
    }

    // build an Examine query
    var query = index.GetSearcher().CreateQuery()
        .GroupedOr(new [] { "pageTitle", "pageContent"},
            Request.QueryString["query"].MultipleCharacterWildcard());


    var pageSize = 10;
    var pageIndex = int.Parse(Request.QueryString["page"]);
    var search = Current.PublishedContentQuery.Search(
        query,                  // pass the examine query in!
        pageIndex * pageSize,   // skip
        pageSize,               // take
        out var totalRecords);
}

&amp;lt;div&amp;gt;
    &amp;lt;h3&amp;gt;Search Results&amp;lt;/h3&amp;gt;
    &amp;lt;ul&amp;gt;
        @foreach (var result in search)
        {
            &amp;lt;li&amp;gt;
                Id: @result.Content.Id
                &amp;lt;br/&amp;gt;
                Name: @result.Content.Name
                &amp;lt;br /&amp;gt;
                Score: @result.Score
            &amp;lt;/li&amp;gt;
        }
    &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The base interface of the fluent parts of Examine’s queries are &lt;em&gt;IQueryExecutor&lt;/em&gt; so you can just pass in your query to the method and it will work.&lt;/p&gt;
&lt;h2&gt;Recap&lt;/h2&gt;
&lt;p&gt;The &lt;a rel="noopener" href="https://our.umbraco.com/apidocs/v8/csharp/api/Umbraco.Web.IPublishedContentQuery.html#Umbraco_Web_IPublishedContentQuery_Search_IQueryExecutor_" target="_blank"&gt;IPublishedContentQuery.Search overloads are listed in the API docs&lt;/a&gt;, they are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Search(String term, String culture, String indexName)&lt;/li&gt;
&lt;li&gt;Search(String term, Int32 skip, Int32 take, out Int64 totalRecords, String culture, String indexName)&lt;/li&gt;
&lt;li&gt;Search(IQueryExecutor query)&lt;/li&gt;
&lt;li&gt;Search(IQueryExecutor query, Int32 skip, Int32 take, out Int64 totalRecords)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Should you always use this instead of using Examine directly? As always it just depends on what you are doing. If you need a ton of flexibility with your search results than maybe you want to use Examine’s search results directly but if you want simple and quick access to IPublishedContent results, then these methods will work great.&lt;/p&gt;
&lt;p&gt;Does this all work with &lt;a rel="noopener" href="https://examinex.online/" target="_blank"&gt;ExamineX&lt;/a&gt; ? Absolutely!! One of the best parts of ExamineX is that it’s completely seamless. ExamineX is just an index implementation of Examine itself so all Examine APIs and therefore all Umbraco APIs that use Examine will ‘just work’.&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:10:02 Z</pubDate>
      <a10:updated>2023-03-23T15:10:02Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1252</guid>
      <link>https://shazwazza.com/post/filtering-fields-dynamically-with-examine/</link>
      <category>Umbraco</category>
      <category>Examine</category>
      <title>Filtering fields dynamically with Examine</title>
      <description>&lt;p&gt;The index fields created by Umbraco in Examine by default can lead to quite a substantial amount of fields. This is primarily due in part by how Umbraco handles variant/culture data because it will create a different field per culture but there are other factors as well. Umbraco will create a “__Raw_” field for each rich text field and if you use the grid, it will create different fields for each grid row type. There are good reasons for all of these fields and this allows you by default to have the most flexibility when querying and retrieving your data from the Examine indexes. But in some cases these default fields can be problematic. Examine by default uses Lucene as it’s indexing engine and Lucene itself doesn’t have any hard limits on field count (as far as I know), however if you swap the indexing engine in Examine to something else like &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/azure/search/search-what-is-azure-search" target="_blank"&gt;Azure Search&lt;/a&gt; with &lt;a rel="noopener" href="https://examinex.online/" target="_blank"&gt;ExamineX&lt;/a&gt; then you may find your indexes are exceeding Azure Search’s limits.&lt;/p&gt;
&lt;h2&gt;Azure Search field count limits&lt;/h2&gt;
&lt;p&gt;Azure Search has &lt;a rel="noopener" href="https://docs.microsoft.com/en-us/azure/search/search-limits-quotas-capacity#index-limits" target="_blank"&gt;varying limits for field counts&lt;/a&gt; based on the tier service level you have (strangely the Free tier allows more fields than the Basic tier). The absolute maximum however is 1000 fields and although that might seem like quite a lot when you take into account all of the fields created by Umbraco you might realize it’s not that difficult to exceed this limit. As an example, lets say you have an Umbraco site using language variants and you have 20 languages in use. Then let’s say you have 15 document types each with 5 fields (all with unique aliases) and each field is variant and you have content for each of these document types and languages created. This immediately means you are exceeding the field count limits: 20 x 15 x 10 = 1500 fields! And that’s not including the “__Raw_” fields or the extra grid fields or the required system fields like “id” and “nodeName”. I’m unsure why Azure Search even has this restriction in place&lt;/p&gt;
&lt;h2&gt;Why is Umbraco creating a field per culture?&lt;/h2&gt;
&lt;p&gt;When v8 was being developed a choice had to be made about how to handle multi-lingual data in Examine/Lucene. There’s a couple factors to consider with making this decision which mostly boils down to how Lucene’s analyzers work. The choice is either: language per field or language per index. Some folks might think, can’t we ‘just’ have a language per document? Unfortunately the answer is no because that would require you to apply a specific language analyzer for that document and then scoring would no longer work between documents. &lt;a rel="noopener" href="https://www.elastic.co/guide/en/elasticsearch/guide/current/language-pitfalls.html" target="_blank"&gt;Elastic Search has a good write up about this&lt;/a&gt;. So either language per field or different indexes per language. Each has pros/cons but Umbraco went with language per field since it’s quite easy to setup, supports different analyzers per language and doesn’t require a ton of indexes which also incurs a lot more overhead and configuration.&lt;/p&gt;
&lt;h2&gt;Do I need all of these fields?&lt;/h2&gt;
&lt;p&gt;That really depends on what you are searching on but the answer is most likely ‘no’. You probably aren’t going to be searching on over 1000s fields, but who knows every site’s requirements are different. Umbraco Examine has something called an &lt;em&gt;IValueSetValidator&lt;/em&gt; which you can configure to include/exclude certain fields or document types. This is synonymous with part of the old XML configuration in Examine. This is one of those things where configuration can make sense for Examine and &lt;a rel="noopener" href="https://twitter.com/callumbwhyte" target="_blank"&gt;@callumwhyte&lt;/a&gt; has done exactly that with &lt;a rel="noopener" href="https://github.com/callumbwhyte/umbraco-examine-config" target="_blank"&gt;his package “Umbraco Examine Config”&lt;/a&gt;. But the &lt;em&gt;IValueSetValidator&lt;/em&gt; isn’t all that flexible and works based on exact naming which will work great for filtering content types but perhaps not field names. &lt;em&gt;(Side note – I’m unsure if the Umbraco Examine Config package will work alongside ExamineX, need to test that out).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Since Umbraco creates fields with the same prefixed names for all languages it’s relatively easy to filter the fields based on a matching prefix for the fields you want to keep.&lt;/p&gt;
&lt;h2&gt;Here’s some code!&lt;/h2&gt;
&lt;p&gt;The following code is relatively straight forward with inline comments: A custom class “&lt;em&gt;IndexFieldFilter&lt;/em&gt;” that does the filtering and can be applied different for any index by name, a Component to apply the filtering, a Composer to register services. This code will also ensure that all Umbraco required fields are retained so anything that Umbraco is reliant upon will still work.&lt;/p&gt;
&lt;pre class="lang-csharp"&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Register services
/// &amp;lt;/summary&amp;gt;
public class MyComposer : ComponentComposer&amp;lt;MyComponent&amp;gt;
{
    public override void Compose(Composition composition)
    {
        base.Compose(composition);
        composition.RegisterUnique&amp;lt;IndexFieldFilter&amp;gt;();
    }
}

public class MyComponent : IComponent
{
    private readonly IndexFieldFilter _indexFieldFilter;

    public MyComponent(IndexFieldFilter indexFieldFilter)
    {
        _indexFieldFilter = indexFieldFilter;
    }

    public void Initialize()
    {
        // Apply an index field filter to an index
        _indexFieldFilter.ApplyFilter(
            // Filter the external index 
            Umbraco.Core.Constants.UmbracoIndexes.ExternalIndexName, 
            // Ensure fields with this prefix are retained
            new[] { "description", "title" },
            // optional: only keep data for these content types, else keep all
            new[] { "home" });
    }

    public void Terminate() =&amp;gt; _indexFieldFilter.Dispose();
}

/// &amp;lt;summary&amp;gt;
/// Used to filter out fields from an index
/// &amp;lt;/summary&amp;gt;
public class IndexFieldFilter : IDisposable
{
    private readonly IExamineManager _examineManager;
    private readonly IUmbracoTreeSearcherFields _umbracoTreeSearcherFields;
    private ConcurrentDictionary&amp;lt;string, (string[] internalFields, string[] fieldPrefixes, string[] contentTypes)&amp;gt; _fieldNames
        = new ConcurrentDictionary&amp;lt;string, (string[], string[], string[])&amp;gt;();
    private bool disposedValue;

    /// &amp;lt;summary&amp;gt;
    /// Constructor
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name="examineManager"&amp;gt;&amp;lt;/param&amp;gt;
    /// &amp;lt;param name="umbracoTreeSearcherFields"&amp;gt;&amp;lt;/param&amp;gt;
    public IndexFieldFilter(
        IExamineManager examineManager,
        IUmbracoTreeSearcherFields umbracoTreeSearcherFields)
    {
        _examineManager = examineManager;
        _umbracoTreeSearcherFields = umbracoTreeSearcherFields;
    }

    /// &amp;lt;summary&amp;gt;
    /// Apply a filter to the specified index
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name="indexName"&amp;gt;&amp;lt;/param&amp;gt;
    /// &amp;lt;param name="includefieldNamePrefixes"&amp;gt;
    /// Retain all fields prefixed with these names
    /// &amp;lt;/param&amp;gt;
    public void ApplyFilter(
        string indexName,
        string[] includefieldNamePrefixes,
        string[] includeContentTypes = null)
    {
        if (_examineManager.TryGetIndex(indexName, out var e)
            &amp;amp;&amp;amp; e is BaseIndexProvider index)
        {
            // gather all internal index names used by Umbraco 
            // to ensure they are retained
            var internalFields = new[]
                {
                LuceneIndex.CategoryFieldName,
                LuceneIndex.ItemIdFieldName,
                LuceneIndex.ItemTypeFieldName,
                UmbracoExamineIndex.IconFieldName,
                UmbracoExamineIndex.IndexPathFieldName,
                UmbracoExamineIndex.NodeKeyFieldName,
                UmbracoExamineIndex.PublishedFieldName,
                UmbracoExamineIndex.UmbracoFileFieldName,
                "nodeName"
            }
                .Union(_umbracoTreeSearcherFields.GetBackOfficeFields())
                .Union(_umbracoTreeSearcherFields.GetBackOfficeDocumentFields())
                .Union(_umbracoTreeSearcherFields.GetBackOfficeMediaFields())
                .Union(_umbracoTreeSearcherFields.GetBackOfficeMembersFields())
                .ToArray();

            _fieldNames.TryAdd(indexName, (internalFields, includefieldNamePrefixes, includeContentTypes ?? Array.Empty&amp;lt;string&amp;gt;()));

            // Bind to the event to filter the fields
            index.TransformingIndexValues += TransformingIndexValues;
        }
        else
        {
            throw new InvalidOperationException(
                $"No index with name {indexName} found that is of type {typeof(BaseIndexProvider)}");
        }
    }

    private void TransformingIndexValues(object sender, IndexingItemEventArgs e)
    {
        if (_fieldNames.TryGetValue(e.Index.Name, out var fields))
        {
            // check if we should ignore this doc by content type
            if (fields.contentTypes.Length &amp;gt; 0 &amp;amp;&amp;amp; !fields.contentTypes.Contains(e.ValueSet.ItemType))
            {
                e.Cancel = true;
            }
            else
            {
                // filter the fields
                e.ValueSet.Values.RemoveAll(x =&amp;gt;
                {
                    if (fields.internalFields.Contains(x.Key)) return false;
                    if (fields.fieldPrefixes.Any(f =&amp;gt; x.Key.StartsWith(f))) return false;
                    return true;
                });
            }
        }
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // Unbind from the event for any bound indexes
                foreach (var keys in _fieldNames.Keys)
                {
                    if (_examineManager.TryGetIndex(keys, out var e)
                        &amp;amp;&amp;amp; e is BaseIndexProvider index)
                    {
                        index.TransformingIndexValues -= TransformingIndexValues;
                    }
                }
            }
            disposedValue = true;
        }
    }

    public void Dispose()
    {
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That should give you the tools you need to dynamically filter your index based on fields and content type’s if you need to get your field counts down. This would also be handy even if you aren’t using ExamineX and Azure Search since keeping an index size down and storing less data means less IO operations and storage size.&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:09:58 Z</pubDate>
      <a10:updated>2023-03-23T15:09:58Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1239</guid>
      <link>https://shazwazza.com/post/examine-and-azure-blob-storage/</link>
      <category>Umbraco</category>
      <category>Examine</category>
      <title>Examine and Azure Blob Storage</title>
      <description>&lt;p&gt;Quite some time ago - probably close to 2 years - I created an alpha version of an extension library to Examine to allow storing Lucene indexes in Blob Storage called &lt;em&gt;Examine.AzureDirectory&lt;/em&gt;. This idea isn’t new at all and in fact there’s been a library to do this for many years called &lt;a rel="noopener" href="https://github.com/azure-contrib/AzureDirectory" target="_blank"&gt;AzureDirectory&lt;/a&gt; but it previously had issues and it wasn’t clear on exactly what it’s limitations are. The &lt;a rel="noopener" href="https://github.com/Shazwazza/Examine/tree/master/src/Examine.AzureDirectory" target="_blank"&gt;Examine.AzureDirectory&lt;/a&gt; implementation was built using a lot of the original code of AzureDirectory but has a bunch of fixes (which I contributed back to the project) and different ways of working with the data. Also since Examine 0.1.90 still worked with lucene 2.x, this also made this compatible with the older Lucene version.&lt;/p&gt;
&lt;p&gt;… And 2 years later, I’ve actually released a real version &lt;/p&gt;
&lt;h2&gt;Why is this needed?&lt;/h2&gt;
&lt;p&gt;There’s a couple reasons – firstly Azure web apps storage run on a network share and Lucene absolutely does not like it’s files hosted on a network share, this will bring all sorts of strange performance issues among other things. The way AzureDirectory works is to store the ‘master’ index in Blob Storage and then sync the required Lucene files to the local ‘fast drive’. In Azure web apps there’s 2x drives: ‘slow drive’ (the network share) and the ‘fast drive’ which is the local server’s temp files on local storage with limited space. By syncing the Lucene files to the local fast drive it means that Lucene is no longer operating over a network share. When writes occur, it writes back to the local fast drive and then pushes those changes back to the master index in Blob Storage. This isn’t the only way to overcome this limitation of Lucene, in fact Examine has shipped a work around for many years which uses something called SyncDirectory which does more or less the same thing but instead of storing the master index in Blob Storage, the master index is just stored on the ‘slow drive’.  Someone has actually taken this code and &lt;a rel="noopener" href="https://github.com/yohsii/SyncDirectory" target="_blank"&gt;made a separate standalone project&lt;/a&gt; with this logic called SyncDirectory which is pretty cool!&lt;/p&gt;
&lt;h2&gt;Load balancing/Scaling&lt;/h2&gt;
&lt;p&gt;There’s a couple of ways to work around the network share storage in Azure web apps (as above), but in my opinion the main reason why this is important is for load balancing and being able to scale out. Since Lucene doesn’t work well over a network share, it means that Lucene files must exist local to the process it’s running in. That means that when you are load balancing or scaling out, each server that is handling requests will have it’s own local Lucene index. So what happens when you scale out further and another new worker goes online? This really depending on the hosting application… for example in Umbraco, this would mean that the new worker will create it’s own local indexes by rebuilding the indexes from the source data (i.e. database). This isn’t an ideal scenario especially in Umbraco v7 where requests won’t be served until the index is built and ready. A better scenario is that the new worker comes online and then syncs an existing index from master storage that is shared between all workers …. yes! like Blob Storage.&lt;/p&gt;
&lt;h3&gt;Read/Write vs Read only&lt;/h3&gt;
&lt;p&gt;Lucene can’t be written to concurrently by multiple processes. There are some workarounds here a there to try to achieve this by synchronizing processes with named mutex/semaphore locks and even AzureSearch tries to handle some of this by utilizing Blob Storage leases but it’s not a seamless experience. This is one of the reasons why Umbraco requires a ‘master’ web app for writing and a separate web app for scaling which guarantees that only one process writes to the indexes. This is the setup that &lt;em&gt;Examine.AzureDirectory&lt;/em&gt; supports too and on the front-end/replica/slave web app that scales you will configure the provider to be readonly which guarantees it will never try to write back to the (probably locked) Blob Storage.&lt;/p&gt;
&lt;p&gt;With this in place, when a new front-end worker goes online it doesn’t need to rebuild it’s own local indexes, it will just check if indexes exist and to do that will make sure the master index is there and then continue booting. At this stage there’s actually almost no performance overhead. Nothing actually happens with the local indexes until the index is referenced by this worker and when that happens Examine will lazily just sync the Lucene files that it needs locally.&lt;/p&gt;
&lt;h2&gt;How do I get it?&lt;/h2&gt;
&lt;p&gt;First thing to point out is that this first release is only for Examine 0.1.90 which is for Umbraco v7. Support for Examine 1.x and Umbraco 8.x will come out very soon with some slightly different install instructions.&lt;/p&gt;
&lt;p&gt;The &lt;a rel="noopener" href="https://github.com/Shazwazza/Examine/releases/tag/v0.1.90-azure-directory" target="_blank"&gt;release notes of this are here&lt;/a&gt;, the &lt;a rel="noopener" href="https://github.com/Shazwazza/Examine/wiki/Examine-with-Azure-Directory-(Blob-Storage)" target="_blank"&gt;install docs are here&lt;/a&gt;, and the Nuget package for this can be found &lt;a rel="noopener" href="https://www.nuget.org/packages/Examine.AzureDirectory" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PM&amp;gt; Install-Package Examine.AzureDirectory -Version 0.1.90&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To activate it, you need to add these settings to your web.config&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-xml"&gt;&amp;lt;add key="examine:AzureStorageConnString" value="YOUR-STORAGE-CONNECTION-STRING" /&amp;gt;
&amp;lt;add key="examine:AzureStorageContainer" value="YOUR-CONTAINER-NAME" /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then for your master server/web app you’ll want to add a &lt;em&gt;directoryFactory&lt;/em&gt; attribute to each of your indexers in &lt;em&gt;ExamineSettings.config&lt;/em&gt;, for example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-xml"&gt;&amp;lt;add name="InternalIndexer" type="UmbracoExamine.UmbracoContentIndexer, UmbracoExamine"
      supportUnpublished="true"
      supportProtected="true"
      directoryFactory="Examine.AzureDirectory.AzureDirectoryFactory, Examine.AzureDirectory"
      analyzer="Lucene.Net.Analysis.WhitespaceAnalyzer, Lucene.Net"/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For your front-end/replicate/slave server you’ll want a different readonly value for the &lt;em&gt;directoryFactory&lt;/em&gt; like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-xml"&gt;&amp;lt;add name="InternalIndexer" type="UmbracoExamine.UmbracoContentIndexer, UmbracoExamine"
      supportUnpublished="true"
      supportProtected="true"
      directoryFactory="Examine.AzureDirectory.ReadOnlyAzureDirectoryFactory, Examine.AzureDirectory"
      analyzer="Lucene.Net.Analysis.WhitespaceAnalyzer, Lucene.Net"/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Does it work?&lt;/h2&gt;
&lt;p&gt;Great question :) With the testing that I’ve done it works and I’ve had this running on this site for all of last year without issue but I haven’t rigorously tested this at scale with high traffic sites, etc… I’ve decided to release a real version of this because having this as an alpha/proof of concept means that nobody will test or use it. So now hopefully a few of you will give this a whirl and let everyone know how it goes. Any bugs can be submitted to the &lt;a rel="noopener" href="https://github.com/Shazwazza/Examine" target="_blank"&gt;Examine repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:09:45 Z</pubDate>
      <a10:updated>2023-03-23T15:09:45Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1251</guid>
      <link>https://shazwazza.com/post/examine-hits-rc1/</link>
      <category>Examine</category>
      <title>Examine hits RC1</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;I’m happy to announce that Examine and UmbracoExamine have today hit &lt;a href="http://examine.codeplex.com/releases/view/43099" target="_blank"&gt;RC1&lt;/a&gt;!&lt;a href="http://examine.codeplex.com" target="_blank"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="FileDownload[1]" border="0" alt="FileDownload[1]" align="right" src="/image.axd?picture=WindowsLiveWriter/ExaminehitsRC1/3B4831D4/FileDownload1.png" width="244" height="85"&gt;&lt;/a&gt;  &lt;p&gt;The Codeplex site also has more extensive documentation about how to get UmbracoExamine up and running within your Umbraco website.&lt;/p&gt; &lt;p&gt;&lt;a href="http://examine.codeplex.com/releases/view/43099" target="_blank"&gt;Go, download your copy today.&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:14 Z</pubDate>
      <a10:updated>2023-03-23T15:08:14Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1282</guid>
      <link>https://shazwazza.com/post/examine-s-fluent-search-api/</link>
      <category>Examine</category>
      <title>Examine’s fluent search API</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;As I mentioned in my last blog post we’ve done a lot of work to refactor Examine (and Umbraco Examine) to use a fluent search API rather than a string based search API.&lt;/p&gt;  &lt;p&gt;The primary reason for this was to do with how we were handling the string searching and opening up the Lucene.Net search API. In the initial preview version we would take the text which you entered as a search term and then produce a Lucene.Net search against &lt;em&gt;all the fields in your index&lt;/em&gt;. This is &lt;em&gt;ok&lt;/em&gt;, but it’s not great. The problem came when we wanted to implement a dynamic search query. There were several different search parameters, which were to check against different fields in the index.     &lt;br /&gt;It was sort of possible to achieve this, but you needed to understand the internals of Examine and you also needed to understand the Lucene query language, and also that you couldn’t use the AND/ OR/ NOT operators, you had to use +, – or blank.&lt;/p&gt;  &lt;p&gt;This is fine if you’re into search API’s, but really, how many people are actually like that? Ok, I must admit that I’m rather smitten with Lucene but I’m not exactly a good example of a normal person..&lt;/p&gt;  &lt;p&gt;So I set about addressing this problem, we needed to get a much simpler way in which your &lt;em&gt;average Joe&lt;/em&gt; could come and without knowing the underlying technology write complex and useful search queries.     &lt;br /&gt;For this we’ve build a set of interfaces which you require:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;ISearchCriteria &lt;/li&gt;    &lt;li&gt;IQuery &lt;/li&gt;    &lt;li&gt;IBooleanOperation &lt;/li&gt; &lt;/ul&gt;  &lt;h3&gt;ISearchCriteria&lt;/h3&gt;  &lt;p&gt;The ISearchCriteria interface is the real workhorse of the API, it’s the first interface you start with, and it’s the last interface you deal with. In fact, ISearchCriteria implements IQuery, meaning that all the query operations start here.&lt;/p&gt;  &lt;p&gt;In addition to query operations there are several additional properties for such as the maximum number of results and the type of data being searched.&lt;/p&gt;  &lt;p&gt;Because ISearchCriteria is tightly coupled with the BaseSearchProvider implementation it is actually created via a factory pattern, like so:&lt;/p&gt;  &lt;pre&gt;ISearchCriteria searchCriteria = ExamineManager.Instance.SearchProviderCollection[&amp;quot;MySearcher&amp;quot;].CreateSearchCriteria(100, IndexType.Content);&lt;/pre&gt;

&lt;p&gt;What we’re doing here is requesting that our BaseSearchProvider creates an instance of an ISearchCriteria. It takes two parameters:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;int maxResults &lt;/li&gt;

  &lt;li&gt;Examine.IndexType indexType &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This data can/ should be then used by the search method to return what’s required.&lt;/p&gt;

&lt;h3&gt;IQuery&lt;/h3&gt;

&lt;p&gt;The IQuery interface is really the heart of the fluent API, it’s what you use to construct the search for your site. Since Examine is designed to be technology agnostic the methods which are exposed via IQuery are fairly generic. A lot of the concepts are borrowed from Lucene.Net, but they are fairly generic and should be viable for any searcher.&lt;/p&gt;

&lt;p&gt;The IQuery API exposes the following methods:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;IBooleanOperation Id(int id); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation NodeName(string nodeName); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation NodeName(IExamineValue nodeName); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation NodeTypeAlias(string nodeTypeAlias); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation NodeTypeAlias(IExamineValue nodeTypeAlias); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation ParentId(int id); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation Field(string fieldName, string fieldValue); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation Field(string fieldName, IExamineValue fieldValue); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation MultipleFields(IEnumerable&amp;lt;string&amp;gt; fieldNames, string fieldValue); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation MultipleFields(IEnumerable&amp;lt;string&amp;gt; fieldNames, IExamineValue fieldValue); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation Range(string fieldName, DateTime start, DateTime end); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation Range(string fieldName, DateTime start, DateTime end, bool includeLower, bool includeUpper); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation Range(string fieldName, int start, int end); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation Range(string fieldName, int start, int end, bool includeLower, bool includeUpper); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation Range(string fieldName, string start, string end); &lt;/li&gt;

  &lt;li&gt;IBooleanOperation Range(string fieldName, string start, string end, bool includeLower, bool includeUpper); &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see all the methods within the IQuery interface return an IBooleanOperator, this is how the fluent API works!&lt;/p&gt;

&lt;p&gt;Hopefully it’s fairly obvious what each of the methods are, but the one you’re most likely to use is Field. Field allows you to specify any field in your index, and then provide a word to lookup within that field.&lt;/p&gt;

&lt;h4&gt;IExamineValue&lt;/h4&gt;

&lt;p&gt;You’ve probably noticed the IExamineValue parameter which is passable to a lot of the different methods, methods which take a string, but what is IExamineValue? 
  &lt;br /&gt;Well obviously it’s some-what provider dependant, so I’ll talk about it as part of Umbraco Examine, as that’s what I think most initial uptakers will want.&lt;/p&gt;

&lt;p&gt;Because Lucene supports several different &lt;a href="http://lucene.apache.org/java/2_3_2/queryparsersyntax.html#Term Modifiers" target="_blank"&gt;term modifiers&lt;/a&gt; for text we decided it would be great to have those exposed in the API for people to leverage. For this we’ve got a series of string extension methods which reside in the namespace&lt;/p&gt;

&lt;pre&gt;UmbracoExamine.SearchCriteria&lt;/pre&gt;

&lt;p&gt;So once you add a using statement for that you’ll have the following extension methods:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;public static IExamineValue SingleCharacterWildcard(this string s) &lt;/li&gt;

  &lt;li&gt;public static IExamineValue MultipleCharacterWildcard(this string s) &lt;/li&gt;

  &lt;li&gt;public static IExamineValue Fuzzy(this string s) &lt;/li&gt;

  &lt;li&gt;public static IExamineValue Fuzzy(this string s, double fuzzieness) &lt;/li&gt;

  &lt;li&gt;public static IExamineValue Boost(this string s, double boost) &lt;/li&gt;

  &lt;li&gt;public static IExamineValue Proximity(this string s, double proximity) &lt;/li&gt;

  &lt;li&gt;public static IExamineValue Excape(this string s) &lt;/li&gt;

  &lt;li&gt;public static string Then(this IExamineValue vv, string s) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these (with the exception of Then) return an IExamineValue (which UmbracoExamine internally handles), and it tells Lucene.Net how to handle the term modifier you required.&lt;/p&gt;

&lt;p&gt;I wont repeat what is said within the Lucene documentation, I suggest you read that to get an idea of what to use and when. 
  &lt;br /&gt;The only exceptions are Escape and Then.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Escape&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you’re wanting to search on multiple works &lt;em&gt;together&lt;/em&gt; then Lucene requires them to be ‘escaped’, otherwise it’ll (generally) treat the space character as a break in the query. So if you wanted to search for &lt;em&gt;Umbraco Rocks&lt;/em&gt; and didn’t escape it you’d match on both Umbraco and Rocks, where as when it’s escaped you’ll then match on the two words in sequence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then &lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Then method just allows you to combine multiple strings or multiple IExamineValues, so you can boost your fuzzy query with a proximity of 0.1 :P.&lt;/p&gt;

&lt;h3&gt;&lt;/h3&gt;

&lt;h3&gt;IBooleanOpeation&lt;/h3&gt;

&lt;p&gt;IBooleanOperation allows your to join multiple IQuery methods together using:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;IQuery And() &lt;/li&gt;

  &lt;li&gt;IQuery Or() &lt;/li&gt;

  &lt;li&gt;IQuery Not() &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are then translated into the underlying searcher so it can determine how to deal with your chaining. At the time of writing we don’t support nested conditionals (grouped OR’s operating like an And).&lt;/p&gt;

&lt;p&gt;There’s another method on IBooleanOperation which doesn’t fall into the above, but it’s very critical to the overall idea:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ISearchCriteria Compile() &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Compile method will then return an ISearchCriteria which you then pass into your searcher. It’s expected that this is the last method which is called and it’s meant to prepare all search queries for execution. 
  &lt;br /&gt;The reason we’re going with this rather than passing the IQuery into the Searcher is that it means we don’t have to have the max results/ etc into every IQuery instance, it’s not something that is relevant in that scope, so it’d just introduce code smell, and no one wants that.&lt;/p&gt;

&lt;h3&gt;Bringing it all together&lt;/h3&gt;

&lt;p&gt;So now you know the basics, how do you go about producing a query?&lt;/p&gt;

&lt;p&gt;Well the first thing you need to do is get an instance of your ISearchCriteria:&lt;/p&gt;

&lt;pre&gt;var sc = ExamineManager.Instance.CreateSearchCriteria();&lt;/pre&gt;

&lt;p&gt;Now lets do a search for a few things across a few different fields:&lt;/p&gt;

&lt;pre&gt;var query = sc.NodeName(&amp;quot;umbraco&amp;quot;).And().Field(&amp;quot;bodyText&amp;quot;, &amp;quot;is awesome&amp;quot;.Escape()).Or().Field(&amp;quot;bodyText&amp;quot;, &amp;quot;rock&amp;quot;.Fuzzy());&lt;/pre&gt;

&lt;p&gt;Now we’ve got a query across a few different fields, lastly we need to pass it to our searcher:&lt;/p&gt;

&lt;pre&gt;var results = ExamineManager.Instance.Search(query.Compile());&lt;/pre&gt;

&lt;p&gt;It’s just that simple!&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Hopefully the fluent API is clean enough that people can build nice and complex queries and are able to search their websites with not problem. If you’ve got any feedback please leave it here, as we’re working to get an RC out soon.&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:14 Z</pubDate>
      <a10:updated>2023-03-23T15:08:14Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1298</guid>
      <link>https://shazwazza.com/post/examine-rc2-posted/</link>
      <category>Examine</category>
      <title>Examine RC2 posted</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;I’ve just released Examine RC2 into the while, you can &lt;a href="http://examine.codeplex.com/releases/view/43765" target="_blank"&gt;&lt;font size="6"&gt;download&lt;/font&gt;&lt;/a&gt; it from our &lt;a href="http://examine.codeplex.com" target="_blank"&gt;CodePlex&lt;/a&gt; site.  &lt;p&gt;RC2 fixes a bug in RC1 which wasn’t indexing user fields, only attribute fields.&lt;/p&gt; &lt;p&gt;There’s a few breaking changes with RC2:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;IQuery.MultipleFields has been removed. Use IQuery.GroupedAnd, IQuery.GroupedOr, IQuery.GroupedNot or IQuery.GroupedFlexible to define how multiple fields are added  &lt;li&gt;ISearchCriteria.RawQuery added which allows you to pass a raw query string to the underlying provider  &lt;li&gt;ISearcher.Search returns a new interface ISearchResults (which inherits IEnumerable&amp;lt;SearchResult&amp;gt;)  &lt;li&gt;New interface ISearchResults which exposes a Skip to support paging and TotalItemCount &lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Will be working on more documentation to explain some of the newly added and obscure features shortly :P.&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:14 Z</pubDate>
      <a10:updated>2023-03-23T15:08:14Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1217</guid>
      <link>https://shazwazza.com/post/examine-but-not-as-you-knew-it/</link>
      <category>Umbraco</category>
      <category>Examine</category>
      <title>Examine, but not as you knew it</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;Almost 12 months ago Shannon &lt;a href="http://www.farmcode.org/post/2009/04/20/Umbraco-Examine-v4x-Powerful-Umbraco-Indexing.aspx" target="_blank"&gt;blogged&lt;/a&gt; about &lt;a href="http://www.farmcode.org/page/Umbraco-Examine.aspx" target="_blank"&gt;Umbraco Examine&lt;/a&gt; a Lucene.NET indexer which works nicely with Umbraco 4.x. Since then we’ve done quite a bit of work on Examine, and as people will may be aware we’ve integrated Examine into the Umbraco core and it will be shipped out of the box with Umbraco 4.1.&lt;/p&gt;  &lt;p&gt;Something Shannon and I had discussed a few times was that we wanted to decouple Examine from Umbraco so it could be used for indexing on sites other than Umbraco.    &lt;br /&gt;You’ll also notice that I keep referring to it as Examine, not Umbraco Examine which most people are more familiar with.     &lt;br /&gt;This is because over the last week we have achieved what we’d wanted to do, we’ve decoupled Examine from Umbraco!&lt;/p&gt;  &lt;h3&gt;So what’s Examine?&lt;/h3&gt;  &lt;p&gt;Examine is a provider based, config driven search and indexer framework. Examine provides all the methods required for indexing and searching any data source you want to use.&lt;/p&gt;  &lt;p&gt;Examine is now agnostic of the indexer/ searcher API, as well as the data source. That’s right Examine has no references within itself to Umbraco, nor does it have any references to Lucene.NET.    &lt;br /&gt;We have still maintained a usage of XML internally for passing the data-to-index around, as it’s the easiest construct which we could think to work with and pass around.&lt;/p&gt;  &lt;p&gt;You could implement the Examine framework in any solution, to index any data you want, it could be from a SQL server, or it could be from web-scraped content.&lt;/p&gt;  &lt;h3&gt;Where does that leave Umbraco Examine?&lt;/h3&gt;  &lt;p&gt;Umbraco Examine still exists, in fact it’s the primary (and currently only) implementer of Examine. Over the last week though we’ve done a lot of refactoring of Umbraco Examine to work with some changes we’ve done to the underlying Examine API.&lt;/p&gt;  &lt;h3&gt;Changes? What changes?&lt;/h3&gt;  &lt;p&gt;Last week anyone who follows me on &lt;a href="http://twitter.com/slace" target="_blank"&gt;Twitter&lt;/a&gt; will have seen a lot of tweets around Umbraco Examine which was about a new search API and the breaking changes we were implementing.&lt;/p&gt;  &lt;p&gt;While looking to refactor the underlying API of a large Umbraco site we have running I found that Examine was actually not properly designed if you wanted to search for data in specific fields, or build complex search queries.&lt;/p&gt;  &lt;p&gt;This was a real bugger, I had many different parameters I needed to optionally search on, and only in certain fields, but since Umbraco Examine works with just a raw string this wasn’t possible.&lt;/p&gt;  &lt;p&gt;So I set about creating a new &lt;em&gt;fluent search API. &lt;/em&gt;This has actually turned out quite well, in fact so well that we new have this as the recommended search method, not raw text (which is still available).&lt;/p&gt;  &lt;p&gt;The fluent API is part of the Examine API so it’s also available for any implementation, not just Umbraco! Since we’ve used Lucene.NET as the initial support model the API is designed similarly to what you’d expect from Lucene.NET, but we hope that it’s generic enough to look and feel right for any indexer/ searcher.&lt;/p&gt;  &lt;p&gt;Here’s how the fluent API looks:&lt;/p&gt;  &lt;pre&gt;searchCriteria
.Id(1080)
.Or()
.Field(&amp;quot;headerText&amp;quot;, &amp;quot;umb&amp;quot;.Fuzzy())
.And()
.NodeTypeAlias(&amp;quot;cws&amp;quot;.MultipleCharacterWildcard())
.Not()
.NodeName(&amp;quot;home&amp;quot;);&lt;/pre&gt;

&lt;p&gt;All you have to do is pass that into your searcher. That easy, and that beautiful. I’ll do a blog post where we’ll look more deeply into the fluent API separately.&lt;/p&gt;

&lt;p&gt;Additionally we’ve done some other changes, because of what the framework new is we’ve renamed our assemblies and namespaces:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Examine.dll&lt;/li&gt;

  &lt;ul&gt;
    &lt;li&gt;This was formally UmbracoExamine.Core.dll&lt;/li&gt;

    &lt;li&gt;Root namespace Examine&lt;/li&gt;

    &lt;li&gt;Contains all the classes and methods to create your own indexer and searcher&lt;/li&gt;
  &lt;/ul&gt;

  &lt;li&gt;UmbracoExamine.dll&lt;/li&gt;

  &lt;ul&gt;
    &lt;li&gt;This was formally UmbracoExamine.Providers.dll&lt;/li&gt;

    &lt;li&gt;Root namespace UmbracoExamine.dll&lt;/li&gt;

    &lt;li&gt;Contains all the classes and methods of an Umbraco &amp;amp; Lucene.NET &lt;/li&gt;
  &lt;/ul&gt;
&lt;/ul&gt;

&lt;p&gt;Apologies to any existing implementations of Umbraco Examine, this will result in breaking changes but since we’ve not hit RC yet too bad :P.&lt;/p&gt;

&lt;p&gt;There are also some changes to the config, &amp;lt;IndexUserFields /&amp;gt; has become &amp;lt;IndexStandardFields /&amp;gt;, and obviously the config registrations are different with the assembly and namspace changes.&lt;/p&gt;

&lt;p&gt;The last change is that we’ve moved to the &lt;a href="http://www.opensource.org/licenses/ms-pl.html" target="_blank"&gt;Ms-PL license&lt;/a&gt; for Examine, whos source is available on &lt;a href="http://umbracoexamine.codeplex.com" target="_blank"&gt;codeplex&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Currently we’re working to tidy up the API and the documentation so that we can get the RC release out shortly, so watch this space.&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:14 Z</pubDate>
      <a10:updated>2023-03-23T15:08:14Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1219</guid>
      <link>https://shazwazza.com/post/examine-s-fluent-search-api-elevator-pitch/</link>
      <category>Examine</category>
      <title>Examine’s Fluent Search API – Elevator Pitch</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;I realised that with &lt;a href="/post/2010/03/25/Examinee28099s-fluent-search-API.aspx" target="_blank"&gt;my blog post about Examine&lt;/a&gt; it was fairly in-depth and a lot of people were probably bored before they got to the good bits about how easy searching can be.     &lt;br /&gt;So I decided that a smaller, more concise post was in order.&lt;/p&gt;  &lt;h3&gt;What?&lt;/h3&gt;  &lt;p&gt;The Fluent Search API is a chainable (like jQuery) API for building complex searches for a data source, in this case Umbraco. It doesn’t require you to know any “search language”, it just works via standard .NET style method calls, with intellisense to help guide you along the way.&lt;/p&gt;  &lt;h3&gt;How?&lt;/h3&gt;  &lt;p&gt;This is achieved by combining &lt;em&gt;IQuery&lt;/em&gt; methods (search methods) with &lt;em&gt;IBooleanOperation&lt;/em&gt; methods (And, Or, Not) to produce something cool. For example:&lt;/p&gt;  &lt;pre&gt;var query = sc
	.NodeName(&amp;quot;umbraco&amp;quot;)
	.And()
	.Field(&amp;quot;bodyText&amp;quot;, &amp;quot;is awesome&amp;quot;.Escape())
	.Or()
	.Field(&amp;quot;bodyText&amp;quot;, &amp;quot;rock&amp;quot;.Fuzzy()); &lt;/pre&gt;

&lt;p&gt;&lt;em&gt;Examineness&lt;/em&gt; can be implemented to do special things to search text, like making it a wild card query, or escaping several terms to have them used as a search sentence.&lt;/p&gt;

&lt;h3&gt;&amp;#160;&lt;/h3&gt;

&lt;p&gt;Hopefully this more direct post will engage your attention better and make you want more Examine sexiness.&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:14 Z</pubDate>
      <a10:updated>2023-03-23T15:08:14Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1228</guid>
      <link>https://shazwazza.com/post/how-to-build-a-search-query-in-examine/</link>
      <category>Examine</category>
      <title>How to build a search query in Examine</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;Now that Examine is able to be used by a &lt;a href="http://farmcode.org/post/2010/08/10/Using-Examine-to-index-search-with-ANY-data-source.aspx"&gt;wider audience than Umbraco&lt;/a&gt; understanding how search works is possibly a bit more important ;).  &lt;p&gt;So today while &lt;a href="http://our.umbraco.org/forum/developers/extending-umbraco/11608-Examine-results-doesn't-match-what-Luke-says?p=0#comment42851"&gt;answering a question&lt;/a&gt; on the Umbraco forum I thought that what I was going on about is something that more people might want to hear about. And really, I do like the sound of my own (virtual) voice…&lt;/p&gt; &lt;h2&gt;Understanding Lucene.Net from Examine&lt;/h2&gt; &lt;p&gt;For this I’m going to be looking at the Lucene.Net implementation of Examine, and this is agnostic of whether you’re using UmbracoExamine or Examine.LuceneEngine.&lt;/p&gt; &lt;p&gt;To get started you should familiarize yourself a bit with the Lucene &lt;a href="http://lucene.apache.org/java/2_3_2/queryparsersyntax.html"&gt;Query Parser Syntax&lt;/a&gt;, as that’s what we’re using internally to get the data back from Lucene.Net.&lt;/p&gt; &lt;p&gt;Also, we’re going to be working with the Fluent API for Examine, so fell free to read up on that &lt;a href="http://farmcode.org/post/2010/03/25/Examinee28099s-fluent-search-API.aspx"&gt;here&lt;/a&gt; and &lt;a href="http://farmcode.org/post/2010/03/31/Examinee28099s-Fluent-Search-API-e28093-Elevator-Pitch.aspx"&gt;here&lt;/a&gt;. With Examine we’ve made it easy to see what the Lucene query you’re building up is as you’re working with the Fluent API, in fact if you to a .ToString() call on the ISearchCriteria instance you’ll be able to see what you’re search query looks like (we’ve also got some other information in the result of that method call too).&lt;/p&gt; &lt;p&gt;So you can see what’s been generated, let’s build a query and dissect it:&lt;/p&gt;&lt;pre&gt;var criteria = searcher.CreateSearchCriteria(IndexTypes.Content);
criteria = criteria.NodeName("Hello").And().NodeTypeAlias("world").Compile();

Console.WriteLine(criteria.ToString()); //+(+nodeName:Hello +nodeTypeAlias:world) +__IndexType:content&lt;/pre&gt;
&lt;p&gt;So this is what we've got, a total of three conditions… But wait, there’s only two conditions that I entered right?&lt;/p&gt;
&lt;p&gt;Not quite, Examine has some smarts built into it around what you’re searching on and it’ll add that restriction on your behalf. This is so you don’t get results back from a different index type in your query. &lt;em&gt;Note: If you don’t specify the IndexType then this condition wont be added for your.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Also, to ensure that all your entered queries don’t get killed by the IndexType (a problem in earlier Examine builds) we combine everything you enter into a &lt;em&gt;GroupedAnd&lt;/em&gt; statement.&lt;/p&gt;
&lt;p&gt;Let’s have a look at the parts we did request, and how they are comprised:&lt;/p&gt;&lt;pre&gt;+nodeName:Hello +nodeTypeAlias:world&lt;/pre&gt;
&lt;p&gt;Ok, so what we've got here is our two conditions which we've added using Examine. Each is an AND (or in Lucene terminology SHOULD) and this is denoted by the &lt;strong&gt;+&lt;/strong&gt;. Next we have a &lt;strong&gt;field name&lt;/strong&gt;, in this case either &lt;em&gt;nodeName&lt;/em&gt; or &lt;em&gt;nodeTypeAlias&lt;/em&gt;. Youl’ll notice back in our Fluent API query we actually generated all of that using the build in methods, rather than having to use more magic strings. Next there is a &lt;strong&gt;:&lt;/strong&gt; to indicate where the field name ends. Lastly we have the &lt;strong&gt;term&lt;/strong&gt; which we’re going to search against, either &lt;em&gt;Hello&lt;/em&gt; or &lt;em&gt;world&lt;/em&gt;. &lt;/p&gt;
&lt;p&gt;So essentially it’s built up of &lt;strong&gt;BOOLEAN_OPERATION&amp;amp;FieldName:Term&lt;/strong&gt; (the &amp;amp; is so it’s slightly readable).&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This was a brief look at how the Fluent API for Examine will turn your typed query into a Lucene query that is then searching.&lt;/p&gt;
&lt;p&gt;With this knowledge you should be better able to design complex queries, use mixed conditionals and just plain go crazy.&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:13 Z</pubDate>
      <a10:updated>2023-03-23T15:08:13Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1152</guid>
      <link>https://shazwazza.com/post/paging-with-examine/</link>
      <category>Examine</category>
      <title>Paging with Examine</title>
      <description>&lt;p&gt;Paging with Lucene and Examine requires some specific API usage. It's very easy to get wrong by using Linq's Skip/Take methods and when doing this you'll inadvertently end up loading in all search results from Lucene and then filtering in memory when what you really want to do is have Lucene only create the minimal search result objects that you are interested in.&lt;/p&gt;
&lt;p&gt;There are 2 important parts to this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://github.com/Shazwazza/Examine/blob/master/src/Examine/ISearchResults.cs#L11"&gt;&lt;strong&gt;Skip&lt;/strong&gt; &lt;/a&gt;method on the ISearchResults object&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://github.com/Shazwazza/Examine/blob/master/src/Examine/Providers/BaseSearchProvider.cs#L22"&gt;Search &lt;/a&gt;overload on the BaseSearchProvider where you can specify &lt;strong&gt;maxResults&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ISearchResults.Skip&lt;/h2&gt;
&lt;p&gt;This is very different from the Linq Skip method so you need to be sure you are using the Skip method on the ISearchResults object. This tells Lucene to skip over a specific number of results without allocating the result objects. If you use Linq’s Skip method on the underlying IEnumerable&amp;lt;SearchResult&amp;gt; of ISearchResults, this will allocate all of the result objects and then filter them in memory which is what you don’t want to do.&lt;/p&gt;
&lt;h2&gt;Search with maxResults&lt;/h2&gt;
&lt;p&gt;Lucene isn’t perfect for paging because it doesn’t natively support the Linq equivalent to “Skip/Take”. It understands Skip (as above) but doesn’t understand Take, instead it only knows how to limit the max results so that it doesn’t allocate every result, most of which you would probably not need when paging.&lt;/p&gt;
&lt;p&gt;With the combination of ISearchResult.Skip and maxResults, we can tell Lucene to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Skip over a certain number of results without allocating them and tell Lucene&lt;/li&gt;
&lt;li&gt;only allocate a certain number of results after skipping&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Show me the code&lt;/h2&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;//for example purposes, we want to show page #4 (which is pageIndex of 3)
var pageIndex = 3;   
//for this example, the page size is 10 items
var pageSize = 10;
var searchResult = searchProvider.Search(criteria, 
   //don't return more results than we need for the paging
   //this is the 'trick' - we need to load enough search results to fill
   //all pages from 1 to the current page of 4
   maxResults: pageSize*(pageIndex + 1));
//then we use the Skip method to tell Lucene to not allocate search results
//for the first 3 pages
var pagedResults = searchResult.Skip(pageIndex*pageSize);
var totalResults = searchResult.TotalItemCount;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So that is the correct way to do paging with Examine and Lucene which ensures max performance and minimal object allocations.&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:13 Z</pubDate>
      <a10:updated>2023-03-23T15:08:13Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1311</guid>
      <link>https://shazwazza.com/post/using-examine-to-index-search-with-any-data-source/</link>
      <category>Examine</category>
      <title>Using Examine to index &amp; search with ANY data source</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt; &lt;p&gt;During &lt;a title="CodeGarden 2010" href="http://codegarden10.com/" target="_blank"&gt;CodeGarden 2010&lt;/a&gt; a few people were asking how to use &lt;a title="Examine" href="http://farmcode.org/page/Umbraco-Examine.aspx" target="_blank"&gt;Examine&lt;/a&gt; to index and search on data from any data source such as custom database tables, etc… Previously, the only way to do this was to override the Umbraco Examine indexing provider, remove the Umbraco functionality embedded in there, and then do a lot of coding yourself.&amp;nbsp; …But now there’s some great news! As of now you can use all of the Examine goodness with it’s embedded &lt;a title="Lucene.Net" href="http://www.lucene.net/" target="_blank"&gt;Lucene.Net&lt;/a&gt; with any data source and you can do it VERY easily. &lt;/p&gt; &lt;p&gt;Some things you need to know about the new version:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;I haven’t made a release version of this yet as it still needs some more testing, though we are putting this into a production site next week.  &lt;li&gt;If you want to try this, currently you’ll need to get the latest source from &lt;a title="Examine @ CodePlex" href="http://examine.codeplex.com" target="_blank"&gt;Examine @ CodePlex&lt;/a&gt;  &lt;li&gt;If you are using a previous version of Examine, there’s a few breaking changes as some of the class structures have been moved, however you config file should still work as is… HOWEVER, you should update your config file to reflect the new one with the new class names  &lt;li&gt;There is now 3 DLLs, not just 2:  &lt;ul&gt; &lt;li&gt;Examine.DLL  &lt;ul&gt; &lt;li&gt;Still pretty much the same… contains the abstraction layer &lt;/li&gt;&lt;/ul&gt; &lt;li&gt;Examine.LuceneEngine.DLL  &lt;ul&gt; &lt;li&gt;The new DLL to use to work with data that is not Umbraco specific &lt;/li&gt;&lt;/ul&gt; &lt;li&gt;UmbracoExamine.DLL  &lt;ul&gt; &lt;li&gt;The DLL that the Umbraco providers are in &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Ok, now on to the good stuff. First, I’ve added a demo project to this post which you can download &lt;a title="Examine Demo App" href="/examinedemoapp/examinedemo.zip" target="_blank"&gt;&lt;strong&gt;&lt;font size="5"&gt;HERE&lt;/font&gt;&lt;/strong&gt;&lt;/a&gt;. This project is a simple console app that contains a sample XML data file that has 5 records in it. Here’s what the app does:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;This re-indexes all data  &lt;li&gt;Searches the index for node id 1  &lt;li&gt;Ensures one record is found in the index  &lt;li&gt;Updates the dateUpdated time stamp for the data record  &lt;li&gt;Re-indexes the record with node id 1’ &lt;/li&gt;&lt;/ol&gt; &lt;p&gt;So assuming that you have some custom data like a custom database table, xml file, or whatever, there’s really only 3 things that you need to do to get Examine indexing your custom data:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Create your own &lt;em&gt;ISimpleDataService &lt;/em&gt; &lt;ul&gt; &lt;li&gt;There is only 1 method to implement: &lt;em&gt;IEnumerable&amp;lt;SimpleDataSet&amp;gt; GetAllData(string indexType)&lt;/em&gt;  &lt;li&gt;This is the method that Examine will call to re-index your data  &lt;li&gt;A &lt;em&gt;SimpleDataSet &lt;/em&gt;is a simple object containing a &lt;em&gt;Dictionary&amp;lt;string, string&amp;gt;&lt;/em&gt; and a &lt;em&gt;IndexedNode&lt;/em&gt; object (which consists of a Node Id and a Node Type)  &lt;li&gt;For example, if you had a database row, your SimpleDataSet object for the row would be the dictionary of the rows values, it’s node id and type … easy. &lt;/li&gt;&lt;/ul&gt; &lt;li&gt;Use the &lt;em&gt;ToExamineXml()&lt;/em&gt; extension method to re-index individual nodes/records  &lt;ul&gt; &lt;li&gt;Examine relies on data being in the same XML structure as Umbraco (which we might change in version 2 sometime in the future… like next year) so we need to transform simple data into the XML structure. We’ve made this quite easy for you; all you have to do is get the data from your custom data source into a &lt;em&gt;Dictionary&amp;lt;string, string&amp;gt;&lt;/em&gt; object and use this extension method to pass the xml structure in to Examine’s &lt;em&gt;ReIndexNode&lt;/em&gt; method.  &lt;li&gt;For example: &lt;em&gt;ExamineManager.Instance.ReIndexNode(dataSet.ToExamineXml(dataSet["Id"], "CustomData"), "CustomData");&amp;nbsp; &lt;/em&gt;where dataSet is a &lt;em&gt;Dictionary&amp;lt;string, string&amp;gt;&lt;/em&gt; . &lt;/li&gt;&lt;/ul&gt; &lt;li&gt;Update your Examine config to use the new &lt;em&gt;SimpleDataIndexer&lt;/em&gt; index provider and the new &lt;em&gt;LuceneSearcher &lt;/em&gt;search provider &lt;/li&gt;&lt;/ol&gt; &lt;p&gt;If you’re not using Umbraco at all, then you’ll only need to have the 2 Examine DLLs which don’t reference the Umbraco DLLs whatsoever so everything is decoupled.&lt;/p&gt; &lt;p&gt;I’d recommend downloading the demo app and running it as it will show you everything you need to know on how to get Examine running with custom data. However, i know that people just like to see code in blog posts, so here’s the config for the demo app:&lt;/p&gt;   &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:18fee6e6-5c56-4986-9781-fac4483ebbbe" class="wlWriterSmartContent"&gt;&lt;pre style="background-color: white; width: 579px; height: 742px; overflow: auto"&gt;&lt;div&gt;&lt;!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;?&lt;/span&gt;&lt;span style="color: #ff00ff"&gt;xml version="1.0" encoding="utf-8" &lt;/span&gt;&lt;span style="color: #0000ff"&gt;?&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;configuration&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;

  &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;configSections&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section &lt;/span&gt;&lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="Examine"&lt;/span&gt;&lt;span style="color: #ff0000"&gt; type&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="Examine.Config.ExamineSettings, Examine"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;section &lt;/span&gt;&lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="ExamineLuceneIndexSets"&lt;/span&gt;&lt;span style="color: #ff0000"&gt; 
             type&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="Examine.LuceneEngine.Config.IndexSets, Examine.LuceneEngine"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
  &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;configSections&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;

  &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;Examine&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;ExamineIndexProviders&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
      &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;providers&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;

        &lt;/span&gt;&lt;span style="color: #008000"&gt;&amp;lt;!--&lt;/span&gt;&lt;span style="color: #008000"&gt; 
        Define the indexer for our custom data.
        Since we're only indexing one type of data, there's 
        only 1 indexType specified: 'CustomData', however
        if you have more than one type of index (i.e. Media, Content) 
        then you just need to list them as a comma seperated list without spaces.
        
        The dataService is how Examine queries whatever data source you have, 
        in this case it's a custom data service defined in this project.
        A custom data service only has to implement one method... very easy.        
        &lt;/span&gt;&lt;span style="color: #008000"&gt;--&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
        &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;add &lt;/span&gt;&lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="CustomIndexer"&lt;/span&gt;&lt;span style="color: #ff0000"&gt;
             type&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="Examine.LuceneEngine.Providers.SimpleDataIndexer, 
                Examine.LuceneEngine"&lt;/span&gt;&lt;span style="color: #ff0000"&gt;
             dataService&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="ExamineDemo.CustomDataService, ExamineDemo"&lt;/span&gt;&lt;span style="color: #ff0000"&gt;
             indexTypes&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="CustomData"&lt;/span&gt;&lt;span style="color: #ff0000"&gt;
             runAsync&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="false"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;

      &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;providers&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;ExamineIndexProviders&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;ExamineSearchProviders &lt;/span&gt;&lt;span style="color: #ff0000"&gt;defaultProvider&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="CustomSearcher"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
      &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;providers&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
     
        &lt;/span&gt;&lt;span style="color: #008000"&gt;&amp;lt;!--&lt;/span&gt;&lt;span style="color: #008000"&gt; 
        A search provider that can query a lucene index, no other 
        work is required here 
        &lt;/span&gt;&lt;span style="color: #008000"&gt;--&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
        &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;add &lt;/span&gt;&lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="CustomSearcher"&lt;/span&gt;&lt;span style="color: #ff0000"&gt;
             type&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="Examine.LuceneEngine.Providers.LuceneSearcher, 
                Examine.LuceneEngine"&lt;/span&gt;&lt;span style="color: #ff0000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;

      &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;providers&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;ExamineSearchProviders&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
  &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;Examine&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;

  &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;ExamineLuceneIndexSets&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;

    &lt;/span&gt;&lt;span style="color: #008000"&gt;&amp;lt;!--&lt;/span&gt;&lt;span style="color: #008000"&gt; Create an index set to hold the data for our index &lt;/span&gt;&lt;span style="color: #008000"&gt;--&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;IndexSet &lt;/span&gt;&lt;span style="color: #ff0000"&gt;SetName&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="CustomIndexSet"&lt;/span&gt;&lt;span style="color: #ff0000"&gt; 
              IndexPath&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="App_Data\CustomIndexSet"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
      &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;IndexUserFields&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;        
        &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;add &lt;/span&gt;&lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="name"&lt;/span&gt;&lt;span style="color: #ff0000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
        &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;add &lt;/span&gt;&lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="description"&lt;/span&gt;&lt;span style="color: #ff0000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
        &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;add &lt;/span&gt;&lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="dateUpdated"&lt;/span&gt;&lt;span style="color: #ff0000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
      &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;IndexUserFields&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;IndexSet&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
    
  &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;ExamineLuceneIndexSets&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;

&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;configuration&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:13 Z</pubDate>
      <a10:updated>2023-03-23T15:08:13Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1241</guid>
      <link>https://shazwazza.com/post/examine-slide-deck-for-codegarden-2010/</link>
      <category>Umbraco</category>
      <category>Examine</category>
      <title>Examine slide deck for CodeGarden 2010</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;A few people had asked during &lt;a href="http://www.codegarden10.com/" target="_blank"&gt;CodeGarden 2010&lt;/a&gt; if I would post up the slide deck for my &lt;a href="http://farmcode.org/page/Umbraco-Examine.aspx" target="_blank"&gt;Examine&lt;/a&gt; presentation, so here it is. There’s not a heap of information in there since i think people would have soaked up most of the info during the examples and coding demos but it’s posted here regardless and hopefully it helps a few people.  &lt;p&gt;I’ve included a PDF version (link at the bottom) and also the image version below (if you’re too lazy to download it :)&lt;/p&gt; &lt;p&gt;&lt;a href="/image.axd?picture=Slide2.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide2" border="0" alt="Slide2" src="/image.axd?picture=Slide2_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide3.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide3" border="0" alt="Slide3" src="/image.axd?picture=Slide3_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide4.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide4" border="0" alt="Slide4" src="/image.axd?picture=Slide4_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide5.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide5" border="0" alt="Slide5" src="/image.axd?picture=Slide5_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide6.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide6" border="0" alt="Slide6" src="/image.axd?picture=Slide6_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide7.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide7" border="0" alt="Slide7" src="/image.axd?picture=Slide7_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide8.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide8" border="0" alt="Slide8" src="/image.axd?picture=Slide8_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide9.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide9" border="0" alt="Slide9" src="/image.axd?picture=Slide9_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide10.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide10" border="0" alt="Slide10" src="/image.axd?picture=Slide10_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide11.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide11" border="0" alt="Slide11" src="/image.axd?picture=Slide11_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide12.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide12" border="0" alt="Slide12" src="/image.axd?picture=Slide12_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide13.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide13" border="0" alt="Slide13" src="/image.axd?picture=Slide13_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide14.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide14" border="0" alt="Slide14" src="/image.axd?picture=Slide14_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt; &lt;a href="/image.axd?picture=Slide15.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Slide15" border="0" alt="Slide15" src="/image.axd?picture=Slide15_thumb.jpg" width="244" height="184"&gt;&lt;/a&gt;&lt;/p&gt; &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:1b5d2913-dbe5-46b3-ba32-d697014fd1f3" class="wlWriterSmartContent"&gt; &lt;p&gt;Download slide deck &lt;a href="http://farmcode.org/file.axd?file=Examine2_1.pdf" target="_blank"&gt;here&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:13 Z</pubDate>
      <a10:updated>2023-03-23T15:08:13Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1316</guid>
      <link>https://shazwazza.com/post/examine-v10-rtm/</link>
      <category>Examine</category>
      <title>Examine v1.0 RTM</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;We finally released Examine version 1.0 a week or so ago. You can find the latest download package from the CodePlex downloads page for Examine: &lt;a title="http://examine.codeplex.com/releases/view/50781" href="http://examine.codeplex.com/releases/view/50781"&gt;http://examine.codeplex.com/releases/view/50781&lt;/a&gt;&amp;nbsp; &lt;h2&gt;Here’s what you’ll need to know&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;There are some breaking changes from the version that is shipped with &lt;a title="Umbraco" href="http://umbraco.org" target="_blank"&gt;Umbraco&lt;/a&gt; 4.5 and also from the Examine RC3 release. The downloads tab on CodePlex contains the &lt;a title="Release Notes" href="http://examine.codeplex.com/releases/view/50781#DownloadId=153823" target="_blank"&gt;Release Notes&lt;/a&gt; for download which contains all of the information on upgrading &amp;amp; breaking changes  &lt;li&gt;READ THE RELEASE NOTES BEFORE UPGRADING  &lt;li&gt;There’s a ton of bugs fixed in this release from the version shipped with Umbraco 4.5  &lt;li&gt;Lots of new features have been added:  &lt;ul&gt; &lt;li&gt;Indexing ANY type of data easily using the LuceneEngine index/search providers  &lt;li&gt;PDF Indexing for Umbraco  &lt;li&gt;XSLT extensions for Umbraco  &lt;li&gt;Data Type declarations for indexed fields  &lt;li&gt;Date &amp;amp; Number range searching &lt;/li&gt;&lt;/ul&gt; &lt;li&gt;New &lt;a title="Documentation" href="http://examine.codeplex.com/documentation" target="_blank"&gt;documentation&lt;/a&gt; has been added to CodePlex &lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;Using v1.0 RTM on Umbraco 4.5&lt;/h2&gt; &lt;p&gt;The upgrade process from the Examine version shipped with 4.5 to v1.0 RTM should be pretty seamless (unless you are using some specific API calls as noted in the release notes). However, once you drop in the new DLLs you’ll probably notice that the &lt;em&gt;internal search no longer works&lt;/em&gt;. This is due to a bug in the Umbraco 4.5. codebase and an non-optimal implementation of Examine which has to do with case sensitivity for application aliases (i.e. &lt;em&gt;&lt;strong&gt;C&lt;/strong&gt;ontent&lt;/em&gt; vs &lt;em&gt;&lt;strong&gt;c&lt;/strong&gt;ontent&lt;/em&gt; ). The work-around is simple though: all we need to do is change the Analyzer used for the internal searcher in the Examine configuration file to use the StandardAnalyzer instead of the WhitespaceAnalyzer. This is because the WhitespaceAnalyzer is case sensitive whereas the StandardAnalyzer is not. This issue is fixed in Umbraco Juno (4.6) and will continue to use the WhitespaceAnalyzer so that Examine doesn’t tokenize strings that contain punctuation. For more info on Analyzers, have a look at &lt;a title="Lucene Analyzers" href="http://www.aaron-powell.com/lucene-analyzer" target="_blank"&gt;Aaron’s post&lt;/a&gt;.&lt;/p&gt; &lt;h2&gt;Next Versions&lt;/h2&gt; &lt;p&gt;There probably won’t be too many more changes coming for Examine v1.0 apart from any bug fixing that needs to be done and maybe some tweaks to the Fluent API. We will start working on v2.0 at some point this year or early next year which will take Examine to the next level. It will be less focused on configuration, have a smaller foot print and be much more configurable through code (such as how ASP.Net MVC works). &lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:09 Z</pubDate>
      <a10:updated>2023-03-23T15:08:09Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1161</guid>
      <link>https://shazwazza.com/post/examine-output-indexing/</link>
      <category>Examine</category>
      <title>Examine output indexing</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;Last week Pete Gregory (&lt;a href="http://twitter.com/pgregorynz" target="_blank"&gt;@pgregorynz&lt;/a&gt;) and I were discussing different implementations of Examine. Particularly when you need to use Examine events to collate information from different nodes to put into the index for the page being rendered. An example of this is an FAQ engine where you might have an &lt;a href="http://umbraco.org" target="_blank"&gt;Umbraco&lt;/a&gt; content structure such as:  &lt;ul&gt; &lt;li&gt;Site Container  &lt;ul&gt; &lt;li&gt;Public  &lt;ul&gt; &lt;li&gt;FAQs  &lt;ul&gt; &lt;li&gt;FAQ Item 1  &lt;li&gt;FAQ Item 2  &lt;li&gt;FAQ Item 3 &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;In this example, the page that is rendered to the end user is FAQs but the data from all 4 nodes (FAQs, FAQ Item 1 –&amp;gt; 4) needs to be added to the index for the FAQs page. To do this you can use Examine events, either using the &lt;em&gt;GatheringNodeData&lt;/em&gt; of the &lt;em&gt;BaseIndexProvider&lt;/em&gt;, or by using the &lt;em&gt;DocumentWriting&lt;/em&gt; event of the &lt;em&gt;UmbracoContentIndexer&lt;/em&gt; (I’ll write another post covering the difference between these two events and why they both exist). Though writing Examine event handlers to put the data from FAQ Item 1 –&amp;gt; 4 into the FAQs index isn’t very difficult, it would still be really cool if all of this could be done automatically.&lt;/p&gt; &lt;p&gt;Pete mentioned it would be cool if we could just index the output html of a page (sort of like Google) and suddenly the ideas started to flow. This concept is actually quite easy to do so within the next month or so we’ll probably release a beta of Examine Output Indexing. Here’s the way it’ll all get put together:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;An &lt;em&gt;HttpModule&lt;/em&gt; will be created to do 2 things:  &lt;ul&gt; &lt;li&gt;Check if the current request is an Umbraco page request  &lt;ul&gt; &lt;li&gt;If it is, we can easily get the current node being rendered since it’s already been added to the &lt;em&gt;HttpContext&lt;/em&gt; items by Umbraco  &lt;li&gt;Use the standard Examine handlers to enter the node’s data into the indexes based on the configuration you’ve specified in your Examine configuration files &lt;/li&gt;&lt;/ul&gt; &lt;li&gt;Get the HTML output of the page before it is rendered to the end user, parse the html to get the relevant data and put it into the index for the current Umbraco page &lt;/li&gt;&lt;/ul&gt; &lt;li&gt;We figured that it would also be cool to have an Examine node property that developers could defined called something like: &lt;em&gt;examineNoIndex&lt;/em&gt; which we could check for when we determine that it’s an Umbraco page and if this property is set to true, we’ll not index this page.  &lt;ul&gt; &lt;li&gt;This could give developers more control over what specific pages shouldn’t be indexed based directly from the CMS properties instead of writing custom events &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;With the above, a developer will simply need to put the HttpModule in their web.config, define an Examine index based on a new provider we create and that’s it. There will be no need to manually collate node data such as the above FAQ example. However, please note that this will work for straight forward searching so if you have complex searching &amp;amp; indexing requirements, I would still recommend using events since you have far more control over what information is indexed.&lt;/p&gt; &lt;p&gt;Any feedback is much appreciated since we haven’t started developing this quite yet.&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:09 Z</pubDate>
      <a10:updated>2023-03-23T15:08:09Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1167</guid>
      <link>https://shazwazza.com/post/examine-rc3-released/</link>
      <category>Examine</category>
      <title>Examine RC3 Released</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;Hopefully this will be a quick RC! I’m really hoping to release v1.0 RTM by early next week (latest). If you are able to help out with some testing it would be amazing!!  &lt;p&gt;Here's what's new: &lt;/p&gt; &lt;ul&gt; &lt;li&gt;PDF Indexing  &lt;li&gt;&lt;strong&gt;Easily&lt;/strong&gt; implement custom data indexing outside of &lt;a title="Umbraco" href="http://umbraco.org" target="_blank"&gt;Umbraco&lt;/a&gt;  &lt;li&gt;More XSLT Extensions for Umbraco  &lt;li&gt;Some framework refactoring so a new DLL: Examine.LuceneEngine.dll which contains all of the &lt;a title="Lucene.Net" href="http://lucene.apache.org/java/docs/index.html" target="_blank"&gt;Lucene.Net&lt;/a&gt; implementation  &lt;ul&gt; &lt;li&gt;Because of this refactoring, if you've built your own providers, you may need to update our code to work, &lt;b&gt;otherwise it is backwards compatible for most people&lt;/b&gt;. &lt;/li&gt;&lt;/ul&gt; &lt;li&gt;More unit tests  &lt;li&gt;More documentation &lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Get it while it’s hot! And don’t forget to read the release notes.&lt;/p&gt; &lt;p&gt;&lt;a title="DOWNLOAD FROM CODEPLEX HERE" href="http://examine.codeplex.com/releases/view/50780" target="_blank"&gt;DOWNLOAD FROM CODEPLEX HERE&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:09 Z</pubDate>
      <a10:updated>2023-03-23T15:08:09Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1176</guid>
      <link>https://shazwazza.com/post/searching-multi-node-tree-picker-data-or-any-collection-with-examine/</link>
      <category>Umbraco</category>
      <category>Examine</category>
      <title>Searching Multi-Node Tree Picker data (or any collection) with Examine</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;With the release of &lt;a href="http://ucomponents.codeplex.com" target="_blank"&gt;uComponents&lt;/a&gt; recently a lot of people are starting to work with a new data type called the MultiNodeTreePicker, and with this I’ve seen a few questions around searching the data it generates using Examine.  &lt;p&gt;The problem is there is a catch, if you’re using the CSV storage (which you must if you’re working with Examine) you’ll hit a problem, the Examine index will have something like this:&lt;/p&gt; &lt;p&gt;1011,1231,1232,1225&lt;/p&gt; &lt;p&gt;But how do you search on that? Searching for ‘1231’ will not return anything, because it’s prefixed with ‘,’ and postfixed with ‘,’. So this brings a problem, how do you search?&lt;/p&gt; &lt;h2&gt;Bring on Events&lt;/h2&gt; &lt;p&gt;As Shannon spoke about at CodeGarden 10 Examine has a number of different events you can hook into to do different things (&lt;a href="http://farmcode.org/post/2010/06/29/Examine-slide-deck-for-CodeGarden-2010.aspx" target="_blank"&gt;slides&lt;/a&gt; and &lt;a href="http://farmcode.org/post/2010/07/01/Examine-demo-site-source-code-from-CodeGarden-2010.aspx" target="_blank"&gt;code&lt;/a&gt;) and this is what we’re going to need to work with.&lt;/p&gt; &lt;p&gt;I’ve touched on &lt;a href="http://farmcode.org/post/2010/08/23/Text-casing-and-Examine.aspx" target="_blank"&gt;events before&lt;/a&gt; but this time we’re going to look at a different event, we’re going to look at the GatheringNodeData event.&lt;/p&gt; &lt;h3&gt;GatheringNodeData event&lt;/h3&gt; &lt;p&gt;So this event in Examine is fired while Examine is scraping the data out of an XML element which it has received. This XML could be from Umbraco (in the scenario we’re looking at here) or it could be from your own data source, and the event is raised once Examine as turned the XML into a Key/ Value representation of it.&lt;/p&gt; &lt;p&gt;The event that raises has custom event arguments, which has a property called Fields. This Fields property is a dictionary which contains the full Key/ Value representation of the data which will end up in Examine!&lt;/p&gt; &lt;p&gt;Now this dictionary is able to be manipulated, so you can add/ remove data as you see if (but that’s a topic for another blog), it also means you can &lt;em&gt;change the data&lt;/em&gt;!&lt;/p&gt; &lt;h2&gt;Changing the data for our needs&lt;/h2&gt; &lt;p&gt;As I mentioned at the start of this we end up with comma-separated string from the datatype which isn’t useful for searching, so we can use an event handler to change what we’ve got. First we need to tie an event handler&lt;/p&gt;&lt;pre&gt;public class ExamineEvents : ApplicationBase 
{
	public ExamineEvents() 
	{
		var indexer = ExamineManager.Instance.IndexProviderCollection["MyIndexer"];
		indexer.GatheringNodeData += new EventHandler&lt;indexingnodedataeventargs&gt;(GatheringNodeDataHandler);
	}

	void GatheringNodeDataHandler(object sender, IndexingNodeDataEventArgs e)
	{
		//do stuff here
	}
}&lt;/pre&gt;
&lt;p&gt;So this is just a simple wire-up, using the ApplicationBase class in Umbraco so that it’ll be created on application start-up. Next we need to implement the event handler:&lt;/p&gt;&lt;pre&gt;void GatheringNodeDataHandler(object sender, IndexingNodeDataEventArgs e)
{
	//grab the current data from the Fields collection
	var mntp = e.Fields["TreePicker"];
	//let's get rid of those commas!
	mntp = mntp.Replace(",", " ");
	//now put it back into the Fields so we can pretend nothing happened!
	e.Fields["TreePicker"] = mntp;
}&lt;/pre&gt;
&lt;p&gt;And you’re done! Now the data will be written into the index with spaces rather than commas meaning that you can search on each ID without the need for wildcards or any other “hacks” to get it to work.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: This will work in the majority of cases, the only reason it’ll fail is if you’re using an analyzer that strips out numbers before indexing. For more information about Lucene analyzers take a look at this article: &lt;a href="http://www.aaron-powell.com/lucene-analyzer"&gt;http://www.aaron-powell.com/lucene-analyzer&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:09 Z</pubDate>
      <a10:updated>2023-03-23T15:08:09Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1177</guid>
      <link>https://shazwazza.com/post/text-casing-and-examine/</link>
      <category>Examine</category>
      <title>Text casing and Examine</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;A few times I’ve seen questions posted on the Umbraco forums which ask how to deal with case insensitivity text with Examine, and it’s also something that we’ve had to handle a few times within our own company.  &lt;p&gt;Here’s a scenario:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;You have a site search  &lt;li&gt;You use examine  &lt;li&gt;You want to show the results looking exactly the same as it was before it went into Examine &lt;/li&gt;&lt;/ul&gt; &lt;p&gt;If you’re running a standard install you’ll notice that the content &lt;em&gt;always ends up lowercased!&lt;/em&gt;&lt;/p&gt; &lt;p&gt;This is a bit of a problem, page titles will be lowercase, body content will be lowercase, etc. Part of this will be due to a mistake in Examine, part of it is due to the design of Lucene.&lt;/p&gt; &lt;p&gt;In this article I’ll have a look at what you need to do to make it work as you’d expect.&lt;/p&gt; &lt;h2&gt;First, some background&lt;/h2&gt; &lt;p&gt;Before we dive directly into what to do to fix it you really should understand what is happening. If you don’t care feel free to skip over this bit though :P.&lt;/p&gt; &lt;p&gt;Searching is a tricky thing, and when searching the statement Examine == examine = false; To get around this searching is best done in a case insensitive manner. To make this work Examine did a forced lowercase of the content before it was pushed into Lucene.Net. This was to ensure that everything was exactly the same when it was searched against. &lt;br&gt;In hindsight this is not &lt;em&gt;really&lt;/em&gt; a great idea, it really should be the responsibility of the Lucene Analyzer to handle this for you.&lt;/p&gt; &lt;p&gt;Many of the common Lucene.Net analyzers actually do automatic lowercasing of content, these analysers are:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;StandardAnalyzer  &lt;li&gt;StopAnalyzer  &lt;li&gt;SimpleAnalyzer &lt;/li&gt;&lt;/ul&gt; &lt;p&gt;So if you’re using the standard Examine config you’ll find yourself using the &lt;strong&gt;StandardAnalyzer &lt;/strong&gt;and still have your content lowercased.&lt;/p&gt; &lt;p&gt;This means that there’s no need to Lucene to concern itself about case sensitivity when searching, everything is parsed by the analyzer (field terms and queries) and you’ll get more matches.&lt;/p&gt; &lt;h2&gt;So how do I get around this?&lt;/h2&gt; &lt;p&gt;Now that we’ve seen why all your content is &lt;em&gt;generally&lt;/em&gt; lower case, how can we work with it in the original format and display it back to the UI?&lt;/p&gt; &lt;p&gt;Well we need some way in which we can have the field data stored without the analyzer screwing around with it.&lt;/p&gt; &lt;p&gt;&lt;em&gt;Note: This doesn’t need to be done if you’re using an analyzer which doesn’t have a LowerCaseTokenizer or LowercaseFilter. If you’re using a different analyzer, like KeywordAnalyzer then this post wont cover what you’re after (since the KeywordAnalyzer isn’t lowercasing, you’re actually using an out-dated version of Examine, I recommend you grab the latest release :)). More information on Analyzers can be found at &lt;a href="http://www.aaron-powell.com/lucene-analyzer"&gt;http://www.aaron-powell.com/lucene-analyzer&lt;/a&gt;&lt;/em&gt;&lt;/p&gt; &lt;p&gt;Luckily we’ve got some hooks into Examine to allow us to do what we need here, it’s in the form of an event on the Examine.LuceneEngine.Providers.LuceneIndexer, called DocumentWriting. Note that this event is on the &lt;em&gt;LuceneIndexer&lt;/em&gt;, not the &lt;em&gt;BaseIndexProvider. &lt;/em&gt;This event is Lucene.Net specific and not logical on the base class which is agnostic of any other framework.&lt;/p&gt; &lt;p&gt;What we can do with this event is interact directly with Lucene.Net while Examine is working with it. &lt;br&gt;You’ll need to have a bit of an understanding of how to work with a Lucene.Net Document (and for that I’d recommend having a read of this article from me: &lt;a href="http://www.aaron-powell.com/documents-in-lucene-net"&gt;http://www.aaron-powell.com/documents-in-lucene-net&lt;/a&gt;), cuz what you’re able to do is play with Lucene.Net… Feel the power!&lt;/p&gt; &lt;p&gt;So we can attach the event handler the same way as you would do any other event in Umbraco, using an Action Handler:&lt;/p&gt;&lt;pre&gt;public class UmbracoEvents : ApplicationBase
{
	public UmbracoEvents()
        {
            var indexer = (LuceneIndexer)ExamineManager.Instance.IndexProviderCollection["DefaultIndexer"];

            indexer.DocumentWriting +=new System.EventHandler&lt;documentwritingeventargs&gt;(indexer_DocumentWriting);
        }
}&lt;/pre&gt;
&lt;p&gt;To do this we’ve got to cast the indexer so we’ve got the Lucene version to work with, then we’re attaching to our event handler. Let’s have a look at the event handler&lt;/p&gt;&lt;pre&gt;void indexer_DocumentWriting(object sender, DocumentWritingEventArgs e)
{
	//grab out lucene document from the event arguments
	var doc = e.Document;

	//the e.Fields dictionary is all the fields which are about to be inserted into Lucene.Net
	//we'll grab out the "bodyContent" one, if there is one to be indexed
	if(e.Fields.ContainsKey("bodyContent")) 
	{
		string content = e.Fields["bodyContent"];
		//Give the field a name which you'll be able to easily remember
		//also, we're telling Lucene to just put this data in, nothing more
		doc.Add(new Field("__bodyContent", content, Field.Store.YES, Field.Index.NOT_ANALYZED));
	}
}&lt;/pre&gt;
&lt;p&gt;And that’s how you can push data in. I’d recommend that you do a conditional check to ensure that the property you’re looking for does exist in the Fields property of the event args, unless you’re 100% sure that it appears on all the objects which you’re indexing.&lt;/p&gt;
&lt;p&gt;Lastly we need to display that on the UI, well it’s easy, rather accessing the &lt;em&gt;bodyContent&lt;/em&gt; property of the SearchResults, use the &lt;em&gt;__bodyContent&lt;/em&gt; and you’ll get your unanalyzed version.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Here we’ve looked at how we can use the Examine events to interact with the Lucene.Net Document. We’ve decided that we want to push in unanalyzed text, but you could use this idea to really tweak your Lucene.Net document. But really playing with the Document is not recommended unless you *really* know what you’re doing ;).&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:09 Z</pubDate>
      <a10:updated>2023-03-23T15:08:09Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1196</guid>
      <link>https://shazwazza.com/post/searching-umbraco-using-razor-and-examine/</link>
      <category>Umbraco</category>
      <category>Examine</category>
      <title>Searching Umbraco using Razor and Examine</title>
      <description>&lt;div class="imported-post"&gt;This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.&lt;/div&gt;Since Razor is really just c# it’s super simple to run a search in &lt;a href="http://umbraco.com" target="_blank"&gt;Umbraco&lt;/a&gt; using Razor and &lt;a href="http://examine.codeplex.com" target="_blank"&gt;Examine&lt;/a&gt;.&amp;nbsp; In MVC the actual searching should be left up to the controller to give the search results to your view, but in Umbraco 4.6 + , Razor is used as macros which actually ‘do stuff’. Here’s how incredibly simple it is to do a search:  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:bb6b2591-32f0-4cfe-be6a-84cb1abd44fc" class="wlWriterSmartContent"&gt;&lt;pre style="background-color: white; width: 600px; height: 405px; overflow: auto"&gt;&lt;div&gt;&lt;!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt;&lt;span style="color: #000000"&gt;@using Examine;

@* Get the search term from query string *@
@{var searchTerm = Request.QueryString["search"];}

&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;ul &lt;/span&gt;&lt;span style="color: #ff0000"&gt;class&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="search-results"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
@foreach (var result in 
    ExamineManager.Instance.Search(searchTerm, true)) {    
    
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;li&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
        &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;span&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;@result.Score&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;span&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
        &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;a &lt;/span&gt;&lt;span style="color: #ff0000"&gt;href&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="@umbraco.library.NiceUrl(result.Id)"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;
            @result.Fields["nodeName"]
        &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;a&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;        
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;li&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;    
    
}
&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;ul&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;p&gt;That’s it! Pretty darn easy.&lt;/p&gt;
&lt;p&gt;And for all you sceptics who think there’s too much configuration involved to setup Examine, configuring Examine requires 3 lines of code. Yes its true, 3 lines, that’s it. Here’s the bare minimum setup:&lt;/p&gt;
&lt;p&gt;1. Create an indexer under the &lt;em&gt;ExamineIndexProviders&lt;/em&gt; section:&lt;/p&gt;
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:a7d23357-b9db-4aab-9e0e-e901299f1031" class="wlWriterSmartContent"&gt;&lt;pre style="background-color: white; width: 600px; height: 51px; overflow: auto"&gt;&lt;div&gt;&lt;!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;add &lt;/span&gt;&lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="MyIndexer"&lt;/span&gt;&lt;span style="color: #ff0000"&gt;
      type&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="UmbracoExamine.UmbracoContentIndexer, UmbracoExamine"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;p&gt;2. Create a searcher under the &lt;em&gt;ExamineSearchProviders&lt;/em&gt; section:&lt;/p&gt;
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:4254bc1d-2c86-43c1-99af-18dec27197c0" class="wlWriterSmartContent"&gt;&lt;pre style="background-color: white; width: 600px; height: 51px; overflow: auto"&gt;&lt;div&gt;&lt;!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;add &lt;/span&gt;&lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="MySearcher"&lt;/span&gt;&lt;span style="color: #ff0000"&gt;
      type&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="UmbracoExamine.UmbracoExamineSearcher, UmbracoExamine"&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;p&gt;3. Create an index set under the &lt;em&gt;ExamineLuceneIndexSets&lt;/em&gt; config section:&lt;/p&gt;
&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:4d5d9534-825e-419a-87df-a63bb8eeeb15" class="wlWriterSmartContent"&gt;&lt;pre style="background-color: white; width: 600px; height: 51px; overflow: auto"&gt;&lt;div&gt;&lt;!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;IndexSet &lt;/span&gt;&lt;span style="color: #ff0000"&gt;SetName&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="MyIndexSet"&lt;/span&gt;&lt;span style="color: #ff0000"&gt; 
    IndexPath&lt;/span&gt;&lt;span style="color: #0000ff"&gt;="~/App_Data/TEMP/MyIndex"&lt;/span&gt;&lt;span style="color: #ff0000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;
&lt;p&gt;This will index all of your data in Umbraco and allow you to search against all of it. If you want to search on specific subsets, you can use the FluentAPI to search and of course if you want to modify your index, there’s much more you can do with the config if you like.&lt;/p&gt;
&lt;p&gt;With Examine the sky is the limit, you can have an incredibly simple index and search mechanism up to an incredibly complex index with event handlers, etc… and a very complex search with fuzzy logic, proximity searches, etc…&amp;nbsp; And no matter what flavour you choose it is guaranteed to be VERY fast and doesn’t matter how much data you’re searching against.&lt;/p&gt;
&lt;p&gt;I also STRONGLY advise you to use the latest release on CodePlex: &lt;a href="http://examine.codeplex.com/releases/view/50781"&gt;http://examine.codeplex.com/releases/view/50781&lt;/a&gt; . There will also be version 1.1 coming out very soon.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:09 Z</pubDate>
      <a10:updated>2023-03-23T15:08:09Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1207</guid>
      <link>https://shazwazza.com/post/examine-151-released/</link>
      <category>Umbraco</category>
      <category>Examine</category>
      <title>Examine 1.5.1 released</title>
      <description>&lt;style&gt;
div.nuget-badge p code {
background: none;
background-color: #202020 !important;
border: 4px solid silver !important;
border-bottom-left-radius: 5px 5px !important;
border-bottom-right-radius: 5px 5px !important;
border-top-left-radius: 5px 5px !important;
border-top-right-radius: 5px 5px !important;
color: #e2e2e2 !important;
display: block !important;
font: normal normal normal 1.5em/normal 'andale mono', 'lucida console', monospace !important;
line-height: 1.5em !important;
overflow: auto !important;
padding: 15px !important;
}
&lt;/style&gt;  &lt;p&gt;I’ve created a new release of Examine today, version 1.5.1. There’s nothing really new in this release, just a bunch of bug fixes. The other cool thing is that I’ve finally got Examine on Nuget now. The v1.5.1 release page is &lt;a href="https://examine.codeplex.com/releases/view/104554" target="_blank"&gt;here&lt;/a&gt; on CodePlex with upgrade instructions… which is really just replacing the DLLs.&lt;/p&gt; &lt;p&gt;Its &lt;em&gt;important&lt;/em&gt; to note that if you have installed Umbraco 6.0.1+ or 4.11.5+ then you already have Examine 1.5.0&amp;nbsp; installed (which isn’t an official release on the CodePlex page) which has 8 of these 10 bugs fixed already.&lt;/p&gt; &lt;h2&gt;Bugs fixed&lt;/h2&gt; &lt;p&gt;Here’s the full list of bugs fixed in this release:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a title="https://examine.codeplex.com/releases/view/10357" href="https://examine.codeplex.com/releases/view/10357"&gt;&lt;a href="https://examine.codeplex.com/workitem/10357"&gt;https://examine.codeplex.com/workitem/10357&lt;/a&gt;&lt;/a&gt;  &lt;li&gt;&lt;a title="https://examine.codeplex.com/releases/view/10356" href="https://examine.codeplex.com/releases/view/10356"&gt;&lt;a href="https://examine.codeplex.com/workitem/10356"&gt;https://examine.codeplex.com/workitem/10356&lt;/a&gt;&lt;/a&gt;  &lt;li&gt;&lt;a title="https://examine.codeplex.com/releases/view/10355" href="https://examine.codeplex.com/releases/view/10355"&gt;&lt;a href="https://examine.codeplex.com/workitem/10355"&gt;https://examine.codeplex.com/workitem/10355&lt;/a&gt;&lt;/a&gt;  &lt;li&gt;&lt;a title="https://examine.codeplex.com/releases/view/10363" href="https://examine.codeplex.com/releases/view/10363"&gt;&lt;a href="https://examine.codeplex.com/workitem/10363"&gt;https://examine.codeplex.com/workitem/10363&lt;/a&gt;&lt;/a&gt;  &lt;li&gt;&lt;a title="https://examine.codeplex.com/releases/view/10359" href="https://examine.codeplex.com/releases/view/10359"&gt;&lt;a href="https://examine.codeplex.com/workitem/10359"&gt;https://examine.codeplex.com/workitem/10359&lt;/a&gt;&lt;/a&gt;  &lt;li&gt;&lt;a title="https://examine.codeplex.com/releases/view/10358" href="https://examine.codeplex.com/releases/view/10358"&gt;&lt;a title="https://examine.codeplex.com/workitem/" href="https://examine.codeplex.com/workitem/10358"&gt;https://examine.codeplex.com/workitem/10358&lt;/a&gt;&lt;/a&gt;  &lt;li&gt;&lt;a title="https://examine.codeplex.com/releases/view/10358" href="https://examine.codeplex.com/releases/view/10358"&gt;&lt;a href="https://examine.codeplex.com/workitem/10360"&gt;https://examine.codeplex.com/workitem/10360&lt;/a&gt;&lt;/a&gt;  &lt;li&gt;&lt;a title="https://examine.codeplex.com/releases/view/10358" href="https://examine.codeplex.com/releases/view/10358"&gt;&lt;a href="https://examine.codeplex.com/workitem/10361"&gt;https://examine.codeplex.com/workitem/10361&lt;/a&gt;&lt;/a&gt;  &lt;li&gt;&lt;a title="https://examine.codeplex.com/releases/view/10358" href="https://examine.codeplex.com/releases/view/10358"&gt;&lt;a href="https://examine.codeplex.com/workitem/10362"&gt;https://examine.codeplex.com/workitem/10362&lt;/a&gt;&lt;/a&gt;  &lt;li&gt;&lt;a title="https://examine.codeplex.com/releases/view/10358" href="https://examine.codeplex.com/releases/view/10358"&gt;&lt;a href="https://examine.codeplex.com/workitem/10349"&gt;https://examine.codeplex.com/workitem/10349&lt;/a&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;UmbracoExamine&lt;/h2&gt; &lt;p&gt;You may already know this but we’ve moved the UmbracoExamine libraries in to the core of Umbraco so that the Umbraco core team can better support the implementation. That means that only the basic Examine libraries will continue to exist @ examine.codeplex.com. The release of 1.5.1 only relates to the base Examine libraries, not the UmbracoExamine libraries, but that’s ok you can still upgrade these base libraries without issue.&lt;/p&gt; &lt;h2&gt;Nuget&lt;/h2&gt; &lt;p&gt;There’s 2 Examine projects up on Nuget, the basic Examine package and the Azure package if you wish to use Azure directory for your indexes.&lt;/p&gt; &lt;p&gt;Standard package:&lt;/p&gt; &lt;div class="nuget-badge"&gt; &lt;p&gt;&lt;code&gt;PM&amp;gt; Install-Package Examine&lt;/code&gt;&lt;/p&gt;&lt;/div&gt; &lt;p&gt;Azure package:&lt;/p&gt; &lt;div class="nuget-badge"&gt; &lt;p&gt;&lt;code&gt;PM&amp;gt; Install-Package Examine.Azure&lt;/code&gt;&lt;/p&gt;&lt;/div&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Happy searching!&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:08 Z</pubDate>
      <a10:updated>2023-03-23T15:08:08Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1327</guid>
      <link>https://shazwazza.com/post/new-examine-updates-and-features-for-umbraco/</link>
      <category>Umbraco</category>
      <category>Examine</category>
      <title>New Examine updates and features for Umbraco</title>
      <description>&lt;p&gt;It’s been a long while since &lt;a href="http://examine.codeplex.com/" target="_blank"&gt;Examine&lt;/a&gt; got some much needed attention and I’m pleased to say it is now happening. If you didn’t know already, we’ve moved the Umbraco Examine source in to the core of &lt;a href="http://umbraco.com" target="_blank"&gt;Umbraco&lt;/a&gt;. The underlying Examine (Examine.dll) core will remain on CodePlex but all the Umbraco bits and pieces which is found in &lt;em&gt;UmbracoExamine.dll&lt;/em&gt; are in the Umbraco core from version 6.1+. This is great news because now we can all better support the implementation of Examine for Umbraco. More good news is that even versions prior to Umbraco 6.1 will have some bugs fixed (&lt;a title="http://issues.umbraco.org/issue/U4-1768" href="http://issues.umbraco.org/issue/U4-1768"&gt;http://issues.umbraco.org/issue/U4-1768&lt;/a&gt;) ! &lt;a href="https://twitter.com/nielskuhnel" target="_blank"&gt;Niels Kuhnel&lt;/a&gt; has also jumped aboard the Examine train and is helping out a ton by adding his amazing &lt;a href="http://examine.codeplex.com/wikipage?title=Facets" target="_blank"&gt;‘facet’ features&lt;/a&gt; which will probably make it into an Umbraco release around version 6.2 (maybe 6.1, but still need to do some review, etc… to make sure its 100% backwards compatible). &lt;/p&gt; &lt;p&gt;One other bit of cool news is that we’re adding an official Examine Management dashboard to Umbraco 6.1. In its present state it supports optimizing indexes, rebuilding indexes and searching them. I’ve created a quick video showing its features :)&lt;/p&gt; &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:5737277B-5D6D-4f48-ABFC-DD9C333F4C5D:2774c052-a2ec-4a30-9110-e227387523b8" class="wlWriterEditableSmartContent"&gt;&lt;div&gt;&lt;object width="448" height="336"&gt;&lt;param name="movie" value="http://www.youtube.com/v/23_GMDfZQXA?hl=en&amp;amp;hd=1"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/23_GMDfZQXA?hl=en&amp;amp;hd=1" type="application/x-shockwave-flash" width="448" height="336"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;div style="width:448px;clear:both;font-size:.8em"&gt;Examine management dashboard for Umbraco&lt;/div&gt;&lt;/div&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:08 Z</pubDate>
      <a10:updated>2023-03-23T15:08:08Z</a10:updated>
    </item>
  </channel>
</rss>