From acb0c02024afe092e1684d4e5617bc025fa3df54 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 26 Apr 2022 11:37:36 +1000 Subject: [PATCH 1/8] Fix broken link --- docs/searching.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/searching.md b/docs/searching.md index f3ea7609c..6820810da 100644 --- a/docs/searching.md +++ b/docs/searching.md @@ -8,7 +8,7 @@ order: 2 Searching === -_**Tip**: There are many examples of searching in the [`FluentApiTests` source code](https://github.com/Shazwazza/Examine/blob/master/src/Examine.Test/Search/FluentApiTests.cs) to use as examples/reference._ +_**Tip**: There are many examples of searching in the [`FluentApiTests` source code](https://github.com/Shazwazza/Examine/blob/dev/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs) to use as examples/reference._ ## All fields (managed queries) From 2a23a808086a9c08a62dd7183c9b688369198bc0 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 26 Apr 2022 11:38:14 +1000 Subject: [PATCH 2/8] Fix broken link --- docs/indexing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/indexing.md b/docs/indexing.md index 2b71bae64..bb34b7957 100644 --- a/docs/indexing.md +++ b/docs/indexing.md @@ -8,7 +8,7 @@ order: 0 Indexing === -_**Tip**: There are many examples of indexing in the [`LuceneIndexTests` source code](https://github.com/Shazwazza/Examine/blob/dev/src/Examine.Test/Index/LuceneIndexTests.cs) to use as examples/reference._ +_**Tip**: There are many examples of indexing in the [`LuceneIndexTests` source code](https://github.com/Shazwazza/Examine/blob/dev/src/Examine.Test/Examine.Lucene/Index/LuceneIndexTests.cs) to use as examples/reference._ Examine will index any data you give it within a `ValueSet`. You can index one or multiple items at once and there's a few different ways to do that. Each field in a `ValueSet` can also contain one or more values. @@ -191,4 +191,4 @@ You can use this event to entirely customize how the data is stored in the Lucen - \ No newline at end of file + From ee0ba8cc0bf86e918f7406ede3c922889777062d Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 6 Jun 2022 11:16:02 -0600 Subject: [PATCH 3/8] updates searching docs --- docs/searching.md | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/docs/searching.md b/docs/searching.md index 6820810da..22fdb5a68 100644 --- a/docs/searching.md +++ b/docs/searching.md @@ -33,8 +33,8 @@ be searched. In most cases the field value type will be 'Full Text', others may ```csharp _var searcher = myIndex.GetSearcher(); // Get a searcher var results = searcher.CreateQuery() // Create a query - .Field("Address", "Hills") // Look for any "Hills" addresses - .Execute(); // Execute the search + .Field("Address", "Hills") // Look for any "Hills" addresses + .Execute(); // Execute the search ``` ## Range queries @@ -69,22 +69,25 @@ _TODO: Fill this in..._ ### Native Query ```csharp -var searcher = indexer.GetSearcher(); -var criteria = (LuceneSearchQuery)searcher.CreateQuery(); -//combine a custom lucene query with raw lucene query -var op = criteria.NativeQuery("hello:world").And(); +var searcher = indexer.GetSearcher(); + +var results = searcher.CreateQuery().NativeQuery("hello:world").Execute(); ``` ### Combine a custom lucene query with raw lucene query ```csharp -var criteria = (LuceneSearchQuery)searcher.CreateQuery(); - -//combine a custom lucene query with raw lucene query +var searcher = indexer.GetSearcher(); +var query = (LuceneSearchQuery)searcher.CreateQuery(); +// first create the Native Query and suffix with an And() call. var op = criteria.NativeQuery("hello:world").And(); -criteria.LuceneQuery(NumericRangeQuery.NewLongRange("numTest", 4, 5, true, true)); +// use the original LuceneSearchQuery instance to append +// the next query value. +query.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); + +var results = query.Execute(); ``` ## Boosting, Proximity, Fuzzy & Escape @@ -133,3 +136,23 @@ var exactfilter = exactcriteria.Field("__Path", "-1,123,456,789".Escape()); var results2 = exactfilter.Execute(); ``` + +### Lucene Searches + +Sometimes you need access to the underlying Lucene.NET APIs to perform a manual Lucene search. + +```csharp +var searcher = (LuceneSearcher)indexer.Searcher; +var searchContext = searcher.GetSearchContext(); + +using (ISearcherReference searchRef = searchContext.GetSearcher()) +{ + // Access the instance of the underlying Lucene + // IndexSearcher instance. + var indexSearcher = searchRef.IndexSearcher; + + // You can then call indexSearcher.Search(...) + // which is a Lucene API, customize the parameters + // accordingly and handle the Lucene response. +} +``` \ No newline at end of file From 2b67edf50f8f32552b5e18271c5064f3f61b4770 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 10 Aug 2022 11:51:20 -0600 Subject: [PATCH 4/8] adds message about spatial --- docs/searching.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/searching.md b/docs/searching.md index 22fdb5a68..a917255bb 100644 --- a/docs/searching.md +++ b/docs/searching.md @@ -141,6 +141,8 @@ var results2 = exactfilter.Execute(); Sometimes you need access to the underlying Lucene.NET APIs to perform a manual Lucene search. +An example of Spatial Search with the Lucene APIs is in the [Examine source code](https://github.com/Shazwazza/Examine/blob/dev/src/Examine.Test/Examine.Lucene/Extensions/SpatialSearch.cs). + ```csharp var searcher = (LuceneSearcher)indexer.Searcher; var searchContext = searcher.GetSearchContext(); From f5afffee54c4f6cde7f0f111992af6c62d77cd2f Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 10 Aug 2022 11:51:57 -0600 Subject: [PATCH 5/8] Remove underscore from searcher # Conflicts: # docs/searching.md --- docs/searching.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/searching.md b/docs/searching.md index a917255bb..036fce9e8 100644 --- a/docs/searching.md +++ b/docs/searching.md @@ -31,8 +31,8 @@ be searched. In most cases the field value type will be 'Full Text', others may ## Per field ```csharp -_var searcher = myIndex.GetSearcher(); // Get a searcher - var results = searcher.CreateQuery() // Create a query +var searcher = myIndex.GetSearcher(); // Get a searcher +var results = searcher.CreateQuery() // Create a query .Field("Address", "Hills") // Look for any "Hills" addresses .Execute(); // Execute the search ``` @@ -108,12 +108,10 @@ var filter = criteria.Field("nodeTypeAlias", "CWS_Home".Boost(20)); ```csharp var searcher = indexer.GetSearcher(); - //Arrange var criteria = searcher.CreateQuery("content"); - //get all nodes that contain the words warren and creative within 5 words of each other var filter = criteria.Field("metaKeywords", "Warren creative".Proximity(5)); ``` From bbc436a8638622b95fdf5b64cbde3603b146f6d4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 2 Mar 2023 10:32:03 -0700 Subject: [PATCH 6/8] Cherry picks docfx to master --- .github/workflows/docfx-gh-pages.yml | 51 +++ docs/v2/.gitignore | 9 + docs/v2/404.html | 25 ++ docs/v2/404.md | 10 + docs/v2/android-chrome-192x192.png | Bin 0 -> 18546 bytes docs/v2/android-chrome-384x384.png | Bin 0 -> 44910 bytes docs/v2/api/.gitignore | 5 + docs/v2/api/index.md | 8 + docs/v2/articles/configuration.md | 194 +++++++++++ docs/v2/articles/indexing.md | 159 +++++++++ docs/v2/articles/searching.md | 260 +++++++++++++++ docs/v2/articles/sorting.md | 79 +++++ docs/v2/articles/toc.yml | 10 + docs/v2/docfx.json | 85 +++++ docs/v2/docs-v1-v2/configuration.md | 250 ++++++++++++++ docs/v2/docs-v1-v2/indexing.md | 182 ++++++++++ docs/v2/docs-v1-v2/quickstart.md | 127 +++++++ docs/v2/docs-v1-v2/searching.md | 252 ++++++++++++++ docs/v2/docs-v1-v2/sorting.md | 115 +++++++ docs/v2/docs-v1-v2/toc.yml | 10 + docs/v2/favicon-16x16.png | Bin 0 -> 1222 bytes docs/v2/favicon-32x32.png | Bin 0 -> 2081 bytes docs/v2/images/favicon.ico | Bin 0 -> 15086 bytes docs/v2/images/headerlogo.png | Bin 0 -> 3417 bytes docs/v2/index.md | 85 +++++ docs/v2/mstile-150x150.png | Bin 0 -> 11695 bytes docs/v2/safari-pinned-tab.svg | 27 ++ docs/v2/site.webmanifest | 19 ++ .../material/partials/head.tmpl.partial | 21 ++ docs/v2/templates/material/styles/main.css | 313 ++++++++++++++++++ docs/v2/toc.yml | 7 + 31 files changed, 2303 insertions(+) create mode 100644 .github/workflows/docfx-gh-pages.yml create mode 100644 docs/v2/.gitignore create mode 100644 docs/v2/404.html create mode 100644 docs/v2/404.md create mode 100644 docs/v2/android-chrome-192x192.png create mode 100644 docs/v2/android-chrome-384x384.png create mode 100644 docs/v2/api/.gitignore create mode 100644 docs/v2/api/index.md create mode 100644 docs/v2/articles/configuration.md create mode 100644 docs/v2/articles/indexing.md create mode 100644 docs/v2/articles/searching.md create mode 100644 docs/v2/articles/sorting.md create mode 100644 docs/v2/articles/toc.yml create mode 100644 docs/v2/docfx.json create mode 100644 docs/v2/docs-v1-v2/configuration.md create mode 100644 docs/v2/docs-v1-v2/indexing.md create mode 100644 docs/v2/docs-v1-v2/quickstart.md create mode 100644 docs/v2/docs-v1-v2/searching.md create mode 100644 docs/v2/docs-v1-v2/sorting.md create mode 100644 docs/v2/docs-v1-v2/toc.yml create mode 100644 docs/v2/favicon-16x16.png create mode 100644 docs/v2/favicon-32x32.png create mode 100644 docs/v2/images/favicon.ico create mode 100644 docs/v2/images/headerlogo.png create mode 100644 docs/v2/index.md create mode 100644 docs/v2/mstile-150x150.png create mode 100644 docs/v2/safari-pinned-tab.svg create mode 100644 docs/v2/site.webmanifest create mode 100644 docs/v2/templates/material/partials/head.tmpl.partial create mode 100644 docs/v2/templates/material/styles/main.css create mode 100644 docs/v2/toc.yml diff --git a/.github/workflows/docfx-gh-pages.yml b/.github/workflows/docfx-gh-pages.yml new file mode 100644 index 000000000..c9d147c87 --- /dev/null +++ b/.github/workflows/docfx-gh-pages.yml @@ -0,0 +1,51 @@ +name: Deploy DocFX with GitHub Pages dependencies preinstalled + +on: + # Runs on pushes targeting the default branch + push: + branches: ["feature/docfx"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Github Pages + uses: actions/configure-pages@v3 + + - name: Build DocFX Site + uses: VakuWare/docfx-pdf-action@v1.4.0 + with: + args: docs/v2/docfx.json + - name: Upload docfx built site artifact + uses: actions/upload-pages-artifact@v1 + with: + path: docs/v2/_site + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/docs/v2/.gitignore b/docs/v2/.gitignore new file mode 100644 index 000000000..4378419e7 --- /dev/null +++ b/docs/v2/.gitignore @@ -0,0 +1,9 @@ +############### +# folder # +############### +/**/DROP/ +/**/TEMP/ +/**/packages/ +/**/bin/ +/**/obj/ +_site diff --git a/docs/v2/404.html b/docs/v2/404.html new file mode 100644 index 000000000..086a5c9ea --- /dev/null +++ b/docs/v2/404.html @@ -0,0 +1,25 @@ +--- +permalink: /404.html +layout: default +--- + + + +
+

404

+ +

Page not found :(

+

The requested page could not be found.

+
diff --git a/docs/v2/404.md b/docs/v2/404.md new file mode 100644 index 000000000..adc417b2d --- /dev/null +++ b/docs/v2/404.md @@ -0,0 +1,10 @@ +--- +layout: page +title: 404 Error +tagline: Ah snap! We could not find what you are looking for! +permalink: /404.html +--- + +Try got get back to the Home Page and start over! + +[Go to the Home Page]({{ '/' | absolute_url }}) diff --git a/docs/v2/android-chrome-192x192.png b/docs/v2/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..847c17743182def732194ed48a7dd77729f13c21 GIT binary patch literal 18546 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 ZBZx>@fxh8jUJ8bQxHx%|o;U=i{~IP`p;Z6? literal 0 HcmV?d00001 diff --git a/docs/v2/android-chrome-384x384.png b/docs/v2/android-chrome-384x384.png new file mode 100644 index 0000000000000000000000000000000000000000..ed0c71a28d41e9c94c24726f47ec74a2971f7bf0 GIT binary patch literal 44910 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≪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 WOhJ3v@_9fJAVWP9-3D#Pg#QB^D);>W literal 0 HcmV?d00001 diff --git a/docs/v2/api/.gitignore b/docs/v2/api/.gitignore new file mode 100644 index 000000000..e8079a3be --- /dev/null +++ b/docs/v2/api/.gitignore @@ -0,0 +1,5 @@ +############### +# temp file # +############### +*.yml +.manifest diff --git a/docs/v2/api/index.md b/docs/v2/api/index.md new file mode 100644 index 000000000..6b256ce8a --- /dev/null +++ b/docs/v2/api/index.md @@ -0,0 +1,8 @@ +--- +uid: apidocsindex +--- +# Examine V3 API Documentation + +API documentation is automatically generated. + +_**Tip**: There are many unit tests in the source code that can be used as Examples of how to do things. There is also a test web project that has plenty of examples of how to configure indexes and search them._ \ No newline at end of file diff --git a/docs/v2/articles/configuration.md b/docs/v2/articles/configuration.md new file mode 100644 index 000000000..330d9551e --- /dev/null +++ b/docs/v2/articles/configuration.md @@ -0,0 +1,194 @@ +--- +layout: page +title: Configuration +permalink: /configuration +uid: configuration +order: 1 +--- + +Configuration +=== + +An index can be configured in many ways including different configurations per field such as how those values are analyzed, indexed, tokenized ... basically how the data is stored and retrieved. This is done via Examine ["Value Types"](#value-types). + +_**Note**: This documentation refers to using Lucene based indexes in Examine (the default index type shipped in Examine)._ + +## Field definitions + +A Field Definition is a mapping of a field name to a ["Value Types"](#value-types). By default all fields are mapped to the default Value Type: [`FieldDefinitionTypes.FullText`](xref:Examine.FieldDefinitionTypes#Examine_FieldDefinitionTypes_FullText). + +You can map a field to any value type when configuring the index. + +### IConfigureNamedOptions + +Configuration of Examine indexes is done with [.NET's Options pattern](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0). For Examine, this is done with named options: [`IConfigureNamedOptions`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.options.iconfigurenamedoptions-1). + +There are several options that can be configured, the most common ones are: + +* __FieldDefinitions__ _[`FieldDefinitionCollection`](xref:Examine.FieldDefinitionCollection)_ - Manages the mappings between a field name and it's index value type +* __Analyzer__ _`Analyzer`_ - The default Lucene Analyzer to use for each field (default = [`StandardAnalyzer`](https://lucenenet.apache.org/docs/4.8.0-beta00016/api/analysis-common/Lucene.Net.Analysis.Standard.StandardAnalyzer.html)) +* __Validator__ _[`IValueSetValidator`]([`IValueSetValidator`](xref:Examine.IValueSetValidator))_ - Used to validate a value set to be indexed, if validation fails it will not be indexed +* __[IndexValueTypesFactory](xref:Examine.Lucene.IFieldValueTypeFactory)__ _`IReadOnlyDictionary`_ - Allows you to define custom Value Types + +```cs +/// +/// Configure Examine indexes using .NET IOptions +/// +public sealed class ConfigureIndexOptions : IConfigureNamedOptions +{ + public void Configure(string name, LuceneDirectoryIndexOptions options) + { + switch (name) + { + case "MyIndex": + // Set the "Price" field to map to the 'Double' value type. + options.FieldDefinitions.AddOrUpdate( + new FieldDefinition("Price", FieldDefinitionTypes.Double)); + break; + } + } + + public void Configure(LuceneDirectoryIndexOptions options) + => Configure(string.Empty, options); +} +``` + +### After construction + +You can modify the field definitions [FieldDefinitionCollection](xref:Examine.FieldDefinitionCollection) for an index after it is constructed by using any of the following methods: + +* `myIndex.FieldDefinitionCollection.TryAdd` +* `myIndex.FieldDefinitionCollection.AddOrUpdate` +* `myIndex.FieldDefinitionCollection.GetOrAdd` + +These modifications __must__ be done before any indexing or searching is executed. + +### Add a field value type after construction + +It is possible to add custom field value types after the construction of the index, but this must be done before the index is used. Some people may prefer this method of adding custom field value types. Generally, these should be modified directly after the construction of the index. + +```cs +// Create the index with all of the defaults +var myIndex = new LuceneIndex( + "MyIndex", + new SimpleFSDirectory(new DirectoryInfo("C:\\TestIndexes"))); + +// Add a custom field value type +myIndex.FieldValueTypeCollection.ValueTypeFactories + .TryAdd( + "phonenumber", + name => new GenericAnalyzerFieldValueType( + name, + new PhoneNumberAnalyzer())); + +// Map a field to use the custom field value type +myIndex.FieldDefinitionCollection.TryAdd( + new FieldDefinition("Phone", "phonenumber")); +``` + +## Value types + +Value types are responsible for: + +* Defining a field name and if the field should be sortable, the field to store the sortable data +* Adding a field value to an index document +* Configuring how the value will be stored in the index +* Configuring the analyzer for the field +* Generating the Query for the field + +These are the default field value types provided with Examine. Each value type can be resolved from the static class [`Examine.FieldDefinitionTypes`](xref:Examine.FieldDefinitionTypes) (i.e. [`Examine.FieldDefinitionTypes.FullText`](xref:Examine.FieldDefinitionTypes#Examine_FieldDefinitionTypes_FullText)). + +| Value Type | Description | Sortable | +|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| +| FullText | __Default__.
The field will be indexed with the index's
default Analyzer without any sortability. 
Generally this is fine for normal text searching. | ❌ | +| FullTextSortable | Will be indexed with FullText but also 
enable sorting on this field for search results. 
_FullText sortability adds additional overhead 
since it requires an additional index field._ | ✅ | +| Integer | Stored as a numerical structure. | ✅ | +| Float | Stored as a numerical structure. | ✅ | +| Double | Stored as a numerical structure. | ✅ | +| Long | Stored as a numerical structure. | ✅ | +| DateTime | Stored as a DateTime, 
represented by a numerical structure. | ✅ | +| DateYear | Just like DateTime but with 
precision only to the year. | ✅ | +| DateMonth | Just like DateTime but with 
precision only to the month. | ✅ | +| DateDay | Just like DateTime but with 
precision only to the day. | ✅ | +| DateHour | Just like DateTime but with 
precision only to the hour. | ✅ | +| DateMinute | Just like DateTime but with 
precision only to the minute. | ✅ | +| EmailAddress | Uses custom analyzers for dealing 
with email address searching. | ❌ | +| InvariantCultureIgnoreCase | Uses custom analyzers for dealing with text so it
 can be searched on regardless of the culture/casing. | ❌ | +| Raw | Will be indexed without analysis, searching will
 only match with an exact value. | ❌ | + +### Custom field value types + +A field value type is defined by [`IIndexFieldValueType`](xref:Examine.Lucene.Indexing.IIndexFieldValueType) + +_**Tip**: There are many implementations of IIndexFieldValueType in the source code to use as examples/reference._ + +A common base class that can be used for field value types is: [`IndexFieldValueTypeBase`](xref:Examine.Lucene.Indexing.IndexFieldValueTypeBase). + +A common implementation that can be used for field value types for custom Analyzers is: [`GenericAnalyzerFieldValueType`](xref:Examine.Lucene.Indexing.GenericAnalyzerFieldValueType). + +#### Example - Phone Number + +A phone number stored in Lucene could require a custom analyzer to index and search it properly. So the best way to set this up in Examine would be to have a custom field value type for it. Since this field value type doesn't need to do anything more fancy than to provide a custom analyzer, we can create it with the [`GenericAnalyzerFieldValueType`](xref:Examine.Lucene.Indexing.GenericAnalyzerFieldValueType). + +```cs +/// +/// Configure Examine indexes using .NET IOptions +/// +public sealed class ConfigureIndexOptions : IConfigureNamedOptions +{ + private readonly ILoggerFactory _loggerFactory; + + public ConfigureIndexOptions(ILoggerFactory loggerFactory) + => _loggerFactory = loggerFactory; + + public void Configure(string name, LuceneDirectoryIndexOptions options) + { + switch (name) + { + case "MyIndex": + // Create a dictionary for custom value types. + // They keys are the value type names. + options.IndexValueTypesFactory = new Dictionary + { + // Create a phone number value type using the GenericAnalyzerFieldValueType + // to pass in a custom analyzer. As an example, it could use Examine's + // PatternAnalyzer to pass in a phone number pattern to match. + ["phone"] = new DelegateFieldValueTypeFactory(name => + new GenericAnalyzerFieldValueType( + name, + _loggerFactory, + new PatternAnalyzer(@"\d{3}\s\d{3}\s\d{4}", 0))) + }; + + // Add the field definition for a field called "phone" which maps + // to a Value Type called "phone" defined above. + options.FieldDefinitions.AddOrUpdate(new FieldDefinition("phone", "phone")); + break; + } + } + + public void Configure(LuceneDirectoryIndexOptions options) + => throw new NotImplementedException("This is never called and is just part of the interface"); +} +``` + +The above creates a custom field value type using a custom analyzer and maps the "Phone" field to use this value type. + +## ValueSet validators + +An [`IValueSetValidator`](xref:Examine.IValueSetValidator) is a simple interface: + +```cs +public interface IValueSetValidator +{ + ValueSetValidationResult Validate(ValueSet valueSet); +} +``` + +That returns an result [`ValueSetValidationResult`](xref:Examine.ValueSetValidationResult) with an enum of [`ValueSetValidationStatus`](xref:Examine.ValueSetValidationStatus) values: + +* `Valid` - The ValueSet is valid and will be indexed +* `Failed` - The ValueSet was invalid and will not be indexed +* `Filtered` - The ValueSet has been filtered/modified by the validator and will be indexed + +Examine only has one implementation: [`ValueSetValidatorDelegate`](xref:Examine.Lucene.Providers.ValueSetValidatorDelegate) which can be used by developers as a simple way to create a validator based on a callback, else developers can implement this interface if required. By default, no ValueSet validation is done with Examine. diff --git a/docs/v2/articles/indexing.md b/docs/v2/articles/indexing.md new file mode 100644 index 000000000..2358cb8b8 --- /dev/null +++ b/docs/v2/articles/indexing.md @@ -0,0 +1,159 @@ +--- +layout: page +title: Indexing +permalink: /indexing +uid: indexing +order: 0 +--- + +Indexing +=== +_**Tip**: There are many examples of indexing in the [`LuceneIndexTests` source code](https://github.com/Shazwazza/Examine/blob/dev/src/Examine.Test/Index/LuceneIndexTests.cs) to use as examples/reference._ + +Examine will index any data you give it within a [`ValueSet`](xref:Examine.ValueSet). You can index one or multiple items at once and there's a few different ways to do that. Each field in a [`ValueSet`](xref:Examine.ValueSet) can also contain one or more values. + +A [`ValueSet`](xref:Examine.ValueSet) is fairly simple, it is really just: + +* __Id__ _`string`_ - unique identifier for the document +* __Category__ _`string`_ - Required. 1st level categorization +* __ItemType__ _`string`_ - Optional. 2nd level categorization +* __Values__ _`IDictionary>`_ - Any data associated with the document + +It also has some methods that you can use to manipulate it's data. + +## Single values + +_How to index a single [`ValueSet`](xref:Examine.ValueSet) + +[See quickstart](xref:index#quick-start) + +## Multiple values + +_How to index multiple [`ValueSet`](xref:Examine.ValueSet) at once_ + +### With Dictionaries (default) + +```cs +myIndex.IndexItems(new[] +{ + new ValueSet( + "SKU123", + "Product", + new Dictionary() + { + {"Name", "Loud Headphones" }, + {"Brand", "LOUDER" } + }), + new ValueSet( + "SKU987", + "Product", + new Dictionary() + { + {"Name", "USB-C Cable" }, + {"Brand", "Cablez-R-Us" } + }), +}); +``` + +### With Objects + +```cs + +// For example, perhaps you looked up the product from a service +var headphones = ProductService.Get("SKU123"); + +myIndex.IndexItems(new[] +{ + ValueSet.FromObject( + headphones.Id, + "Product", + headphones, + ValueSet.FromObject( + "SKU987", + "Product", + new //Anonymous objects work too + { + Name = "USB-C Cable", + Brand = "Cablez-R-Us" + }), +}); +``` + +### Multiple values per field + +It is possible to have multiple values for an individual field, you can just pass in an instance of `IDictionary>` to the [`ValueSet`](xref:Examine.ValueSet) constructor. + +```cs +myIndex.IndexItem(new ValueSet( + Guid.NewGuid().ToString(), + "TestType", + new Dictionary>() + { + {"Name", new object[]{ "Frank" }}, + // For example, perhaps each address part is a separate value + {"Address", new object[]{ "Beverly Hills", "90210" } } + })); +``` + +### Strongly typed + +As you can see, the values being passed into the ValueSet are type `object`. Examine will determine if the object type maps to a field definition + +```cs +myIndex.IndexItem(new ValueSet( + "SKU987", + "Product", + new Dictionary() + { + {"Name", "USB-C Cable" }, + {"Brand", "Cablez-R-Us" }, + {"Price", 19.99} // non-string value + })); +``` + +### Synchronously + +Be default all indexing is done asynchronously. If you need to run indexing synchronously you should create a synchronous scope. This is for instance a necessary step for unit tests. + +```cs +using (myIndex.ProcessNonAsync()) +{ + myIndex.IndexItem(new ValueSet( + "SKU987", + "Product", + new Dictionary() + { + {"Name", "USB-C Cable" }, + {"Brand", "Cablez-R-Us" }, + {"Price", 19.99} // non-string value + })); +} +``` + +## Deleting index data + +Data is easily deleted from the index by the unique identifier you provided in your [`ValueSet`](xref:Examine.ValueSet) by using the `DeleteFromIndex` method. For example: + +```cs + indexer.DeleteFromIndex("SKU987"); +``` + +## Events + +#### [IIndex.IndexOperationComplete](xref:Examine.IIndex#Examine_IIndex_IndexOperationComplete) + +This event is part of the base interface [`IIndex`](xref:Examine.IIndex) so it is available to use on any implementation of an Examine index. This can be useful to know when an indexing operation is completed. + +#### [IIndex.TransformingIndexValues](xref:Examine.IIndex#Examine_IIndex_TransformingIndexValues) + +This event allows for customizing the [`ValueSet`](xref:Examine.ValueSet) before it is passed to the indexer to be indexed. You can use this event to add additional field values or modify existing field values. + +#### [IIndex.IndexingError](xref:Examine.IIndex#Examine_IIndex_IndexingError) + +This event can be used for reacting to when an error occurs during index. For example, you could add an event handler for this event to facilitate error logging. + +#### [LuceneIndex.DocumentWriting](xref:Examine.Lucene.Providers.LuceneIndex#Examine_Lucene_Providers_LuceneIndex_DocumentWriting) + +If using Examine with the default Lucene implementation then the [`IIndex`](xref:Examine.IIndex) implementation will be [`LuceneIndex`](xref:Examine.Lucene.Providers.LuceneIndex). This event provides access to the Lucene [`Document`](https://lucenenet.apache.org/docs/4.8.0-beta00016/api/core/Lucene.Net.Documents.Document.html) object before it gets added to the Lucene Index. + +You can use this event to entirely customize how the data is stored in the Lucene index, including adding custom boosting profiles, changing the [`Document`](https://lucenenet.apache.org/docs/4.8.0-beta00016/api/core/Lucene.Net.Documents.Document.html)'s field values or types, etc... \ No newline at end of file diff --git a/docs/v2/articles/searching.md b/docs/v2/articles/searching.md new file mode 100644 index 000000000..017fc1e68 --- /dev/null +++ b/docs/v2/articles/searching.md @@ -0,0 +1,260 @@ +--- +layout: page +title: Searching +permalink: /searching +uid: searching +order: 2 +--- +Searching +=== + +_**Tip**: There are many examples of searching in the [`FluentApiTests` source code](https://github.com/Shazwazza/Examine/blob/release/3.0/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs) to use as examples/reference._ + + +## Common + +Obtain an instance of [`ISearcher`](xref:Examine.ISearcher) for the index to be searched from [`IExamineManager`](xref:Examine.IExamineManager). + +## All fields (managed queries) + +The simplest way of querying with Examine is with the [`Search`](xref:Examine.ISearcher#Examine_ISearcher_Search_System_String_Examine_Search_QueryOptions_) method: + +```cs +var results = searcher.Search("hello world"); +``` + +The above is just shorthand for doing this: + +```cs +var query = searcher.CreateQuery().ManagedQuery("hello world"); +var results = query.Execute(QueryOptions.Default); +``` + +A Managed query is a search operation that delegates to the underlying field types to determine how the field should +be searched. In most cases the field value type will be 'Full Text', others may be numeric fields, etc... So the query is built up based on the data passed in and what each field type is capable of searching. + +## Per field + +To create a query using the fluent builder query syntax start by calling [`ISearcher.CreateQuery()`](xref:Examine.ISearcher#Examine_ISearcher_Search_System_String_Examine_Search_QueryOptions_) then chain options as required. +Finally call [`Execute`](xref:Examine.Search.IQueryExecutor#Examine_Search_IQueryExecutor_Execute_Examine_Search_QueryOptions_) to execute the query. + +```csharp +var searcher = myIndex.Searcher; // Get a searcher +var results = searcher.CreateQuery() // Create a query + .Field("Address", "Hills") // Look for any "Hills" addresses + .Execute(); // Execute the search +``` + +### Terms and Phrases + +When searching on fields like in the example above you might want to search on more than one word/term. In Examine this can be done by simply adding more terms to the field filter. + +```csharp +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + // Look for any addresses that has "Hills" or "Rockyroad" or "Hollywood" + .Field("Address", "Hills Rockyroad Hollywood") + .Execute(); +``` + +The way that terms are split depends on the Analyzer being used. The [`StandardAnalyzer`](https://lucenenet.apache.org/docs/4.8.0-beta00016/api/analysis-common/Lucene.Net.Analysis.Standard.StandardAnalyzer.html) is the default. An example of how Analyzers work are: + +- [`StandardAnalyzer`](https://lucenenet.apache.org/docs/4.8.0-beta00016/api/analysis-common/Lucene.Net.Analysis.Standard.StandardAnalyzer.html) - will split a string based on whitespace and 'stop words' (i.e. common words that are not normally searched on like "and") +- [`WhitespaceAnalyzer`](https://lucenenet.apache.org/docs/4.8.0-beta00016/api/analysis-common/Lucene.Net.Analysis.Core.WhitespaceAnalyzer.html) - will split a string based only on whitespace +- [`KeywordAnalyzer`](https://lucenenet.apache.org/docs/4.8.0-beta00016/api/analysis-common/Lucene.Net.Analysis.Core.KeywordAnalyzer.html) - will not split a string and will treat the single string as one term - this means that searching will be done on an exact match + +There are many [Analyzers](https://lucenenet.apache.org/docs/4.8.0-beta00016/api/core/Lucene.Net.Analysis.html) and you can even create your own. See more about analyzers in [configuration](./configuration.md#example---phone-number). + +Looking at this example when using the default StandardAnalyser the code above means that the `Address` in this example has to match any the values set in the statement. This is because Examine will create a Lucene query like this one where every word is matched separately: `Address:hills Address:rockyroad Address:hollywood`. + +Instead, if you want to search for entries with the values above in that exact order you specified you will need to use the [`.Escape()`](xref:Examine.SearchExtensions#Examine_SearchExtensions_Escape_System_String_) method. See under [Escape](#escape). + +```csharp +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + // Look for any addresses with the exact phrase "Hills Rockyroad Hollywood" + .Field("Address", "Hills Rockyroad Hollywood".Escape()) + .Execute(); +``` + +This creates a query like this instead: `Address:"Hills Rockyroad Hollywood"`. This means that you're now searching for the exact phrase instead of entries where terms appear. + +## Range queries + +Range Queries allow one to match documents whose field(s) values are between the lower and upper bound specified by the Range Query + +### Float Range + +Example: + +```csharp +var searcher = myIndex.Searcher; +var query = searcher.CreateQuery(); +query.RangeQuery(new[] { "SomeFloat" }, 0f, 100f, minInclusive: true, maxInclusive: true); +var results = query.Execute(QueryOptions.Default); +``` + +This will return results where the field `SomeFloat` is within the range 0 - 100 (min value and max value included). + +### Date Range + +Example: + +```csharp +var searcher = indexer.Searcher; + +var query = searcher.CreateQuery() + .RangeQuery( + new[] { "created" }, + new DateTime(2000, 01, 02), + new DateTime(2000, 01, 05), + minInclusive: true, + maxInclusive: false); + +var results = query.Execute(); +``` + +This will return results where the field `created` is within the date 2000/01/02 and 2000/01/05 (min value included and max value excluded). + +## Booleans, Groups & Sub Groups + +_TODO: Fill this in..._ + +## Boosting + +Boosting is the practice of making some parts of your query more relevant than others. This means that you can have terms that will make entries matching that term score higher in the search results. + +Example: + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery("content"); +query.Field("nodeTypeAlias", "CWS_Home".Boost(20)); +var results = query.Execute(); +``` + +This will boost the term `CWS_Home` and make enteries with `nodeTypeAlias:CWS_Home` score higher in the results. + +## Proximity + +Proximity searching helps in finding entries where words that are within a specific distance away from each other. + +Example: + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery("content"); + +// Get all nodes that contain the words warren and creative within 5 words of each other +query.Field("metaKeywords", "Warren creative".Proximity(5)); +var results = query.Execute(); +``` + +## Fuzzy + +Fuzzy searching is the practice of finding spellings that are similar to each other. Examine searches based on the [Damerau-Levenshtein Distance](https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance). The parameter given in the [`.Fuzzy()`](xref:Examine.SearchExtensions#Examine_SearchExtensions_Fuzzy_System_String_) method is the edit distance allowed by default this value is `0.5` if not specified. + +The value on [`.Fuzzy()`](xref:Examine.SearchExtensions#Examine_SearchExtensions_Fuzzy_System_String_System_Single_) can be between 0 and 2. Any number higher than 2 will be lowered to 2 when creating the query. + +Example: + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery(); + +query.Field("Content", "think".Fuzzy(0.1F)); +var results = query.Execute(); +``` + +## Escape + +Escapes the string within Lucene. + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery("content"); + +query.Field("__Path", "-1,123,456,789".Escape()); +var results = query.Execute(); +``` + +## Wildcards + +Examine supports single and multiple-character wildcards on single terms. (Cannot be used with the `.Escape()` method) + +### Examine query (Single character) + +The [`.SingleCharacterWildcard()`](xref:Examine.SearchExtensions#Examine_SearchExtensions_Escape_System_String_) method will add a single character wildcard to the end of the term or terms being searched on a field. + +```csharp +var query = searcher.CreateQuery() + .Field("type", "test".SingleCharacterWildcard()); +``` + +This will match for example: `test` and `tests` + +### Examine query (Multiple characters) + +The [`.MultipleCharacterWildcard()`](xref:Examine.SearchExtensions#Examine_SearchExtensions_MultipleCharacterWildcard_System_String_) method will add a multiple characters wildcard to the end of the term or terms being searched on a field. + +```csharp +var query = searcher.CreateQuery() + .Field("type", "test".MultipleCharacterWildcard()); +``` + +This will match for example: `test`, `tests` , `tester`, `testers` + +### Lucene native query (Multiple characters) + +The multiple wildcard character is `*`. It will match 0 or more characters. + +Example + +```csharp +var query = searcher.CreateQuery() + .NativeQuery("equipment:t*pad"); +``` + +This will match for example: `Trackpad` and `Teleportationpad` + +Example + +```csharp +var query = searcher.CreateQuery() + .NativeQuery("role:test*"); +``` + +This will match for example: `test`, `tests` and `tester` + +## Lucene queries + +Find a reference to how to write Lucene queries in the [Lucene 4.8.0 docs](https://lucene.apache.org/core/4_8_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package_description). + +### Native Query + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery(); +var results = query.NativeQuery("hello:world").Execute(); +``` + +### Combine a native query and Fluent API searching. + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery(); +query.NativeQuery("hello:world"); +query.And().Field("Address", "Hills"); // Combine queries +var results = query.Execute(); +``` + +### Combine a custom lucene query with raw lucene query + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery(); + +var query = (LuceneSearchQuery)query.NativeQuery("hello:world").And(); // Make query ready for extending +query.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); // Add the raw lucene query +var results = query.Execute(); +``` \ No newline at end of file diff --git a/docs/v2/articles/sorting.md b/docs/v2/articles/sorting.md new file mode 100644 index 000000000..3b5a8565a --- /dev/null +++ b/docs/v2/articles/sorting.md @@ -0,0 +1,79 @@ +--- +layout: page +title: Sorting +permalink: /sorting +uid: sorting +order: 3 +--- + +Sorting +=== + +_**Tip**: There are many examples of sorting in the [`FluentApiTests` source code](https://github.com/Shazwazza/Examine/blob/master/src/Examine.Test/Search/FluentApiTests.cs) to use as examples/reference._ + +## Score + +By default search results are ordered by Score descending so there's nothing specific that needs to be done to support this. If you use a different sorting operation then the [`Score`](xref:Examine.ISearchResult#Examine_ISearchResult_Score) value will be 0 for all results. + +## Custom sorting + +Any field that is a [numerical or date based](https://shazwazza.github.io/Examine/configuration.html#default-value-types) is automatically sortable. To make text based fields sortable you need to explicitly opt-in for that behavior. By default all fields are [`FieldDefinitionTypes.FullText`](https://shazwazza.github.io/Examine/configuration.html#default-value-types) which are not sortable. To make a text field sortable it needs to be [`FieldDefinitionTypes.FullTextSortable`](xref:Examine.FieldDefinitionTypes#Examine_FieldDefinitionTypes_FullTextSortable). + +_You cannot sort on both the score and a custom field._ + +Sorting is done by either the [`OrderBy`](xref:Examine.Search.IOrdering#Examine_Search_IOrdering_OrderBy_Examine_Search_SortableField___) or [`OrderByDescending`](xref:Examine.Search.IOrdering#Examine_Search_IOrdering_OrderByDescending_Examine_Search_SortableField___) methods using a [`SortableField`](xref:Examine.Search.SortableField) and a [`SortType`](xref:Examine.Search.SortType). The [`SortType`](xref:Examine.Search.SortType) should typically match the field definition type (i.e. Int, Long, Double, etc...) + +* For [`FieldDefinitionTypes.FullTextSortable`](xref:Examine.FieldDefinitionTypes#Examine_FieldDefinitionTypes_FullTextSortable) use [`SortType.String`](xref:Examine.Search.SortType) +* For [`FieldDefinitionTypes.DateTime`](xref:Examine.FieldDefinitionTypes#Examine_FieldDefinitionTypes_DateTime) use [`SortType.Long`](xref:Examine.Search.SortType). + +Example: + +```cs + var searcher = indexer.GetSearcher(); + + var orderedResults = searcher + .CreateQuery("content") + .Field("writerName", "administrator") + .OrderBy(new SortableField("name", SortType.String)) + .Execute(); + +var orderedDescendingResults = searcher + .CreateQuery("content") + .Field("writerName", "administrator") + .OrderByDescending(new SortableField("name", SortType.String)) + .Execute(); +``` + +## Paging and Limiting results + +To limit results we can use the [`QueryOptions`](xref:Examine.Search.QueryOptions) class when executing a search query. The [`QueryOptions`](xref:Examine.Search.QueryOptions) class provides the ability to skip and take. + +Examples: + +```csharp + var searcher = indexer.GetSearcher(); + + var takeFirstTenInIndex = searcher + .CreateQuery() + .All() + .Execute(QueryOptions.SkipTake(0, 10)) + + var skipFiveAndTakeFirstTenInIndex = searcher + .CreateQuery() + .All() + .Execute(QueryOptions.SkipTake(5, 10)) + + var takeThreeResults = searcher + .CreateQuery("content") + .Field("writerName", "administrator") + .OrderBy(new SortableField("name", SortType.String)) + .Execute(QueryOptions.SkipTake(0, 3)); + +var takeSevenHundredResults = searcher + .CreateQuery("content") + .Field("writerName", "administrator") + .OrderByDescending(new SortableField("name", SortType.String)) + .Execute(QueryOptions.SkipTake(0, 700)); +``` + +By default when using [`Execute()`](xref:Examine.Search.IQueryExecutor#Examine_Search_IQueryExecutor_Execute_Examine_Search_QueryOptions_) or `Execute(QueryOptions.SkipTake(0))` where no take parameter is provided the take of the search will be set to [`QueryOptions.DefaultMaxResults`](xref:Examine.Search.QueryOptions#Examine_Search_QueryOptions_DefaultMaxResults) (500). diff --git a/docs/v2/articles/toc.yml b/docs/v2/articles/toc.yml new file mode 100644 index 000000000..2c385e520 --- /dev/null +++ b/docs/v2/articles/toc.yml @@ -0,0 +1,10 @@ +- name: Configuration + href: configuration.md +- name: Indexing + href: indexing.md +- name: Searching + href: searching.md +- name: Sorting + href: sorting.md +- name: Paging + href: sorting.md#paging-and-limiting-results \ No newline at end of file diff --git a/docs/v2/docfx.json b/docs/v2/docfx.json new file mode 100644 index 000000000..a239d9530 --- /dev/null +++ b/docs/v2/docfx.json @@ -0,0 +1,85 @@ +{ + "metadata": [ + { + "src": [ + { + "files": [ + "src/**.csproj" + ], + "exclude":[ + "**/bin/**", + "**/obj/**", + "**/Examine.Test**", + "**/Examine.Web**" + ], + "src": "../../" + } + ], + "dest": "api", + "disableGitFeatures": false, + "disableDefaultFilter": false + } + ], + "build": { + "content": [ + { + "files": [ + "api/**.yml", + "api/index.md" + ] + }, + { + "files": [ + "articles/**.md", + "articles/**/toc.yml", + "toc.yml", + "*.md", + "docs-v1-v2/**.md", + "docs-v1-v2/**/toc.yml" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "overwrite": [ + { + "files": [ + "apidoc/**.md" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "dest": "_site", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "template": [ + "default", + "templates/material" + ], + "postProcessors": ["ExtractSearchIndex"], + "markdownEngineName": "markdig", + "noLangKeyword": false, + "keepFileLink": false, + "cleanupCacheHistory": false, + "disableGitFeatures": false, + "globalMetadata": { + "_appTitle": "Examine", + "_appFooter": "Examine", + "_enableSearch": true, + "_gitContribute": { + "repo": "https://github.com/Shazwazza/Examine", + "branch": "release/v3.0" + }, + "_appLogoPath": "/images/headerlogo.png", + "_appFaviconPath": "/images/favicon.ico" + } + } +} \ No newline at end of file diff --git a/docs/v2/docs-v1-v2/configuration.md b/docs/v2/docs-v1-v2/configuration.md new file mode 100644 index 000000000..70b09967e --- /dev/null +++ b/docs/v2/docs-v1-v2/configuration.md @@ -0,0 +1,250 @@ +--- +layout: page +title: V1/V2 Configuration +permalink: /configuration +uid: v2configuration +order: 1 +--- + +Configuration +=== + +An index can be configured in many ways including different configurations per field such as how those values are analyzed, indexed, tokenized ... basically how the data is stored and retrieved. This is done via Examine ["Value Types"](#value-types). + +_**Note**: This documentation refers to using Lucene based indexes in Examine (the default index type shipped in Examine)._ + +## Field definitions + +A Field Definition is a mapping of a field name to a ["Value Types"](#value-types). By default all fields are mapped to the default Value Type: `FieldDefinitionTypes.FullText`. + +You can map a field to any value type when configuring the index. + +### Examine V2 + +#### IConfigureNamedOptions + +Configuration of Examine indexes is done with [.NET's Options pattern](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0). For Examine, this is done with named options: `IConfigureNamedOptions`. + +There are several options that can be configured, the most common ones are: + +* __FieldDefinitions__ _`FieldDefinitionCollection`_ - Manages the mappings between a field name and it's index value type +* __Analyzer__ _`Analyzer`_ - The default Lucene Analyzer to use for each field (default = `StandardAnalyzer`) +* __Validator__ _`IValueSetValidator`_ - Used to validate a value set to be indexed, if validation fails it will not be indexed +* __IndexValueTypesFactory__ _`IReadOnlyDictionary`_ - Allows you to define custom Value Types + +```cs +/// +/// Configure Examine indexes using .NET IOptions +/// +public sealed class ConfigureIndexOptions : IConfigureNamedOptions +{ + public void Configure(string name, LuceneDirectoryIndexOptions options) + { + switch (name) + { + case "MyIndex": + // Set the "Price" field to map to the 'Double' value type. + options.FieldDefinitions.AddOrUpdate( + new FieldDefinition("Price", FieldDefinitionTypes.Double)); + break; + } + } + + public void Configure(LuceneDirectoryIndexOptions options) + => Configure(string.Empty, options); +} +``` + +### Examine V1 + +#### Via constructor + +The `LuceneIndex` constructor has several **optional** parameters that can be supplied to configure the index: + +* __fieldDefinitions__ _`FieldDefinitionCollection`_ - Manages the mappings between a field name and it's index value type +* __analyzer__ _`Analyzer`_ - The default Lucene Analyzer to use for each field (default = `StandardAnalyzer`) +* __validator__ _`IValueSetValidator`_ - Used to validate a value set to be indexed, if validation fails it will not be indexed +* __indexValueTypesFactory__ _`IReadOnlyDictionary`_ - Allows you to define custom Value Types + +```cs +// Create and add a new index to the manager +var myIndex = examineManager.AddIndex( + new LuceneIndex( + "MyIndex", + new SimpleFSDirectory(new DirectoryInfo("C:\\TestIndexes")), + // Pass in a custom field definition collection + new FieldDefinitionCollection( + // Set the "Price" field to map to the 'Double' value type. + new FieldDefinition("Price", FieldDefinitionTypes.Double)))); +``` + +#### After construction + +You can modify the field definitions for an index after it is constructed by using any of the following methods: + +* `myIndex.FieldDefinitionCollection.TryAdd` +* `myIndex.FieldDefinitionCollection.AddOrUpdate` +* `myIndex.FieldDefinitionCollection.GetOrAdd` + +These modifications __must__ be done before any indexing or searching is executed. + +#### Add a field value type after construction + +It is possible to add custom field value types after the construction of the index, but this must be done before the index is used. Some people may prefer this method of adding custom field value types. Generally, these should be modified directly after the construction of the index. + +```cs +// Create the index with all of the defaults +var myIndex = new LuceneIndex( + "MyIndex", + new SimpleFSDirectory(new DirectoryInfo("C:\\TestIndexes"))); + +// Add a custom field value type +myIndex.FieldValueTypeCollection.ValueTypeFactories + .TryAdd( + "phonenumber", + name => new GenericAnalyzerFieldValueType( + name, + new PhoneNumberAnalyzer())); + +// Map a field to use the custom field value type +myIndex.FieldDefinitionCollection.TryAdd( + new FieldDefinition("Phone", "phonenumber")); +``` + + +## Value types + +Value types are responsible for: + +* Defining a field name and if the field should be sortable, the field to store the sortable data +* Adding a field value to an index document +* Configuring how the value will be stored in the index +* Configuring the analyzer for the field +* Generating the Query for the field + +These are the default field value types provided with Examine. Each value type can be resolved from the static class `Examine.FieldDefinitionTypes` (i.e. `Examine.FieldDefinitionTypes.FullText`). + +| Value Type | Description | Sortable | +|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| +| FullText | __Default__.
The field will be indexed with the index's
default Analyzer without any sortability. 
Generally this is fine for normal text searching. | ❌ | +| FullTextSortable | Will be indexed with FullText but also 
enable sorting on this field for search results. 
_FullText sortability adds additional overhead 
since it requires an additional index field._ | ✅ | +| Integer | Stored as a numerical structure. | ✅ | +| Float | Stored as a numerical structure. | ✅ | +| Double | Stored as a numerical structure. | ✅ | +| Long | Stored as a numerical structure. | ✅ | +| DateTime | Stored as a DateTime, 
represented by a numerical structure. | ✅ | +| DateYear | Just like DateTime but with 
precision only to the year. | ✅ | +| DateMonth | Just like DateTime but with 
precision only to the month. | ✅ | +| DateDay | Just like DateTime but with 
precision only to the day. | ✅ | +| DateHour | Just like DateTime but with 
precision only to the hour. | ✅ | +| DateMinute | Just like DateTime but with 
precision only to the minute. | ✅ | +| EmailAddress | Uses custom analyzers for dealing 
with email address searching. | ❌ | +| InvariantCultureIgnoreCase | Uses custom analyzers for dealing with text so it
 can be searched on regardless of the culture/casing. | ❌ | +| Raw | Will be indexed without analysis, searching will
 only match with an exact value. | ❌ | + +### Custom field value types + +A field value type is defined by `IIndexFieldValueType` + +_**Tip**: There are many implementations of IIndexFieldValueType in the source code to use as examples/reference._ + +A common base class that can be used for field value types is: `IndexFieldValueTypeBase`. + +A common implementation that can be used for field value types for custom Analyzers is: `GenericAnalyzerFieldValueType`. + +#### Example - Phone Number + +A phone number stored in Lucene could require a custom analyzer to index and search it properly. So the best way to set this up in Examine would be to have a custom field value type for it. Since this field value type doesn't need to do anything more fancy than to provide a custom analyzer, we can create it with the `GenericAnalyzerFieldValueType`. + +##### Examine V2 + +```cs +/// +/// Configure Examine indexes using .NET IOptions +/// +public sealed class ConfigureIndexOptions : IConfigureNamedOptions +{ + private readonly ILoggerFactory _loggerFactory; + + public ConfigureIndexOptions(ILoggerFactory loggerFactory) + => _loggerFactory = loggerFactory; + + public void Configure(string name, LuceneDirectoryIndexOptions options) + { + switch (name) + { + case "MyIndex": + // Create a dictionary for custom value types. + // They keys are the value type names. + options.IndexValueTypesFactory = new Dictionary + { + // Create a phone number value type using the GenericAnalyzerFieldValueType + // to pass in a custom analyzer. As an example, it could use Examine's + // PatternAnalyzer to pass in a phone number pattern to match. + ["phone"] = new DelegateFieldValueTypeFactory(name => + new GenericAnalyzerFieldValueType( + name, + _loggerFactory, + new PatternAnalyzer(@"\d{3}\s\d{3}\s\d{4}", 0))) + }; + + // Add the field definition for a field called "phone" which maps + // to a Value Type called "phone" defined above. + options.FieldDefinitions.AddOrUpdate(new FieldDefinition("phone", "phone")); + break; + } + } + + public void Configure(LuceneDirectoryIndexOptions options) + => throw new NotImplementedException("This is never called and is just part of the interface"); +} +``` + +##### Examine V1 + +```cs +// Create a writeable dictionary based off of the +// Examine default field value types +var fieldValueTypes = ValueTypeFactoryCollection.DefaultValueTypes + .ToDictionary(x => x.Key, x => x.Value); + +// Add a new phone number field value type +fieldValueTypes.Add( + "phonenumber", // Each field value type needs a unique name + new DelegateFieldValueTypeFactory(name => + new GenericAnalyzerFieldValueType( + name, + new PhoneNumberAnalyzer()))); // Pass in a custom analyzer + +// Create the index with the dictionary +var myIndex = new LuceneIndex( + "MyIndex", + new SimpleFSDirectory(new DirectoryInfo("C:\\TestIndexes")), + // Pass in a custom field definition collection + new FieldDefinitionCollection( + // Set the "Phone" field to map to the 'phonenumber' value type. + new FieldDefinition("Phone", "phonenumber")) + // Pass in the custom field value type dictionary with the phonenumber type + indexValueTypesFactory: fieldValueTypes); +``` + +The above creates a custom field value type using a custom analyzer and maps the "Phone" field to use this value type. + +## ValueSet validators + +An `IValueSetValidator` is a simple interface: + +```cs +public interface IValueSetValidator +{ + ValueSetValidationResult Validate(ValueSet valueSet); +} +``` + +That returns an enum `ValueSetValidationResult` of values: + +* `Valid` - The ValueSet is valid and will be indexed +* `Failed` - The ValueSet was invalid and will not be indexed +* `Filtered` - The ValueSet has been filtered/modified by the validator and will be indexed + +Examine only has one implementation: `ValueSetValidatorDelegate` which can be used by developers as a simple way to create a validator based on a callback, else developers can implement this interface if required. By default, no ValueSet validation is done with Examine. diff --git a/docs/v2/docs-v1-v2/indexing.md b/docs/v2/docs-v1-v2/indexing.md new file mode 100644 index 000000000..b439a0749 --- /dev/null +++ b/docs/v2/docs-v1-v2/indexing.md @@ -0,0 +1,182 @@ +--- +layout: page +title: V1/V2 Indexing +permalink: /indexing +uid: v2indexing +order: 0 +--- + +Indexing +=== +_**Tip**: There are many examples of indexing in the [`LuceneIndexTests` source code](https://github.com/Shazwazza/Examine/blob/dev/src/Examine.Test/Index/LuceneIndexTests.cs) to use as examples/reference._ + +Examine will index any data you give it within a `ValueSet`. You can index one or multiple items at once and there's a few different ways to do that. Each field in a `ValueSet` can also contain one or more values. + +A `ValueSet` is fairly simple, it is really just: + +* __Id__ _`string`_ - unique identifier for the document +* __Category__ _`string`_ - Required. 1st level categorization +* __ItemType__ _`string`_ - Optional. 2nd level categorization +* __Values__ _`IDictionary>`_ - Any data associated with the document + +It also has some methods that you can use to manipulate it's data. + +## Single values + +_How to index a single `ValueSet`_ + +[See quickstart](xref:index#quick-start) + +## Multiple values + +_How to index multiple `ValueSet` at once_ + +### With Dictionaries (default) + +```cs +myIndex.IndexItems(new[] +{ + new ValueSet( + "SKU123", + "Product", + new Dictionary() + { + {"Name", "Loud Headphones" }, + {"Brand", "LOUDER" } + }), + new ValueSet( + "SKU987", + "Product", + new Dictionary() + { + {"Name", "USB-C Cable" }, + {"Brand", "Cablez-R-Us" } + }), +}); +``` + +### With Objects + +```cs + +// For example, perhaps you looked up the product from a service +var headphones = ProductService.Get("SKU123"); + +myIndex.IndexItems(new[] +{ + ValueSet.FromObject( + headphones.Id, + "Product", + headphones, + ValueSet.FromObject( + "SKU987", + "Product", + new //Anonymous objects work too + { + Name = "USB-C Cable", + Brand = "Cablez-R-Us" + }), +}); +``` + +### Multiple values per field + +It is possible to have multiple values for an individual field, you can just pass in an instance of `IDictionary>` to the `ValueSet` constructor. + +```cs +myIndex.IndexItem(new ValueSet( + Guid.NewGuid().ToString(), + "TestType", + new Dictionary>() + { + {"Name", new object[]{ "Frank" }}, + // For example, perhaps each address part is a separate value + {"Address", new object[]{ "Beverly Hills", "90210" } } + })); +``` + +### Strongly typed + +As you can see, the values being passed into the ValueSet are type `object`. Examine will determine if the object type maps to a field definition + +```cs +myIndex.IndexItem(new ValueSet( + "SKU987", + "Product", + new Dictionary() + { + {"Name", "USB-C Cable" }, + {"Brand", "Cablez-R-Us" }, + {"Price", 19.99} // non-string value + })); +``` + +### Synchronously + +Be default all indexing is done asynchronously. If you need to run indexing synchronously you should create a synchronous scope. This is for instance a necessary step for unit tests. + +```cs +using (myIndex.ProcessNonAsync()) +{ + myIndex.IndexItem(new ValueSet( + "SKU987", + "Product", + new Dictionary() + { + {"Name", "USB-C Cable" }, + {"Brand", "Cablez-R-Us" }, + {"Price", 19.99} // non-string value + })); +} +``` + +## Deleting index data + +Data is easily deleted from the index by the unique identifier you provided in your `ValueSet` by using the `DeleteFromIndex` method. For example: + +```cs + indexer.DeleteFromIndex("SKU987"); +``` + +## Events + +### Examine V2 + +#### IIndex.IndexOperationComplete + +This event is part of the base interface `IIndex` so it is available to use on any implementation of an Examine index. This can be useful to know when an indexing operation is completed. + +#### IIndex.TransformingIndexValues + +This event allows for customizing the `ValueSet` before it is passed to the indexer to be indexed. You can use this event to add additional field values or modify existing field values. + +#### IIndex.IndexingError + +This event can be used for reacting to when an error occurs during index. For example, you could add an event handler for this event to facilitate error logging. + +#### LuceneIndex.DocumentWriting + +If using Examine with the default Lucene implementation then the `IIndex` implementation will be `LuceneIndex`. This event provides access to the Lucene `Document` object before it gets added to the Lucene Index. + +You can use this event to entirely customize how the data is stored in the Lucene index, including adding custom boosting profiles, changing the `Document`'s field values or types, etc... + + +### Examine V1 + +#### IIndex.IndexOperationComplete + +This event is part of the base interface `IIndex` so it is available to use on any implementation of an Examine index. This can be useful to know when an indexing operation is completed. + +#### BaseIndexProvider.TransformingIndexValues + +Most Examine index implementations will inherit from `BaseIndexProvider`. This event allows for customizing the `ValueSet` before it is passed to the indexer to be indexed. You can use this event to add additional field values or modify existing field values. + +#### BaseIndexProvider.IndexingError + +Most Examine index implementations will inherit from `BaseIndexProvider`. This event can be used for reacting to when an error occurs during index. For example, you could add an event handler for this event to facilitate error logging. + +#### LuceneIndex.DocumentWriting + +If using Examine with the default Lucene implementation then the `IIndex` implementation will be `LuceneIndex`. This event provides access to the Lucene `Document` object before it gets added to the Lucene Index. + +You can use this event to entirely customize how the data is stored in the Lucene index, including adding custom boosting profiles, changing the `Document`'s field values or types, etc... diff --git a/docs/v2/docs-v1-v2/quickstart.md b/docs/v2/docs-v1-v2/quickstart.md new file mode 100644 index 000000000..3142483a7 --- /dev/null +++ b/docs/v2/docs-v1-v2/quickstart.md @@ -0,0 +1,127 @@ +--- +uid: v2index +title: V1/V2 Quickstart +--- + +Examine Documentation +=== + +## What is Examine? + + Examine allows you to index and search data easily and wraps the Lucene.Net indexing/searching engine. Lucene is _super_ fast and allows for very fast searching even on very large amounts of data. Examine is very extensible and allows you to configure as many indexes as you like and each may be configured individually. Out of the box Examine gives you a Lucene based index implementation as well as a Fluent API that can be used to search for your data. + +Examine is installed via Nuget: [https://www.nuget.org/packages/Examine](https://www.nuget.org/packages/Examine) + +## [Conceptual Documentation](xref:v2indexing) +Conceptual documentation is available. + + +_**Tip**: There are many unit tests in the source code that can be used as Examples of how to do things. There is also a test web project that has plenty of examples of how to configure indexes and search them._ + +* [Indexing](xref:v2indexing) +* [Configuration](xref:v2configuration) +* [Searching](xref:v2searching) +* [Sorting](xref:v2sorting) + +## Minimum requirements + +| Examine Version | .NET | +| --------------- | ---- | +| V2 | .NET Standard 2.0 | +| V1 | .NET Framework 4.5.2 | + +## Quick Start + + +#### Examine V2 + +**Tip**: `IExamineManager` is the gateway to working with examine. It is registered in DI as a singleton and can be injected into your services._ + +1. Install + + ```powershell + > dotnet add package Examine --version 2.1.0 + ``` + +1. Configure Services and create an index + + ```cs + + // Adds Examine Core services + services.AddExamine(); + + // Create a Lucene based index + services.AddExamineLuceneIndex("MyIndex"); + ``` + +1. Populate the index + + ```cs + // Add a "ValueSet" (document) to the index + // which can contain any data you want. + myIndex.IndexItem(new ValueSet( + Guid.NewGuid().ToString(), //Give the doc an ID of your choice + "MyCategory", //Each doc has a "Category" + new Dictionary() + { + {"Name", "Frank" }, + {"Address", "Beverly Hills, 90210" } + })); + ``` + +1. Search the index + + ```cs + // Create a query + var results = myIndex.Searcher.CreateQuery() + .Field("Address", "Hills") // Look for any "Hills" addresses + .Execute(); // Execute the search + ``` + +#### Examine V1 + +**Tip**: `IExamineManager` is the gateway to working with examine. It can be registered in DI as a singleton or can be accessed via `ExamineManager.Instance`._ + +1. Install + + ```powershell + PM> Install-Package Examine -Version 1.2.2 + ``` + +1. Create an index + + ```cs + public void CreateIndexes(IExamineManager examineManager) + { + //Create and add a new index to the manager + var myIndex = examineManager.AddIndex( + new LuceneIndex( // Create a Lucene based index + "MyIndex", // Named MyIndex + new SimpleFSDirectory( // In a location of your choice + new DirectoryInfo("C:\\TestIndexes")))); + } + ``` + +1. Populate the index + + ```cs + // Add a "ValueSet" (document) to the index + // which can contain any data you want. + myIndex.IndexItem(new ValueSet( + Guid.NewGuid().ToString(), //Give the doc an ID of your choice + "MyCategory", //Each doc has a "Category" + new Dictionary() + { + {"Name", "Frank" }, + {"Address", "Beverly Hills, 90210" } + })); + ``` + +1. Search the index + + ```cs + var searcher = myIndex.GetSearcher(); // Get a searcher + var results = searcher.CreateQuery() // Create a query + .Field("Address", "Hills") // Look for any "Hills" addresses + .Execute(); // Execute the search + ``` \ No newline at end of file diff --git a/docs/v2/docs-v1-v2/searching.md b/docs/v2/docs-v1-v2/searching.md new file mode 100644 index 000000000..bca9a0a37 --- /dev/null +++ b/docs/v2/docs-v1-v2/searching.md @@ -0,0 +1,252 @@ +--- +layout: page +title: V1/V2 Searching +permalink: /searching +uid: v2searching +order: 2 +--- +Searching +=== + +_**Tip**: There are many examples of searching in the [`FluentApiTests` source code](https://github.com/Shazwazza/Examine/blob/master/src/Examine.Test/Search/FluentApiTests.cs) to use as examples/reference._ + +## All fields (managed queries) + +The simplest way of querying with Examine is with the `Search` method: + +```cs +var results = searcher.Search("hello world"); +``` + +The above is just shorthand for doing this: + +```cs +var query = searcher.CreateQuery().ManagedQuery("hello world"); +var results = query.Execute(QueryOptions.Default); +``` + +A Managed query is a search operation that delegates to the underlying field types to determine how the field should +be searched. In most cases the field value type will be 'Full Text', others may be numeric fields, etc... So the query is built up based on the data passed in and what each field type is capable of searching. + +## Per field + +```csharp +var searcher = myIndex.Searcher; // Get a searcher +var results = searcher.CreateQuery() // Create a query + .Field("Address", "Hills") // Look for any "Hills" addresses + .Execute(); // Execute the search +``` + +### Terms and Phrases + +When searching on fields like in the example above you might want to search on more than one word/term. In Examine this can be done by simply adding more terms to the field filter. + +```csharp +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + // Look for any addresses that has "Hills" or "Rockyroad" or "Hollywood" + .Field("Address", "Hills Rockyroad Hollywood") + .Execute(); +``` + +The way that terms are split depends on the Analyzer being used. The StandardAnalyzer is the default. An example of how Analyzers work are: + +- StandardAnalyzer - will split a string based on whitespace and 'stop words' (i.e. common words that are not normally searched on like "and") +- WhitespaceAnalyzer - will split a string based only on whitespace +- KeywordAnalyzer - will not split a string and will treat the single string as one term - this means that searching will be done on an exact match + +There are many [Analyzers](https://lucenenet.apache.org/docs/4.8.0-beta00016/api/core/Lucene.Net.Analysis.html) and you can even create your own. See more about analyzers in [configuration](./configuration.md#example---phone-number). + +Looking at this example when using the default StandardAnalyser the code above means that the `Address` in this example has to match any the values set in the statement. This is because Examine will create a Lucene query like this one where every word is matched separately: `Address:hills Address:rockyroad Address:hollywood`. + +Instead, if you want to search for entries with the values above in that exact order you specified you will need to use the `.Escape()` method. See under [Escape](#escape). + +```csharp +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + // Look for any addresses with the exact phrase "Hills Rockyroad Hollywood" + .Field("Address", "Hills Rockyroad Hollywood".Escape()) + .Execute(); +``` + +This creates a query like this instead: `Address:"Hills Rockyroad Hollywood"`. This means that you're now searching for the exact phrase instead of entries where terms appear. + +## Range queries + +Range Queries allow one to match documents whose field(s) values are between the lower and upper bound specified by the Range Query + +### Float Range + +Example: + +```csharp +var searcher = myIndex.Searcher; +var query = searcher.CreateQuery(); +query.RangeQuery(new[] { "SomeFloat" }, 0f, 100f, minInclusive: true, maxInclusive: true); +var results = query.Execute(QueryOptions.Default); +``` + +This will return results where the field `SomeFloat` is within the range 0 - 100 (min value and max value included). + +### Date Range + +Example: + +```csharp +var searcher = indexer.Searcher; + +var query = searcher.CreateQuery() + .RangeQuery( + new[] { "created" }, + new DateTime(2000, 01, 02), + new DateTime(2000, 01, 05), + minInclusive: true, + maxInclusive: false); + +var results = query.Execute(); +``` + +This will return results where the field `created` is within the date 2000/01/02 and 2000/01/05 (min value included and max value excluded). + +## Booleans, Groups & Sub Groups + +_TODO: Fill this in..._ + +## Boosting + +Boosting is the practice of making some parts of your query more relevant than others. This means that you can have terms that will make entries matching that term score higher in the search results. + +Example: + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery("content"); +query.Field("nodeTypeAlias", "CWS_Home".Boost(20)); +var results = query.Execute(); +``` + +This will boost the term `CWS_Home` and make enteries with `nodeTypeAlias:CWS_Home` score higher in the results. + +## Proximity + +Proximity searching helps in finding entries where words that are within a specific distance away from each other. + +Example: + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery("content"); + +// Get all nodes that contain the words warren and creative within 5 words of each other +query.Field("metaKeywords", "Warren creative".Proximity(5)); +var results = query.Execute(); +``` + +## Fuzzy + +Fuzzy searching is the practice of finding spellings that are similar to each other. Examine searches based on the [Damerau-Levenshtein Distance](https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance). The parameter given in the `.Fuzzy()` method is the edit distance allowed by default this value is `0.5` if not specified. + +The value on `.Fuzzy()` can be between 0 and 2. Any number higher than 2 will be lowered to 2 when creating the query. + +Example: + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery(); + +query.Field("Content", "think".Fuzzy(0.1F)); +var results = query.Execute(); +``` + +## Escape + +Escapes the string within Lucene. + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery("content"); + +query.Field("__Path", "-1,123,456,789".Escape()); +var results = query.Execute(); +``` + +## Wildcards + +Examine supports single and multiple-character wildcards on single terms. (Cannot be used with the `.Escape()` method) + +### Examine query (Single character) + +The `.SingleCharacterWildcard()` method will add a single character wildcard to the end of the term or terms being searched on a field. + +```csharp +var query = searcher.CreateQuery() + .Field("type", "test".SingleCharacterWildcard()); +``` + +This will match for example: `test` and `tests` + +### Examine query (Multiple characters) + +The `.MultipleCharacterWildcard()` method will add a multiple characters wildcard to the end of the term or terms being searched on a field. + +```csharp +var query = searcher.CreateQuery() + .Field("type", "test".MultipleCharacterWildcard()); +``` + +This will match for example: `test`, `tests` , `tester`, `testers` + +### Lucene native query (Multiple characters) + +The multiple wildcard character is `*`. It will match 0 or more characters. + +Example + +```csharp +var query = searcher.CreateQuery() + .NativeQuery("equipment:t*pad"); +``` + +This will match for example: `Trackpad` and `Teleportationpad` + +Example + +```csharp +var query = searcher.CreateQuery() + .NativeQuery("role:test*"); +``` + +This will match for example: `test`, `tests` and `tester` + +## Lucene queries + +Find a reference to how to write Lucene queries in the [Lucene 4.8.0 docs](https://lucene.apache.org/core/4_8_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package_description). + +### Native Query + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery(); +var results = query.NativeQuery("hello:world").Execute(); +``` + +### Combine a native query and Fluent API searching. + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery(); +query.NativeQuery("hello:world"); +query.And().Field("Address", "Hills"); // Combine queries +var results = query.Execute(); +``` + +### Combine a custom lucene query with raw lucene query + +```csharp +var searcher = indexer.Searcher; +var query = searcher.CreateQuery(); + +var query = (LuceneSearchQuery)query.NativeQuery("hello:world").And(); // Make query ready for extending +query.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); // Add the raw lucene query +var results = query.Execute(); +``` \ No newline at end of file diff --git a/docs/v2/docs-v1-v2/sorting.md b/docs/v2/docs-v1-v2/sorting.md new file mode 100644 index 000000000..9382d79cc --- /dev/null +++ b/docs/v2/docs-v1-v2/sorting.md @@ -0,0 +1,115 @@ +--- +layout: page +title: V1/V2 Sorting +permalink: /sorting +uid: v2sorting +order: 3 +--- + +Sorting +=== + +_**Tip**: There are many examples of sorting in the [`FluentApiTests` source code](https://github.com/Shazwazza/Examine/blob/master/src/Examine.Test/Search/FluentApiTests.cs) to use as examples/reference._ + +## Score + +By default search results are ordered by Score descending so there's nothing specific that needs to be done to support this. If you use a different sorting operation then the `Score` value will be 0 for all results. + +## Custom sorting + +Any field that is a [numerical or date based](https://shazwazza.github.io/Examine/configuration.html#default-value-types) is automatically sortable. To make text based fields sortable you need to explicitly opt-in for that behavior. By default all fields are [`FieldDefinitionTypes.FullText`](https://shazwazza.github.io/Examine/configuration.html#default-value-types) which are not sortable. To make a text field sortable it needs to be [`FieldDefinitionTypes.FullTextSortable`](https://shazwazza.github.io/Examine/configuration.html#default-value-types). + +_You cannot sort on both the score and a custom field._ + +Sorting is done by either the `OrderBy` or `OrderByDescending` methods using a `SortableField` and a `SortType`. The `SortType` should typically match the field definition type (i.e. Int, Long, Double, etc...) + +* For `FieldDefinitionTypes.FullTextSortable` use `SortType.String` +* For `FieldDefinitionTypes.DateTime` use `SortType.Long`. + +Example: + +```cs + var searcher = indexer.GetSearcher(); + + var orderedResults = searcher + .CreateQuery("content") + .Field("writerName", "administrator") + .OrderBy(new SortableField("name", SortType.String)) + .Execute(); + +var orderedDescendingResults = searcher + .CreateQuery("content") + .Field("writerName", "administrator") + .OrderByDescending(new SortableField("name", SortType.String)) + .Execute(); +``` + +## Limiting results + +To limit results we can use the `QueryOptions` class when executing a search query. The `QueryOptions` class provides the ability to skip and take. + +Examples: + +```csharp + var searcher = indexer.GetSearcher(); + + var takeFirstTenInIndex = searcher + .CreateQuery() + .All() + .Execute(QueryOptions.SkipTake(0, 10)) + + var skipFiveAndTakeFirstTenInIndex = searcher + .CreateQuery() + .All() + .Execute(QueryOptions.SkipTake(5, 10)) + + var takeThreeResults = searcher + .CreateQuery("content") + .Field("writerName", "administrator") + .OrderBy(new SortableField("name", SortType.String)) + .Execute(QueryOptions.SkipTake(0, 3)); + +var takeSevenHundredResults = searcher + .CreateQuery("content") + .Field("writerName", "administrator") + .OrderByDescending(new SortableField("name", SortType.String)) + .Execute(QueryOptions.SkipTake(0, 700)); +``` + +By default when using `Execute()` or `Execute(QueryOptions.SkipTake(0))` where no take parameter is provided the take of the search will be set to `QueryOptions.DefaultMaxResults` (500). + +## Paging + +There's a [blog post writeup here](https://shazwazza.com/post/paging-with-examine/) on how to properly page with Examine (and Lucene). + +There are 2 important parts to this: + +* The [Skip](https://github.com/Shazwazza/Examine/blob/master/src/Examine/ISearchResults.cs#L11) method on the `ISearchResults` object +* The [Search](https://github.com/Shazwazza/Examine/blob/master/src/Examine/Providers/BaseSearchProvider.cs#L22) overload on the `BaseSearchProvider` where you can specify `maxResults` + +`ISearchResults.Skip` 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` 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. + +Lucene isn't perfect for paging because it doesn’t natively support the Linq equivalent to "Skip/Take" _(UPDATE: In an upcoming Examine version, it can natively support this!)_. 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. + +With the combination of `ISearchResult.Skip` and `maxResults`, we can tell Lucene to: + +* Skip over a certain number of results without allocating them and tell Lucene +* only allocate a certain number of results after skipping + +### Example + +```cs +//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; +``` diff --git a/docs/v2/docs-v1-v2/toc.yml b/docs/v2/docs-v1-v2/toc.yml new file mode 100644 index 000000000..7b182156b --- /dev/null +++ b/docs/v2/docs-v1-v2/toc.yml @@ -0,0 +1,10 @@ +- name: V1 / V2 Articles + href: quickstart.md +- name: Configuration + href: configuration.md +- name: Indexing + href: indexing.md +- name: Searching + href: searching.md +- name: Sorting + href: sorting.md diff --git a/docs/v2/favicon-16x16.png b/docs/v2/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b6fe8ef9db19dbd7f196812d5d1ae647d130f4 GIT binary patch literal 1222 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>6 zAINB{WBC6L#4XMPkyuf29!psvTX_+7S+EwM0xYs{GvN%Z=2sL6#m7LjD+dMgW~D=o zDlNb?lex5zxvYqbWpyv5#%JGQK`v3HS_aUUAp_>^Yd>% zu0ML*I&IprW5<>rJ-YD3G1ZP1_MCLj>W1oL7uqi0?!9umeBm;~iVBI=CeivT-s&=m z)TH3*x~zpuBRYGuvopo&YlIu?WZPT0tIK8LV#HIE1adMsveNjfO1Y{^#hM$``+CKi z8hBF@H3I`!igNjCE9E-cxT?z;i*i*0{6$j|8A}V)LV}r#^Zx(;ADpdv4;ZudB|(0m z^a2Dt&P_`eH#l-JTCRKl@87%CrooRN_&vI3!o6-er^{>G#($NwXO+KwWxa~S?EY;- zzx#JQjaR&RZBSwLYU-kYK>dtK-tI2*Uu>N`7s%l(@Q5sCV2E)8VMdWD&cMXTz+U3% z>&pI=LsHa0C&Az298l=Dr;B5V#O36KgoKoolu&_$k)X-Aj+}K*@ zuxV@_3adA++_`k?+P#a7wY?`dT5F$PyL$KX?d$6g9r27j6vbn6>*nO;K4;lh%{Hrb zo7Shty#C&q$jy#_)~vCNjGXg}r-!Yr_<5>h<3z&?OV6k+GuSuN@`SO@8nJ0%haw{n zU7KST@ZLyk)r#zl8@FCI|6mZ}aBcYf=V&u97$i$vBTAg}b8}PkN*J7rQWHy3QxwWG zOEMJPJ$(bh8~Mb6ic~?0Qh-uc$zT->Mg~TPx&{`yMy4S~hE^sPRwgFe1_o9J2Eol? z7g03i=BH$)RpQp5bnjg*P(yfCNJL3cV!1*=QGQxxPO3slWkIS!W2I`Q&6e6=(&6r>mdKI;Vst0Co$;ng9R* literal 0 HcmV?d00001 diff --git a/docs/v2/favicon-32x32.png b/docs/v2/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..20c3b209393c303396c79f5328793cf8fe12c431 GIT binary patch literal 2081 zcmZ`)dpOe#8~)84X3EAYNo97}T7skemJIZ5nI$YD0U-u3j{UQ(0xz<0CtjycJ5+E z{sKf=+|MK=tcpqUw6(J}05n~Z+oDSS5`&2D&H#{r1^{*%00_k+_E!Kniv@s%006+< z1OO#Q=@T~#u^=6I+`$gm{#E5qtMkPZSq71E1^|5Re*v_sz*f8(kYSRX2{N+~<=xmL z0WQRTaWI2qXHE9*dQe1(@byyeNsccw-{}8TynGIw$FGbDtgU=hkcDoL468-jSS+)l zunz0WXTUvJhL`_22$_Ia2xJkE?mFs_w`mfYcPmNbZ!x=Tx#v_y4Q7_>d`l~U%|&m|@=s^Qq+qRQVm4!pCFumFD+ z`N2;;1Wo3I!oYSnSX(KVNg`L)yn2O72zo{~uNW;9;s(PXs*T7e7(3TKBlVnZI)7!& z9^A9hT3Ex$(cuYfoWPb#S~M5v7k~S|2tVmlbNDCW=XDMGs`ISyYw<@J1DQc%qT!jG zJ#yY9>3q}rF@e(DfaO5lt&TtRy?NI=r(OY7Ngpd5 z@F1coZLx)-gIibwlvEY>v7gHxLb^|(qlq>sEA3_2$z02%)JD#?Ki;k2y0u6zV+(l3 zWlo)auFy)Y*>tNFxKjVm^nu(f21Z5%xa4SfNtJ>NTJYz{Z;bUd*3}RQPtcRXGQ0Pg z{G2a&e=h$745Zk6qdj`W&>4Z+)AemQl+*EpI34{4+uQxeL?oA8Wgoh@Xnykg61gys zOP~yE#OiP~W1f0CM!&^}~I9Iv9y5a%IxD=OQ20ahzT@Z4NdMC1wxCAcv&h zI#}VyIq+NpjV5W2zMM8yWKy)TRSh?3+uY!Fc-85(a#GzNaN3?_%_Rn7veP4j+M{Zc zjDFyNi7su$$W(52H~gw917Mm{+=j2qA9rdQ#M?A2N(rCg(o={R|!V&tg($zbNmuH|D3WyY9x z18sR-kbmoeZBWfB?ZwicI)?~Uy*pj+$m@lEmq%|Or*m!IN4<}W(8&-?vLnN#7d3|GtO>eM%_xya?f@0~sJte%81>4#u*vAN0ngIhq(zZDx z2o!7vOWG|^7m;7v_UbHM%vcY5xbgycq9|Vnoo-ywn10H*xzX;DF6*Q2)T}Du#hzO7 z;$DEmTlk5Po*K$MYU}MAp~C8Jgk@4G&|nI4suVqUXE+2}DK*(`l^#A5foIj1&jF63E_6 zcWOM65gSdTM}#1mEJg^D&R_-sz`4?yl^u{6Pi^ftF1$X{tUCZk$>|2kAyA0p`Mbaf yq#fI^D;SL8l99aO553)e-TS*`cx+<1qb{(Zq`+LUF)9?F07!%rb`3TGss94r@Q{@N literal 0 HcmV?d00001 diff --git a/docs/v2/images/favicon.ico b/docs/v2/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5c339ae7422966140d1f469553ee3d9dd4e844f0 GIT binary patch literal 15086 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 LROf}3bL@WrV|F2( literal 0 HcmV?d00001 diff --git a/docs/v2/index.md b/docs/v2/index.md new file mode 100644 index 000000000..ef03772e4 --- /dev/null +++ b/docs/v2/index.md @@ -0,0 +1,85 @@ +--- +uid: index +--- + +Examine Documentation +=== + +## What is Examine? + + Examine allows you to index and search data easily and wraps the [Lucene.NET](https://lucenenet.apache.org/) indexing/searching engine. [Lucene](https://lucene.apache.org/) is _super_ fast and allows for very fast searching even on very large amounts of data. Examine is very extensible and allows you to configure as many indexes as you like and each may be configured individually. Out of the box Examine gives you a Lucene based index implementation as well as a Fluent API that can be used to search for your data. + +Examine is installed via Nuget: [https://www.nuget.org/packages/Examine](https://www.nuget.org/packages/Examine) + +## [API Documentation](xref:apidocsindex) + +Autogenerated API documentation is available. + +## [Conceptual Documentation](xref:indexing) + +Conceptual documentation is available. + +Not all features of [Lucene.NET](https://lucenenet.apache.org/) have Examine abstractions. See the [Lucene.NET documentation](https://lucenenet.apache.org/docs/4.8.0-beta00016/) for [Lucene.NET](https://lucenenet.apache.org/) specific API. + +**Tip**: There are many unit tests in the source code that can be used as Examples of how to do things. There is also a test web project that has plenty of examples of how to configure indexes and search them. + +## [Examine V1 / V2 Conceptual Documentation](xref:v2index) + +Conceptual documentation is available for V1 and V2 of Examine. + +## [Releases](https://github.com/Shazwazza/Examine/releases) + +Releases are available [here](https://github.com/Shazwazza/Examine/releases) and on [NuGet](https://www.nuget.org/packages/Examine/). + + +## Minimum requirements + +| Examine Version | .NET | +| --------------- | ---- | +| V3 | .NET Standard 2.0 | +| V2 | .NET Standard 2.0 | +| V1 | .NET Framework 4.5.2 | + +## Quick Start + +**Tip**: [`IExamineManager`](xref:Examine.IExamineManager) is the gateway to working with examine. It is registered in DI as a singleton and can be injected into your services. + +1. Install + + ```powershell + > dotnet add package Examine --version 3.0.1 + ``` + +1. Configure Services and create an index + + ```cs + // Adds Examine Core services + services.AddExamine(); + + // Create a Lucene based index + services.AddExamineLuceneIndex("MyIndex"); + ``` + +1. Populate the index + + ```cs + // Add a "ValueSet" (document) to the index + // which can contain any data you want. + myIndex.IndexItem(new ValueSet( + Guid.NewGuid().ToString(), //Give the doc an ID of your choice + "MyCategory", //Each doc has a "Category" + new Dictionary() + { + {"Name", "Frank" }, + {"Address", "Beverly Hills, 90210" } + })); + ``` + +1. Search the index + + ```cs + // Create a query + var results = myIndex.Searcher.CreateQuery() + .Field("Address", "Hills") // Look for any "Hills" addresses + .Execute(); // Execute the search + ``` diff --git a/docs/v2/mstile-150x150.png b/docs/v2/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..e865541668607d95121f4f9e0ead4b3a9dfe4268 GIT binary patch literal 11695 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 S&OlB9Q2wAHU;p0X%l`vI9Bvl? literal 0 HcmV?d00001 diff --git a/docs/v2/safari-pinned-tab.svg b/docs/v2/safari-pinned-tab.svg new file mode 100644 index 000000000..2761b998c --- /dev/null +++ b/docs/v2/safari-pinned-tab.svg @@ -0,0 +1,27 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + diff --git a/docs/v2/site.webmanifest b/docs/v2/site.webmanifest new file mode 100644 index 000000000..a1553eb86 --- /dev/null +++ b/docs/v2/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-384x384.png", + "sizes": "384x384", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/docs/v2/templates/material/partials/head.tmpl.partial b/docs/v2/templates/material/partials/head.tmpl.partial new file mode 100644 index 000000000..c05e8c1b0 --- /dev/null +++ b/docs/v2/templates/material/partials/head.tmpl.partial @@ -0,0 +1,21 @@ +{{!Copyright (c) Oscar Vasquez. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} + + + + + {{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}} + + + + {{#_description}}{{/_description}} + + + + + + + + {{#_noindex}}{{/_noindex}} + {{#_enableSearch}}{{/_enableSearch}} + {{#_enableNewTab}}{{/_enableNewTab}} + \ No newline at end of file diff --git a/docs/v2/templates/material/styles/main.css b/docs/v2/templates/material/styles/main.css new file mode 100644 index 000000000..c1ce23cbb --- /dev/null +++ b/docs/v2/templates/material/styles/main.css @@ -0,0 +1,313 @@ +/* COLOR VARIABLES*/ +:root { + --header-bg-color: #0d47a1; + --header-ft-color: #fff; + --highlight-light: #5e92f3; + --highlight-dark: #003c8f; + --accent-dim: #e0e0e0; + --accent-super-dim: #f3f3f3; + --font-color: #34393e; + --card-box-shadow: 0 1px 2px 0 rgba(61, 65, 68, 0.06), 0 1px 3px 1px rgba(61, 65, 68, 0.16); + --search-box-shadow: 0 1px 2px 0 rgba(41, 45, 48, 0.36), 0 1px 3px 1px rgba(41, 45, 48, 0.46); + --transition: 350ms; +} + +body { + color: var(--font-color); + font-family: "Roboto", sans-serif; + line-height: 1.5; + font-size: 16px; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + word-wrap: break-word; +} + +/* HIGHLIGHT COLOR */ + +button, +a { + color: var(--highlight-dark); + cursor: pointer; +} + +button:hover, +button:focus, +a:hover, +a:focus { + color: var(--highlight-light); + text-decoration: none; +} + +.toc .nav > li.active > a { + color: var(--highlight-dark); +} + +.toc .nav > li.active > a:hover, +.toc .nav > li.active > a:focus { + color: var(--highlight-light); +} + +.pagination > .active > a { + background-color: var(--header-bg-color); + border-color: var(--header-bg-color); +} + +.pagination > .active > a, +.pagination > .active > a:focus, +.pagination > .active > a:hover, +.pagination > .active > span, +.pagination > .active > span:focus, +.pagination > .active > span:hover { + background-color: var(--highlight-light); + border-color: var(--highlight-light); +} + +/* HEADINGS */ + +h1 { + font-weight: 600; + font-size: 32px; +} + +h2 { + font-weight: 600; + font-size: 24px; + line-height: 1.8; +} + +h3 { + font-weight: 600; + font-size: 20px; + line-height: 1.8; +} + +h5 { + font-size: 14px; + padding: 10px 0px; +} + +article h1, +article h2, +article h3, +article h4 { + margin-top: 35px; + margin-bottom: 15px; +} + +article h4 { + padding-bottom: 8px; + border-bottom: 2px solid #ddd; +} + +/* NAVBAR */ + +.navbar-brand > img { + color: var(--header-ft-color); +} + +.navbar { + border: none; + /* Both navbars use box-shadow */ + -webkit-box-shadow: var(--card-box-shadow); + -moz-box-shadow: var(--card-box-shadow); + box-shadow: var(--card-box-shadow); +} + +.subnav { + border-top: 1px solid #ddd; + background-color: #fff; +} + +.navbar-inverse { + background-color: var(--header-bg-color); + z-index: 100; +} + +.navbar-inverse .navbar-nav > li > a, +.navbar-inverse .navbar-text { + color: var(--header-ft-color); + background-color: var(--header-bg-color); + border-bottom: 3px solid transparent; + padding-bottom: 12px; + transition: 350ms; +} + +.navbar-inverse .navbar-nav > li > a:focus, +.navbar-inverse .navbar-nav > li > a:hover { + color: var(--header-ft-color); + background-color: var(--header-bg-color); + border-bottom: 3px solid white; +} + +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:focus, +.navbar-inverse .navbar-nav > .active > a:hover { + color: var(--header-ft-color); + background-color: var(--header-bg-color); + border-bottom: 3px solid white; +} + +.navbar-form .form-control { + border: 0; + border-radius: 4px; + box-shadow: var(--search-box-shadow); + transition:var(--transition); +} + +.navbar-form .form-control:hover { + background-color: var(--accent-dim); +} + +/* NAVBAR TOGGLED (small screens) */ + +.navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { + border: none; +} +.navbar-inverse .navbar-toggle { + box-shadow: var(--card-box-shadow); + border: none; +} + +.navbar-inverse .navbar-toggle:focus, +.navbar-inverse .navbar-toggle:hover { + background-color: var(--highlight-dark); +} + +/* SIDEBAR */ + +.toc .level1 > li { + font-weight: 400; +} + +.toc .nav > li > a { + color: var(--font-color); +} + +.sidefilter { + background-color: #fff; + border-left: none; + border-right: none; +} + +.sidefilter { + background-color: #fff; + border-left: none; + border-right: none; +} + +.toc-filter { + padding: 5px; + margin: 0; + box-shadow: var(--card-box-shadow); + transition:var(--transition); +} + +.toc-filter:hover { + background-color: var(--accent-super-dim); +} + +.toc-filter > input { + border: none; + background-color: inherit; + transition: inherit; +} + +.toc-filter > .filter-icon { + display: none; +} + +.sidetoc > .toc { + background-color: #fff; + overflow-x: hidden; +} + +.sidetoc { + background-color: #fff; + border: none; +} + +/* ALERTS */ + +.alert { + padding: 0px 0px 5px 0px; + color: inherit; + background-color: inherit; + border: none; + box-shadow: var(--card-box-shadow); +} + +.alert > p { + margin-bottom: 0; + padding: 5px 10px; +} + +.alert > ul { + margin-bottom: 0; + padding: 5px 40px; +} + +.alert > h5 { + padding: 10px 15px; + margin-top: 0; + text-transform: uppercase; + font-weight: bold; + border-radius: 4px 4px 0 0; +} + +.alert-info > h5 { + color: #1976d2; + border-bottom: 4px solid #1976d2; + background-color: #e3f2fd; +} + +.alert-warning > h5 { + color: #f57f17; + border-bottom: 4px solid #f57f17; + background-color: #fff3e0; +} + +.alert-danger > h5 { + color: #d32f2f; + border-bottom: 4px solid #d32f2f; + background-color: #ffebee; +} + +/* CODE HIGHLIGHT */ +pre { + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + word-break: break-all; + word-wrap: break-word; + background-color: #fffaef; + border-radius: 4px; + border: none; + box-shadow: var(--card-box-shadow); +} + +/* STYLE FOR IMAGES */ + +.article .small-image { + margin-top: 15px; + box-shadow: var(--card-box-shadow); + max-width: 350px; +} + +.article .medium-image { + margin-top: 15px; + box-shadow: var(--card-box-shadow); + max-width: 550px; +} + +.article .large-image { + margin-top: 15px; + box-shadow: var(--card-box-shadow); + max-width: 700px; +} + +/* Examine Docs Overrides */ + +/* Logo */ +#logo { + vertical-align: middle; +} \ No newline at end of file diff --git a/docs/v2/toc.yml b/docs/v2/toc.yml new file mode 100644 index 000000000..815535a27 --- /dev/null +++ b/docs/v2/toc.yml @@ -0,0 +1,7 @@ +- name: Articles + href: articles/ +- name: Api Documentation + href: api/ + homepage: api/index.md +- name: Source Code + href: https://github.com/Shazwazza/Examine From a360f7d62608c6fe9a78d3faad45ec6f38dd7bdd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 23:43:13 +0000 Subject: [PATCH 7/8] Bump nokogiri from 1.13.9 to 1.15.4 in /docs Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.9 to 1.15.4. - [Release notes](https://github.com/sparklemotion/nokogiri/releases) - [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md) - [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.13.9...v1.15.4) --- updated-dependencies: - dependency-name: nokogiri dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index e797fb438..3cea47fdf 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -52,18 +52,18 @@ GEM rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) - mini_portile2 (2.8.0) + mini_portile2 (2.8.4) minima (2.5.1) jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - nokogiri (1.13.9) - mini_portile2 (~> 2.8.0) + nokogiri (1.15.4) + mini_portile2 (~> 2.8.2) racc (~> 1.4) pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (4.0.7) - racc (1.6.0) + racc (1.7.1) rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) From 48b4b638b0f0f2809f940394729a252ab62e8066 Mon Sep 17 00:00:00 2001 From: Vivek Kumar <115945472+vivekBoii@users.noreply.github.com> Date: Sat, 28 Oct 2023 01:47:56 +0530 Subject: [PATCH 8/8] Update searching.md --- docs/searching.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/searching.md b/docs/searching.md index 505e36fcc..c61de9443 100644 --- a/docs/searching.md +++ b/docs/searching.md @@ -125,7 +125,7 @@ query.Field("nodeTypeAlias", "CWS_Home".Boost(20)); var results = query.Execute(); ``` -This will boost the term `CWS_Home` and make enteries with `nodeTypeAlias:CWS_Home` score higher in the results. +This will boost the term `CWS_Home` and make entries with `nodeTypeAlias:CWS_Home` score higher in the results. ## Proximity @@ -249,4 +249,4 @@ var query = searcher.CreateQuery(); var query = (LuceneSearchQuery)query.NativeQuery("hello:world").And(); // Make query ready for extending query.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); // Add the raw lucene query var results = query.Execute(); -``` \ No newline at end of file +```