From 585033e2a478c334e4b99cb52650c7f1e909cc12 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Mon, 14 Nov 2022 16:13:51 +0100 Subject: [PATCH 01/42] wip; Implementation of Facets --- src/Examine.Core/Examine.Core.csproj | 1 + src/Examine.Core/FieldDefinitionTypes.cs | 64 ++++++++++++++++++- src/Examine.Core/Search/FacetDoubleField.cs | 24 +++++++ src/Examine.Core/Search/FacetFullTextField.cs | 21 ++++++ src/Examine.Core/Search/FacetLongField.cs | 20 ++++++ src/Examine.Core/Search/IFacetDoubleField.cs | 13 ++++ src/Examine.Core/Search/IFacetField.cs | 15 +++++ .../Search/IFacetFullTextField.cs | 17 +++++ src/Examine.Core/Search/IFacetLongField.cs | 10 +++ src/Examine.Core/Search/IFacetQueryField.cs | 15 +++++ .../Search/IFacetRangeQueryField.cs | 10 +++ src/Examine.Core/Search/IFacetResult.cs | 8 +++ src/Examine.Core/Search/IFacetResults.cs | 12 ++++ src/Examine.Core/Search/IFacetValue.cs | 15 +++++ src/Examine.Core/Search/IQuery.cs | 34 ++++++++++ src/Examine.Lucene/FacetExtensions.cs | 37 +++++++++++ .../Indexing/FacetDateTimeType.cs | 25 ++++++++ .../Indexing/FacetDoubleType.cs | 22 +++++++ .../Indexing/FacetFullTextType.cs | 26 ++++++++ src/Examine.Lucene/Indexing/FacetInt32Type.cs | 22 +++++++ src/Examine.Lucene/Indexing/FacetInt64Type.cs | 22 +++++++ .../Indexing/FacetSingleType.cs | 22 +++++++ src/Examine.Lucene/LuceneIndexOptions.cs | 10 +++ src/Examine.Lucene/Search/FacetQueryField.cs | 28 ++++++++ .../Search/FacetRangeQueryField.cs | 22 +++++++ src/Examine.Lucene/Search/LuceneQuery.cs | 17 ++++- .../Search/LuceneSearchQuery.cs | 45 +++++++++++++ .../Search/LuceneSearchQueryBase.cs | 9 +++ .../Search/LuceneSearchResults.cs | 14 +++- .../ValueTypeFactoryCollection.cs | 14 +++- 30 files changed, 608 insertions(+), 6 deletions(-) create mode 100644 src/Examine.Core/Search/FacetDoubleField.cs create mode 100644 src/Examine.Core/Search/FacetFullTextField.cs create mode 100644 src/Examine.Core/Search/FacetLongField.cs create mode 100644 src/Examine.Core/Search/IFacetDoubleField.cs create mode 100644 src/Examine.Core/Search/IFacetField.cs create mode 100644 src/Examine.Core/Search/IFacetFullTextField.cs create mode 100644 src/Examine.Core/Search/IFacetLongField.cs create mode 100644 src/Examine.Core/Search/IFacetQueryField.cs create mode 100644 src/Examine.Core/Search/IFacetRangeQueryField.cs create mode 100644 src/Examine.Core/Search/IFacetResult.cs create mode 100644 src/Examine.Core/Search/IFacetResults.cs create mode 100644 src/Examine.Core/Search/IFacetValue.cs create mode 100644 src/Examine.Lucene/FacetExtensions.cs create mode 100644 src/Examine.Lucene/Indexing/FacetDateTimeType.cs create mode 100644 src/Examine.Lucene/Indexing/FacetDoubleType.cs create mode 100644 src/Examine.Lucene/Indexing/FacetFullTextType.cs create mode 100644 src/Examine.Lucene/Indexing/FacetInt32Type.cs create mode 100644 src/Examine.Lucene/Indexing/FacetInt64Type.cs create mode 100644 src/Examine.Lucene/Indexing/FacetSingleType.cs create mode 100644 src/Examine.Lucene/Search/FacetQueryField.cs create mode 100644 src/Examine.Lucene/Search/FacetRangeQueryField.cs diff --git a/src/Examine.Core/Examine.Core.csproj b/src/Examine.Core/Examine.Core.csproj index 1d26fd6e1..a44dc2af8 100644 --- a/src/Examine.Core/Examine.Core.csproj +++ b/src/Examine.Core/Examine.Core.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Examine.Core/FieldDefinitionTypes.cs b/src/Examine.Core/FieldDefinitionTypes.cs index 8a967b0c9..616e0d520 100644 --- a/src/Examine.Core/FieldDefinitionTypes.cs +++ b/src/Examine.Core/FieldDefinitionTypes.cs @@ -1,4 +1,4 @@ -namespace Examine +namespace Examine { /// /// Contains the names of field definition types @@ -40,5 +40,65 @@ public static class FieldDefinitionTypes /// Will be indexed with an email address analyzer /// public const string EmailAddress = "emailaddress"; + + /// + /// Facetable version of + /// + public const string FacetInteger = "int"; + + /// + /// Facetable version of + /// + public const string FacetFloat = "float"; + + /// + /// Facetable version of + /// + public const string FacetDouble = "double"; + + /// + /// Facetable version of + /// + public const string FacetLong = "long"; + + /// + /// Facetable version of + /// + public const string FacetDateTime = "datetime"; + + /// + /// Facetable version of + /// + public const string FacetDateYear = "date.year"; + + /// + /// Facetable version of + /// + public const string FacetDateMonth = "date.month"; + + /// + /// Facetable version of + /// + public const string FacetDateDay = "date.day"; + + /// + /// Facetable version of + /// + public const string FacetDateHour = "date.hour"; + + /// + /// Facetable version of + /// + public const string FacetDateMinute = "date.minute"; + + /// + /// Facetable version of + /// + public const string FacetFullText = "fulltext"; + + /// + /// Facetable version of + /// + public const string FacetFullTextSortable = "fulltextsortable"; } -} \ No newline at end of file +} diff --git a/src/Examine.Core/Search/FacetDoubleField.cs b/src/Examine.Core/Search/FacetDoubleField.cs new file mode 100644 index 000000000..672d800bc --- /dev/null +++ b/src/Examine.Core/Search/FacetDoubleField.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Examine.Lucene.Search; +using Lucene.Net.Facet.Range; + +namespace Examine.Search +{ + public class FacetDoubleField : IFacetDoubleField + { + public DoubleRange[] DoubleRanges { get; } + + public string Field { get; } + + public string FacetField { get; set; } + + public FacetDoubleField(string field, DoubleRange[] doubleRanges, string facetField = "$facets") + { + Field = field; + DoubleRanges = doubleRanges; + FacetField = facetField; + } + } +} diff --git a/src/Examine.Core/Search/FacetFullTextField.cs b/src/Examine.Core/Search/FacetFullTextField.cs new file mode 100644 index 000000000..ee80dd056 --- /dev/null +++ b/src/Examine.Core/Search/FacetFullTextField.cs @@ -0,0 +1,21 @@ +namespace Examine.Search +{ + public class FacetFullTextField : IFacetFullTextField + { + public int MaxCount { get; set; } + + public string[] Values { get; set; } + + public string Field { get; set; } + + public string FacetField { get; set; } + + public FacetFullTextField(string field, string[] values, int maxCount = 10, string facetField = "$facets") + { + Field = field; + Values = values; + MaxCount = maxCount; + FacetField = facetField; + } + } +} diff --git a/src/Examine.Core/Search/FacetLongField.cs b/src/Examine.Core/Search/FacetLongField.cs new file mode 100644 index 000000000..33145d34c --- /dev/null +++ b/src/Examine.Core/Search/FacetLongField.cs @@ -0,0 +1,20 @@ +using Lucene.Net.Facet.Range; + +namespace Examine.Search +{ + public class FacetLongField : IFacetLongField + { + public string Field { get; } + + public Int64Range[] LongRanges { get; } + + public string FacetField { get; set; } + + public FacetLongField(string field, Int64Range[] longRanges, string facetField = "$facets") + { + Field = field; + LongRanges = longRanges; + FacetField = facetField; + } + } +} diff --git a/src/Examine.Core/Search/IFacetDoubleField.cs b/src/Examine.Core/Search/IFacetDoubleField.cs new file mode 100644 index 000000000..4f7744c84 --- /dev/null +++ b/src/Examine.Core/Search/IFacetDoubleField.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Examine.Lucene.Search; +using Lucene.Net.Facet.Range; + +namespace Examine.Search +{ + public interface IFacetDoubleField : IFacetField + { + DoubleRange[] DoubleRanges { get; } + } +} diff --git a/src/Examine.Core/Search/IFacetField.cs b/src/Examine.Core/Search/IFacetField.cs new file mode 100644 index 000000000..fe3eedaf8 --- /dev/null +++ b/src/Examine.Core/Search/IFacetField.cs @@ -0,0 +1,15 @@ +namespace Examine.Lucene.Search +{ + public interface IFacetField + { + /// + /// The field name + /// + string Field { get; } + + /// + /// The field to get the facet field from + /// + string FacetField { get; set; } + } +} diff --git a/src/Examine.Core/Search/IFacetFullTextField.cs b/src/Examine.Core/Search/IFacetFullTextField.cs new file mode 100644 index 000000000..7a2188d17 --- /dev/null +++ b/src/Examine.Core/Search/IFacetFullTextField.cs @@ -0,0 +1,17 @@ +using Examine.Lucene.Search; + +namespace Examine.Search +{ + public interface IFacetFullTextField : IFacetField + { + /// + /// Maximum number of terms to return + /// + int MaxCount { get; set; } + + /// + /// Filter values + /// + string[] Values { get; set; } + } +} diff --git a/src/Examine.Core/Search/IFacetLongField.cs b/src/Examine.Core/Search/IFacetLongField.cs new file mode 100644 index 000000000..0e8b5d538 --- /dev/null +++ b/src/Examine.Core/Search/IFacetLongField.cs @@ -0,0 +1,10 @@ +using Examine.Lucene.Search; +using Lucene.Net.Facet.Range; + +namespace Examine.Search +{ + public interface IFacetLongField : IFacetField + { + Int64Range[] LongRanges { get; } + } +} diff --git a/src/Examine.Core/Search/IFacetQueryField.cs b/src/Examine.Core/Search/IFacetQueryField.cs new file mode 100644 index 000000000..79a3bf51a --- /dev/null +++ b/src/Examine.Core/Search/IFacetQueryField.cs @@ -0,0 +1,15 @@ +namespace Examine.Search +{ + public interface IFacetQueryField : IBooleanOperation + { + /// + /// Maximum number of terms to return + /// + IFacetQueryField MaxCount(int count); + + /// + /// Sets the field where the facet information will be read from + /// + IFacetQueryField FacetField(string fieldName); + } +} diff --git a/src/Examine.Core/Search/IFacetRangeQueryField.cs b/src/Examine.Core/Search/IFacetRangeQueryField.cs new file mode 100644 index 000000000..69343d7f8 --- /dev/null +++ b/src/Examine.Core/Search/IFacetRangeQueryField.cs @@ -0,0 +1,10 @@ +namespace Examine.Search +{ + public interface IFacetRangeQueryField : IBooleanOperation + { + /// + /// Sets the field where the facet information will be read from + /// + IFacetRangeQueryField FacetField(string fieldName); + } +} diff --git a/src/Examine.Core/Search/IFacetResult.cs b/src/Examine.Core/Search/IFacetResult.cs new file mode 100644 index 000000000..5e2630460 --- /dev/null +++ b/src/Examine.Core/Search/IFacetResult.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Examine.Lucene.Search +{ + public interface IFacetResult : IEnumerable + { + } +} diff --git a/src/Examine.Core/Search/IFacetResults.cs b/src/Examine.Core/Search/IFacetResults.cs new file mode 100644 index 000000000..eaca7aa4f --- /dev/null +++ b/src/Examine.Core/Search/IFacetResults.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Examine.Lucene.Search +{ + public interface IFacetResults + { + /// + /// Facets from the search + /// + IDictionary Facets { get; } + } +} diff --git a/src/Examine.Core/Search/IFacetValue.cs b/src/Examine.Core/Search/IFacetValue.cs new file mode 100644 index 000000000..3ccfd6b28 --- /dev/null +++ b/src/Examine.Core/Search/IFacetValue.cs @@ -0,0 +1,15 @@ +namespace Examine.Lucene.Search +{ + public interface IFacetValue + { + /// + /// The label of the facet value + /// + string Label { get; } + + /// + /// The occurrence of the facet field + /// + float Value { get; } + } +} diff --git a/src/Examine.Core/Search/IQuery.cs b/src/Examine.Core/Search/IQuery.cs index 7fdfb630c..26454c9da 100644 --- a/src/Examine.Core/Search/IQuery.cs +++ b/src/Examine.Core/Search/IQuery.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Lucene.Net.Facet.Range; namespace Examine.Search { @@ -134,5 +135,38 @@ public interface IQuery /// /// IBooleanOperation RangeQuery(string[] fields, T? min, T? max, bool minInclusive = true, bool maxInclusive = true) where T : struct; + + /// + /// Add a facet string to the current query + /// + /// + /// + IFacetQueryField Facet(string field); + + /// + /// Add a facet string to the current query, filtered by value + /// + /// + /// + /// + IFacetQueryField Facet(string field, string value); + + /// + /// Add a facet string to the current query, filtered by multiple values + /// + /// + /// + /// + IFacetQueryField Facet(string field, string[] values); + + /// + /// Add a range facet to the current query + /// + IFacetRangeQueryField Facet(string field, DoubleRange[] doubleRanges); + + /// + /// Add a range facet to the current query + /// + IFacetRangeQueryField Facet(string field, Int64Range[] longRanges); } } diff --git a/src/Examine.Lucene/FacetExtensions.cs b/src/Examine.Lucene/FacetExtensions.cs new file mode 100644 index 000000000..532de516a --- /dev/null +++ b/src/Examine.Lucene/FacetExtensions.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using Examine.Lucene.Search; + +namespace Examine.Lucene +{ + public static class FacetExtensions + { + /// + /// Get the values for a particular facet in the results + /// + public static IFacetResult GetFacet(this ISearchResults searchResults, string field) + { + if (!(searchResults is IFacetResults facetResults)) + { + throw new ArgumentException("Result does not support facets"); + } + + facetResults.Facets.TryGetValue(field, out IFacetResult facet); + + return facet; + } + + /// + /// Get all of the facets in the results + /// + public static IEnumerable GetFacets(this ISearchResults searchResults) + { + if (!(searchResults is IFacetResults facetResults)) + { + throw new ArgumentException("Result does not support facets"); + } + + return facetResults.Facets.Values; + } + } +} diff --git a/src/Examine.Lucene/Indexing/FacetDateTimeType.cs b/src/Examine.Lucene/Indexing/FacetDateTimeType.cs new file mode 100644 index 000000000..0fa53f4f6 --- /dev/null +++ b/src/Examine.Lucene/Indexing/FacetDateTimeType.cs @@ -0,0 +1,25 @@ +using System; +using Lucene.Net.Documents; +using Microsoft.Extensions.Logging; + +namespace Examine.Lucene.Indexing +{ + public class FacetDateTimeType : DateTimeType + { + public FacetDateTimeType(string fieldName, ILoggerFactory logger, DateResolution resolution, bool store = true) : base(fieldName, logger, resolution, store) + { + } + + protected override void AddSingleValue(Document doc, object value) + { + base.AddSingleValue(doc, value); + + if (!TryConvert(value, out DateTime parsedVal)) + return; + + var val = DateToLong(parsedVal); + + doc.Add(new NumericDocValuesField(FieldName, val)); + } + } +} diff --git a/src/Examine.Lucene/Indexing/FacetDoubleType.cs b/src/Examine.Lucene/Indexing/FacetDoubleType.cs new file mode 100644 index 000000000..438b22f41 --- /dev/null +++ b/src/Examine.Lucene/Indexing/FacetDoubleType.cs @@ -0,0 +1,22 @@ +using Lucene.Net.Documents; +using Microsoft.Extensions.Logging; + +namespace Examine.Lucene.Indexing +{ + public class FacetDoubleType : DoubleType + { + public FacetDoubleType(string fieldName, ILoggerFactory logger, bool store = true) : base(fieldName, logger, store) + { + } + + protected override void AddSingleValue(Document doc, object value) + { + base.AddSingleValue(doc, value); + + if (!TryConvert(value, out double parsedVal)) + return; + + doc.Add(new DoubleDocValuesField(FieldName, parsedVal)); + } + } +} diff --git a/src/Examine.Lucene/Indexing/FacetFullTextType.cs b/src/Examine.Lucene/Indexing/FacetFullTextType.cs new file mode 100644 index 000000000..aa14eee87 --- /dev/null +++ b/src/Examine.Lucene/Indexing/FacetFullTextType.cs @@ -0,0 +1,26 @@ +using Lucene.Net.Analysis; +using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; +using Microsoft.Extensions.Logging; + +namespace Examine.Lucene.Indexing +{ + public class FacetFullTextType : FullTextType + { + public FacetFullTextType(string fieldName, ILoggerFactory logger, Analyzer analyzer = null, bool sortable = false) : base(fieldName, logger, analyzer, sortable) + { + } + + protected override void AddSingleValue(Document doc, object value) + { + base.AddSingleValue(doc, value); + + if (!TryConvert(value, out var str)) + { + return; + } + + doc.Add(new SortedSetDocValuesFacetField(FieldName, str)); + } + } +} diff --git a/src/Examine.Lucene/Indexing/FacetInt32Type.cs b/src/Examine.Lucene/Indexing/FacetInt32Type.cs new file mode 100644 index 000000000..c2bf21dac --- /dev/null +++ b/src/Examine.Lucene/Indexing/FacetInt32Type.cs @@ -0,0 +1,22 @@ +using Lucene.Net.Documents; +using Microsoft.Extensions.Logging; + +namespace Examine.Lucene.Indexing +{ + public class FacetInt32Type : Int32Type + { + public FacetInt32Type(string fieldName, ILoggerFactory logger, bool store = true) : base(fieldName, logger, store) + { + } + + protected override void AddSingleValue(Document doc, object value) + { + base.AddSingleValue(doc, value); + + if (!TryConvert(value, out int parsedVal)) + return; + + doc.Add(new NumericDocValuesField(FieldName, parsedVal)); + } + } +} diff --git a/src/Examine.Lucene/Indexing/FacetInt64Type.cs b/src/Examine.Lucene/Indexing/FacetInt64Type.cs new file mode 100644 index 000000000..1e17da6a8 --- /dev/null +++ b/src/Examine.Lucene/Indexing/FacetInt64Type.cs @@ -0,0 +1,22 @@ +using Lucene.Net.Documents; +using Microsoft.Extensions.Logging; + +namespace Examine.Lucene.Indexing +{ + public class FacetInt64Type : Int64Type + { + public FacetInt64Type(string fieldName, ILoggerFactory logger, bool store = true) : base(fieldName, logger, store) + { + } + + protected override void AddSingleValue(Document doc, object value) + { + base.AddSingleValue(doc, value); + + if (!TryConvert(value, out long parsedVal)) + return; + + doc.Add(new NumericDocValuesField(FieldName, parsedVal)); + } + } +} diff --git a/src/Examine.Lucene/Indexing/FacetSingleType.cs b/src/Examine.Lucene/Indexing/FacetSingleType.cs new file mode 100644 index 000000000..35844623c --- /dev/null +++ b/src/Examine.Lucene/Indexing/FacetSingleType.cs @@ -0,0 +1,22 @@ +using Lucene.Net.Documents; +using Microsoft.Extensions.Logging; + +namespace Examine.Lucene.Indexing +{ + public class FacetSingleType : SingleType + { + public FacetSingleType(string fieldName, ILoggerFactory logger, bool store = true) : base(fieldName, logger, store) + { + } + + protected override void AddSingleValue(Document doc, object value) + { + base.AddSingleValue(doc, value); + + if (!TryConvert(value, out float parsedVal)) + return; + + doc.Add(new SingleDocValuesField(FieldName, parsedVal)); + } + } +} diff --git a/src/Examine.Lucene/LuceneIndexOptions.cs b/src/Examine.Lucene/LuceneIndexOptions.cs index 733a87e71..085f0f422 100644 --- a/src/Examine.Lucene/LuceneIndexOptions.cs +++ b/src/Examine.Lucene/LuceneIndexOptions.cs @@ -3,6 +3,7 @@ using System.Text; using Lucene.Net.Analysis; using Lucene.Net.Analysis.Standard; +using Lucene.Net.Facet; using Lucene.Net.Index; namespace Examine.Lucene @@ -15,6 +16,15 @@ public class LuceneIndexOptions : IndexOptions public Analyzer Analyzer { get; set; } + /// + /// Records per-dimension configuration. By default a + /// dimension is flat, single valued and does + /// not require count for the dimension; use + /// the setters in this class to change these settings for + /// each dim. + /// + public FacetsConfig FacetConfig { get; set; } + /// /// Specifies the index value types to use for this indexer, if this is not specified then the result of will be used. /// This is generally used to initialize any custom value types for your indexer since the value type collection cannot be modified at runtime. diff --git a/src/Examine.Lucene/Search/FacetQueryField.cs b/src/Examine.Lucene/Search/FacetQueryField.cs new file mode 100644 index 000000000..1cd1975ed --- /dev/null +++ b/src/Examine.Lucene/Search/FacetQueryField.cs @@ -0,0 +1,28 @@ +using Examine.Lucene.Search; + +namespace Examine.Search +{ + public class FacetQueryField : LuceneBooleanOperation, IFacetQueryField + { + private readonly FacetFullTextField _field; + + public FacetQueryField(LuceneSearchQuery search, FacetFullTextField field) : base(search) + { + _field = field; + } + + public IFacetQueryField FacetField(string fieldName) + { + _field.FacetField = fieldName; + + return this; + } + + public IFacetQueryField MaxCount(int count) + { + _field.MaxCount = count; + + return this; + } + } +} diff --git a/src/Examine.Lucene/Search/FacetRangeQueryField.cs b/src/Examine.Lucene/Search/FacetRangeQueryField.cs new file mode 100644 index 000000000..4a62c6369 --- /dev/null +++ b/src/Examine.Lucene/Search/FacetRangeQueryField.cs @@ -0,0 +1,22 @@ +using Examine.Search; + +namespace Examine.Lucene.Search +{ + public class FacetRangeQueryField : LuceneBooleanOperation, IFacetRangeQueryField + where T : IFacetField + { + private readonly T _field; + + public FacetRangeQueryField(LuceneSearchQuery search, T field) : base(search) + { + _field = field; + } + + public IFacetRangeQueryField FacetField(string fieldName) + { + _field.FacetField = fieldName; + + return this; + } + } +} diff --git a/src/Examine.Lucene/Search/LuceneQuery.cs b/src/Examine.Lucene/Search/LuceneQuery.cs index cecbec18e..43b06a7ca 100644 --- a/src/Examine.Lucene/Search/LuceneQuery.cs +++ b/src/Examine.Lucene/Search/LuceneQuery.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Examine.Search; +using Lucene.Net.Facet.Range; using Lucene.Net.Search; namespace Examine.Lucene.Search @@ -106,5 +107,19 @@ INestedBooleanOperation INestedQuery.ManagedQuery(string query, string[] fields) INestedBooleanOperation INestedQuery.RangeQuery(string[] fields, T? min, T? max, bool minInclusive, bool maxInclusive) => _search.RangeQueryInternal(fields, min, max, minInclusive: minInclusive, maxInclusive: maxInclusive, _occurrence); + public IFacetQueryField Facet(string field) + => _search.FacetInternal(field); + + public IFacetQueryField Facet(string field, string value) + => _search.FacetInternal(field, value); + + public IFacetQueryField Facet(string field, string[] values) + => _search.FacetInternal(field, values); + + public IFacetRangeQueryField Facet(string field, DoubleRange[] doubleRanges) + => _search.FacetInternal(field, doubleRanges); + + public IFacetRangeQueryField Facet(string field, Int64Range[] longRanges) + => _search.FacetInternal(field, longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchQuery.cs b/src/Examine.Lucene/Search/LuceneSearchQuery.cs index 0d782a894..ea2c532cf 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQuery.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQuery.cs @@ -5,6 +5,7 @@ using Examine.Lucene.Indexing; using Examine.Search; using Lucene.Net.Analysis; +using Lucene.Net.Facet.Range; using Lucene.Net.Search; namespace Examine.Lucene.Search @@ -17,6 +18,7 @@ public class LuceneSearchQuery : LuceneSearchQueryBase, IQueryExecutor { private readonly ISearchContext _searchContext; private ISet _fieldsToLoad = null; + private IList _facetFields = new List(); public LuceneSearchQuery( ISearchContext searchContext, @@ -292,5 +294,48 @@ public IBooleanOperation SelectAllFieldsInternal() protected override LuceneBooleanOperationBase CreateOp() => new LuceneBooleanOperation(this); + public override IFacetQueryField Facet(string field) => FacetInternal(field); + + public override IFacetQueryField Facet(string field, string value) => FacetInternal(field, value); + + public override IFacetQueryField Facet(string field, string[] values) => FacetInternal(field, values); + + public override IFacetRangeQueryField Facet(string field, DoubleRange[] doubleRanges) => FacetInternal(field, doubleRanges); + + public override IFacetRangeQueryField Facet(string field, Int64Range[] longRanges) => FacetInternal(field, longRanges); + + internal IFacetQueryField FacetInternal(string field) + => FacetInternal(field, Array.Empty()); + + internal IFacetQueryField FacetInternal(string field, string value) + => FacetInternal(field, new[] { value }); + + internal IFacetQueryField FacetInternal(string field, string[] values) + { + var facet = new FacetFullTextField(field, values); + + _facetFields.Add(facet); + + return new FacetQueryField(this, facet); + } + + + internal IFacetRangeQueryField FacetInternal(string field, DoubleRange[] doubleRanges) + { + var facet = new FacetDoubleField(field, doubleRanges); + + _facetFields.Add(facet); + + return new FacetRangeQueryField(this, facet); + } + + internal IFacetRangeQueryField FacetInternal(string field, Int64Range[] longRanges) + { + var facet = new FacetLongField(field, longRanges); + + _facetFields.Add(facet); + + return new FacetRangeQueryField(this, facet); + } } } diff --git a/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs b/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs index 043b6df7c..05b6372dd 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Examine.Search; +using Lucene.Net.Facet.Range; using Lucene.Net.Index; using Lucene.Net.QueryParsers.Classic; using Lucene.Net.Search; @@ -505,6 +506,14 @@ public override string ToString() return $"{{ Category: {Category}, LuceneQuery: {Query} }}"; } + public abstract IFacetQueryField Facet(string field); + public abstract IFacetQueryField Facet(string field, string value); + + public abstract IFacetQueryField Facet(string field, string[] values); + + public abstract IFacetRangeQueryField Facet(string field, DoubleRange[] doubleRanges); + + public abstract IFacetRangeQueryField Facet(string field, Int64Range[] longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchResults.cs b/src/Examine.Lucene/Search/LuceneSearchResults.cs index 92258d940..c05057055 100644 --- a/src/Examine.Lucene/Search/LuceneSearchResults.cs +++ b/src/Examine.Lucene/Search/LuceneSearchResults.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; namespace Examine.Lucene.Search { - public class LuceneSearchResults : ISearchResults + public class LuceneSearchResults : ISearchResults, IFacetResults { public static LuceneSearchResults Empty { get; } = new LuceneSearchResults(Array.Empty(), 0); @@ -14,10 +14,20 @@ public LuceneSearchResults(IReadOnlyCollection results, int total { _results = results; TotalItemCount = totalItemCount; + Facets = new Dictionary(); + } + + public LuceneSearchResults(IReadOnlyCollection results, int totalItemCount, IDictionary facets) + { + _results = results; + TotalItemCount = totalItemCount; + Facets = facets; } public long TotalItemCount { get; } + public IDictionary Facets { get; } + public IEnumerator GetEnumerator() => _results.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } diff --git a/src/Examine.Lucene/ValueTypeFactoryCollection.cs b/src/Examine.Lucene/ValueTypeFactoryCollection.cs index 8f147d460..92a9646a3 100644 --- a/src/Examine.Lucene/ValueTypeFactoryCollection.cs +++ b/src/Examine.Lucene/ValueTypeFactoryCollection.cs @@ -71,7 +71,19 @@ private static IReadOnlyDictionary> G {FieldDefinitionTypes.FullText, name => new FullTextType(name, loggerFactory, defaultAnalyzer)}, {FieldDefinitionTypes.FullTextSortable, name => new FullTextType(name, loggerFactory, defaultAnalyzer, true)}, {FieldDefinitionTypes.InvariantCultureIgnoreCase, name => new GenericAnalyzerFieldValueType(name, loggerFactory, new CultureInvariantWhitespaceAnalyzer())}, - {FieldDefinitionTypes.EmailAddress, name => new GenericAnalyzerFieldValueType(name, loggerFactory, new EmailAddressAnalyzer())} + {FieldDefinitionTypes.EmailAddress, name => new GenericAnalyzerFieldValueType(name, loggerFactory, new EmailAddressAnalyzer())}, + {FieldDefinitionTypes.FacetInteger, name => new FacetInt32Type(name, loggerFactory)}, + {FieldDefinitionTypes.FacetFloat, name => new FacetSingleType(name, loggerFactory)}, + {FieldDefinitionTypes.FacetDouble, name => new FacetDoubleType(name, loggerFactory)}, + {FieldDefinitionTypes.FacetLong, name => new FacetInt64Type(name, loggerFactory)}, + {FieldDefinitionTypes.FacetDateTime, name => new FacetDateTimeType(name, loggerFactory, DateResolution.MILLISECOND)}, + {FieldDefinitionTypes.FacetDateYear, name => new FacetDateTimeType(name, loggerFactory, DateResolution.YEAR)}, + {FieldDefinitionTypes.FacetDateMonth, name => new FacetDateTimeType(name, loggerFactory, DateResolution.MONTH)}, + {FieldDefinitionTypes.FacetDateDay, name => new FacetDateTimeType(name, loggerFactory, DateResolution.DAY)}, + {FieldDefinitionTypes.FacetDateHour, name => new FacetDateTimeType(name, loggerFactory, DateResolution.HOUR)}, + {FieldDefinitionTypes.FacetDateMinute, name => new FacetDateTimeType(name, loggerFactory, DateResolution.MINUTE)}, + {FieldDefinitionTypes.FacetFullText, name => new FacetFullTextType(name, loggerFactory, defaultAnalyzer)}, + {FieldDefinitionTypes.FacetFullTextSortable, name => new FacetFullTextType(name, loggerFactory, defaultAnalyzer, true)}, }; From e872fa341288f63d1b2627379a6cd871688525ec Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Tue, 15 Nov 2022 10:03:11 +0100 Subject: [PATCH 02/42] wip: Implementation Examine facets --- src/Examine.Core/Search/FacetDoubleField.cs | 7 +- src/Examine.Core/Search/FacetLongField.cs | 5 +- src/Examine.Core/Search/FacetResult.cs | 28 ++++++ src/Examine.Core/Search/FacetValue.cs | 20 ++++ src/Examine.Core/Search/IFacetDoubleField.cs | 2 + .../Search/IFacetDoubleRangeQueryField.cs | 12 +++ .../Search/IFacetLongRangeQueryField.cs | 6 ++ .../Search/IFacetRangeQueryField.cs | 10 -- src/Examine.Core/Search/IQuery.cs | 4 +- src/Examine.Lucene/LuceneIndexOptions.cs | 2 +- src/Examine.Lucene/Providers/LuceneIndex.cs | 2 +- .../Search/FacetDoubleRangeQueryField.cs | 21 ++++ .../Search/FacetLongRangeQueryField.cs | 11 +++ .../Search/FacetRangeQueryField.cs | 22 ----- src/Examine.Lucene/Search/LuceneQuery.cs | 4 +- .../Search/LuceneSearchExecutor.cs | 95 ++++++++++++++++++- .../Search/LuceneSearchQuery.cs | 16 ++-- .../Search/LuceneSearchQueryBase.cs | 4 +- .../Search/LuceneSearchResults.cs | 9 +- 19 files changed, 215 insertions(+), 65 deletions(-) create mode 100644 src/Examine.Core/Search/FacetResult.cs create mode 100644 src/Examine.Core/Search/FacetValue.cs create mode 100644 src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs create mode 100644 src/Examine.Core/Search/IFacetLongRangeQueryField.cs delete mode 100644 src/Examine.Core/Search/IFacetRangeQueryField.cs create mode 100644 src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs create mode 100644 src/Examine.Lucene/Search/FacetLongRangeQueryField.cs delete mode 100644 src/Examine.Lucene/Search/FacetRangeQueryField.cs diff --git a/src/Examine.Core/Search/FacetDoubleField.cs b/src/Examine.Core/Search/FacetDoubleField.cs index 672d800bc..bb9893715 100644 --- a/src/Examine.Core/Search/FacetDoubleField.cs +++ b/src/Examine.Core/Search/FacetDoubleField.cs @@ -12,13 +12,14 @@ public class FacetDoubleField : IFacetDoubleField public string Field { get; } - public string FacetField { get; set; } + public string FacetField { get; set; } = "$facets"; - public FacetDoubleField(string field, DoubleRange[] doubleRanges, string facetField = "$facets") + public bool IsFloat { get; set; } + + public FacetDoubleField(string field, DoubleRange[] doubleRanges) { Field = field; DoubleRanges = doubleRanges; - FacetField = facetField; } } } diff --git a/src/Examine.Core/Search/FacetLongField.cs b/src/Examine.Core/Search/FacetLongField.cs index 33145d34c..26bc8b646 100644 --- a/src/Examine.Core/Search/FacetLongField.cs +++ b/src/Examine.Core/Search/FacetLongField.cs @@ -8,13 +8,12 @@ public class FacetLongField : IFacetLongField public Int64Range[] LongRanges { get; } - public string FacetField { get; set; } + public string FacetField { get; set; } = "$facets"; - public FacetLongField(string field, Int64Range[] longRanges, string facetField = "$facets") + public FacetLongField(string field, Int64Range[] longRanges) { Field = field; LongRanges = longRanges; - FacetField = facetField; } } } diff --git a/src/Examine.Core/Search/FacetResult.cs b/src/Examine.Core/Search/FacetResult.cs new file mode 100644 index 000000000..41488f1ee --- /dev/null +++ b/src/Examine.Core/Search/FacetResult.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Examine.Lucene.Search; + +namespace Examine.Search +{ + public class FacetResult : IFacetResult + { + private readonly IEnumerable _values; + + public FacetResult(IEnumerable values) + { + _values = values; + } + + public IEnumerator GetEnumerator() + { + return _values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/Examine.Core/Search/FacetValue.cs b/src/Examine.Core/Search/FacetValue.cs new file mode 100644 index 000000000..05cc0c5aa --- /dev/null +++ b/src/Examine.Core/Search/FacetValue.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Examine.Lucene.Search; + +namespace Examine.Search +{ + public class FacetValue : IFacetValue + { + public string Label { get; } + + public float Value { get; } + + public FacetValue(string label, float value) + { + Label = label; + Value = value; + } + } +} diff --git a/src/Examine.Core/Search/IFacetDoubleField.cs b/src/Examine.Core/Search/IFacetDoubleField.cs index 4f7744c84..e78f6e3d4 100644 --- a/src/Examine.Core/Search/IFacetDoubleField.cs +++ b/src/Examine.Core/Search/IFacetDoubleField.cs @@ -9,5 +9,7 @@ namespace Examine.Search public interface IFacetDoubleField : IFacetField { DoubleRange[] DoubleRanges { get; } + + bool IsFloat { get; set; } } } diff --git a/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs b/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs new file mode 100644 index 000000000..f62e0a7d8 --- /dev/null +++ b/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs @@ -0,0 +1,12 @@ +namespace Examine.Search +{ + public interface IFacetDoubleRangeQueryField : IBooleanOperation + { + /// + /// Sets if the range query is on values + /// + /// + /// + IFacetDoubleRangeQueryField IsFloat(bool isFloat); + } +} diff --git a/src/Examine.Core/Search/IFacetLongRangeQueryField.cs b/src/Examine.Core/Search/IFacetLongRangeQueryField.cs new file mode 100644 index 000000000..9f8f6d5a3 --- /dev/null +++ b/src/Examine.Core/Search/IFacetLongRangeQueryField.cs @@ -0,0 +1,6 @@ +namespace Examine.Search +{ + public interface IFacetLongRangeQueryField : IBooleanOperation + { + } +} diff --git a/src/Examine.Core/Search/IFacetRangeQueryField.cs b/src/Examine.Core/Search/IFacetRangeQueryField.cs deleted file mode 100644 index 69343d7f8..000000000 --- a/src/Examine.Core/Search/IFacetRangeQueryField.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Examine.Search -{ - public interface IFacetRangeQueryField : IBooleanOperation - { - /// - /// Sets the field where the facet information will be read from - /// - IFacetRangeQueryField FacetField(string fieldName); - } -} diff --git a/src/Examine.Core/Search/IQuery.cs b/src/Examine.Core/Search/IQuery.cs index 26454c9da..96eaa3e7f 100644 --- a/src/Examine.Core/Search/IQuery.cs +++ b/src/Examine.Core/Search/IQuery.cs @@ -162,11 +162,11 @@ public interface IQuery /// /// Add a range facet to the current query /// - IFacetRangeQueryField Facet(string field, DoubleRange[] doubleRanges); + IFacetDoubleRangeQueryField Facet(string field, DoubleRange[] doubleRanges); /// /// Add a range facet to the current query /// - IFacetRangeQueryField Facet(string field, Int64Range[] longRanges); + IFacetLongRangeQueryField Facet(string field, Int64Range[] longRanges); } } diff --git a/src/Examine.Lucene/LuceneIndexOptions.cs b/src/Examine.Lucene/LuceneIndexOptions.cs index 085f0f422..deba631de 100644 --- a/src/Examine.Lucene/LuceneIndexOptions.cs +++ b/src/Examine.Lucene/LuceneIndexOptions.cs @@ -23,7 +23,7 @@ public class LuceneIndexOptions : IndexOptions /// the setters in this class to change these settings for /// each dim. /// - public FacetsConfig FacetConfig { get; set; } + public FacetsConfig FacetConfig { get; set; } = new FacetsConfig(); /// /// Specifies the index value types to use for this indexer, if this is not specified then the result of will be used. diff --git a/src/Examine.Lucene/Providers/LuceneIndex.cs b/src/Examine.Lucene/Providers/LuceneIndex.cs index 6ac9124eb..0ddecfe78 100644 --- a/src/Examine.Lucene/Providers/LuceneIndex.cs +++ b/src/Examine.Lucene/Providers/LuceneIndex.cs @@ -754,7 +754,7 @@ protected virtual void AddDocument(Document doc, ValueSet valueSet) } // TODO: try/catch with OutOfMemoryException (see docs on UpdateDocument), though i've never seen this in real life - _latestGen = IndexWriter.UpdateDocument(new Term(ExamineFieldNames.ItemIdFieldName, valueSet.Id), doc); + _latestGen = IndexWriter.UpdateDocument(new Term(ExamineFieldNames.ItemIdFieldName, valueSet.Id), _options.FacetConfig.Build(doc)); } /// diff --git a/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs b/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs new file mode 100644 index 000000000..41de2d229 --- /dev/null +++ b/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs @@ -0,0 +1,21 @@ +using Examine.Search; + +namespace Examine.Lucene.Search +{ + public class FacetDoubleRangeQueryField : LuceneBooleanOperation, IFacetDoubleRangeQueryField + { + private readonly FacetDoubleField _field; + + public FacetDoubleRangeQueryField(LuceneSearchQuery search, FacetDoubleField field) : base(search) + { + _field = field; + } + + public IFacetDoubleRangeQueryField IsFloat(bool isFloat) + { + _field.IsFloat = isFloat; + + return this; + } + } +} diff --git a/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs b/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs new file mode 100644 index 000000000..7cb8b84fc --- /dev/null +++ b/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs @@ -0,0 +1,11 @@ +using Examine.Search; + +namespace Examine.Lucene.Search +{ + public class FacetLongRangeQueryField : LuceneBooleanOperation, IFacetLongRangeQueryField + { + public FacetLongRangeQueryField(LuceneSearchQuery search, FacetLongField _) : base(search) + { + } + } +} diff --git a/src/Examine.Lucene/Search/FacetRangeQueryField.cs b/src/Examine.Lucene/Search/FacetRangeQueryField.cs deleted file mode 100644 index 4a62c6369..000000000 --- a/src/Examine.Lucene/Search/FacetRangeQueryField.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Examine.Search; - -namespace Examine.Lucene.Search -{ - public class FacetRangeQueryField : LuceneBooleanOperation, IFacetRangeQueryField - where T : IFacetField - { - private readonly T _field; - - public FacetRangeQueryField(LuceneSearchQuery search, T field) : base(search) - { - _field = field; - } - - public IFacetRangeQueryField FacetField(string fieldName) - { - _field.FacetField = fieldName; - - return this; - } - } -} diff --git a/src/Examine.Lucene/Search/LuceneQuery.cs b/src/Examine.Lucene/Search/LuceneQuery.cs index 43b06a7ca..ac7e145b1 100644 --- a/src/Examine.Lucene/Search/LuceneQuery.cs +++ b/src/Examine.Lucene/Search/LuceneQuery.cs @@ -116,10 +116,10 @@ public IFacetQueryField Facet(string field, string value) public IFacetQueryField Facet(string field, string[] values) => _search.FacetInternal(field, values); - public IFacetRangeQueryField Facet(string field, DoubleRange[] doubleRanges) + public IFacetDoubleRangeQueryField Facet(string field, DoubleRange[] doubleRanges) => _search.FacetInternal(field, doubleRanges); - public IFacetRangeQueryField Facet(string field, Int64Range[] longRanges) + public IFacetLongRangeQueryField Facet(string field, Int64Range[] longRanges) => _search.FacetInternal(field, longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs index 1ef6f0e1e..432a34e05 100644 --- a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs +++ b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs @@ -3,7 +3,11 @@ using System.Linq; using Examine.Search; using Lucene.Net.Documents; +using Lucene.Net.Facet; +using Lucene.Net.Facet.Range; +using Lucene.Net.Facet.SortedSet; using Lucene.Net.Index; +using Lucene.Net.Queries.Function.ValueSources; using Lucene.Net.Search; namespace Examine.Lucene.Search @@ -19,15 +23,17 @@ public class LuceneSearchExecutor private readonly ISearchContext _searchContext; private readonly Query _luceneQuery; private readonly ISet _fieldsToLoad; + private readonly IList _facetFields; private int? _maxDoc; - internal LuceneSearchExecutor(QueryOptions options, Query query, IEnumerable sortField, ISearchContext searchContext, ISet fieldsToLoad) + internal LuceneSearchExecutor(QueryOptions options, Query query, IEnumerable sortField, ISearchContext searchContext, ISet fieldsToLoad, IList facetFields) { _options = options ?? QueryOptions.Default; _luceneQuery = query ?? throw new ArgumentNullException(nameof(query)); _fieldsToLoad = fieldsToLoad; _sortField = sortField ?? throw new ArgumentNullException(nameof(sortField)); _searchContext = searchContext ?? throw new ArgumentNullException(nameof(searchContext)); + _facetFields = facetFields; } private int MaxDoc @@ -93,7 +99,17 @@ public ISearchResults Execute() using (ISearcherReference searcher = _searchContext.GetSearcher()) { - searcher.IndexSearcher.Search(_luceneQuery, topDocsCollector); + FacetsCollector facetsCollector; + if(_facetFields.Count > 0) + { + facetsCollector = new FacetsCollector(); + searcher.IndexSearcher.Search(_luceneQuery, MultiCollector.Wrap(topDocsCollector, facetsCollector)); + } + else + { + facetsCollector = null; + searcher.IndexSearcher.Search(_luceneQuery, topDocsCollector); + } TopDocs topDocs; if (sortFields.Length > 0) @@ -114,7 +130,80 @@ public ISearchResults Execute() results.Add(result); } - return new LuceneSearchResults(results, totalItemCount); + var facets = ExtractFacets(facetsCollector, searcher); + + return new LuceneSearchResults(results, totalItemCount, facets); + } + } + + private IDictionary ExtractFacets(FacetsCollector facetsCollector, ISearcherReference searcher) + { + var facets = new Dictionary(); + if (facetsCollector == null || _facetFields.Count == 0) + { + return facets; + } + + var facetFields = _facetFields.OrderBy(field => field.FacetField); + + SortedSetDocValuesReaderState sortedSetReaderState = null; + + foreach(var field in facetFields) + { + if (field is FacetFullTextField facetFullTextField) + { + ExtractFullTextFacets(facetsCollector, searcher, facets, sortedSetReaderState, field, facetFullTextField); + } + else if (field is FacetLongField facetLongField) + { + var longFacetCounts = new Int64RangeFacetCounts(facetLongField.Field, facetsCollector, facetLongField.LongRanges); + + var longFacets = longFacetCounts.GetTopChildren(0, facetLongField.Field); + facets.Add(facetLongField.Field, new Examine.Search.FacetResult(longFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value)))); + } + else if (field is FacetDoubleField facetDoubleField) + { + DoubleRangeFacetCounts doubleFacetCounts; + if (facetDoubleField.IsFloat) + { + doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, new SingleFieldSource(facetDoubleField.Field), facetsCollector, facetDoubleField.DoubleRanges); + } + else + { + doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, facetsCollector, facetDoubleField.DoubleRanges); + } + + var doubleFacets = doubleFacetCounts.GetTopChildren(0, facetDoubleField.Field); + facets.Add(facetDoubleField.Field, new Examine.Search.FacetResult(doubleFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value)))); + } + } + + return facets; + } + + private static void ExtractFullTextFacets(FacetsCollector facetsCollector, ISearcherReference searcher, Dictionary facets, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field, FacetFullTextField facetFullTextField) + { + if (sortedSetReaderState == null || !sortedSetReaderState.Field.Equals(field.FacetField)) + { + sortedSetReaderState = new DefaultSortedSetDocValuesReaderState(searcher.IndexSearcher.IndexReader, field.FacetField); + } + + var sortedFacetsCounts = new SortedSetDocValuesFacetCounts(sortedSetReaderState, facetsCollector); + + if (facetFullTextField.Values != null && facetFullTextField.Values.Length > 0) + { + var facetValues = new List(); + foreach (var label in facetFullTextField.Values) + { + var value = sortedFacetsCounts.GetSpecificValue(facetFullTextField.Field, label); + facetValues.Add(new FacetValue(label, value)); + } + facets.Add(facetFullTextField.Field, new Examine.Search.FacetResult(facetValues.OrderBy(value => value.Value).Take(facetFullTextField.MaxCount))); + } + else + { + var sortedFacets = sortedFacetsCounts.GetTopChildren(facetFullTextField.MaxCount, facetFullTextField.Field); + facets.Add(facetFullTextField.Field, new Examine.Search.FacetResult(sortedFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value)))); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchQuery.cs b/src/Examine.Lucene/Search/LuceneSearchQuery.cs index ea2c532cf..8d5d5d979 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQuery.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQuery.cs @@ -18,7 +18,7 @@ public class LuceneSearchQuery : LuceneSearchQueryBase, IQueryExecutor { private readonly ISearchContext _searchContext; private ISet _fieldsToLoad = null; - private IList _facetFields = new List(); + private readonly IList _facetFields = new List(); public LuceneSearchQuery( ISearchContext searchContext, @@ -212,7 +212,7 @@ private ISearchResults Search(QueryOptions options) } } - var executor = new LuceneSearchExecutor(options, query, SortFields, _searchContext, _fieldsToLoad); + var executor = new LuceneSearchExecutor(options, query, SortFields, _searchContext, _fieldsToLoad, _facetFields); var pagesResults = executor.Execute(); @@ -300,9 +300,9 @@ public IBooleanOperation SelectAllFieldsInternal() public override IFacetQueryField Facet(string field, string[] values) => FacetInternal(field, values); - public override IFacetRangeQueryField Facet(string field, DoubleRange[] doubleRanges) => FacetInternal(field, doubleRanges); + public override IFacetDoubleRangeQueryField Facet(string field, DoubleRange[] doubleRanges) => FacetInternal(field, doubleRanges); - public override IFacetRangeQueryField Facet(string field, Int64Range[] longRanges) => FacetInternal(field, longRanges); + public override IFacetLongRangeQueryField Facet(string field, Int64Range[] longRanges) => FacetInternal(field, longRanges); internal IFacetQueryField FacetInternal(string field) => FacetInternal(field, Array.Empty()); @@ -320,22 +320,22 @@ internal IFacetQueryField FacetInternal(string field, string[] values) } - internal IFacetRangeQueryField FacetInternal(string field, DoubleRange[] doubleRanges) + internal IFacetDoubleRangeQueryField FacetInternal(string field, DoubleRange[] doubleRanges) { var facet = new FacetDoubleField(field, doubleRanges); _facetFields.Add(facet); - return new FacetRangeQueryField(this, facet); + return new FacetDoubleRangeQueryField(this, facet); } - internal IFacetRangeQueryField FacetInternal(string field, Int64Range[] longRanges) + internal IFacetLongRangeQueryField FacetInternal(string field, Int64Range[] longRanges) { var facet = new FacetLongField(field, longRanges); _facetFields.Add(facet); - return new FacetRangeQueryField(this, facet); + return new FacetLongRangeQueryField(this, facet); } } } diff --git a/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs b/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs index 05b6372dd..c59f0dbb0 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs @@ -512,8 +512,8 @@ public override string ToString() public abstract IFacetQueryField Facet(string field, string[] values); - public abstract IFacetRangeQueryField Facet(string field, DoubleRange[] doubleRanges); + public abstract IFacetDoubleRangeQueryField Facet(string field, DoubleRange[] doubleRanges); - public abstract IFacetRangeQueryField Facet(string field, Int64Range[] longRanges); + public abstract IFacetLongRangeQueryField Facet(string field, Int64Range[] longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchResults.cs b/src/Examine.Lucene/Search/LuceneSearchResults.cs index c05057055..10b518125 100644 --- a/src/Examine.Lucene/Search/LuceneSearchResults.cs +++ b/src/Examine.Lucene/Search/LuceneSearchResults.cs @@ -6,17 +6,10 @@ namespace Examine.Lucene.Search { public class LuceneSearchResults : ISearchResults, IFacetResults { - public static LuceneSearchResults Empty { get; } = new LuceneSearchResults(Array.Empty(), 0); + public static LuceneSearchResults Empty { get; } = new LuceneSearchResults(Array.Empty(), 0, new Dictionary()); private readonly IReadOnlyCollection _results; - public LuceneSearchResults(IReadOnlyCollection results, int totalItemCount) - { - _results = results; - TotalItemCount = totalItemCount; - Facets = new Dictionary(); - } - public LuceneSearchResults(IReadOnlyCollection results, int totalItemCount, IDictionary facets) { _results = results; From aa3f545dce0acf8dfc1a6a7ac1398c5b99a662bf Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Tue, 15 Nov 2022 13:47:33 +0100 Subject: [PATCH 03/42] wip: Implementation Examine facets --- src/Examine.Core/ExamineFieldNames.cs | 7 +- src/Examine.Core/FieldDefinitionTypes.cs | 24 +- src/Examine.Core/Search/FacetFullTextField.cs | 2 +- src/Examine.Core/Search/FacetResult.cs | 6 + src/Examine.Core/Search/IFacetResult.cs | 6 + .../Indexing/FacetDateTimeType.cs | 2 + .../Indexing/FacetDoubleType.cs | 2 + src/Examine.Lucene/Indexing/FacetInt32Type.cs | 2 + src/Examine.Lucene/Indexing/FacetInt64Type.cs | 2 + .../Indexing/FacetSingleType.cs | 2 + .../Search/LuceneSearchExecutor.cs | 18 + src/Examine.Lucene/Search/SearchContext.cs | 2 +- .../Search/FacetFluentApiTests.cs | 2679 +++++++++++++++++ 13 files changed, 2739 insertions(+), 15 deletions(-) create mode 100644 src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs diff --git a/src/Examine.Core/ExamineFieldNames.cs b/src/Examine.Core/ExamineFieldNames.cs index b2a3f1fd2..01a85f72e 100644 --- a/src/Examine.Core/ExamineFieldNames.cs +++ b/src/Examine.Core/ExamineFieldNames.cs @@ -1,4 +1,4 @@ -namespace Examine +namespace Examine { public static class ExamineFieldNames { @@ -23,6 +23,11 @@ public static class ExamineFieldNames public const string ItemIdFieldName = "__NodeId"; public const string ItemTypeFieldName = "__NodeTypeAlias"; + + /// + /// The default field name for storing facet information + /// + public const string DefaultFacetsName = "$facets"; } diff --git a/src/Examine.Core/FieldDefinitionTypes.cs b/src/Examine.Core/FieldDefinitionTypes.cs index 616e0d520..d3b140020 100644 --- a/src/Examine.Core/FieldDefinitionTypes.cs +++ b/src/Examine.Core/FieldDefinitionTypes.cs @@ -44,61 +44,61 @@ public static class FieldDefinitionTypes /// /// Facetable version of /// - public const string FacetInteger = "int"; + public const string FacetInteger = "facetint"; /// /// Facetable version of /// - public const string FacetFloat = "float"; + public const string FacetFloat = "facetfloat"; /// /// Facetable version of /// - public const string FacetDouble = "double"; + public const string FacetDouble = "facetdouble"; /// /// Facetable version of /// - public const string FacetLong = "long"; + public const string FacetLong = "facetlong"; /// /// Facetable version of /// - public const string FacetDateTime = "datetime"; + public const string FacetDateTime = "facetdatetime"; /// /// Facetable version of /// - public const string FacetDateYear = "date.year"; + public const string FacetDateYear = "facetdate.year"; /// /// Facetable version of /// - public const string FacetDateMonth = "date.month"; + public const string FacetDateMonth = "facetdate.month"; /// /// Facetable version of /// - public const string FacetDateDay = "date.day"; + public const string FacetDateDay = "facetdate.day"; /// /// Facetable version of /// - public const string FacetDateHour = "date.hour"; + public const string FacetDateHour = "facetdate.hour"; /// /// Facetable version of /// - public const string FacetDateMinute = "date.minute"; + public const string FacetDateMinute = "facetdate.minute"; /// /// Facetable version of /// - public const string FacetFullText = "fulltext"; + public const string FacetFullText = "facetfulltext"; /// /// Facetable version of /// - public const string FacetFullTextSortable = "fulltextsortable"; + public const string FacetFullTextSortable = "facetfulltextsortable"; } } diff --git a/src/Examine.Core/Search/FacetFullTextField.cs b/src/Examine.Core/Search/FacetFullTextField.cs index ee80dd056..1a64fb10f 100644 --- a/src/Examine.Core/Search/FacetFullTextField.cs +++ b/src/Examine.Core/Search/FacetFullTextField.cs @@ -10,7 +10,7 @@ public class FacetFullTextField : IFacetFullTextField public string FacetField { get; set; } - public FacetFullTextField(string field, string[] values, int maxCount = 10, string facetField = "$facets") + public FacetFullTextField(string field, string[] values, int maxCount = 10, string facetField = ExamineFieldNames.DefaultFacetsName) { Field = field; Values = values; diff --git a/src/Examine.Core/Search/FacetResult.cs b/src/Examine.Core/Search/FacetResult.cs index 41488f1ee..e58d23bac 100644 --- a/src/Examine.Core/Search/FacetResult.cs +++ b/src/Examine.Core/Search/FacetResult.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Text; using Examine.Lucene.Search; @@ -20,6 +21,11 @@ public IEnumerator GetEnumerator() return _values.GetEnumerator(); } + public IFacetValue Facet(string label) + { + return _values.FirstOrDefault(field => field.Label.Equals(label)); + } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); diff --git a/src/Examine.Core/Search/IFacetResult.cs b/src/Examine.Core/Search/IFacetResult.cs index 5e2630460..81dc59547 100644 --- a/src/Examine.Core/Search/IFacetResult.cs +++ b/src/Examine.Core/Search/IFacetResult.cs @@ -4,5 +4,11 @@ namespace Examine.Lucene.Search { public interface IFacetResult : IEnumerable { + /// + /// Gets the facet for a label + /// + /// + /// + IFacetValue Facet(string label); } } diff --git a/src/Examine.Lucene/Indexing/FacetDateTimeType.cs b/src/Examine.Lucene/Indexing/FacetDateTimeType.cs index 0fa53f4f6..43558c41a 100644 --- a/src/Examine.Lucene/Indexing/FacetDateTimeType.cs +++ b/src/Examine.Lucene/Indexing/FacetDateTimeType.cs @@ -1,5 +1,6 @@ using System; using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; using Microsoft.Extensions.Logging; namespace Examine.Lucene.Indexing @@ -19,6 +20,7 @@ protected override void AddSingleValue(Document doc, object value) var val = DateToLong(parsedVal); + doc.Add(new SortedSetDocValuesFacetField(FieldName, val.ToString())); doc.Add(new NumericDocValuesField(FieldName, val)); } } diff --git a/src/Examine.Lucene/Indexing/FacetDoubleType.cs b/src/Examine.Lucene/Indexing/FacetDoubleType.cs index 438b22f41..ffc112ee4 100644 --- a/src/Examine.Lucene/Indexing/FacetDoubleType.cs +++ b/src/Examine.Lucene/Indexing/FacetDoubleType.cs @@ -1,4 +1,5 @@ using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; using Microsoft.Extensions.Logging; namespace Examine.Lucene.Indexing @@ -16,6 +17,7 @@ protected override void AddSingleValue(Document doc, object value) if (!TryConvert(value, out double parsedVal)) return; + doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); doc.Add(new DoubleDocValuesField(FieldName, parsedVal)); } } diff --git a/src/Examine.Lucene/Indexing/FacetInt32Type.cs b/src/Examine.Lucene/Indexing/FacetInt32Type.cs index c2bf21dac..15f6a6fd6 100644 --- a/src/Examine.Lucene/Indexing/FacetInt32Type.cs +++ b/src/Examine.Lucene/Indexing/FacetInt32Type.cs @@ -1,4 +1,5 @@ using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; using Microsoft.Extensions.Logging; namespace Examine.Lucene.Indexing @@ -16,6 +17,7 @@ protected override void AddSingleValue(Document doc, object value) if (!TryConvert(value, out int parsedVal)) return; + doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); doc.Add(new NumericDocValuesField(FieldName, parsedVal)); } } diff --git a/src/Examine.Lucene/Indexing/FacetInt64Type.cs b/src/Examine.Lucene/Indexing/FacetInt64Type.cs index 1e17da6a8..037a6120f 100644 --- a/src/Examine.Lucene/Indexing/FacetInt64Type.cs +++ b/src/Examine.Lucene/Indexing/FacetInt64Type.cs @@ -1,4 +1,5 @@ using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; using Microsoft.Extensions.Logging; namespace Examine.Lucene.Indexing @@ -16,6 +17,7 @@ protected override void AddSingleValue(Document doc, object value) if (!TryConvert(value, out long parsedVal)) return; + doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); doc.Add(new NumericDocValuesField(FieldName, parsedVal)); } } diff --git a/src/Examine.Lucene/Indexing/FacetSingleType.cs b/src/Examine.Lucene/Indexing/FacetSingleType.cs index 35844623c..e6969c160 100644 --- a/src/Examine.Lucene/Indexing/FacetSingleType.cs +++ b/src/Examine.Lucene/Indexing/FacetSingleType.cs @@ -1,4 +1,5 @@ using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; using Microsoft.Extensions.Logging; namespace Examine.Lucene.Indexing @@ -16,6 +17,7 @@ protected override void AddSingleValue(Document doc, object value) if (!TryConvert(value, out float parsedVal)) return; + doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); doc.Add(new SingleDocValuesField(FieldName, parsedVal)); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs index 432a34e05..0b46bd4f5 100644 --- a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs +++ b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs @@ -159,6 +159,12 @@ private IDictionary ExtractFacets(FacetsCollector facetsCo var longFacetCounts = new Int64RangeFacetCounts(facetLongField.Field, facetsCollector, facetLongField.LongRanges); var longFacets = longFacetCounts.GetTopChildren(0, facetLongField.Field); + + if(longFacets == null) + { + continue; + } + facets.Add(facetLongField.Field, new Examine.Search.FacetResult(longFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value)))); } else if (field is FacetDoubleField facetDoubleField) @@ -174,6 +180,12 @@ private IDictionary ExtractFacets(FacetsCollector facetsCo } var doubleFacets = doubleFacetCounts.GetTopChildren(0, facetDoubleField.Field); + + if(doubleFacets == null) + { + continue; + } + facets.Add(facetDoubleField.Field, new Examine.Search.FacetResult(doubleFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value)))); } } @@ -203,6 +215,12 @@ private static void ExtractFullTextFacets(FacetsCollector facetsCollector, ISear else { var sortedFacets = sortedFacetsCounts.GetTopChildren(facetFullTextField.MaxCount, facetFullTextField.Field); + + if(sortedFacets == null) + { + return; + } + facets.Add(facetFullTextField.Field, new Examine.Search.FacetResult(sortedFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value)))); } } diff --git a/src/Examine.Lucene/Search/SearchContext.cs b/src/Examine.Lucene/Search/SearchContext.cs index 406a6941f..7cdd6987a 100644 --- a/src/Examine.Lucene/Search/SearchContext.cs +++ b/src/Examine.Lucene/Search/SearchContext.cs @@ -40,7 +40,7 @@ public string[] SearchableFields //exclude the special index fields _searchableFields = fields - .Where(x => !x.StartsWith(ExamineFieldNames.SpecialFieldPrefix)) + .Where(x => !x.StartsWith(ExamineFieldNames.SpecialFieldPrefix) && !x.Equals(ExamineFieldNames.DefaultFacetsName)) .ToArray(); } finally diff --git a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs new file mode 100644 index 000000000..47de73b44 --- /dev/null +++ b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs @@ -0,0 +1,2679 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Examine.Lucene; +using Examine.Lucene.Providers; +using Examine.Lucene.Search; +using Examine.Search; +using J2N; +using Lucene.Net.Analysis.En; +using Lucene.Net.Analysis.Standard; +using Lucene.Net.Facet; +using Lucene.Net.Facet.Range; +using Lucene.Net.QueryParsers.Classic; +using Lucene.Net.Search; +using NUnit.Framework; + + + +namespace Examine.Test.Examine.Lucene.Search +{ + [TestFixture] + public class FacetFluentApiTests : ExamineBaseTest + { + + /* + * Below is a copy of the tests from FluentApiTests + * With faceted search added to ensure that faceting + * doesn't break the search when used + */ + + [Test] + public void Allow_Leading_Wildcards_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "location 1", bodyText = "Zanzibar is in Africa"}), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "location 2", bodyText = "In Canada there is a town called Sydney in Nova Scotia"}), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "location 3", bodyText = "Sydney is the capital of NSW in Australia"}) + }); + + var searcher = (BaseLuceneSearcher)indexer.Searcher; + + var query1 = searcher.CreateQuery( + "content", + BooleanOperation.And, + searcher.LuceneAnalyzer, + new LuceneSearchOptions + { + AllowLeadingWildcard = true + }).NativeQuery("*dney") + .And() + .Facet("nodeName"); + + Assert.Throws(() => + searcher.CreateQuery( + "content", + BooleanOperation.And, + searcher.LuceneAnalyzer, + new LuceneSearchOptions + { + AllowLeadingWildcard = false + }).NativeQuery("*dney")); + + var results1 = query1.Execute(); + + var facetResults = results1.GetFacet("nodeName"); + + Assert.AreEqual(2, results1.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + Assert.AreEqual(1, facetResults.First().Value); + } + } + + [Test] + public void NativeQuery_Single_Word_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "location 1", bodyText = "Zanzibar is in Africa"}), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "location 2", bodyText = "In Canada there is a town called Sydney in Nova Scotia"}), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "location 3", bodyText = "Sydney is the capital of NSW in Australia"}) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("content") + .NativeQuery("sydney") + .And() + .Facet("nodeName"); + + Console.WriteLine(query); + + var results = query.Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + Assert.AreEqual(1, facetResults.Last().Value); + } + } + + [Test] + public void Uppercase_Category_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "cOntent", + new { nodeName = "location 1", bodyText = "Zanzibar is in Africa"}), + ValueSet.FromObject(2.ToString(), "cOntent", + new { nodeName = "location 2", bodyText = "In Canada there is a town called Sydney in Nova Scotia"}), + ValueSet.FromObject(3.ToString(), "cOntent", + new { nodeName = "location 3", bodyText = "Sydney is the capital of NSW in Australia"}) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("cOntent") + .Facet("nodeName") + .And() + .All(); + + Console.WriteLine(query); + + var results = query.Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(3, results.TotalItemCount); + Assert.AreEqual(3, facetResults.Count()); + } + } + + [Test] + public void NativeQuery_Phrase_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("bodyText", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "location 1", bodyText = "Zanzibar is in Africa"}), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "location 2", bodyText = "In Canada there is a town called Sydney in Nova Scotia"}), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "location 3", bodyText = "In Australia there is a town called Bateau Bay in NSW"}) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("content").NativeQuery("\"town called\"").And().Facet("bodyText"); + + Console.WriteLine(query); + Assert.AreEqual("{ Category: content, LuceneQuery: +(nodeName:\"town called\" bodyText:\"town called\") }", query.ToString()); + + var results = query.Execute(); + + var facetResults = results.GetFacet("bodyText"); + + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + } + } + + [Test] + public void Managed_Range_Date_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("created", FieldDefinitionTypes.FacetDateTime)))) + { + + + indexer.IndexItems(new[] + { + ValueSet.FromObject(123.ToString(), "content", + new + { + created = new DateTime(2000, 01, 02), + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Home" + }), + ValueSet.FromObject(2123.ToString(), "content", + new + { + created = new DateTime(2000, 01, 04), + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Test" + }), + ValueSet.FromObject(3123.ToString(), "content", + new + { + created = new DateTime(2000, 01, 05), + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Page" + }) + }); + + + var searcher = indexer.Searcher; + + var numberSortedCriteria = searcher.CreateQuery() + .RangeQuery(new[] { "created" }, new DateTime(2000, 01, 02), new DateTime(2000, 01, 05)) + .And() + .Facet("created", new Int64Range[] + { + new Int64Range("First days", new DateTime(2000, 01, 01).Ticks, true, new DateTime(2000, 01, 03).Ticks, true), + new Int64Range("Last days", new DateTime(2000, 01, 04).Ticks, true, new DateTime(2000, 01, 06).Ticks, true) + }); + + var numberSortedResult = numberSortedCriteria.Execute(); + + var facetResult = numberSortedResult.GetFacet("created"); + + Assert.AreEqual(3, numberSortedResult.TotalItemCount); + Assert.AreEqual(2, facetResult.Count()); + Assert.AreEqual(1, facetResult.First().Value); + Assert.AreEqual("First days", facetResult.First().Label); + Assert.AreEqual(2, facetResult.Last().Value); + Assert.AreEqual("Last days", facetResult.Last().Label); + } + } + + [Test] + public void Managed_Full_Text_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + + using (var luceneDir1 = new RandomIdRAMDirectory()) + using (var indexer1 = GetTestIndex( + luceneDir1, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("item1", FieldDefinitionTypes.FacetFullText)))) + { + indexer1.IndexItem(ValueSet.FromObject("1", "content", new { item1 = "value1", item2 = "The agitated zebras gallop back and forth in short, panicky dashes, then skitter off into the total absolute darkness." })); + indexer1.IndexItem(ValueSet.FromObject("2", "content", new { item1 = "value2", item2 = "The festival lasts five days and celebrates the victory of good over evil, light over darkness, and knowledge over ignorance." })); + indexer1.IndexItem(ValueSet.FromObject("3", "content", new { item1 = "value3", item2 = "They are expected to confront the darkness and show evidence that they have done so in their papers" })); + indexer1.IndexItem(ValueSet.FromObject("4", "content", new { item1 = "value4", item2 = "Scientists believe the lake could be home to cold-loving microbial life adapted to living in total darkness." })); + indexer1.IndexItem(ValueSet.FromObject("5", "content", new { item1 = "value3", item2 = "Scotch scotch scotch, i love scotch" })); + indexer1.IndexItem(ValueSet.FromObject("6", "content", new { item1 = "value4", item2 = "60% of the time, it works everytime" })); + indexer1.IndexItem(ValueSet.FromObject("7", "content", new { SomeField = "value5", AnotherField = "another value" })); + + var searcher = indexer1.Searcher; + + var result = searcher.CreateQuery() + .ManagedQuery("darkness") + .And() + .Facet("item1") + .Execute(); + + var facetResults = result.GetFacet("item1"); + + Assert.AreEqual(4, result.TotalItemCount); + Assert.AreEqual(4, facetResults.Count()); + + Console.WriteLine("Search 1:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + + result = searcher.CreateQuery() + .ManagedQuery("total darkness") + .And() + .Facet("item1") + .Execute(); + facetResults = result.GetFacet("item1"); + + Assert.AreEqual(2, result.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + Console.WriteLine("Search 2:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + } + } + + [Test] + public void Managed_Full_Text_With_Bool_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + + using (var luceneDir1 = new RandomIdRAMDirectory()) + using (var indexer1 = GetTestIndex( + luceneDir1, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("item1", FieldDefinitionTypes.FacetFullText)))) + { + indexer1.IndexItem(ValueSet.FromObject("1", "content", new { item1 = "value1", item2 = "The agitated zebras gallop back and forth in short, panicky dashes, then skitter off into the total absolute darkness." })); + indexer1.IndexItem(ValueSet.FromObject("2", "content", new { item1 = "value2", item2 = "The festival lasts five days and celebrates the victory of good over evil, light over darkness, and knowledge over ignorance." })); + indexer1.IndexItem(ValueSet.FromObject("3", "content", new { item1 = "value3", item2 = "They are expected to confront the darkness and show evidence that they have done so in their papers" })); + indexer1.IndexItem(ValueSet.FromObject("4", "content", new { item1 = "value4", item2 = "Scientists believe the lake could be home to cold-loving microbial life adapted to living in total darkness." })); + indexer1.IndexItem(ValueSet.FromObject("5", "content", new { item1 = "value3", item2 = "Scotch scotch scotch, i love scotch" })); + indexer1.IndexItem(ValueSet.FromObject("6", "content", new { item1 = "value4", item2 = "60% of the time, it works everytime" })); + + var searcher = indexer1.Searcher; + + var qry = searcher.CreateQuery().ManagedQuery("darkness").And().Field("item1", "value1").And().Facet("item1"); + Console.WriteLine(qry); + var result = qry.Execute(); + + var facetResults = result.GetFacet("item1"); + + Assert.AreEqual(1, result.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Console.WriteLine("Search 1:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + + qry = searcher.CreateQuery().ManagedQuery("darkness") + .And(query => query.Field("item1", "value1").Or().Field("item1", "value2"), BooleanOperation.Or) + .And() + .Facet("item1"); + Console.WriteLine(qry); + result = qry.Execute(); + + facetResults = result.GetFacet("item1"); + + Assert.AreEqual(2, result.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + Console.WriteLine("Search 2:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + } + } + + [Test] + public void Not_Managed_Full_Text_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + + using (var luceneDir1 = new RandomIdRAMDirectory()) + using (var indexer1 = GetTestIndex( + luceneDir1, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("item1", FieldDefinitionTypes.FacetFullText)) + )) + { + indexer1.IndexItem(ValueSet.FromObject("1", "content", new { item1 = "value1", item2 = "The agitated zebras gallop back and forth in short, panicky dashes, then skitter off into the total absolute chaos." })); + indexer1.IndexItem(ValueSet.FromObject("2", "content", new { item1 = "value1", item2 = "The festival lasts five days and celebrates the victory of good over evil, light over darkness, and knowledge over ignorance." })); + indexer1.IndexItem(ValueSet.FromObject("3", "content", new { item1 = "value3", item2 = "They are expected to confront the darkness and show evidence that they have done so in their papers" })); + indexer1.IndexItem(ValueSet.FromObject("4", "content", new { item1 = "value4", item2 = "Scientists believe the lake could be home to cold-loving microbial life adapted to living in total darkness." })); + indexer1.IndexItem(ValueSet.FromObject("5", "content", new { item1 = "value3", item2 = "Scotch scotch scotch, i love scotch" })); + indexer1.IndexItem(ValueSet.FromObject("6", "content", new { item1 = "value4", item2 = "60% of the time, it works everytime" })); + + var searcher = indexer1.Searcher; + + var qry = searcher.CreateQuery() + .Field("item1", "value1") + .Not().ManagedQuery("darkness") + .And() + .Facet("item1"); + + Console.WriteLine(qry); + var result = qry.Execute(); + + var facetResults = result.GetFacet("item1"); + + Assert.AreEqual(1, result.TotalItemCount); + Assert.AreEqual("1", result.ElementAt(0).Id); + Assert.AreEqual(1, facetResults.Count()); + + Console.WriteLine("Search 1:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + } + } + + [Test] + public void Managed_Range_Int_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)))) + { + + + indexer.IndexItems(new[] + { + ValueSet.FromObject(123.ToString(), "content", + new + { + parentID = 121, + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Home" + }), + ValueSet.FromObject(2.ToString(), "content", + new + { + parentID = 123, + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Test" + }), + ValueSet.FromObject(3.ToString(), "content", + new + { + parentID = 124, + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Page" + }) + }); + + var searcher = indexer.Searcher; + + var numberSortedCriteria = searcher.CreateQuery() + .RangeQuery(new[] { "parentID" }, 122, 124) + .And() + .Facet("parentID", new Int64Range[] + { + new Int64Range("120-122", 120, true, 122, true), + new Int64Range("123-125", 123, true, 125, true) + }); + + var numberSortedResult = numberSortedCriteria.Execute(); + + var facetResults = numberSortedResult.GetFacet("parentID"); + + Assert.AreEqual(2, numberSortedResult.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + Assert.AreEqual(0, facetResults.First(result => result.Label == "120-122").Value); + Assert.AreEqual(2, facetResults.First(result => result.Label == "123-125").Value); + } + } + + [Test] + public void Legacy_ParentId_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)))) + { + + + indexer.IndexItems(new[] + { + ValueSet.FromObject(123.ToString(), "content", + new + { + nodeName = "my name 1", + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Home" + }), + ValueSet.FromObject(2.ToString(), "content", + new + { + parentID = 123, + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Test" + }), + ValueSet.FromObject(3.ToString(), "content", + new + { + parentID = 123, + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Page" + }) + }); + + var searcher = indexer.Searcher; + + var numberSortedCriteria = searcher.CreateQuery() + .Field("parentID", 123) + .And() + .Facet("parentID") + .OrderBy(new SortableField("sortOrder", SortType.Int)); + + var numberSortedResult = numberSortedCriteria.Execute(); + + var facetResults = numberSortedResult.GetFacet("parentID"); + + Assert.AreEqual(2, numberSortedResult.TotalItemCount); + } + + + } + + [Test] + public void Grouped_Or_Examiness_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] + { + ValueSet.FromObject(1.ToString(), "content", + new + { + nodeName = "my name 1", + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Home" + }), + ValueSet.FromObject(2.ToString(), "content", + new + { + nodeName = "About us", + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Test" + }), + ValueSet.FromObject(3.ToString(), "content", + new + { + nodeName = "my name 3", + bodyText = "lorem ipsum", + nodeTypeAlias = "CWS_Page" + }) + }); + + var searcher = indexer.Searcher; + + //paths contain punctuation, we'll escape it and ensure an exact match + var criteria = searcher.CreateQuery("content").Facet("nodeTypeAlias").And(); + + //get all node type aliases starting with CWS_Home OR and all nodees starting with "About" + var filter = criteria.GroupedOr( + new[] { "nodeTypeAlias", "nodeName" }, + new[] { "CWS_Home".Boost(10), "About".MultipleCharacterWildcard() }); + + Console.WriteLine(filter); + + var results = filter.Execute(); + + var facetResults = results.GetFacet("nodeTypeAlias"); + + foreach (var r in results) + { + Console.WriteLine($"Id = {r.Id}"); + } + + Assert.AreEqual(2, results.TotalItemCount); + + Assert.AreEqual(2, facetResults.Count()); + + } + } + + [Test] + public void Grouped_Or_Query_Output_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer)) + + { + var searcher = indexer.Searcher; + + Console.WriteLine("GROUPED OR - SINGLE FIELD, MULTI VAL"); + var criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet"); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3)", criteria.Query.ToString()); + + Console.WriteLine("GROUPED OR - MULTI FIELD, MULTI VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet"); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3)", criteria.Query.ToString()); + + Console.WriteLine("GROUPED OR - MULTI FIELD, EQUAL MULTI VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet"); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3 blahID:1 blahID:2 blahID:3)", criteria.Query.ToString()); + + Console.WriteLine("GROUPED OR - MULTI FIELD, SINGLE VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().Facet("SomeFacet"); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 parentID:1)", criteria.Query.ToString()); + + Console.WriteLine("GROUPED OR - SINGLE FIELD, SINGLE VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }).And().Facet("SomeFacet"); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1)", criteria.Query.ToString()); + + } + + + } + + [Test] + public void Grouped_And_Query_Output_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer)) + + { + var searcher = indexer.Searcher; + //new LuceneSearcher("testSearcher", luceneDir, analyzer); + + Console.WriteLine("GROUPED AND - SINGLE FIELD, MULTI VAL"); + var criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", "SomeValue"); + Console.WriteLine(criteria.Query); + //We used to assert this, but it must be allowed to do an add on the same field multiple times + //Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +id:2 +id:3)", criteria.Query.ToString()); + + Console.WriteLine("GROUPED AND - MULTI FIELD, EQUAL MULTI VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", "SomeValue"); + Console.WriteLine(criteria.Query); + //The field/value array lengths are equal so we will match the key/value pairs + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2 +blahID:3)", criteria.Query.ToString()); + + Console.WriteLine("GROUPED AND - MULTI FIELD, MULTI VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", "SomeValue"); + Console.WriteLine(criteria.Query); + //There are more than one field and there are more values than fields, in this case we align the key/value pairs + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2)", criteria.Query.ToString()); + + Console.WriteLine("GROUPED AND - MULTI FIELD, SINGLE VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().Facet("SomeFacet", "SomeValue"); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:1)", criteria.Query.ToString()); + + Console.WriteLine("GROUPED AND - SINGLE FIELD, SINGLE VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }).And().Facet("SomeFacet", "SomeValue"); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); + } + } + + /// + /// CANNOT BE A MUST WITH NOT i.e. +(-id:1 -id:2 -id:3) --> That will not work with the "+" + /// + [Test] + public void Grouped_Not_Query_Output_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer)) + + { + var searcher = indexer.Searcher; + + Console.WriteLine("GROUPED NOT - SINGLE FIELD, MULTI VAL"); + var criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3", criteria.Query.ToString()); + + Console.WriteLine("GROUPED NOT - MULTI FIELD, MULTI VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3", criteria.Query.ToString()); + + Console.WriteLine("GROUPED NOT - MULTI FIELD, EQUAL MULTI VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3 -blahID:1 -blahID:2 -blahID:3", criteria.Query.ToString()); + + Console.WriteLine("GROUPED NOT - MULTI FIELD, SINGLE VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().Facet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -parentID:1", criteria.Query.ToString()); + + Console.WriteLine("GROUPED NOT - SINGLE FIELD, SINGLE VAL"); + criteria = (LuceneSearchQuery)searcher.CreateQuery(); + criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }).And().Facet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + Console.WriteLine(criteria.Query); + Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1", criteria.Query.ToString()); + } + } + + [Test] + public void Grouped_Not_Single_Field_Single_Value_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "lorem ficus", headerText = "header 2", umbracoNaviHide = "0" }) + }); + + var searcher = indexer.Searcher; + + var query = (LuceneSearchQuery)searcher.CreateQuery("content"); + query.GroupedNot(new[] { "umbracoNaviHide" }, 1.ToString()).And().Facet("nodeName"); + Console.WriteLine(query.Query); + var results = query.Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + } + + [Test] + public void Grouped_Not_Multi_Field_Single_Value_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", bodyText = "lorem ficus", show = "1", umbracoNaviHide = "1" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "lorem ficus", show = "2", umbracoNaviHide = "0" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "my name 3", bodyText = "lorem ficus", show = "1", umbracoNaviHide = "0" }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "my name 4", bodyText = "lorem ficus", show = "0", umbracoNaviHide = "1" }) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("content").GroupedNot(new[] { "umbracoNaviHide", "show" }, 1.ToString()).And().Facet("nodeName"); + Console.WriteLine(query); + var results = query.Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(1, facetResults.Facet("my name 2").Value); + } + } + + [Test] + public void Grouped_Or_With_Not_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + + //TODO: Making this a number makes the query fail - i wonder how to make it work correctly? + // It's because the searching is NOT using a managed search + //new[] { new FieldDefinition("umbracoNaviHide", FieldDefinitionTypes.Integer) }, + + luceneDir, analyzer, + new FieldDefinitionCollection(new FieldDefinition("headerText", FieldDefinitionTypes.FacetFullText)))) + { + + + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", bodyText = "lorem ipsum", headerText = "header 1", umbracoNaviHide = "1" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) + }); + + var searcher = indexer.Searcher; + + //paths contain punctuation, we'll escape it and ensure an exact match + var criteria = searcher.CreateQuery("content"); + var filter = criteria.GroupedOr(new[] { "nodeName", "bodyText", "headerText" }, "ipsum").Not().Field("umbracoNaviHide", "1").And().Facet("headerText"); + var results = filter.Execute(); + + var facetResults = results.GetFacet("headerText"); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Facet("header 2").Value); + } + } + + [Test] + public void And_Grouped_Not_Single_Value_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("content") + .Field("nodeName", "name") + .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) + .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString() }) + .And().Facet("nodeName"); + + Console.WriteLine(query); + var results = query.Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + } + + [Test] + public void And_Grouped_Not_Multi_Value_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("content") + .Field("nodeName", "name") + .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) + .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString(), 2.ToString() }) + .And().Facet("nodeName"); + + Console.WriteLine(query); + var results = query.Execute(); + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + } + + [Test] + public void And_Not_Single_Field_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("content") + .Field("nodeName", "name") + .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) + .Not().Field("umbracoNaviHide", 1.ToString()) + .And().Facet("nodeName"); + + Console.WriteLine(query); + var results = query.Execute(); + + var facetResults = results.GetFacets(); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(1, facetResults.First().Count()); + } + } + + [Test] + public void AndNot_Nested_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("content") + .Field("nodeName", "name") + .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) + .AndNot(x => x.Field("umbracoNaviHide", 1.ToString())) + .And().Facet("nodeName"); + + // TODO: This results in { Category: content, LuceneQuery: +nodeName:name +(bodyText:ficus bodyText:ipsum) -(+umbracoNaviHide:1) } + // Which I don't think is right with the -(+ syntax but it still seems to work. + + Console.WriteLine(query); + var results = query.Execute(); + + var facetResults = results.GetFacet("nodeName"); + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + } + + [Test] + public void And_Not_Added_Later_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("content") + .Field("nodeName", "name"); + + query = query + .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString(), 2.ToString() }) + .And().Facet("nodeName"); + + // Results in { Category: content, LuceneQuery: +nodeName:name -umbracoNaviHide:1 -umbracoNaviHide:2 } + + Console.WriteLine(query); + var results = query.Execute(); + + var facetResults = results.GetFacet("nodeName"); + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + } + + [Test] + public void Not_Range_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("start", FieldDefinitionTypes.FacetInteger)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", start = 100 }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", start = 200 }) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("content") + .Field("nodeName", "name") + .Not().Field("start", 200) + .And().Facet("start", new Int64Range[] { new Int64Range("Label", 100, false, 200, false)}); + + Console.WriteLine(query); + var results = query.Execute(); + + var facetResults = results.GetFacet("start"); + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(results.First().Id, 1.ToString()); + Assert.AreEqual(0, facetResults.Facet("Label").Value); + } + } + + [Test] + public void Match_By_Path_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("__Path", "raw"), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + + + indexer.IndexItems(new[] { + new ValueSet(1.ToString(), "content", + new Dictionary + { + {"nodeName", "my name 1"}, + {"bodyText", "lorem ipsum"}, + {"__Path", "-1,123,456,789"} + }), + new ValueSet(2.ToString(), "content", + new Dictionary + { + {"nodeName", "my name 2"}, + {"bodyText", "lorem ipsum"}, + {"__Path", "-1,123,456,987"} + }) + }); + + + + var searcher = indexer.Searcher; + + //paths contain punctuation, we'll escape it and ensure an exact match + var criteria = searcher.CreateQuery("content"); + var filter = criteria.Field("__Path", "-1,123,456,789").And().Facet("nodeName"); + var results1 = filter.Execute(); + var facetResults1 = results1.GetFacet("nodeName"); + Assert.AreEqual(1, results1.TotalItemCount); + Assert.AreEqual(1, facetResults1.Count()); + + //now escape it + var exactcriteria = searcher.CreateQuery("content"); + var exactfilter = exactcriteria.Field("__Path", "-1,123,456,789".Escape()).And().Facet("nodeName"); + var results2 = exactfilter.Execute(); + var facetResults2 = results2.GetFacet("nodeName"); + Assert.AreEqual(1, results2.TotalItemCount); + Assert.AreEqual(1, facetResults2.Count()); + + //now try with native + var nativeCriteria = searcher.CreateQuery(); + var nativeFilter = nativeCriteria.NativeQuery("__Path:\\-1,123,456,789").And().Facet("nodeName"); + Console.WriteLine(nativeFilter); + var results5 = nativeFilter.Execute(); + var facetResults5 = results5.GetFacet("nodeName"); + Assert.AreEqual(1, results5.TotalItemCount); + Assert.AreEqual(1, facetResults5.Count()); + + //now try wildcards + var wildcardcriteria = searcher.CreateQuery("content"); + var wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,456,".MultipleCharacterWildcard()).And().Facet("nodeName"); + var results3 = wildcardfilter.Execute(); + var facetResults3 = results3.GetFacet("nodeName"); + Assert.AreEqual(2, results3.TotalItemCount); + Assert.AreEqual(2, facetResults3.Count()); + //not found + wildcardcriteria = searcher.CreateQuery("content"); + wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,457,".MultipleCharacterWildcard()).And().Facet("nodeName"); + results3 = wildcardfilter.Execute(); + facetResults3 = results3.GetFacet("nodeName"); + Assert.AreEqual(0, results3.TotalItemCount); + Assert.AreEqual(0, facetResults3?.Count() ?? 0); + } + + + } + + [Test] + public void Find_By_ParentId_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", bodyText = "lorem ipsum", parentID = "1235" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "lorem ipsum", parentID = "1139" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "my name 3", bodyText = "lorem ipsum", parentID = "1139" }) + }); + + var searcher = indexer.Searcher; + + var criteria = searcher.CreateQuery("content"); + var filter = criteria.Field("parentID", 1139).And().Facet("nodeName"); + + var results = filter.Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + } + } + + [Test] + public void Find_By_ParentId_Native_Query_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", bodyText = "lorem ipsum", parentID = "1235" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "lorem ipsum", parentID = "1139" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "my name 3", bodyText = "lorem ipsum", parentID = "1139" }) + }); + + var searcher = indexer.Searcher; + + var criteria = searcher.CreateQuery("content").Facet("parentID").And(); + + //NOTE: This will not work :/ + // It seems that this answer is along the lines of why: https://stackoverflow.com/questions/45516870/apache-lucene-6-queryparser-range-query-is-not-working-with-intpoint + // because the field is numeric, this range query will generate a TermRangeQuery which isn't compatible with numerics and what is annoying + // is the query parser docs uses a numerical figure as examples: https://lucene.apache.org/core/2_9_4/queryparsersyntax.html#Range%20Searches + // BUT looking closely, those numeric figures are actually dates stored in a specific way that this will work. + var filter = criteria.NativeQuery("parentID:[1139 TO 1139]"); + + //This thread says we could potentially make this work by overriding the query parser: https://stackoverflow.com/questions/5026185/how-do-i-make-the-queryparser-in-lucene-handle-numeric-ranges + + //We can use a Lucene query directly instead: + //((LuceneSearchQuery)criteria).LuceneQuery(NumericRangeQuery) + + var results = filter.Execute(); + + var facetResults = results.GetFacet("parentID"); + + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(2, facetResults.Facet("1139").Value); + } + } + + [Test] + public void Find_By_NodeTypeAlias_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", "raw"), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + + + indexer.IndexItems(new[] { + new ValueSet(1.ToString(), "content", + new Dictionary + { + {"nodeName", "my name 1"}, + {"nodeTypeAlias", "CWS_Home"} + //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Home"} + }), + new ValueSet(2.ToString(), "content", + new Dictionary + { + {"nodeName", "my name 2"}, + {"nodeTypeAlias", "CWS_Home"} + //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Home"} + }), + new ValueSet(3.ToString(), "content", + new Dictionary + { + {"nodeName", "my name 3"}, + {"nodeTypeAlias", "CWS_Page"} + //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Page"} + }) + }); + + + + var searcher = indexer.Searcher; + + var criteria = searcher.CreateQuery("content"); + var filter = criteria.Field("nodeTypeAlias", "CWS_Home".Escape()).And().Facet("nodeName"); + + var results = filter.Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + } + } + + [Test] + public void Search_With_Stop_Words_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + + + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "into 1", bodyText = "It was one thing to bring Carmen into it, but Jonathan was another story." }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", bodyText = "Hands shoved backwards into his back pockets, he took slow deliberate steps, as if he had something on his mind." }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "my name 3", bodyText = "Slowly carrying the full cups into the living room, she handed one to Alex." }) + }); + + var searcher = indexer.Searcher; + + var criteria = searcher.CreateQuery(); + + // TODO: This isn't testing correctly because the search parser is actually removing stop words to generate the search so we actually + // end up with an empty search and then by fluke this test passes. + + var filter = criteria.Field("bodyText", "into") + .Or().Field("nodeName", "into") + .And().Facet("nodeName"); + + Console.WriteLine(filter); + + var results = filter.Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(0, results.TotalItemCount); + Assert.AreEqual(0, facetResults?.Count() ?? 0); + } + } + + [Test] + public void Search_Native_Query_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", FieldDefinitionTypes.FacetFullText)))) + + + { + + + indexer.IndexItems(new[] { + new ValueSet(1.ToString(), "content", + new Dictionary + { + {"nodeName", "my name 1"}, + {"nodeTypeAlias", "CWS_Home"} + //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Home"} + }), + new ValueSet(2.ToString(), "content", + new Dictionary + { + {"nodeName", "my name 2"}, + {"nodeTypeAlias", "CWS_Home"} + //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Home"} + }), + new ValueSet(3.ToString(), "content", + new Dictionary + { + {"nodeName", "my name 3"}, + {"nodeTypeAlias", "CWS_Page"} + //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Page"} + }) + }); + + var searcher = indexer.Searcher; + + var criteria = searcher.CreateQuery("content"); + + var results = criteria.NativeQuery("nodeTypeAlias:CWS_Home").And().Facet("nodeTypeAlias").Execute(); + + var facetResults = results.GetFacet("nodeTypeAlias"); + + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(2, facetResults.Facet("CWS_Home").Value); + } + + } + + + [Test] + public void Find_Only_Image_Media_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", FieldDefinitionTypes.FacetFullText)))) + + + { + + + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "media", + new { nodeName = "my name 1", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), + ValueSet.FromObject(2.ToString(), "media", + new { nodeName = "my name 2", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), + ValueSet.FromObject(3.ToString(), "media", + new { nodeName = "my name 3", bodyText = "lorem ipsum", nodeTypeAlias = "file" }) + }); + + var searcher = indexer.Searcher; + + var criteria = searcher.CreateQuery("media"); + var filter = criteria.Field("nodeTypeAlias", "image").And().Facet("nodeTypeAlias"); + + var results = filter.Execute(); + + var facetResults = results.GetFacet("nodeTypeAlias"); + + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(2, facetResults.Facet("image").Value); + } + } + + //[Test] + //public void Find_Both_Media_And_Content() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "media", + // new { nodeName = "my name 1", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), + // ValueSet.FromObject(2.ToString(), "media", + // new { nodeName = "my name 2", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "my name 3", bodyText = "lorem ipsum", nodeTypeAlias = "file" }), + // ValueSet.FromObject(4.ToString(), "other", + // new { nodeName = "my name 4", bodyText = "lorem ipsum", nodeTypeAlias = "file" }) + // }); + + // var searcher = indexer.Searcher; + + // var criteria = searcher.CreateQuery(defaultOperation: BooleanOperation.Or); + // var filter = criteria + // .Field(ExamineFieldNames.CategoryFieldName, "media") + // .Or() + // .Field(ExamineFieldNames.CategoryFieldName, "content"); + + // var results = filter.Execute(); + + // Assert.AreEqual(3, results.TotalItemCount); + // } + //} + + //[Test] + //public void Sort_Result_By_Number_Field() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex( + // luceneDir, + // analyzer, + // //Ensure it's set to a number, otherwise it's not sortable + // new FieldDefinitionCollection(new FieldDefinition("sortOrder", FieldDefinitionTypes.Integer), new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "my name 1", sortOrder = "3", parentID = "1143" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "my name 2", sortOrder = "1", parentID = "1143" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "my name 3", sortOrder = "2", parentID = "1143" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "my name 4", bodyText = "lorem ipsum", parentID = "2222" }) + // }); + + // var searcher = indexer.Searcher; + + // var sc = searcher.CreateQuery("content"); + // var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("sortOrder", SortType.Int)); + + // var results1 = sc1.Execute().ToArray(); + + // Assert.AreEqual(3, results1.Length); + + // var currSort = 0; + // for (var i = 0; i < results1.Length; i++) + // { + // Assert.GreaterOrEqual(int.Parse(results1[i].Values["sortOrder"]), currSort); + // currSort = int.Parse(results1[i].Values["sortOrder"]); + // } + // } + //} + + //[Test] + //public void Sort_Result_By_Date_Field() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex( + // luceneDir, + // analyzer, + // //Ensure it's set to a date, otherwise it's not sortable + // new FieldDefinitionCollection(new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime), new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) + // { + + + // var now = DateTime.Now; + + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "my name 1", updateDate = now.AddDays(2).ToString("yyyy-MM-dd"), parentID = "1143" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "my name 2", updateDate = now.ToString("yyyy-MM-dd"), parentID = 1143 }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "my name 3", updateDate = now.AddDays(1).ToString("yyyy-MM-dd"), parentID = 1143 }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "my name 4", updateDate = now, parentID = "2222" }) + // }); + + // var searcher = indexer.Searcher; + + // var sc = searcher.CreateQuery("content"); + // //note: dates internally are stored as Long, see DateTimeType + // var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("updateDate", SortType.Long)); + + // var results1 = sc1.Execute().ToArray(); + + // Assert.AreEqual(3, results1.Length); + + // double currSort = 0; + // for (var i = 0; i < results1.Length; i++) + // { + // Assert.GreaterOrEqual(double.Parse(results1[i].Values["updateDate"]), currSort); + // currSort = double.Parse(results1[i].Values["updateDate"]); + // } + // } + //} + + //[Test] + //public void Sort_Result_By_Single_Field() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex( + // luceneDir, + // analyzer, + // //Ensure it's set to a fulltextsortable, otherwise it's not sortable + // new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FullTextSortable)))) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "my name 1", writerName = "administrator", parentID = "1143" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "my name 2", writerName = "administrator", parentID = "1143" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "my name 3", writerName = "administrator", parentID = "1143" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "my name 4", writerName = "writer", parentID = "2222" }) + // }); + + // var searcher = indexer.Searcher; + + // var sc = searcher.CreateQuery("content"); + // var sc1 = sc.Field("writerName", "administrator") + // .OrderBy(new SortableField("nodeName", SortType.String)); + + // sc = searcher.CreateQuery("content"); + // var sc2 = sc.Field("writerName", "administrator") + // .OrderByDescending(new SortableField("nodeName", SortType.String)); + + // var results1 = sc1.Execute(); + // var results2 = sc2.Execute(); + + // Assert.AreNotEqual(results1.First().Id, results2.First().Id); + // } + + + //} + + //[TestCase(FieldDefinitionTypes.Double, SortType.Double)] + ////[TestCase(FieldDefinitionTypes.Double, SortType.String)] // This differs from Lucene 3.x, if string is specified it will still sort like as string + //[TestCase(FieldDefinitionTypes.FullText, SortType.Double)] + //[TestCase(FieldDefinitionTypes.FullText, SortType.String)] + //[TestCase(FieldDefinitionTypes.FullTextSortable, SortType.Double)] + //[TestCase(FieldDefinitionTypes.FullTextSortable, SortType.String)] + //public void Sort_Result_By_Double_Fields(string fieldType, SortType sortType) + //{ + // // See: https://github.com/Shazwazza/Examine/issues/242 + + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex( + // luceneDir, + // analyzer, + // new FieldDefinitionCollection( + // new FieldDefinition("field1", fieldType)))) + // { + // indexer.IndexItems(new[] + // { + // ValueSet.FromObject(1.ToString(), "content", new { field1 = 5.0 }), + // ValueSet.FromObject(2.ToString(), "content", new { field1 = 4.9 }), + // ValueSet.FromObject(3.ToString(), "content", new { field1 = 4.5 }), + // ValueSet.FromObject(4.ToString(), "content", new { field1 = 3.9 }), + // ValueSet.FromObject(5.ToString(), "content", new { field1 = 3.8 }), + // ValueSet.FromObject(6.ToString(), "content", new { field1 = 2.6 }), + // }); + + // var searcher = indexer.Searcher; + + // var sc = searcher.CreateQuery("content"); + // var sc1 = sc.All() + // .OrderBy(new SortableField("field1", sortType)); + + // sc = searcher.CreateQuery("content"); + // var sc2 = sc.All() + // .OrderByDescending(new SortableField("field1", sortType)); + + // var results1 = sc1.Execute().ToList(); + // var results2 = sc2.Execute().ToList(); + + // Assert.AreEqual(2.6, double.Parse(results1[0].Values["field1"])); + // Assert.AreEqual(3.8, double.Parse(results1[1].Values["field1"])); + // Assert.AreEqual(3.9, double.Parse(results1[2].Values["field1"])); + // Assert.AreEqual(4.5, double.Parse(results1[3].Values["field1"])); + // Assert.AreEqual(4.9, double.Parse(results1[4].Values["field1"])); + // Assert.AreEqual(5.0, double.Parse(results1[5].Values["field1"])); + + + // Assert.AreEqual(2.6, double.Parse(results2[5].Values["field1"])); + // Assert.AreEqual(3.8, double.Parse(results2[4].Values["field1"])); + // Assert.AreEqual(3.9, double.Parse(results2[3].Values["field1"])); + // Assert.AreEqual(4.5, double.Parse(results2[2].Values["field1"])); + // Assert.AreEqual(4.9, double.Parse(results2[1].Values["field1"])); + // Assert.AreEqual(5.0, double.Parse(results2[0].Values["field1"])); + // } + //} + + //[Test] + //public void Sort_Result_By_Multiple_Fields() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex( + // luceneDir, + // analyzer, + // new FieldDefinitionCollection( + // new FieldDefinition("field1", FieldDefinitionTypes.Double), + // new FieldDefinition("field2", FieldDefinitionTypes.Integer)))) + // { + // indexer.IndexItems(new[] + // { + // ValueSet.FromObject(1.ToString(), "content", new { field1 = 5.0, field2 = 2 }), + // ValueSet.FromObject(2.ToString(), "content", new { field1 = 4.9, field2 = 2 }), + // ValueSet.FromObject(3.ToString(), "content", new { field1 = 4.5, field2 = 2 }), + // ValueSet.FromObject(4.ToString(), "content", new { field1 = 3.9, field2 = 1 }), + // ValueSet.FromObject(5.ToString(), "content", new { field1 = 3.8, field2 = 1 }), + // ValueSet.FromObject(6.ToString(), "content", new { field1 = 2.6, field2 = 1 }), + // }); + + // var searcher = indexer.Searcher; + + // var sc = searcher.CreateQuery("content"); + // var sc1 = sc.All() + // .OrderByDescending(new SortableField("field2", SortType.Int)) + // .OrderBy(new SortableField("field1", SortType.Double)); + + // var results1 = sc1.Execute().ToList(); + + // Assert.AreEqual("3", results1[0].Id); + // Assert.AreEqual("2", results1[1].Id); + // Assert.AreEqual("1", results1[2].Id); + // Assert.AreEqual("6", results1[3].Id); + // Assert.AreEqual("5", results1[4].Id); + // Assert.AreEqual("4", results1[5].Id); + // } + //} + + //[Test] + //public void Standard_Results_Sorted_By_Score() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "umbraco", headerText = "world", bodyText = "blah" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "umbraco", headerText = "umbraco", bodyText = "blah" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "umbraco", headerText = "umbraco", bodyText = "umbraco" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "hello", headerText = "world", bodyText = "blah" }) + // }); + + // var searcher = indexer.Searcher; + + // var sc = searcher.CreateQuery("content", BooleanOperation.Or); + // var sc1 = sc.Field("nodeName", "umbraco").Or().Field("headerText", "umbraco").Or().Field("bodyText", "umbraco"); + + // var results = sc1.Execute(); + + // //Assert + // for (int i = 0; i < results.TotalItemCount - 1; i++) + // { + // var curr = results.ElementAt(i); + // var next = results.ElementAtOrDefault(i + 1); + + // if (next == null) + // break; + + // Assert.IsTrue(curr.Score >= next.Score, string.Format("Result at index {0} must have a higher score than result at index {1}", i, i + 1)); + // } + // } + + //} + + //[Test] + //public void Skip_Results_Returns_Different_Results() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "umbraco", headerText = "world", writerName = "administrator" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "hello", headerText = "world", writerName = "blah" }) + // }); + + // var searcher = indexer.Searcher; + + // //Arrange + // var sc = searcher.CreateQuery("content").Field("writerName", "administrator"); + + // //Act + // var results = sc.Execute(); + + // //Assert + // Assert.AreNotEqual(results.First(), results.Skip(2).First(), "Third result should be different"); + // } + //} + + //[Test] + //public void Escaping_Includes_All_Words() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "codegarden09", headerText = "world", writerName = "administrator" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "codegarden 09", headerText = "umbraco", writerName = "administrator" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "codegarden 09", headerText = "umbraco", writerName = "administrator" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "codegarden 090", headerText = "world", writerName = "blah" }) + // }); + + // var searcher = indexer.Searcher; + + // //Arrange + // var sc = searcher.CreateQuery("content").Field("nodeName", "codegarden 09".Escape()); + + // Console.WriteLine(sc.ToString()); + + // //Act + // var results = sc.Execute(); + + // //Assert + // //NOTE: The result is 2 because the double space is removed with the analyzer + // Assert.AreEqual(2, results.TotalItemCount); + // } + + + //} + + //[Test] + //public void Grouped_And_Examiness() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "Aloha", nodeTypeAlias = "CWS_Hello" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "Helo", nodeTypeAlias = "CWS_World" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "Another node", nodeTypeAlias = "SomethingElse" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "Always consider this", nodeTypeAlias = "CWS_World" }) + // }); + + // var searcher = indexer.Searcher; + + // //Arrange + // var criteria = searcher.CreateQuery("content"); + + // //get all node type aliases starting with CWS and all nodees starting with "A" + // var filter = criteria.GroupedAnd( + // new[] { "nodeTypeAlias", "nodeName" }, + // new[] { "CWS".MultipleCharacterWildcard(), "A".MultipleCharacterWildcard() }); + + + // //Act + // var results = filter.Execute(); + + // //Assert + // Assert.AreEqual(2, results.TotalItemCount); + // } + //} + + //[Test] + //public void Examiness_Proximity() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "Aloha", metaKeywords = "Warren is likely to be creative" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "Helo", metaKeywords = "Creative is Warren middle name" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "Another node", metaKeywords = "If Warren were creative... well, he actually is" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "Always consider this", metaKeywords = "Warren is a very talented individual and quite creative" }) + // }); + + // var searcher = indexer.Searcher; + + // //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)); + + // //Act + // var results = filter.Execute(); + + // foreach (var r in results) + // { + // Console.WriteLine($"Id = {r.Id}"); + // } + + // //Assert + // Assert.AreEqual(3, results.TotalItemCount); + // } + //} + + ///// + ///// test range query with a Float structure + ///// + //[Test] + //public void Float_Range_SimpleIndexSet() + //{ + + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex( + // luceneDir, + // analyzer, + // //Ensure it's set to a float + // new FieldDefinitionCollection(new FieldDefinition("SomeFloat", FieldDefinitionTypes.Float)))) + // { + + + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "Aloha", SomeFloat = 1 }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "Helo", SomeFloat = 123 }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "Another node", SomeFloat = 12 }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "Always consider this", SomeFloat = 25 }) + // }); + + // var searcher = indexer.Searcher; + + // //all numbers should be between 0 and 100 based on the data source + // var criteria1 = searcher.CreateQuery(); + // var filter1 = criteria1.RangeQuery(new[] { "SomeFloat" }, 0f, 100f, true, true); + + // var criteria2 = searcher.CreateQuery(); + // var filter2 = criteria2.RangeQuery(new[] { "SomeFloat" }, 101f, 200f, true, true); + + // //Act + // var results1 = filter1.Execute(); + // var results2 = filter2.Execute(); + + // //Assert + // Assert.AreEqual(3, results1.TotalItemCount); + // Assert.AreEqual(1, results2.TotalItemCount); + // } + + + //} + + ///// + ///// test range query with a Number structure + ///// + //[Test] + //public void Number_Range_SimpleIndexSet() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex( + // luceneDir, + // analyzer, + // //Ensure it's set to a float + // new FieldDefinitionCollection(new FieldDefinition("SomeNumber", FieldDefinitionTypes.Integer)))) + // { + + + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "Aloha", SomeNumber = 1 }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "Helo", SomeNumber = 123 }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "Another node", SomeNumber = 12 }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "Always consider this", SomeNumber = 25 }) + // }); + + // var searcher = indexer.Searcher; + + // //all numbers should be between 0 and 100 based on the data source + // var criteria1 = searcher.CreateQuery(); + // var filter1 = criteria1.RangeQuery(new[] { "SomeNumber" }, 0, 100, true, true); + + // var criteria2 = searcher.CreateQuery(); + // var filter2 = criteria2.RangeQuery(new[] { "SomeNumber" }, 101, 200, true, true); + + // //Act + // var results1 = filter1.Execute(); + // var results2 = filter2.Execute(); + + // //Assert + // Assert.AreEqual(3, results1.TotalItemCount); + // Assert.AreEqual(1, results2.TotalItemCount); + // } + //} + + ///// + ///// test range query with a Number structure + ///// + //[Test] + //public void Double_Range_SimpleIndexSet() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex( + // luceneDir, + // analyzer, + // //Ensure it's set to a float + // new FieldDefinitionCollection(new FieldDefinition("SomeDouble", FieldDefinitionTypes.Double)))) + // { + + + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "Aloha", SomeDouble = 1d }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "Helo", SomeDouble = 123d }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "Another node", SomeDouble = 12d }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "Always consider this", SomeDouble = 25d }) + // }); + + // var searcher = indexer.Searcher; + + // //all numbers should be between 0 and 100 based on the data source + // var criteria1 = searcher.CreateQuery(); + // var filter1 = criteria1.RangeQuery(new[] { "SomeDouble" }, 0d, 100d, true, true); + + // var criteria2 = searcher.CreateQuery("content"); + // var filter2 = criteria2.RangeQuery(new[] { "SomeDouble" }, 101d, 200d, true, true); + + // //Act + // var results1 = filter1.Execute(); + // var results2 = filter2.Execute(); + + // //Assert + // Assert.AreEqual(3, results1.TotalItemCount); + // Assert.AreEqual(1, results2.TotalItemCount); + // } + //} + + ///// + ///// test range query with a Double structure + ///// + //[Test] + //public void Long_Range_SimpleIndexSet() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex( + // luceneDir, + // analyzer, + // //Ensure it's set to a float + // new FieldDefinitionCollection(new FieldDefinition("SomeLong", "long")))) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "Aloha", SomeLong = 1L }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "Helo", SomeLong = 123L }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "Another node", SomeLong = 12L }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "Always consider this", SomeLong = 25L }) + // }); + + // var searcher = indexer.Searcher; + + // //all numbers should be between 0 and 100 based on the data source + // var criteria1 = searcher.CreateQuery(); + // var filter1 = criteria1.RangeQuery(new[] { "SomeLong" }, 0L, 100L, true, true); + + // var criteria2 = searcher.CreateQuery(); + // var filter2 = criteria2.RangeQuery(new[] { "SomeLong" }, 101L, 200L, true, true); + + // //Act + // var results1 = filter1.Execute(); + // var results2 = filter2.Execute(); + + // //Assert + // Assert.AreEqual(3, results1.TotalItemCount); + // Assert.AreEqual(1, results2.TotalItemCount); + // } + //} + + + + ///// + ///// Test range query with a DateTime structure + ///// + //[Test] + //public void Date_Range_SimpleIndexSet() + //{ + // var reIndexDateTime = DateTime.Now; + + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex( + // luceneDir, + // analyzer, + // new FieldDefinitionCollection(new FieldDefinition("DateCreated", "datetime")))) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { DateCreated = reIndexDateTime }), + // ValueSet.FromObject(2.ToString(), "content", + // new { DateCreated = reIndexDateTime }), + // ValueSet.FromObject(3.ToString(), "content", + // new { DateCreated = reIndexDateTime.AddMonths(-10) }), + // ValueSet.FromObject(4.ToString(), "content", + // new { DateCreated = reIndexDateTime }) + // }); + + // var searcher = indexer.Searcher; + + // var criteria = searcher.CreateQuery(); + // var filter = criteria.RangeQuery(new[] { "DateCreated" }, reIndexDateTime, DateTime.Now, true, true); + + // var criteria2 = searcher.CreateQuery(); + // var filter2 = criteria2.RangeQuery(new[] { "DateCreated" }, reIndexDateTime.AddDays(-1), reIndexDateTime.AddSeconds(-1), true, true); + + // ////Act + // var results = filter.Execute(); + // var results2 = filter2.Execute(); + + // ////Assert + // Assert.IsTrue(results.TotalItemCount > 0); + // Assert.IsTrue(results2.TotalItemCount == 0); + // } + + + //} + + //[Test] + //public void Fuzzy_Search() + //{ + // var analyzer = new EnglishAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { Content = "I'm thinking here" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { Content = "I'm a thinker" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { Content = "I am pretty thoughtful" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { Content = "I thought you were cool" }) + // }); + + // var searcher = indexer.Searcher; + + // var criteria = searcher.CreateQuery(); + // var filter = criteria.Field("Content", "think".Fuzzy(0.1F)); + + // var criteria2 = searcher.CreateQuery(); + // var filter2 = criteria2.Field("Content", "thought".Fuzzy()); + + // Console.WriteLine(filter); + // Console.WriteLine(filter2); + + // ////Act + // var results = filter.Execute(); + // var results2 = filter2.Execute(); + + // foreach (var r in results) + // { + // Console.WriteLine($"Result Id: {r.Id}"); + // } + + // foreach (var r in results2) + // { + // Console.WriteLine($"Result2 Id: {r.Id}"); + // } + + // ////Assert + // Assert.AreEqual(2, results.TotalItemCount); + // Assert.AreEqual(2, results2.TotalItemCount); + // } + //} + + + //[Test] + //public void Execute_With_Take() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { Content = "hello world" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { Content = "hello worlds" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { Content = "hello you cruel world" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { Content = "hi there, hello world" }) + // }); + + // var searcher = indexer.Searcher; + + // var criteria = searcher.CreateQuery(); + // var filter = criteria.Field("Content", "hello"); + + // //Act + // var results = filter.Execute(QueryOptions.SkipTake(0, 3)); + + // //Assert + + // Assert.AreEqual(3, results.Count()); + + // //NOTE: These are the total matched! The actual results are limited + // Assert.AreEqual(4, results.TotalItemCount); + // } + //} + + //[Test] + //public void Execute_With_Take_Max_Results() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // for (int i = 0; i < 1000; i++) + // { + // indexer.IndexItems(new[] {ValueSet.FromObject(i.ToString(), "content", new { Content = "hello world" })}); + // } + + // indexer.IndexItems(new[] { ValueSet.FromObject(2000.ToString(), "content", new { Content = "donotfind" }) }); + + // var searcher = indexer.Searcher; + + // var criteria = searcher.CreateQuery(); + // var filter = criteria.Field("Content", "hello"); + + // //Act + // var results = filter.Execute(QueryOptions.SkipTake(0, int.MaxValue)); + + // //Assert + + // Assert.AreEqual(1000, results.Count()); + // } + //} + + + //[Test] + //public void Inner_Or_Query() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + + + // { + + + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { Content = "hello world", Type = "type1" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { Content = "hello something or other", Type = "type1" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { Content = "hello you cruel world", Type = "type2" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { Content = "hi there, hello world", Type = "type2" }) + // }); + + // var searcher = indexer.Searcher; + + // var criteria = searcher.CreateQuery(); + + // //Query = + // // +Type:type1 +(Content:world Content:something) + + // var filter = criteria.Field("Type", "type1") + // .And(query => query.Field("Content", "world").Or().Field("Content", "something"), BooleanOperation.Or); + + // //Act + // var results = filter.Execute(); + + // //Assert + // Assert.AreEqual(2, results.TotalItemCount); + // } + //} + + //[Test] + //public void Inner_And_Query() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + + + // { + + + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { Content = "hello world", Type = "type1" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { Content = "hello something or other", Type = "type1" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { Content = "hello something or world", Type = "type1" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { Content = "hello you cruel world", Type = "type2" }), + // ValueSet.FromObject(5.ToString(), "content", + // new { Content = "hi there, hello world", Type = "type2" }) + // }); + + // var searcher = indexer.Searcher; + + // var criteria = searcher.CreateQuery(); + + // //Query = + // // +Type:type1 +(+Content:world +Content:hello) + + // var filter = criteria.Field("Type", "type1") + // .And(query => query.Field("Content", "world").And().Field("Content", "hello")); + + // //Act + // var results = filter.Execute(); + + // //Assert + // Assert.AreEqual(2, results.TotalItemCount); + // } + //} + + //[Test] + //public void Inner_Not_Query() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + + + // { + + + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { Content = "hello world", Type = "type1" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { Content = "hello something or other", Type = "type1" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { Content = "hello something or world", Type = "type1" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { Content = "hello you cruel world", Type = "type2" }), + // ValueSet.FromObject(5.ToString(), "content", + // new { Content = "hi there, hello world", Type = "type2" }) + // }); + + // var searcher = indexer.Searcher; + + // var criteria = searcher.CreateQuery(); + + // //Query = + // // +Type:type1 +(+Content:world -Content:something) + + // var filter = criteria.Field("Type", "type1") + // .And(query => query.Field("Content", "world").Not().Field("Content", "something")); + + // //Act + // var results = filter.Execute(); + + // //Assert + // Assert.AreEqual(1, results.TotalItemCount); + // } + //} + + //[Test] + //public void Complex_Or_Group_Nested_Query() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { Content = "hello world", Type = "type1" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { Content = "hello something or other", Type = "type1" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { Content = "hello you guys", Type = "type1" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { Content = "hello you cruel world", Type = "type2" }), + // ValueSet.FromObject(5.ToString(), "content", + // new { Content = "hi there, hello world", Type = "type2" }) + // }); + + // var searcher = indexer.Searcher; + + // var criteria = searcher.CreateQuery(defaultOperation: BooleanOperation.Or); + + // //Query = + // // (+Type:type1 +(Content:world Content:something)) (+Type:type2 +(+Content:world +Content:cruel)) + + // var filter = criteria + // .Group(group => group.Field("Type", "type1") + // .And(query => query.Field("Content", "world").Or().Field("Content", "something"), BooleanOperation.Or), + // // required so that the type1 query is required + // BooleanOperation.And) + // .Or() + // .Group(group => group.Field("Type", "type2") + // .And(query => query.Field("Content", "world").And().Field("Content", "cruel")), + // // required so that the type2 query is required + // BooleanOperation.And); + + // Console.WriteLine(filter); + + // //Act + // var results = filter.Execute(); + + // //Assert + // foreach (var r in results) + // { + // Console.WriteLine($"Result Id: {r.Id}"); + // } + // Assert.AreEqual(3, results.TotalItemCount); + // } + //} + + + //[Test] + //public void Custom_Lucene_Query_With_Native() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // var searcher = indexer.Searcher; + // var criteria = (LuceneSearchQuery)searcher.CreateQuery(); + + // //combine a custom lucene query with raw lucene query + // var op = criteria.NativeQuery("hello:world").And(); + + // criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); + + // Console.WriteLine(criteria.Query); + // Assert.AreEqual("+hello:world +numTest:[4 TO 5]", criteria.Query.ToString()); + // } + //} + + //[Test] + //public void Category() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { Content = "hello world", Type = "type1" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { Content = "hello something or other", Type = "type1" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { Content = "hello you guys", Type = "type1" }), + // ValueSet.FromObject(4.ToString(), "media", + // new { Content = "hello you cruel world", Type = "type2" }), + // ValueSet.FromObject(5.ToString(), "media", + // new { Content = "hi there, hello world", Type = "type2" }) + // }); + + // var searcher = indexer.Searcher; + + // var query = searcher.CreateQuery("content").ManagedQuery("hello"); + // Console.WriteLine(query); + + // var results = query.Execute(); + + // //Assert + // Assert.AreEqual(3, results.TotalItemCount); + // } + //} + + + //[Test] + //public void Select_Field() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + + // { + // indexer.IndexItems(new[] { + // new ValueSet(1.ToString(), "content", + // new Dictionary + // { + // {"id","1" }, + // {"nodeName", "my name 1"}, + // {"bodyText", "lorem ipsum"}, + // {"__Path", "-1,123,456,789"} + // }), + // new ValueSet(2.ToString(), "content", + // new Dictionary + // { + // {"id","2" }, + // {"nodeName", "my name 2"}, + // {"bodyText", "lorem ipsum"}, + // {"__Path", "-1,123,456,987"} + // }) + // }); + + // var searcher = indexer.Searcher; + // var sc = searcher.CreateQuery("content"); + // var sc1 = sc.Field("nodeName", "my name 1").SelectField("__Path"); + + // var results = sc1.Execute(); + // var expectedLoadedFields = new string[] { "__Path" }; + // var keys = results.First().Values.Keys.ToArray(); + // Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + // Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + // } + //} + + //[Test] + //public void Select_Fields() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + + // { + // indexer.IndexItems(new[] { + // new ValueSet(1.ToString(), "content", + // new Dictionary + // { + // {"id","1" }, + // {"nodeName", "my name 1"}, + // {"bodyText", "lorem ipsum"}, + // {"__Path", "-1,123,456,789"} + // }), + // new ValueSet(2.ToString(), "content", + // new Dictionary + // { + // {"id","2" }, + // {"nodeName", "my name 2"}, + // {"bodyText", "lorem ipsum"}, + // {"__Path", "-1,123,456,987"} + // }) + // }); + + // var searcher = indexer.Searcher; + // var sc = searcher.CreateQuery("content"); + // var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new[] { "nodeName", "bodyText", "id", "__NodeId" })); + + // var results = sc1.Execute(); + // var expectedLoadedFields = new string[] { "nodeName", "bodyText", "id", "__NodeId" }; + // var keys = results.First().Values.Keys.ToArray(); + // Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + // Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + // } + + //} + + //[Test] + //public void Select_Fields_HashSet() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + + // { + // indexer.IndexItems(new[] { + // new ValueSet(1.ToString(), "content", + // new Dictionary + // { + // {"id","1" }, + // {"nodeName", "my name 1"}, + // {"bodyText", "lorem ipsum"}, + // {"__Path", "-1,123,456,789"} + // }), + // new ValueSet(2.ToString(), "content", + // new Dictionary + // { + // {"id","2" }, + // {"nodeName", "my name 2"}, + // {"bodyText", "lorem ipsum"}, + // {"__Path", "-1,123,456,987"} + // }) + // }); + + // var searcher = indexer.Searcher; + // var sc = searcher.CreateQuery("content"); + // var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new string[] { "nodeName", "bodyText" })); + + // var results = sc1.Execute(); + // var expectedLoadedFields = new string[] { "nodeName", "bodyText" }; + // var keys = results.First().Values.Keys.ToArray(); + // Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + // Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + // } + //} + + //[Test] + //public void Select_Fields_Native_Query() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + + // { + // indexer.IndexItems(new[] { + // new ValueSet(1.ToString(), "content", + // new Dictionary + // { + // {"id","1" }, + // {"nodeName", "my name 1"}, + // {"bodyText", "lorem ipsum"}, + // {"__Path", "-1,123,456,789"} + // }), + // new ValueSet(2.ToString(), "content", + // new Dictionary + // { + // {"id","2" }, + // {"nodeName", "my name 2"}, + // {"bodyText", "lorem ipsum"}, + // {"__Path", "-1,123,456,987"} + // }) + // }); + + // var searcher = indexer.Searcher; + // var sc = searcher.CreateQuery().NativeQuery("nodeName:'my name 1'"); + // var sc1 = sc.SelectFields(new HashSet(new[] { "bodyText", "id", "__NodeId" })); + + // var results = sc1.Execute(); + // var expectedLoadedFields = new string[] { "bodyText", "id", "__NodeId" }; + // var keys = results.First().Values.Keys.ToArray(); + // Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + // Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + // } + + //} + //public void Can_Skip() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "umbraco", headerText = "world", writerName = "administrator" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "hello", headerText = "world", writerName = "blah" }) + // }); + + // var searcher = indexer.Searcher; + + // //Arrange + + // var sc = searcher.CreateQuery("content").Field("writerName", "administrator"); + + // //Act + + // var results = sc.Execute().ToList(); + // Assert.AreEqual(3, results.Count); + + // results = sc.Execute().Skip(1).ToList(); + // Assert.AreEqual(2, results.Count); + + // results = sc.Execute().Skip(2).ToList(); + // Assert.AreEqual(1, results.Count); + // } + //} + + //[Test] + //public void Paging_With_Skip_Take() + //{ + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // indexer.IndexItems(new[] { + // ValueSet.FromObject(1.ToString(), "content", + // new { nodeName = "umbraco", headerText = "world", writerName = "administrator" }), + // ValueSet.FromObject(2.ToString(), "content", + // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + // ValueSet.FromObject(3.ToString(), "content", + // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + // ValueSet.FromObject(4.ToString(), "content", + // new { nodeName = "hello", headerText = "world", writerName = "blah" }), + // ValueSet.FromObject(5.ToString(), "content", + // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + // ValueSet.FromObject(6.ToString(), "content", + // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }) + // }); + + // var searcher = indexer.Searcher; + + // //Arrange + + // var sc = searcher.CreateQuery("content").Field("writerName", "administrator"); + // int pageIndex = 0; + // int pageSize = 2; + + // //Act + + // var results = sc + // .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) + // .ToList(); + // Assert.AreEqual(2, results.Count); + + // pageIndex++; + + // results = sc + // .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) + // .ToList(); + // Assert.AreEqual(2, results.Count); + + // pageIndex++; + + // results = sc + // .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) + // .ToList(); + // Assert.AreEqual(1, results.Count); + + // pageIndex++; + + // results = sc + // .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) + // .ToList(); + // Assert.AreEqual(0, results.Count); + // } + //} + + //[TestCase(0, 1, 1)] + //[TestCase(0, 2, 2)] + //[TestCase(0, 3, 3)] + //[TestCase(0, 4, 4)] + //[TestCase(0, 5, 5)] + //[TestCase(0, 100, 5)] + //[TestCase(1, 1, 1)] + //[TestCase(1, 2, 2)] + //[TestCase(1, 3, 3)] + //[TestCase(1, 4, 4)] + //[TestCase(1, 5, 4)] + //[TestCase(2, 2, 2)] + //[TestCase(2, 5, 3)] + //public void Given_SkipTake_Returns_ExpectedTotals(int skip, int take, int expectedResults) + //{ + // const int indexSize = 5; + // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + // using (var luceneDir = new RandomIdRAMDirectory()) + // using (var indexer = GetTestIndex(luceneDir, analyzer)) + // { + // var items = Enumerable.Range(0, indexSize).Select(x => ValueSet.FromObject(x.ToString(), "content", + // new { nodeName = "umbraco", headerText = "world", writerName = "administrator" })); + + // indexer.IndexItems(items); + + // var searcher = indexer.Searcher; + + // //Arrange + + // var sc = searcher.CreateQuery("content").Field("writerName", "administrator"); + + // //Act + + // var results = sc.Execute(QueryOptions.SkipTake(skip, take)); + + // Assert.AreEqual(indexSize, results.TotalItemCount); + // Assert.AreEqual(expectedResults, results.Count()); + // } + //} + } +} From a36d0a3a5c50d150dd6c7f739c8e3b81fa6ad2fd Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Tue, 15 Nov 2022 14:59:44 +0100 Subject: [PATCH 04/42] test: Facet tests --- .../Search/FacetFluentApiTests.cs | 1227 ++++++++--------- 1 file changed, 601 insertions(+), 626 deletions(-) diff --git a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs index 47de73b44..16873f598 100644 --- a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs @@ -5,10 +5,7 @@ using Examine.Lucene.Providers; using Examine.Lucene.Search; using Examine.Search; -using J2N; -using Lucene.Net.Analysis.En; using Lucene.Net.Analysis.Standard; -using Lucene.Net.Facet; using Lucene.Net.Facet.Range; using Lucene.Net.QueryParsers.Classic; using Lucene.Net.Search; @@ -1021,7 +1018,7 @@ public void Not_Range_Facet() var query = searcher.CreateQuery("content") .Field("nodeName", "name") .Not().Field("start", 200) - .And().Facet("start", new Int64Range[] { new Int64Range("Label", 100, false, 200, false)}); + .And().Facet("start", new Int64Range[] { new Int64Range("Label", 100, false, 200, false) }); Console.WriteLine(query); var results = query.Execute(); @@ -1367,301 +1364,354 @@ public void Find_Only_Image_Media_Facet() } } - //[Test] - //public void Find_Both_Media_And_Content() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "media", - // new { nodeName = "my name 1", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), - // ValueSet.FromObject(2.ToString(), "media", - // new { nodeName = "my name 2", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "my name 3", bodyText = "lorem ipsum", nodeTypeAlias = "file" }), - // ValueSet.FromObject(4.ToString(), "other", - // new { nodeName = "my name 4", bodyText = "lorem ipsum", nodeTypeAlias = "file" }) - // }); + [Test] + public void Find_Both_Media_And_Content_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "media", + new { nodeName = "my name 1", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), + ValueSet.FromObject(2.ToString(), "media", + new { nodeName = "my name 2", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "my name 3", bodyText = "lorem ipsum", nodeTypeAlias = "file" }), + ValueSet.FromObject(4.ToString(), "other", + new { nodeName = "my name 4", bodyText = "lorem ipsum", nodeTypeAlias = "file" }) + }); - // var searcher = indexer.Searcher; + var searcher = indexer.Searcher; - // var criteria = searcher.CreateQuery(defaultOperation: BooleanOperation.Or); - // var filter = criteria - // .Field(ExamineFieldNames.CategoryFieldName, "media") - // .Or() - // .Field(ExamineFieldNames.CategoryFieldName, "content"); + var criteria = searcher.CreateQuery(defaultOperation: BooleanOperation.Or); + var filter = criteria + .Field(ExamineFieldNames.CategoryFieldName, "media") + .Or() + .Field(ExamineFieldNames.CategoryFieldName, "content") + .And() + .Facet("nodeName"); - // var results = filter.Execute(); + var results = filter.Execute(); - // Assert.AreEqual(3, results.TotalItemCount); - // } - //} + var facetResults = results.GetFacet("nodeName"); - //[Test] - //public void Sort_Result_By_Number_Field() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex( - // luceneDir, - // analyzer, - // //Ensure it's set to a number, otherwise it's not sortable - // new FieldDefinitionCollection(new FieldDefinition("sortOrder", FieldDefinitionTypes.Integer), new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "my name 1", sortOrder = "3", parentID = "1143" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "my name 2", sortOrder = "1", parentID = "1143" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "my name 3", sortOrder = "2", parentID = "1143" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "my name 4", bodyText = "lorem ipsum", parentID = "2222" }) - // }); + Assert.AreEqual(3, results.TotalItemCount); + Assert.AreEqual(3, facetResults.Count()); + } + } - // var searcher = indexer.Searcher; + [Test] + public void Sort_Result_By_Number_Field_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + //Ensure it's set to a number, otherwise it's not sortable + new FieldDefinitionCollection(new FieldDefinition("sortOrder", FieldDefinitionTypes.FacetInteger), new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", sortOrder = "3", parentID = "1143" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", sortOrder = "1", parentID = "1143" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "my name 3", sortOrder = "2", parentID = "1143" }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "my name 4", bodyText = "lorem ipsum", parentID = "2222" }) + }); - // var sc = searcher.CreateQuery("content"); - // var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("sortOrder", SortType.Int)); + var searcher = indexer.Searcher; - // var results1 = sc1.Execute().ToArray(); + var sc = searcher.CreateQuery("content") + .Facet("sortOrder") + .And() + .Facet("parentID") + .And(); + var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("sortOrder", SortType.Int)); - // Assert.AreEqual(3, results1.Length); + var results1 = sc1.Execute(); - // var currSort = 0; - // for (var i = 0; i < results1.Length; i++) - // { - // Assert.GreaterOrEqual(int.Parse(results1[i].Values["sortOrder"]), currSort); - // currSort = int.Parse(results1[i].Values["sortOrder"]); - // } - // } - //} + var facetResults = results1.GetFacets(); - //[Test] - //public void Sort_Result_By_Date_Field() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex( - // luceneDir, - // analyzer, - // //Ensure it's set to a date, otherwise it's not sortable - // new FieldDefinitionCollection(new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime), new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) - // { + Assert.AreEqual(3, results1.Count()); + Assert.AreEqual(2, facetResults.Count()); + Assert.AreEqual(3, facetResults.First().Count()); + Assert.AreEqual(1, facetResults.Last().Count()); + Assert.AreEqual(3, facetResults.Last().Facet("1143").Value); + var results2 = results1.ToArray(); + var currSort = 0; + for (var i = 0; i < results2.Length; i++) + { + Assert.GreaterOrEqual(int.Parse(results2[i].Values["sortOrder"]), currSort); + currSort = int.Parse(results2[i].Values["sortOrder"]); + } + } + } - // var now = DateTime.Now; + [Test] + public void Sort_Result_By_Date_Field_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + //Ensure it's set to a date, otherwise it's not sortable + new FieldDefinitionCollection(new FieldDefinition("updateDate", FieldDefinitionTypes.FacetDateTime), new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)))) + { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "my name 1", updateDate = now.AddDays(2).ToString("yyyy-MM-dd"), parentID = "1143" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "my name 2", updateDate = now.ToString("yyyy-MM-dd"), parentID = 1143 }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "my name 3", updateDate = now.AddDays(1).ToString("yyyy-MM-dd"), parentID = 1143 }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "my name 4", updateDate = now, parentID = "2222" }) - // }); - // var searcher = indexer.Searcher; + var now = DateTime.Now; - // var sc = searcher.CreateQuery("content"); - // //note: dates internally are stored as Long, see DateTimeType - // var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("updateDate", SortType.Long)); + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", updateDate = now.AddDays(2).ToString("yyyy-MM-dd"), parentID = "1143" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", updateDate = now.ToString("yyyy-MM-dd"), parentID = 1143 }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "my name 3", updateDate = now.AddDays(1).ToString("yyyy-MM-dd"), parentID = 1143 }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "my name 4", updateDate = now, parentID = "2222" }) + }); - // var results1 = sc1.Execute().ToArray(); + var searcher = indexer.Searcher; - // Assert.AreEqual(3, results1.Length); + var sc = searcher.CreateQuery("content") + .Facet("updateDate") + .And() + .Facet("parentID") + .And(); + //note: dates internally are stored as Long, see DateTimeType + var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("updateDate", SortType.Long)); - // double currSort = 0; - // for (var i = 0; i < results1.Length; i++) - // { - // Assert.GreaterOrEqual(double.Parse(results1[i].Values["updateDate"]), currSort); - // currSort = double.Parse(results1[i].Values["updateDate"]); - // } - // } - //} + var results1 = sc1.Execute(); - //[Test] - //public void Sort_Result_By_Single_Field() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex( - // luceneDir, - // analyzer, - // //Ensure it's set to a fulltextsortable, otherwise it's not sortable - // new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FullTextSortable)))) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "my name 1", writerName = "administrator", parentID = "1143" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "my name 2", writerName = "administrator", parentID = "1143" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "my name 3", writerName = "administrator", parentID = "1143" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "my name 4", writerName = "writer", parentID = "2222" }) - // }); + var facetResults = results1.GetFacet("updateDate"); + var facetReuslts2 = results1.GetFacet("parentID"); - // var searcher = indexer.Searcher; + Assert.AreEqual(3, results1.Count()); + Assert.AreEqual(3, facetResults.Count()); + Assert.AreEqual(1, facetReuslts2.Count()); - // var sc = searcher.CreateQuery("content"); - // var sc1 = sc.Field("writerName", "administrator") - // .OrderBy(new SortableField("nodeName", SortType.String)); + var results2 = results1.ToArray(); + double currSort = 0; + for (var i = 0; i < results2.Length; i++) + { + Assert.GreaterOrEqual(double.Parse(results2[i].Values["updateDate"]), currSort); + currSort = double.Parse(results2[i].Values["updateDate"]); + } + } + } - // sc = searcher.CreateQuery("content"); - // var sc2 = sc.Field("writerName", "administrator") - // .OrderByDescending(new SortableField("nodeName", SortType.String)); + [Test] + public void Sort_Result_By_Single_Field_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + //Ensure it's set to a fulltextsortable, otherwise it's not sortable + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullTextSortable)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "my name 1", writerName = "administrator", parentID = "1143" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "my name 2", writerName = "administrator", parentID = "1143" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "my name 3", writerName = "administrator", parentID = "1143" }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "my name 4", writerName = "writer", parentID = "2222" }) + }); - // var results1 = sc1.Execute(); - // var results2 = sc2.Execute(); + var searcher = indexer.Searcher; - // Assert.AreNotEqual(results1.First().Id, results2.First().Id); - // } + var sc = searcher.CreateQuery("content") + .Facet("nodeName").And(); + var sc1 = sc.Field("writerName", "administrator") + .OrderBy(new SortableField("nodeName", SortType.String)); + sc = searcher.CreateQuery("content") + .Facet("nodeName").And(); + var sc2 = sc.Field("writerName", "administrator") + .OrderByDescending(new SortableField("nodeName", SortType.String)); - //} + var results1 = sc1.Execute(); + var results2 = sc2.Execute(); - //[TestCase(FieldDefinitionTypes.Double, SortType.Double)] - ////[TestCase(FieldDefinitionTypes.Double, SortType.String)] // This differs from Lucene 3.x, if string is specified it will still sort like as string - //[TestCase(FieldDefinitionTypes.FullText, SortType.Double)] - //[TestCase(FieldDefinitionTypes.FullText, SortType.String)] - //[TestCase(FieldDefinitionTypes.FullTextSortable, SortType.Double)] - //[TestCase(FieldDefinitionTypes.FullTextSortable, SortType.String)] - //public void Sort_Result_By_Double_Fields(string fieldType, SortType sortType) - //{ - // // See: https://github.com/Shazwazza/Examine/issues/242 + var facetResults1 = results1.GetFacet("nodeName"); + var facetResults2 = results2.GetFacet("nodeName"); - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex( - // luceneDir, - // analyzer, - // new FieldDefinitionCollection( - // new FieldDefinition("field1", fieldType)))) - // { - // indexer.IndexItems(new[] - // { - // ValueSet.FromObject(1.ToString(), "content", new { field1 = 5.0 }), - // ValueSet.FromObject(2.ToString(), "content", new { field1 = 4.9 }), - // ValueSet.FromObject(3.ToString(), "content", new { field1 = 4.5 }), - // ValueSet.FromObject(4.ToString(), "content", new { field1 = 3.9 }), - // ValueSet.FromObject(5.ToString(), "content", new { field1 = 3.8 }), - // ValueSet.FromObject(6.ToString(), "content", new { field1 = 2.6 }), - // }); + Assert.AreNotEqual(results1.First().Id, results2.First().Id); - // var searcher = indexer.Searcher; + Assert.AreEqual(3, facetResults1.Count()); + Assert.AreEqual(3, facetResults2.Count()); + } - // var sc = searcher.CreateQuery("content"); - // var sc1 = sc.All() - // .OrderBy(new SortableField("field1", sortType)); - // sc = searcher.CreateQuery("content"); - // var sc2 = sc.All() - // .OrderByDescending(new SortableField("field1", sortType)); + } - // var results1 = sc1.Execute().ToList(); - // var results2 = sc2.Execute().ToList(); + [TestCase(FieldDefinitionTypes.FacetDouble, SortType.Double)] + [TestCase(FieldDefinitionTypes.FacetFullText, SortType.Double)] + [TestCase(FieldDefinitionTypes.FacetFullText, SortType.String)] + [TestCase(FieldDefinitionTypes.FacetFullTextSortable, SortType.Double)] + [TestCase(FieldDefinitionTypes.FacetFullTextSortable, SortType.String)] + public void Sort_Result_By_Double_Fields_Facet(string fieldType, SortType sortType) + { + // See: https://github.com/Shazwazza/Examine/issues/242 - // Assert.AreEqual(2.6, double.Parse(results1[0].Values["field1"])); - // Assert.AreEqual(3.8, double.Parse(results1[1].Values["field1"])); - // Assert.AreEqual(3.9, double.Parse(results1[2].Values["field1"])); - // Assert.AreEqual(4.5, double.Parse(results1[3].Values["field1"])); - // Assert.AreEqual(4.9, double.Parse(results1[4].Values["field1"])); - // Assert.AreEqual(5.0, double.Parse(results1[5].Values["field1"])); + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection( + new FieldDefinition("field1", fieldType)))) + { + indexer.IndexItems(new[] + { + ValueSet.FromObject(1.ToString(), "content", new { field1 = 5.0 }), + ValueSet.FromObject(2.ToString(), "content", new { field1 = 4.9 }), + ValueSet.FromObject(3.ToString(), "content", new { field1 = 4.5 }), + ValueSet.FromObject(4.ToString(), "content", new { field1 = 3.9 }), + ValueSet.FromObject(5.ToString(), "content", new { field1 = 3.8 }), + ValueSet.FromObject(6.ToString(), "content", new { field1 = 2.6 }), + }); + var searcher = indexer.Searcher; - // Assert.AreEqual(2.6, double.Parse(results2[5].Values["field1"])); - // Assert.AreEqual(3.8, double.Parse(results2[4].Values["field1"])); - // Assert.AreEqual(3.9, double.Parse(results2[3].Values["field1"])); - // Assert.AreEqual(4.5, double.Parse(results2[2].Values["field1"])); - // Assert.AreEqual(4.9, double.Parse(results2[1].Values["field1"])); - // Assert.AreEqual(5.0, double.Parse(results2[0].Values["field1"])); - // } - //} + var sc = searcher.CreateQuery("content").Facet("field1").And(); + var sc1 = sc.All() + .OrderBy(new SortableField("field1", sortType)); - //[Test] - //public void Sort_Result_By_Multiple_Fields() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex( - // luceneDir, - // analyzer, - // new FieldDefinitionCollection( - // new FieldDefinition("field1", FieldDefinitionTypes.Double), - // new FieldDefinition("field2", FieldDefinitionTypes.Integer)))) - // { - // indexer.IndexItems(new[] - // { - // ValueSet.FromObject(1.ToString(), "content", new { field1 = 5.0, field2 = 2 }), - // ValueSet.FromObject(2.ToString(), "content", new { field1 = 4.9, field2 = 2 }), - // ValueSet.FromObject(3.ToString(), "content", new { field1 = 4.5, field2 = 2 }), - // ValueSet.FromObject(4.ToString(), "content", new { field1 = 3.9, field2 = 1 }), - // ValueSet.FromObject(5.ToString(), "content", new { field1 = 3.8, field2 = 1 }), - // ValueSet.FromObject(6.ToString(), "content", new { field1 = 2.6, field2 = 1 }), - // }); + sc = searcher.CreateQuery("content").Facet("field1").And(); + var sc2 = sc.All() + .OrderByDescending(new SortableField("field1", sortType)); - // var searcher = indexer.Searcher; + var results1 = sc1.Execute(); + var results2 = sc2.Execute(); - // var sc = searcher.CreateQuery("content"); - // var sc1 = sc.All() - // .OrderByDescending(new SortableField("field2", SortType.Int)) - // .OrderBy(new SortableField("field1", SortType.Double)); + var facetResults1 = results1.GetFacet("field1"); + var facetResults2 = results2.GetFacet("field1"); - // var results1 = sc1.Execute().ToList(); + var results3 = results1.ToList(); + var results4 = results2.ToList(); - // Assert.AreEqual("3", results1[0].Id); - // Assert.AreEqual("2", results1[1].Id); - // Assert.AreEqual("1", results1[2].Id); - // Assert.AreEqual("6", results1[3].Id); - // Assert.AreEqual("5", results1[4].Id); - // Assert.AreEqual("4", results1[5].Id); - // } - //} + Assert.AreEqual(2.6, double.Parse(results3[0].Values["field1"])); + Assert.AreEqual(3.8, double.Parse(results3[1].Values["field1"])); + Assert.AreEqual(3.9, double.Parse(results3[2].Values["field1"])); + Assert.AreEqual(4.5, double.Parse(results3[3].Values["field1"])); + Assert.AreEqual(4.9, double.Parse(results3[4].Values["field1"])); + Assert.AreEqual(5.0, double.Parse(results3[5].Values["field1"])); - //[Test] - //public void Standard_Results_Sorted_By_Score() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "umbraco", headerText = "world", bodyText = "blah" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "umbraco", headerText = "umbraco", bodyText = "blah" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "umbraco", headerText = "umbraco", bodyText = "umbraco" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "hello", headerText = "world", bodyText = "blah" }) - // }); - // var searcher = indexer.Searcher; + Assert.AreEqual(2.6, double.Parse(results4[5].Values["field1"])); + Assert.AreEqual(3.8, double.Parse(results4[4].Values["field1"])); + Assert.AreEqual(3.9, double.Parse(results4[3].Values["field1"])); + Assert.AreEqual(4.5, double.Parse(results4[2].Values["field1"])); + Assert.AreEqual(4.9, double.Parse(results4[1].Values["field1"])); + Assert.AreEqual(5.0, double.Parse(results4[0].Values["field1"])); + + Assert.AreEqual(6, facetResults1.Count()); + Assert.AreEqual(6, facetResults2.Count()); + } + } + + [Test] + public void Sort_Result_By_Multiple_Fields_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection( + new FieldDefinition("field1", FieldDefinitionTypes.FacetDouble), + new FieldDefinition("field2", FieldDefinitionTypes.FacetInteger)))) + { + indexer.IndexItems(new[] + { + ValueSet.FromObject(1.ToString(), "content", new { field1 = 5.0, field2 = 2 }), + ValueSet.FromObject(2.ToString(), "content", new { field1 = 4.9, field2 = 2 }), + ValueSet.FromObject(3.ToString(), "content", new { field1 = 4.5, field2 = 2 }), + ValueSet.FromObject(4.ToString(), "content", new { field1 = 3.9, field2 = 1 }), + ValueSet.FromObject(5.ToString(), "content", new { field1 = 3.8, field2 = 1 }), + ValueSet.FromObject(6.ToString(), "content", new { field1 = 2.6, field2 = 1 }), + }); - // var sc = searcher.CreateQuery("content", BooleanOperation.Or); - // var sc1 = sc.Field("nodeName", "umbraco").Or().Field("headerText", "umbraco").Or().Field("bodyText", "umbraco"); + var searcher = indexer.Searcher; - // var results = sc1.Execute(); + var sc = searcher.CreateQuery("content").Facet("field1").And().Facet("field2").And(); + var sc1 = sc.All() + .OrderByDescending(new SortableField("field2", SortType.Int)) + .OrderBy(new SortableField("field1", SortType.Double)); - // //Assert - // for (int i = 0; i < results.TotalItemCount - 1; i++) - // { - // var curr = results.ElementAt(i); - // var next = results.ElementAtOrDefault(i + 1); + var results1 = sc1.Execute(); - // if (next == null) - // break; + var facetResults = results1.GetFacet("field1"); + var facetResults2 = results1.GetFacet("field2"); - // Assert.IsTrue(curr.Score >= next.Score, string.Format("Result at index {0} must have a higher score than result at index {1}", i, i + 1)); - // } - // } + var results2 = results1.ToList(); + Assert.AreEqual("3", results2[0].Id); + Assert.AreEqual("2", results2[1].Id); + Assert.AreEqual("1", results2[2].Id); + Assert.AreEqual("6", results2[3].Id); + Assert.AreEqual("5", results2[4].Id); + Assert.AreEqual("4", results2[5].Id); - //} + Assert.AreEqual(6, facetResults.Count()); + Assert.AreEqual(2, facetResults2.Count()); + } + } + + [Test] + public void Standard_Results_Sorted_By_Score_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("bodyText", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "umbraco", headerText = "world", bodyText = "blah" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "umbraco", headerText = "umbraco", bodyText = "blah" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "umbraco", headerText = "umbraco", bodyText = "umbraco" }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "hello", headerText = "world", bodyText = "blah" }) + }); + + var searcher = indexer.Searcher; + + var sc = searcher.CreateQuery("content", BooleanOperation.Or).Facet("bodyText").And(); + var sc1 = sc.Field("nodeName", "umbraco").Or().Field("headerText", "umbraco").Or().Field("bodyText", "umbraco"); + + var results = sc1.Execute(); + + var facetResults = results.GetFacet("bodyText"); + + Assert.AreEqual(2, facetResults.Count()); + + //Assert + for (int i = 0; i < results.TotalItemCount - 1; i++) + { + var curr = results.ElementAt(i); + var next = results.ElementAtOrDefault(i + 1); + + if (next == null) + break; + + Assert.IsTrue(curr.Score >= next.Score, string.Format("Result at index {0} must have a higher score than result at index {1}", i, i + 1)); + } + } + + } //[Test] //public void Skip_Results_Returns_Different_Results() @@ -2235,445 +2285,370 @@ public void Find_Only_Image_Media_Facet() // } //} - //[Test] - //public void Inner_Not_Query() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - - - // { + [Test] + public void Inner_Not_Query_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)))) - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { Content = "hello world", Type = "type1" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { Content = "hello something or other", Type = "type1" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { Content = "hello something or world", Type = "type1" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { Content = "hello you cruel world", Type = "type2" }), - // ValueSet.FromObject(5.ToString(), "content", - // new { Content = "hi there, hello world", Type = "type2" }) - // }); + { - // var searcher = indexer.Searcher; - // var criteria = searcher.CreateQuery(); + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { Content = "hello world", Type = "type1" }), + ValueSet.FromObject(2.ToString(), "content", + new { Content = "hello something or other", Type = "type1" }), + ValueSet.FromObject(3.ToString(), "content", + new { Content = "hello something or world", Type = "type1" }), + ValueSet.FromObject(4.ToString(), "content", + new { Content = "hello you cruel world", Type = "type2" }), + ValueSet.FromObject(5.ToString(), "content", + new { Content = "hi there, hello world", Type = "type2" }) + }); - // //Query = - // // +Type:type1 +(+Content:world -Content:something) + var searcher = indexer.Searcher; - // var filter = criteria.Field("Type", "type1") - // .And(query => query.Field("Content", "world").Not().Field("Content", "something")); + var criteria = searcher.CreateQuery().Facet("Type").And(); - // //Act - // var results = filter.Execute(); + //Query = + // +Type:type1 +(+Content:world -Content:something) - // //Assert - // Assert.AreEqual(1, results.TotalItemCount); - // } - //} + var filter = criteria.Field("Type", "type1") + .And(query => query.Field("Content", "world").Not().Field("Content", "something")); - //[Test] - //public void Complex_Or_Group_Nested_Query() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { Content = "hello world", Type = "type1" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { Content = "hello something or other", Type = "type1" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { Content = "hello you guys", Type = "type1" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { Content = "hello you cruel world", Type = "type2" }), - // ValueSet.FromObject(5.ToString(), "content", - // new { Content = "hi there, hello world", Type = "type2" }) - // }); - - // var searcher = indexer.Searcher; - - // var criteria = searcher.CreateQuery(defaultOperation: BooleanOperation.Or); + //Act + var results = filter.Execute(); - // //Query = - // // (+Type:type1 +(Content:world Content:something)) (+Type:type2 +(+Content:world +Content:cruel)) - - // var filter = criteria - // .Group(group => group.Field("Type", "type1") - // .And(query => query.Field("Content", "world").Or().Field("Content", "something"), BooleanOperation.Or), - // // required so that the type1 query is required - // BooleanOperation.And) - // .Or() - // .Group(group => group.Field("Type", "type2") - // .And(query => query.Field("Content", "world").And().Field("Content", "cruel")), - // // required so that the type2 query is required - // BooleanOperation.And); + var facetResults = results.GetFacet("Type"); - // Console.WriteLine(filter); - - // //Act - // var results = filter.Execute(); + //Assert + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + } - // //Assert - // foreach (var r in results) - // { - // Console.WriteLine($"Result Id: {r.Id}"); - // } - // Assert.AreEqual(3, results.TotalItemCount); - // } - //} + [Test] + public void Complex_Or_Group_Nested_Query_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { Content = "hello world", Type = "type1" }), + ValueSet.FromObject(2.ToString(), "content", + new { Content = "hello something or other", Type = "type1" }), + ValueSet.FromObject(3.ToString(), "content", + new { Content = "hello you guys", Type = "type1" }), + ValueSet.FromObject(4.ToString(), "content", + new { Content = "hello you cruel world", Type = "type2" }), + ValueSet.FromObject(5.ToString(), "content", + new { Content = "hi there, hello world", Type = "type2" }) + }); + var searcher = indexer.Searcher; - //[Test] - //public void Custom_Lucene_Query_With_Native() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // var searcher = indexer.Searcher; - // var criteria = (LuceneSearchQuery)searcher.CreateQuery(); + var criteria = searcher.CreateQuery(defaultOperation: BooleanOperation.Or); + + //Query = + // (+Type:type1 +(Content:world Content:something)) (+Type:type2 +(+Content:world +Content:cruel)) + + var filter = criteria + .Group(group => group.Field("Type", "type1") + .And(query => query.Field("Content", "world").Or().Field("Content", "something"), BooleanOperation.Or), + // required so that the type1 query is required + BooleanOperation.And) + .Or() + .Group(group => group.Field("Type", "type2") + .And(query => query.Field("Content", "world").And().Field("Content", "cruel")), + // required so that the type2 query is required + BooleanOperation.And) + .And() + .Facet("Type"); - // //combine a custom lucene query with raw lucene query - // var op = criteria.NativeQuery("hello:world").And(); + Console.WriteLine(filter); - // criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); + //Act + var results = filter.Execute(); - // Console.WriteLine(criteria.Query); - // Assert.AreEqual("+hello:world +numTest:[4 TO 5]", criteria.Query.ToString()); - // } - //} + var facetResults = results.GetFacet("Type"); - //[Test] - //public void Category() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { Content = "hello world", Type = "type1" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { Content = "hello something or other", Type = "type1" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { Content = "hello you guys", Type = "type1" }), - // ValueSet.FromObject(4.ToString(), "media", - // new { Content = "hello you cruel world", Type = "type2" }), - // ValueSet.FromObject(5.ToString(), "media", - // new { Content = "hi there, hello world", Type = "type2" }) - // }); + //Assert + foreach (var r in results) + { + Console.WriteLine($"Result Id: {r.Id}"); + } + Assert.AreEqual(3, results.TotalItemCount); - // var searcher = indexer.Searcher; + Assert.AreEqual(2, facetResults.Count()); + } + } - // var query = searcher.CreateQuery("content").ManagedQuery("hello"); - // Console.WriteLine(query); - // var results = query.Execute(); + [Test] + public void Custom_Lucene_Query_With_Native_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer)) + { + var searcher = indexer.Searcher; + var criteria = (LuceneSearchQuery)searcher.CreateQuery(); - // //Assert - // Assert.AreEqual(3, results.TotalItemCount); - // } - //} + //combine a custom lucene query with raw lucene query + var op = criteria.NativeQuery("hello:world").And().Facet("SomeFacet").And(); + criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); - //[Test] - //public void Select_Field() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) + Console.WriteLine(criteria.Query); + Assert.AreEqual("+hello:world +numTest:[4 TO 5]", criteria.Query.ToString()); + } + } - // { - // indexer.IndexItems(new[] { - // new ValueSet(1.ToString(), "content", - // new Dictionary - // { - // {"id","1" }, - // {"nodeName", "my name 1"}, - // {"bodyText", "lorem ipsum"}, - // {"__Path", "-1,123,456,789"} - // }), - // new ValueSet(2.ToString(), "content", - // new Dictionary - // { - // {"id","2" }, - // {"nodeName", "my name 2"}, - // {"bodyText", "lorem ipsum"}, - // {"__Path", "-1,123,456,987"} - // }) - // }); + [Test] + public void Select_Field() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - // var searcher = indexer.Searcher; - // var sc = searcher.CreateQuery("content"); - // var sc1 = sc.Field("nodeName", "my name 1").SelectField("__Path"); - - // var results = sc1.Execute(); - // var expectedLoadedFields = new string[] { "__Path" }; - // var keys = results.First().Values.Keys.ToArray(); - // Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); - // Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); - // } - //} + { + indexer.IndexItems(new[] { + new ValueSet(1.ToString(), "content", + new Dictionary + { + {"id","1" }, + {"nodeName", "my name 1"}, + {"bodyText", "lorem ipsum"}, + {"__Path", "-1,123,456,789"} + }), + new ValueSet(2.ToString(), "content", + new Dictionary + { + {"id","2" }, + {"nodeName", "my name 2"}, + {"bodyText", "lorem ipsum"}, + {"__Path", "-1,123,456,987"} + }) + }); - //[Test] - //public void Select_Fields() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) + var searcher = indexer.Searcher; + var sc = searcher.CreateQuery("content").Facet("nodeName").And(); + var sc1 = sc.Field("nodeName", "my name 1").SelectField("__Path"); - // { - // indexer.IndexItems(new[] { - // new ValueSet(1.ToString(), "content", - // new Dictionary - // { - // {"id","1" }, - // {"nodeName", "my name 1"}, - // {"bodyText", "lorem ipsum"}, - // {"__Path", "-1,123,456,789"} - // }), - // new ValueSet(2.ToString(), "content", - // new Dictionary - // { - // {"id","2" }, - // {"nodeName", "my name 2"}, - // {"bodyText", "lorem ipsum"}, - // {"__Path", "-1,123,456,987"} - // }) - // }); + var results = sc1.Execute(); + var facetResults = results.GetFacet("nodeName"); - // var searcher = indexer.Searcher; - // var sc = searcher.CreateQuery("content"); - // var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new[] { "nodeName", "bodyText", "id", "__NodeId" })); - - // var results = sc1.Execute(); - // var expectedLoadedFields = new string[] { "nodeName", "bodyText", "id", "__NodeId" }; - // var keys = results.First().Values.Keys.ToArray(); - // Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); - // Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); - // } + var expectedLoadedFields = new string[] { "__Path" }; + var keys = results.First().Values.Keys.ToArray(); + Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); - //} + Assert.AreEqual(2, facetResults.Count()); + } + } - //[Test] - //public void Select_Fields_HashSet() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) + [Test] + public void Select_Fields_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - // { - // indexer.IndexItems(new[] { - // new ValueSet(1.ToString(), "content", - // new Dictionary - // { - // {"id","1" }, - // {"nodeName", "my name 1"}, - // {"bodyText", "lorem ipsum"}, - // {"__Path", "-1,123,456,789"} - // }), - // new ValueSet(2.ToString(), "content", - // new Dictionary - // { - // {"id","2" }, - // {"nodeName", "my name 2"}, - // {"bodyText", "lorem ipsum"}, - // {"__Path", "-1,123,456,987"} - // }) - // }); + { + indexer.IndexItems(new[] { + new ValueSet(1.ToString(), "content", + new Dictionary + { + {"id","1" }, + {"nodeName", "my name 1"}, + {"bodyText", "lorem ipsum"}, + {"__Path", "-1,123,456,789"} + }), + new ValueSet(2.ToString(), "content", + new Dictionary + { + {"id","2" }, + {"nodeName", "my name 2"}, + {"bodyText", "lorem ipsum"}, + {"__Path", "-1,123,456,987"} + }) + }); - // var searcher = indexer.Searcher; - // var sc = searcher.CreateQuery("content"); - // var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new string[] { "nodeName", "bodyText" })); - - // var results = sc1.Execute(); - // var expectedLoadedFields = new string[] { "nodeName", "bodyText" }; - // var keys = results.First().Values.Keys.ToArray(); - // Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); - // Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); - // } - //} + var searcher = indexer.Searcher; + var sc = searcher.CreateQuery("content").Facet("nodeName").And(); + var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new[] { "nodeName", "bodyText", "id", "__NodeId" })); - //[Test] - //public void Select_Fields_Native_Query() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) + var results = sc1.Execute(); + var facetResults = results.GetFacet("nodeName"); - // { - // indexer.IndexItems(new[] { - // new ValueSet(1.ToString(), "content", - // new Dictionary - // { - // {"id","1" }, - // {"nodeName", "my name 1"}, - // {"bodyText", "lorem ipsum"}, - // {"__Path", "-1,123,456,789"} - // }), - // new ValueSet(2.ToString(), "content", - // new Dictionary - // { - // {"id","2" }, - // {"nodeName", "my name 2"}, - // {"bodyText", "lorem ipsum"}, - // {"__Path", "-1,123,456,987"} - // }) - // }); + var expectedLoadedFields = new string[] { "nodeName", "bodyText", "id", "__NodeId" }; + var keys = results.First().Values.Keys.ToArray(); + Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); - // var searcher = indexer.Searcher; - // var sc = searcher.CreateQuery().NativeQuery("nodeName:'my name 1'"); - // var sc1 = sc.SelectFields(new HashSet(new[] { "bodyText", "id", "__NodeId" })); - - // var results = sc1.Execute(); - // var expectedLoadedFields = new string[] { "bodyText", "id", "__NodeId" }; - // var keys = results.First().Values.Keys.ToArray(); - // Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); - // Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); - // } + Assert.AreEqual(2, facetResults.Count()); + } - //} - //public void Can_Skip() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "umbraco", headerText = "world", writerName = "administrator" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "hello", headerText = "world", writerName = "blah" }) - // }); + } - // var searcher = indexer.Searcher; + [Test] + public void Select_Fields_HashSet_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - // //Arrange + { + indexer.IndexItems(new[] { + new ValueSet(1.ToString(), "content", + new Dictionary + { + {"id","1" }, + {"nodeName", "my name 1"}, + {"bodyText", "lorem ipsum"}, + {"__Path", "-1,123,456,789"} + }), + new ValueSet(2.ToString(), "content", + new Dictionary + { + {"id","2" }, + {"nodeName", "my name 2"}, + {"bodyText", "lorem ipsum"}, + {"__Path", "-1,123,456,987"} + }) + }); - // var sc = searcher.CreateQuery("content").Field("writerName", "administrator"); + var searcher = indexer.Searcher; + var sc = searcher.CreateQuery("content").Facet("nodeName").And(); + var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new string[] { "nodeName", "bodyText" })); - // //Act + var results = sc1.Execute(); + var facetResults = results.GetFacet("nodeName"); - // var results = sc.Execute().ToList(); - // Assert.AreEqual(3, results.Count); + var expectedLoadedFields = new string[] { "nodeName", "bodyText" }; + var keys = results.First().Values.Keys.ToArray(); + Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); - // results = sc.Execute().Skip(1).ToList(); - // Assert.AreEqual(2, results.Count); + Assert.AreEqual(2, facetResults.Count()); + } + } - // results = sc.Execute().Skip(2).ToList(); - // Assert.AreEqual(1, results.Count); - // } - //} + [Test] + public void Paging_With_Skip_Take_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("writerName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "umbraco", headerText = "world", writerName = "administrator" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "hello", headerText = "world", writerName = "blah" }), + ValueSet.FromObject(5.ToString(), "content", + new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + ValueSet.FromObject(6.ToString(), "content", + new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }) + }); - //[Test] - //public void Paging_With_Skip_Take() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "umbraco", headerText = "world", writerName = "administrator" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "hello", headerText = "world", writerName = "blah" }), - // ValueSet.FromObject(5.ToString(), "content", - // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - // ValueSet.FromObject(6.ToString(), "content", - // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }) - // }); + var searcher = indexer.Searcher; - // var searcher = indexer.Searcher; + //Arrange - // //Arrange + var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().Facet("writerName"); + int pageIndex = 0; + int pageSize = 2; - // var sc = searcher.CreateQuery("content").Field("writerName", "administrator"); - // int pageIndex = 0; - // int pageSize = 2; + //Act - // //Act + var results = sc + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); + Assert.AreEqual(2, results.Count()); + var facetResults = results.GetFacet("writerName"); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(5, facetResults.Facet("administrator").Value); - // var results = sc - // .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) - // .ToList(); - // Assert.AreEqual(2, results.Count); + pageIndex++; - // pageIndex++; + results = sc + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); + Assert.AreEqual(2, results.Count()); + facetResults = results.GetFacet("writerName"); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(5, facetResults.Facet("administrator").Value); - // results = sc - // .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) - // .ToList(); - // Assert.AreEqual(2, results.Count); + pageIndex++; - // pageIndex++; + results = sc + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); + Assert.AreEqual(1, results.Count()); + facetResults = results.GetFacet("writerName"); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(5, facetResults.Facet("administrator").Value); - // results = sc - // .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) - // .ToList(); - // Assert.AreEqual(1, results.Count); + pageIndex++; - // pageIndex++; + results = sc + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); + Assert.AreEqual(0, results.Count()); + facetResults = results.GetFacet("writerName"); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(5, facetResults.Facet("administrator").Value); + } + } - // results = sc - // .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) - // .ToList(); - // Assert.AreEqual(0, results.Count); - // } - //} + [TestCase(0, 1, 1)] + [TestCase(0, 2, 2)] + [TestCase(0, 3, 3)] + [TestCase(0, 4, 4)] + [TestCase(0, 5, 5)] + [TestCase(0, 100, 5)] + [TestCase(1, 1, 1)] + [TestCase(1, 2, 2)] + [TestCase(1, 3, 3)] + [TestCase(1, 4, 4)] + [TestCase(1, 5, 4)] + [TestCase(2, 2, 2)] + [TestCase(2, 5, 3)] + public void Given_SkipTake_Returns_ExpectedTotals_Facet(int skip, int take, int expectedResults) + { + const int indexSize = 5; + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + var items = Enumerable.Range(0, indexSize).Select(x => ValueSet.FromObject(x.ToString(), "content", + new { nodeName = "umbraco", headerText = "world", writerName = "administrator" })); - //[TestCase(0, 1, 1)] - //[TestCase(0, 2, 2)] - //[TestCase(0, 3, 3)] - //[TestCase(0, 4, 4)] - //[TestCase(0, 5, 5)] - //[TestCase(0, 100, 5)] - //[TestCase(1, 1, 1)] - //[TestCase(1, 2, 2)] - //[TestCase(1, 3, 3)] - //[TestCase(1, 4, 4)] - //[TestCase(1, 5, 4)] - //[TestCase(2, 2, 2)] - //[TestCase(2, 5, 3)] - //public void Given_SkipTake_Returns_ExpectedTotals(int skip, int take, int expectedResults) - //{ - // const int indexSize = 5; - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // var items = Enumerable.Range(0, indexSize).Select(x => ValueSet.FromObject(x.ToString(), "content", - // new { nodeName = "umbraco", headerText = "world", writerName = "administrator" })); + indexer.IndexItems(items); - // indexer.IndexItems(items); + var searcher = indexer.Searcher; - // var searcher = indexer.Searcher; + //Arrange - // //Arrange + var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().Facet("nodeName"); - // var sc = searcher.CreateQuery("content").Field("writerName", "administrator"); + //Act - // //Act + var results = sc.Execute(QueryOptions.SkipTake(skip, take)); - // var results = sc.Execute(QueryOptions.SkipTake(skip, take)); + var facetResults = results.GetFacet("nodeName"); - // Assert.AreEqual(indexSize, results.TotalItemCount); - // Assert.AreEqual(expectedResults, results.Count()); - // } - //} + Assert.AreEqual(indexSize, results.TotalItemCount); + Assert.AreEqual(expectedResults, results.Count()); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(5, facetResults.Facet("umbraco").Value); + } + } } } From 108dd968aa6392cdfc7cfd4adfe83b725f9f01a9 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 16 Nov 2022 10:32:32 +0100 Subject: [PATCH 05/42] fix: FacetsConfig --- src/Examine.Host/ServicesCollectionExtensions.cs | 15 ++++++++++----- src/Examine.Lucene/LuceneIndexOptions.cs | 2 +- src/Examine.Lucene/Providers/LuceneIndex.cs | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Examine.Host/ServicesCollectionExtensions.cs b/src/Examine.Host/ServicesCollectionExtensions.cs index de4424d8f..06084778a 100644 --- a/src/Examine.Host/ServicesCollectionExtensions.cs +++ b/src/Examine.Host/ServicesCollectionExtensions.cs @@ -6,6 +6,7 @@ using Examine.Lucene.Directories; using Examine.Lucene.Providers; using Lucene.Net.Analysis; +using Lucene.Net.Facet; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; @@ -24,8 +25,9 @@ public static IServiceCollection AddExamineLuceneIndex( FieldDefinitionCollection fieldDefinitions = null, Analyzer analyzer = null, IValueSetValidator validator = null, - IReadOnlyDictionary indexValueTypesFactory = null) - => serviceCollection.AddExamineLuceneIndex(name, fieldDefinitions, analyzer, validator, indexValueTypesFactory); + IReadOnlyDictionary indexValueTypesFactory = null, + FacetsConfig facetsConfig = null) + => serviceCollection.AddExamineLuceneIndex(name, fieldDefinitions, analyzer, validator, indexValueTypesFactory, facetsConfig); /// /// Registers a file system based Lucene Examine index @@ -36,9 +38,10 @@ public static IServiceCollection AddExamineLuceneIndex( FieldDefinitionCollection fieldDefinitions = null, Analyzer analyzer = null, IValueSetValidator validator = null, - IReadOnlyDictionary indexValueTypesFactory = null) + IReadOnlyDictionary indexValueTypesFactory = null, + FacetsConfig facetsConfig = null) where TIndex : LuceneIndex - => serviceCollection.AddExamineLuceneIndex(name, fieldDefinitions, analyzer, validator, indexValueTypesFactory); + => serviceCollection.AddExamineLuceneIndex(name, fieldDefinitions, analyzer, validator, indexValueTypesFactory, facetsConfig); /// /// Registers an Examine index @@ -49,7 +52,8 @@ public static IServiceCollection AddExamineLuceneIndex indexValueTypesFactory = null) + IReadOnlyDictionary indexValueTypesFactory = null, + FacetsConfig facetsConfig = null) where TIndex : LuceneIndex where TDirectoryFactory : class, IDirectoryFactory { @@ -65,6 +69,7 @@ public static IServiceCollection AddExamineLuceneIndex(); + options.FacetsConfig = facetsConfig ?? new FacetsConfig(); })); return serviceCollection.AddSingleton(services => diff --git a/src/Examine.Lucene/LuceneIndexOptions.cs b/src/Examine.Lucene/LuceneIndexOptions.cs index deba631de..28b68c7d5 100644 --- a/src/Examine.Lucene/LuceneIndexOptions.cs +++ b/src/Examine.Lucene/LuceneIndexOptions.cs @@ -23,7 +23,7 @@ public class LuceneIndexOptions : IndexOptions /// the setters in this class to change these settings for /// each dim. /// - public FacetsConfig FacetConfig { get; set; } = new FacetsConfig(); + public FacetsConfig FacetsConfig { get; set; } = new FacetsConfig(); /// /// Specifies the index value types to use for this indexer, if this is not specified then the result of will be used. diff --git a/src/Examine.Lucene/Providers/LuceneIndex.cs b/src/Examine.Lucene/Providers/LuceneIndex.cs index 0ddecfe78..c71fa6236 100644 --- a/src/Examine.Lucene/Providers/LuceneIndex.cs +++ b/src/Examine.Lucene/Providers/LuceneIndex.cs @@ -754,7 +754,7 @@ protected virtual void AddDocument(Document doc, ValueSet valueSet) } // TODO: try/catch with OutOfMemoryException (see docs on UpdateDocument), though i've never seen this in real life - _latestGen = IndexWriter.UpdateDocument(new Term(ExamineFieldNames.ItemIdFieldName, valueSet.Id), _options.FacetConfig.Build(doc)); + _latestGen = IndexWriter.UpdateDocument(new Term(ExamineFieldNames.ItemIdFieldName, valueSet.Id), _options.FacetsConfig.Build(doc)); } /// From c00f3ab51fdf7b6c10c00fb5403728d203e1078c Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 16 Nov 2022 16:03:39 +0100 Subject: [PATCH 06/42] test: Added remaining facet fluent api tests --- .../Search/FacetFluentApiTests.cs | 1112 +++++++++-------- 1 file changed, 568 insertions(+), 544 deletions(-) diff --git a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs index 16873f598..265b22d5e 100644 --- a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs @@ -5,6 +5,7 @@ using Examine.Lucene.Providers; using Examine.Lucene.Search; using Examine.Search; +using Lucene.Net.Analysis.En; using Lucene.Net.Analysis.Standard; using Lucene.Net.Facet.Range; using Lucene.Net.QueryParsers.Classic; @@ -1713,577 +1714,600 @@ public void Standard_Results_Sorted_By_Score_Facet() } - //[Test] - //public void Skip_Results_Returns_Different_Results() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "umbraco", headerText = "world", writerName = "administrator" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "hello", headerText = "world", writerName = "blah" }) - // }); - - // var searcher = indexer.Searcher; - - // //Arrange - // var sc = searcher.CreateQuery("content").Field("writerName", "administrator"); - - // //Act - // var results = sc.Execute(); - - // //Assert - // Assert.AreNotEqual(results.First(), results.Skip(2).First(), "Third result should be different"); - // } - //} - - //[Test] - //public void Escaping_Includes_All_Words() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "codegarden09", headerText = "world", writerName = "administrator" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "codegarden 09", headerText = "umbraco", writerName = "administrator" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "codegarden 09", headerText = "umbraco", writerName = "administrator" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "codegarden 090", headerText = "world", writerName = "blah" }) - // }); - - // var searcher = indexer.Searcher; - - // //Arrange - // var sc = searcher.CreateQuery("content").Field("nodeName", "codegarden 09".Escape()); - - // Console.WriteLine(sc.ToString()); - - // //Act - // var results = sc.Execute(); - - // //Assert - // //NOTE: The result is 2 because the double space is removed with the analyzer - // Assert.AreEqual(2, results.TotalItemCount); - // } - - - //} - - //[Test] - //public void Grouped_And_Examiness() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "Aloha", nodeTypeAlias = "CWS_Hello" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "Helo", nodeTypeAlias = "CWS_World" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "Another node", nodeTypeAlias = "SomethingElse" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "Always consider this", nodeTypeAlias = "CWS_World" }) - // }); - - // var searcher = indexer.Searcher; - - // //Arrange - // var criteria = searcher.CreateQuery("content"); - - // //get all node type aliases starting with CWS and all nodees starting with "A" - // var filter = criteria.GroupedAnd( - // new[] { "nodeTypeAlias", "nodeName" }, - // new[] { "CWS".MultipleCharacterWildcard(), "A".MultipleCharacterWildcard() }); - - - // //Act - // var results = filter.Execute(); - - // //Assert - // Assert.AreEqual(2, results.TotalItemCount); - // } - //} - - //[Test] - //public void Examiness_Proximity() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "Aloha", metaKeywords = "Warren is likely to be creative" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "Helo", metaKeywords = "Creative is Warren middle name" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "Another node", metaKeywords = "If Warren were creative... well, he actually is" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "Always consider this", metaKeywords = "Warren is a very talented individual and quite creative" }) - // }); - - // var searcher = indexer.Searcher; - - // //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)); - - // //Act - // var results = filter.Execute(); - - // foreach (var r in results) - // { - // Console.WriteLine($"Id = {r.Id}"); - // } - - // //Assert - // Assert.AreEqual(3, results.TotalItemCount); - // } - //} - - ///// - ///// test range query with a Float structure - ///// - //[Test] - //public void Float_Range_SimpleIndexSet() - //{ - - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex( - // luceneDir, - // analyzer, - // //Ensure it's set to a float - // new FieldDefinitionCollection(new FieldDefinition("SomeFloat", FieldDefinitionTypes.Float)))) - // { - - - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "Aloha", SomeFloat = 1 }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "Helo", SomeFloat = 123 }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "Another node", SomeFloat = 12 }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "Always consider this", SomeFloat = 25 }) - // }); - - // var searcher = indexer.Searcher; - - // //all numbers should be between 0 and 100 based on the data source - // var criteria1 = searcher.CreateQuery(); - // var filter1 = criteria1.RangeQuery(new[] { "SomeFloat" }, 0f, 100f, true, true); - - // var criteria2 = searcher.CreateQuery(); - // var filter2 = criteria2.RangeQuery(new[] { "SomeFloat" }, 101f, 200f, true, true); - - // //Act - // var results1 = filter1.Execute(); - // var results2 = filter2.Execute(); - - // //Assert - // Assert.AreEqual(3, results1.TotalItemCount); - // Assert.AreEqual(1, results2.TotalItemCount); - // } - - - //} - - ///// - ///// test range query with a Number structure - ///// - //[Test] - //public void Number_Range_SimpleIndexSet() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex( - // luceneDir, - // analyzer, - // //Ensure it's set to a float - // new FieldDefinitionCollection(new FieldDefinition("SomeNumber", FieldDefinitionTypes.Integer)))) - // { - - - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "Aloha", SomeNumber = 1 }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "Helo", SomeNumber = 123 }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "Another node", SomeNumber = 12 }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "Always consider this", SomeNumber = 25 }) - // }); - - // var searcher = indexer.Searcher; - - // //all numbers should be between 0 and 100 based on the data source - // var criteria1 = searcher.CreateQuery(); - // var filter1 = criteria1.RangeQuery(new[] { "SomeNumber" }, 0, 100, true, true); - - // var criteria2 = searcher.CreateQuery(); - // var filter2 = criteria2.RangeQuery(new[] { "SomeNumber" }, 101, 200, true, true); - - // //Act - // var results1 = filter1.Execute(); - // var results2 = filter2.Execute(); - - // //Assert - // Assert.AreEqual(3, results1.TotalItemCount); - // Assert.AreEqual(1, results2.TotalItemCount); - // } - //} - - ///// - ///// test range query with a Number structure - ///// - //[Test] - //public void Double_Range_SimpleIndexSet() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex( - // luceneDir, - // analyzer, - // //Ensure it's set to a float - // new FieldDefinitionCollection(new FieldDefinition("SomeDouble", FieldDefinitionTypes.Double)))) - // { - - - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "Aloha", SomeDouble = 1d }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "Helo", SomeDouble = 123d }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "Another node", SomeDouble = 12d }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "Always consider this", SomeDouble = 25d }) - // }); - - // var searcher = indexer.Searcher; - - // //all numbers should be between 0 and 100 based on the data source - // var criteria1 = searcher.CreateQuery(); - // var filter1 = criteria1.RangeQuery(new[] { "SomeDouble" }, 0d, 100d, true, true); - - // var criteria2 = searcher.CreateQuery("content"); - // var filter2 = criteria2.RangeQuery(new[] { "SomeDouble" }, 101d, 200d, true, true); - - // //Act - // var results1 = filter1.Execute(); - // var results2 = filter2.Execute(); - - // //Assert - // Assert.AreEqual(3, results1.TotalItemCount); - // Assert.AreEqual(1, results2.TotalItemCount); - // } - //} - - ///// - ///// test range query with a Double structure - ///// - //[Test] - //public void Long_Range_SimpleIndexSet() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex( - // luceneDir, - // analyzer, - // //Ensure it's set to a float - // new FieldDefinitionCollection(new FieldDefinition("SomeLong", "long")))) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { nodeName = "Aloha", SomeLong = 1L }), - // ValueSet.FromObject(2.ToString(), "content", - // new { nodeName = "Helo", SomeLong = 123L }), - // ValueSet.FromObject(3.ToString(), "content", - // new { nodeName = "Another node", SomeLong = 12L }), - // ValueSet.FromObject(4.ToString(), "content", - // new { nodeName = "Always consider this", SomeLong = 25L }) - // }); - - // var searcher = indexer.Searcher; - - // //all numbers should be between 0 and 100 based on the data source - // var criteria1 = searcher.CreateQuery(); - // var filter1 = criteria1.RangeQuery(new[] { "SomeLong" }, 0L, 100L, true, true); - - // var criteria2 = searcher.CreateQuery(); - // var filter2 = criteria2.RangeQuery(new[] { "SomeLong" }, 101L, 200L, true, true); - - // //Act - // var results1 = filter1.Execute(); - // var results2 = filter2.Execute(); - - // //Assert - // Assert.AreEqual(3, results1.TotalItemCount); - // Assert.AreEqual(1, results2.TotalItemCount); - // } - //} - - - - ///// - ///// Test range query with a DateTime structure - ///// - //[Test] - //public void Date_Range_SimpleIndexSet() - //{ - // var reIndexDateTime = DateTime.Now; - - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex( - // luceneDir, - // analyzer, - // new FieldDefinitionCollection(new FieldDefinition("DateCreated", "datetime")))) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { DateCreated = reIndexDateTime }), - // ValueSet.FromObject(2.ToString(), "content", - // new { DateCreated = reIndexDateTime }), - // ValueSet.FromObject(3.ToString(), "content", - // new { DateCreated = reIndexDateTime.AddMonths(-10) }), - // ValueSet.FromObject(4.ToString(), "content", - // new { DateCreated = reIndexDateTime }) - // }); - - // var searcher = indexer.Searcher; - - // var criteria = searcher.CreateQuery(); - // var filter = criteria.RangeQuery(new[] { "DateCreated" }, reIndexDateTime, DateTime.Now, true, true); - - // var criteria2 = searcher.CreateQuery(); - // var filter2 = criteria2.RangeQuery(new[] { "DateCreated" }, reIndexDateTime.AddDays(-1), reIndexDateTime.AddSeconds(-1), true, true); - - // ////Act - // var results = filter.Execute(); - // var results2 = filter2.Execute(); - - // ////Assert - // Assert.IsTrue(results.TotalItemCount > 0); - // Assert.IsTrue(results2.TotalItemCount == 0); - // } - - - //} - - //[Test] - //public void Fuzzy_Search() - //{ - // var analyzer = new EnglishAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { Content = "I'm thinking here" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { Content = "I'm a thinker" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { Content = "I am pretty thoughtful" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { Content = "I thought you were cool" }) - // }); - - // var searcher = indexer.Searcher; - - // var criteria = searcher.CreateQuery(); - // var filter = criteria.Field("Content", "think".Fuzzy(0.1F)); - - // var criteria2 = searcher.CreateQuery(); - // var filter2 = criteria2.Field("Content", "thought".Fuzzy()); - - // Console.WriteLine(filter); - // Console.WriteLine(filter2); - - // ////Act - // var results = filter.Execute(); - // var results2 = filter2.Execute(); - - // foreach (var r in results) - // { - // Console.WriteLine($"Result Id: {r.Id}"); - // } - - // foreach (var r in results2) - // { - // Console.WriteLine($"Result2 Id: {r.Id}"); - // } - - // ////Assert - // Assert.AreEqual(2, results.TotalItemCount); - // Assert.AreEqual(2, results2.TotalItemCount); - // } - //} - - - //[Test] - //public void Execute_With_Take() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { Content = "hello world" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { Content = "hello worlds" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { Content = "hello you cruel world" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { Content = "hi there, hello world" }) - // }); - - // var searcher = indexer.Searcher; - - // var criteria = searcher.CreateQuery(); - // var filter = criteria.Field("Content", "hello"); - - // //Act - // var results = filter.Execute(QueryOptions.SkipTake(0, 3)); - - // //Assert - - // Assert.AreEqual(3, results.Count()); + [Test] + public void Skip_Results_Returns_Different_Results_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "umbraco", headerText = "world", writerName = "administrator" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "hello", headerText = "world", writerName = "blah" }) + }); - // //NOTE: These are the total matched! The actual results are limited - // Assert.AreEqual(4, results.TotalItemCount); - // } - //} + var searcher = indexer.Searcher; - //[Test] - //public void Execute_With_Take_Max_Results() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) - // { - // for (int i = 0; i < 1000; i++) - // { - // indexer.IndexItems(new[] {ValueSet.FromObject(i.ToString(), "content", new { Content = "hello world" })}); - // } + //Arrange + var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().Facet("nodeName"); - // indexer.IndexItems(new[] { ValueSet.FromObject(2000.ToString(), "content", new { Content = "donotfind" }) }); - - // var searcher = indexer.Searcher; - - // var criteria = searcher.CreateQuery(); - // var filter = criteria.Field("Content", "hello"); - - // //Act - // var results = filter.Execute(QueryOptions.SkipTake(0, int.MaxValue)); - - // //Assert + //Act + var results = sc.Execute(); - // Assert.AreEqual(1000, results.Count()); - // } - //} + var facetResults = results.GetFacet("nodeName"); + //Assert + Assert.AreNotEqual(results.First(), results.Skip(2).First(), "Third result should be different"); + Assert.AreEqual(1, facetResults.Count()); + } + } - //[Test] - //public void Inner_Or_Query() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) + [Test] + public void Escaping_Includes_All_Words_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "codegarden09", headerText = "world", writerName = "administrator" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "codegarden 09", headerText = "umbraco", writerName = "administrator" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "codegarden 09", headerText = "umbraco", writerName = "administrator" }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "codegarden 090", headerText = "world", writerName = "blah" }) + }); + var searcher = indexer.Searcher; - // { + //Arrange + var sc = searcher.CreateQuery("content").Field("nodeName", "codegarden 09".Escape()).And().Facet("nodeName"); + Console.WriteLine(sc.ToString()); - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { Content = "hello world", Type = "type1" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { Content = "hello something or other", Type = "type1" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { Content = "hello you cruel world", Type = "type2" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { Content = "hi there, hello world", Type = "type2" }) - // }); + //Act + var results = sc.Execute(); - // var searcher = indexer.Searcher; + var facetResults = results.GetFacet("nodeName"); - // var criteria = searcher.CreateQuery(); + //Assert + //NOTE: The result is 2 because the double space is removed with the analyzer + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + } - // //Query = - // // +Type:type1 +(Content:world Content:something) - // var filter = criteria.Field("Type", "type1") - // .And(query => query.Field("Content", "world").Or().Field("Content", "something"), BooleanOperation.Or); + } - // //Act - // var results = filter.Execute(); + [Test] + public void Grouped_And_Examiness_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "Aloha", nodeTypeAlias = "CWS_Hello" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "Helo", nodeTypeAlias = "CWS_World" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "Another node", nodeTypeAlias = "SomethingElse" }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "Always consider this", nodeTypeAlias = "CWS_World" }) + }); - // //Assert - // Assert.AreEqual(2, results.TotalItemCount); - // } - //} + var searcher = indexer.Searcher; - //[Test] - //public void Inner_And_Query() - //{ - // var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - // using (var luceneDir = new RandomIdRAMDirectory()) - // using (var indexer = GetTestIndex(luceneDir, analyzer)) + //Arrange + var criteria = searcher.CreateQuery("content").Facet("nodeName").And(); + //get all node type aliases starting with CWS and all nodees starting with "A" + var filter = criteria.GroupedAnd( + new[] { "nodeTypeAlias", "nodeName" }, + new[] { "CWS".MultipleCharacterWildcard(), "A".MultipleCharacterWildcard() }); - // { + //Act + var results = filter.Execute(); - // indexer.IndexItems(new[] { - // ValueSet.FromObject(1.ToString(), "content", - // new { Content = "hello world", Type = "type1" }), - // ValueSet.FromObject(2.ToString(), "content", - // new { Content = "hello something or other", Type = "type1" }), - // ValueSet.FromObject(3.ToString(), "content", - // new { Content = "hello something or world", Type = "type1" }), - // ValueSet.FromObject(4.ToString(), "content", - // new { Content = "hello you cruel world", Type = "type2" }), - // ValueSet.FromObject(5.ToString(), "content", - // new { Content = "hi there, hello world", Type = "type2" }) - // }); + var facetResults = results.GetFacet("nodeName"); - // var searcher = indexer.Searcher; + //Assert + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + } + } - // var criteria = searcher.CreateQuery(); + [Test] + public void Examiness_Proximity_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "Aloha", metaKeywords = "Warren is likely to be creative" }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "Helo", metaKeywords = "Creative is Warren middle name" }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "Another node", metaKeywords = "If Warren were creative... well, he actually is" }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "Always consider this", metaKeywords = "Warren is a very talented individual and quite creative" }) + }); - // //Query = - // // +Type:type1 +(+Content:world +Content:hello) + var searcher = indexer.Searcher; - // var filter = criteria.Field("Type", "type1") - // .And(query => query.Field("Content", "world").And().Field("Content", "hello")); + //Arrange + var criteria = searcher.CreateQuery("content").Facet("nodeName").And(); - // //Act - // var results = filter.Execute(); + //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)); - // //Assert - // Assert.AreEqual(2, results.TotalItemCount); - // } - //} + //Act + var results = filter.Execute(); + + var facetResults = results.GetFacet("nodeName"); + + foreach (var r in results) + { + Console.WriteLine($"Id = {r.Id}"); + } + + //Assert + Assert.AreEqual(3, results.TotalItemCount); + Assert.AreEqual(3, facetResults.Count()); + } + } + + /// + /// test range query with a Float structure + /// + [Test] + public void Float_Range_SimpleIndexSet() + { + + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + //Ensure it's set to a float + new FieldDefinitionCollection(new FieldDefinition("SomeFloat", FieldDefinitionTypes.FacetFloat)))) + { + + + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "Aloha", SomeFloat = 1 }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "Helo", SomeFloat = 123 }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "Another node", SomeFloat = 12 }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "Always consider this", SomeFloat = 25 }) + }); + + var searcher = indexer.Searcher; + + //all numbers should be between 0 and 100 based on the data source + var criteria1 = searcher.CreateQuery().Facet("SomeFloat", new DoubleRange[] + { + new DoubleRange("1", 0, true, 12, true), + new DoubleRange("2", 13, true, 250, true) + }).IsFloat(true).And(); + var filter1 = criteria1.RangeQuery(new[] { "SomeFloat" }, 0f, 100f, true, true); + + var criteria2 = searcher.CreateQuery().Facet("SomeFloat", new DoubleRange[] + { + new DoubleRange("1", 0, true, 12, true), + new DoubleRange("2", 13, true, 250, true) + }).IsFloat(true).And(); + var filter2 = criteria2.RangeQuery(new[] { "SomeFloat" }, 101f, 200f, true, true); + + //Act + var results1 = filter1.Execute(); + var results2 = filter2.Execute(); + + var facetResults1 = results1.GetFacet("SomeFloat"); + var facetResults2 = results2.GetFacet("SomeFloat"); + + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + Assert.AreEqual(2, facetResults1.Facet("1").Value); + Assert.AreEqual(1, facetResults1.Facet("2").Value); + Assert.AreEqual(0, facetResults2.Facet("1").Value); + Assert.AreEqual(1, facetResults2.Facet("2").Value); + } + + + } + + /// + /// test range query with a Number structure + /// + [Test] + public void Number_Range_SimpleIndexSet_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + //Ensure it's set to a float + new FieldDefinitionCollection(new FieldDefinition("SomeNumber", FieldDefinitionTypes.FacetInteger)))) + { + + + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "Aloha", SomeNumber = 1 }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "Helo", SomeNumber = 123 }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "Another node", SomeNumber = 12 }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "Always consider this", SomeNumber = 25 }) + }); + + var searcher = indexer.Searcher; + + //all numbers should be between 0 and 100 based on the data source + var criteria1 = searcher.CreateQuery().Facet("SomeNumber").MaxCount(1).And(); + var filter1 = criteria1.RangeQuery(new[] { "SomeNumber" }, 0, 100, true, true); + + var criteria2 = searcher.CreateQuery().Facet("SomeNumber").MaxCount(1).And(); + var filter2 = criteria2.RangeQuery(new[] { "SomeNumber" }, 101, 200, true, true); + + //Act + var results1 = filter1.Execute(); + var results2 = filter2.Execute(); + + var facetResults1 = results1.GetFacet("SomeNumber"); + var facetResults2 = results2.GetFacet("SomeNumber"); + + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + Assert.AreEqual(1, facetResults1.Count()); + Assert.AreEqual(1, facetResults2.Count()); + } + } + + /// + /// test range query with a Number structure + /// + [Test] + public void Double_Range_SimpleIndexSet_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + //Ensure it's set to a float + new FieldDefinitionCollection(new FieldDefinition("SomeDouble", FieldDefinitionTypes.FacetDouble)))) + { + + + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "Aloha", SomeDouble = 1d }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "Helo", SomeDouble = 123d }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "Another node", SomeDouble = 12d }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "Always consider this", SomeDouble = 25d }) + }); + + var searcher = indexer.Searcher; + + //all numbers should be between 0 and 100 based on the data source + var criteria1 = searcher.CreateQuery().Facet("SomeDouble", new DoubleRange[] + { + new DoubleRange("1", 0, true, 100, true), + new DoubleRange("2", 101, true, 200, true) + }).And(); + var filter1 = criteria1.RangeQuery(new[] { "SomeDouble" }, 0d, 100d, true, true); + + var criteria2 = searcher.CreateQuery("content").Facet("SomeDouble", new DoubleRange[] + { + new DoubleRange("1", 0, true, 100, true), + new DoubleRange("2", 101, true, 200, true) + }).And(); + var filter2 = criteria2.RangeQuery(new[] { "SomeDouble" }, 101d, 200d, true, true); + + //Act + var results1 = filter1.Execute(); + var results2 = filter2.Execute(); + + var facetResults1 = results1.GetFacet("SomeDouble"); + var facetResults2 = results2.GetFacet("SomeDouble"); + + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + Assert.AreEqual(3, facetResults1.Facet("1").Value); + Assert.AreEqual(0, facetResults1.Facet("2").Value); + Assert.AreEqual(0, facetResults2.Facet("1").Value); + Assert.AreEqual(1, facetResults2.Facet("2").Value); + } + } + + /// + /// test range query with a Double structure + /// + [Test] + public void Long_Range_SimpleIndexSet_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + //Ensure it's set to a float + new FieldDefinitionCollection(new FieldDefinition("SomeLong", FieldDefinitionTypes.FacetLong)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { nodeName = "Aloha", SomeLong = 1L }), + ValueSet.FromObject(2.ToString(), "content", + new { nodeName = "Helo", SomeLong = 123L }), + ValueSet.FromObject(3.ToString(), "content", + new { nodeName = "Another node", SomeLong = 12L }), + ValueSet.FromObject(4.ToString(), "content", + new { nodeName = "Always consider this", SomeLong = 25L }) + }); + + var searcher = indexer.Searcher; + + //all numbers should be between 0 and 100 based on the data source + var criteria1 = searcher.CreateQuery().Facet("SomeLong", new Int64Range[] + { + new Int64Range("1", 0L, true, 100L, true), + new Int64Range("2", 101L, true, 200L, true) + }).And(); + var filter1 = criteria1.RangeQuery(new[] { "SomeLong" }, 0L, 100L, true, true); + + var criteria2 = searcher.CreateQuery().Facet("SomeLong", new Int64Range[] + { + new Int64Range("1", 0L, true, 100L, true), + new Int64Range("2", 101L, true, 200L, true) + }).And(); + var filter2 = criteria2.RangeQuery(new[] { "SomeLong" }, 101L, 200L, true, true); + + //Act + var results1 = filter1.Execute(); + var results2 = filter2.Execute(); + + var facetResults1 = results1.GetFacet("SomeLong"); + var facetResults2 = results2.GetFacet("SomeLong"); + + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + Assert.AreEqual(3, facetResults1.Facet("1").Value); + Assert.AreEqual(0, facetResults1.Facet("2").Value); + Assert.AreEqual(0, facetResults2.Facet("1").Value); + Assert.AreEqual(1, facetResults2.Facet("2").Value); + } + } + + + + /// + /// Test range query with a DateTime structure + /// + [Test] + public void Date_Range_SimpleIndexSet_Facet() + { + var reIndexDateTime = DateTime.Now; + + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + new FieldDefinitionCollection(new FieldDefinition("DateCreated", FieldDefinitionTypes.FacetDateTime)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { DateCreated = reIndexDateTime }), + ValueSet.FromObject(2.ToString(), "content", + new { DateCreated = reIndexDateTime }), + ValueSet.FromObject(3.ToString(), "content", + new { DateCreated = reIndexDateTime.AddMonths(-10) }), + ValueSet.FromObject(4.ToString(), "content", + new { DateCreated = reIndexDateTime }) + }); + + var searcher = indexer.Searcher; + + var criteria = searcher.CreateQuery().Facet("DateCreated", new Int64Range[] + { + new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), + new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) + }).And(); + var filter = criteria.RangeQuery(new[] { "DateCreated" }, reIndexDateTime, DateTime.Now, true, true); + + var criteria2 = searcher.CreateQuery().Facet("DateCreated", new Int64Range[] + { + new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), + new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) + }).And(); + var filter2 = criteria2.RangeQuery(new[] { "DateCreated" }, reIndexDateTime.AddDays(-1), reIndexDateTime.AddSeconds(-1), true, true); + + ////Act + var results = filter.Execute(); + var results2 = filter2.Execute(); + + var facetResults1 = results.GetFacet("DateCreated"); + var facetResults2 = results2.GetFacet("DateCreated"); + + ////Assert + Assert.IsTrue(results.TotalItemCount > 0); + Assert.IsTrue(results2.TotalItemCount == 0); + Assert.AreEqual(3, facetResults1.Facet("1").Value); + Assert.AreEqual(0, facetResults1.Facet("2").Value); + Assert.AreEqual(0, facetResults2.Facet("1").Value); + Assert.AreEqual(0, facetResults2.Facet("2").Value); + } + + + } + + [Test] + public void Fuzzy_Search_Facet() + { + var analyzer = new EnglishAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("Content", FieldDefinitionTypes.FacetFullText)))) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { Content = "I'm thinking here" }), + ValueSet.FromObject(2.ToString(), "content", + new { Content = "I'm a thinker" }), + ValueSet.FromObject(3.ToString(), "content", + new { Content = "I am pretty thoughtful" }), + ValueSet.FromObject(4.ToString(), "content", + new { Content = "I thought you were cool" }) + }); + + var searcher = indexer.Searcher; + + var criteria = searcher.CreateQuery().Facet("Content").And(); + var filter = criteria.Field("Content", "think".Fuzzy(0.1F)); + + var criteria2 = searcher.CreateQuery().Facet("Content").And(); + var filter2 = criteria2.Field("Content", "thought".Fuzzy()); + + Console.WriteLine(filter); + Console.WriteLine(filter2); + + ////Act + var results = filter.Execute(); + var results2 = filter2.Execute(); + + var facetResults1 = results.GetFacet("Content"); + var facetResults2 = results2.GetFacet("Content"); + + foreach (var r in results) + { + Console.WriteLine($"Result Id: {r.Id}"); + } + + foreach (var r in results2) + { + Console.WriteLine($"Result2 Id: {r.Id}"); + } + + ////Assert + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, results2.TotalItemCount); + Assert.AreEqual(2, facetResults1.Count()); + Assert.AreEqual(2, facetResults2.Count()); + } + } + + [Test] + public void Inner_Or_Query_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)))) + + + { + + + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { Content = "hello world", Type = "type1" }), + ValueSet.FromObject(2.ToString(), "content", + new { Content = "hello something or other", Type = "type1" }), + ValueSet.FromObject(3.ToString(), "content", + new { Content = "hello you cruel world", Type = "type2" }), + ValueSet.FromObject(4.ToString(), "content", + new { Content = "hi there, hello world", Type = "type2" }) + }); + + var searcher = indexer.Searcher; + + var criteria = searcher.CreateQuery().Facet("Type").And(); + + //Query = + // +Type:type1 +(Content:world Content:something) + + var filter = criteria.Field("Type", "type1") + .And(query => query.Field("Content", "world").Or().Field("Content", "something"), BooleanOperation.Or); + + //Act + var results = filter.Execute(); + + var facetResults = results.GetFacet("Type"); + + //Assert + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + } + + [Test] + public void Inner_And_Query_Facet() + { + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)))) + + + { + + + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "content", + new { Content = "hello world", Type = "type1" }), + ValueSet.FromObject(2.ToString(), "content", + new { Content = "hello something or other", Type = "type1" }), + ValueSet.FromObject(3.ToString(), "content", + new { Content = "hello something or world", Type = "type1" }), + ValueSet.FromObject(4.ToString(), "content", + new { Content = "hello you cruel world", Type = "type2" }), + ValueSet.FromObject(5.ToString(), "content", + new { Content = "hi there, hello world", Type = "type2" }) + }); + + var searcher = indexer.Searcher; + + var criteria = searcher.CreateQuery().Facet("Type").And(); + + //Query = + // +Type:type1 +(+Content:world +Content:hello) + + var filter = criteria.Field("Type", "type1") + .And(query => query.Field("Content", "world").And().Field("Content", "hello")); + + //Act + var results = filter.Execute(); + + var facetResults = results.GetFacet("Type"); + + //Assert + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + } [Test] public void Inner_Not_Query_Facet() From 81e170efc4d9f535319ac384f0585450627adc7e Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 23 Nov 2022 14:06:52 +0100 Subject: [PATCH 07/42] docs: Added facet docs --- docs/configuration.md | 78 ++++++++++++---- docs/searching.md | 213 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 274 insertions(+), 17 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index b9dae3e66..378e4427a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -136,23 +136,35 @@ Value types are responsible for: 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. | ❌ | +| Value Type | Description | Sortable | Facetable | +|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|-------------| +| 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. | ✅ | ❌ | +| FacetFullText | The field will be indexed with the index's
default Analyzer without any sortability. 
Generally this is fine for normal text searching. | ❌ | ✅ | +| FacetFullTextSortable | 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._ | ✅ | ✅ | +| FacetInteger | Stored as a numerical structure. | ✅ | ✅ | +| FacetFloat | Stored as a numerical structure. | ✅ | ✅ | +| FacetDouble | Stored as a numerical structure. | ✅ | ✅ | +| FacetLong | Stored as a numerical structure. | ✅ | ✅ | +| FacetDateTime | Stored as a DateTime, 
represented by a numerical structure. | ✅ | ✅ | +| FacetDateYear | Just like DateTime but with 
precision only to the year. | ✅ | ✅ | +| FacetDateMonth | Just like DateTime but with 
precision only to the month. | ✅ | ✅ | +| FacetDateDay | Just like DateTime but with 
precision only to the day. | ✅ | ✅ | +| FacetDateHour | Just like DateTime but with 
precision only to the hour. | ✅ | ✅ | +| FacetDateMinute | 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 @@ -273,3 +285,35 @@ That returns an enum `ValueSetValidationResult` of values: * `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. + +## Facets configuration + +When using the facets feature it's possible to add facets configuration to change the behavior of the indexing. + +For example, you can allow multiple values in an indexed field with the configuration below. +```csharp +// Create a config +var facetsConfig = new FacetsConfig(); + +// Set field to be able to contain multiple values (This is default for a field in Examine. But you only need this if you are actually using multiple values for a single field) +facetsConfig.SetMultiValued("MultiIdField", true); + +services.AddExamineLuceneIndex("MyIndex", + // Set the indexing of your fields to use the facet type + fieldDefinitions: new FieldDefinitionCollection( + new FieldDefinition("Timestamp", FieldDefinitionTypes.FacetDateTime), + + new FieldDefinition("MultiIdField", FieldDefinitionTypes.FacetFullText) + ), + // Pass your config + facetsConfig: facetsConfig + ); +``` + +Without this configuration for multiple values, you'll notice that your faceted search breaks or behaves differently than expected. + +**Note: See more examples of how facets configuration can be used under 'Searching'** + +To explore other configuration settings see the links below: +- [FacetsConfig API docs](https://lucenenet.apache.org/docs/4.8.0-beta00016/api/facet/Lucene.Net.Facet.FacetsConfig.html#methods) +- [Facets with lucene](https://norconex.com/facets-with-lucene/). See how the config is used in the code examples. \ No newline at end of file diff --git a/docs/searching.md b/docs/searching.md index 1a4c51f36..899941e02 100644 --- a/docs/searching.md +++ b/docs/searching.md @@ -131,3 +131,216 @@ var exactfilter = exactcriteria.Field("__Path", "-1,123,456,789".Escape()); var results2 = exactfilter.Execute(); ``` + +### String Facets + +String facets allows for counting the documents that share the same string value. This type of faceting is possible on all faceted index type. + +Basic example +```csharp +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + .Field("Address", "Hills") + .And() + .Facet("Address") // Get facets of the Address field + .Execute(); + +var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address + +/* +* Example value +* Label: Hills, Value: 2 +* Label: Hollywood, Value: 10 +*/ + +var hillsValue = addressFacetResults.Facet("Hills"); // Gets the IFacetValue for the facet Hills +``` + +Filtered value example +```csharp +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + .Field("Address", "Hills") + .And() + .Facet("Address", "Hills") // Get facets of the Address field + .Execute(); + +var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address + +/* +* Example value +* Label: Hills, Value: 2 <-- As Hills was the only filtered value we will only get this facet +*/ + +var hillsValue = addressFacetResults.Facet("Hills"); // Gets the IFacetValue for the facet Hills +``` + +MaxCount example +```csharp +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + .Field("Address", "Hills") + .And() + .Facet("Address") // Get facets of the Address field + .Execute(); + +var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address + +/* +* Example value +* Label: Hills, Value: 2 +* Label: Hollywood, Value: 10 +* Label: London, Value: 12 +*/ + +results = searcher.CreateQuery() + .Field("Address", "Hills") + .And() + .Facet("Address") // Get facets of the Address field + .MaxCount(2) // Gets the top 2 results (The facets with the highest value) + .Execute(); + +addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address + +/* +* Example value (Notice only 2 values are present) +* Label: Hollywood, Value: 10 +* Label: London, Value: 12 +*/ +``` + +FacetField example +```csharp +// Setup + +// Create a config +var facetsConfig = new FacetsConfig(); + +// Set the index field name to facet_address. This will store facets of this field under facet_address instead of the default $facets. This requires you to use FacetField in your Facet query. (Only works on string facets). +facetsConfig.SetIndexFieldName("Address", "facet_address"); + +services.AddExamineLuceneIndex("MyIndex", + // Set the indexing of your fields to use the facet type + fieldDefinitions: new FieldDefinitionCollection( + new FieldDefinition("Address", FieldDefinitionTypes.FacetFullText) + ), + // Pass your config + facetsConfig: facetsConfig + ); + + +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + .Field("Address", "Hills") + .And() + .Facet("Address") // Get facets of the Address field + .FacetField("address_facet") + .Execute(); + +var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address + +/* +* Example value +* Label: Hills, Value: 2 +* Label: Hollywood, Value: 10 +*/ +``` + +### Numeric Range facet + +Numeric range facets can be used with numbers and get facets for numeric ranges. For example, it would group documents of the same price range. + +There's two categories of numeric ranges - `DoubleRanges` and `Int64Range` for double/float values and int/long/datetime values respectively. + +Double range example +```csharp +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + .Facet("Price", new DoubleRange[] { + new DoubleRange("0-10", 0, true, 10, true), + new DoubleRange("11-20", 11, true, 20, true) + }) // Get facets of the price field + .And() + .All() + .Execute(); + +var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the specific field Price + +/* +* Example value +* Label: 0-10, Value: 2 +* Label: 11-20, Value: 10 +*/ + +var firstRangeValue = priceFacetResults.Facet("0-10"); // Gets the IFacetValue for the facet "0-10" +``` + +Float range example +```csharp +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + .Facet("Price", new DoubleRange[] { + new DoubleRange("0-10", 0, true, 10, true), + new DoubleRange("11-20", 11, true, 20, true) + }) // Get facets of the price field + .IsFloat(true) // Marks that the underlying field is a float + .And() + .All() + .Execute(); + +var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the specific field Price + +/* +* Example value +* Label: 0-10, Value: 2 +* Label: 11-20, Value: 10 +*/ + +var firstRangeValue = priceFacetResults.Facet("0-10"); // Gets the IFacetValue for the facet "0-10" +``` + +Int/Long range example +```csharp +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + .Facet("Price", new Int64Range[] { + new Int64Range("0-10", 0, true, 10, true), + new Int64Range("11-20", 11, true, 20, true) + }) // Get facets of the price field + .And() + .All() + .Execute(); + +var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the specific field Price + +/* +* Example value +* Label: 0-10, Value: 2 +* Label: 11-20, Value: 10 +*/ + +var firstRangeValue = priceFacetResults.Facet("0-10"); // Gets the IFacetValue for the facet "0-10" +``` + +DateTime range example +```csharp +var searcher = myIndex.Searcher; +var results = searcher.CreateQuery() + .Facet("Created", new Int64Range[] { + new Int64Range("first", DateTime.UtcNow.AddDays(-1).Ticks, true, DateTime.UtcNow.Ticks, true), + new Int64Range("last", DateTime.UtcNow.AddDays(1).Ticks, true, DateTime.UtcNow.AddDays(2).Ticks, true) + }) // Get facets of the price field + .And() + .All() + .Execute(); + +var createdFacetResults = results.GetFacet("Created"); // Returns the facets for the specific field Created + +/* +* Example value +* Label: first, Value: 2 +* Label: last, Value: 10 +*/ + +var firstRangeValue = createdFacetResults.Facet("first"); // Gets the IFacetValue for the facet "first" +``` \ No newline at end of file From f15f3a4948c0b526ca131cd81e47f6880b7b3431 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Tue, 13 Dec 2022 08:20:35 +0100 Subject: [PATCH 08/42] refactor: ReadOnly collections --- src/Examine.Core/Search/IFacetResults.cs | 2 +- src/Examine.Lucene/Search/LuceneSearchExecutor.cs | 12 ++++++------ src/Examine.Lucene/Search/LuceneSearchResults.cs | 7 ++++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Examine.Core/Search/IFacetResults.cs b/src/Examine.Core/Search/IFacetResults.cs index eaca7aa4f..27f176bce 100644 --- a/src/Examine.Core/Search/IFacetResults.cs +++ b/src/Examine.Core/Search/IFacetResults.cs @@ -7,6 +7,6 @@ public interface IFacetResults /// /// Facets from the search /// - IDictionary Facets { get; } + IReadOnlyDictionary Facets { get; } } } diff --git a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs index 0b46bd4f5..20512436b 100644 --- a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs +++ b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs @@ -23,10 +23,10 @@ public class LuceneSearchExecutor private readonly ISearchContext _searchContext; private readonly Query _luceneQuery; private readonly ISet _fieldsToLoad; - private readonly IList _facetFields; + private readonly IEnumerable _facetFields; private int? _maxDoc; - internal LuceneSearchExecutor(QueryOptions options, Query query, IEnumerable sortField, ISearchContext searchContext, ISet fieldsToLoad, IList facetFields) + internal LuceneSearchExecutor(QueryOptions options, Query query, IEnumerable sortField, ISearchContext searchContext, ISet fieldsToLoad, IEnumerable facetFields) { _options = options ?? QueryOptions.Default; _luceneQuery = query ?? throw new ArgumentNullException(nameof(query)); @@ -100,7 +100,7 @@ public ISearchResults Execute() using (ISearcherReference searcher = _searchContext.GetSearcher()) { FacetsCollector facetsCollector; - if(_facetFields.Count > 0) + if(_facetFields.Any()) { facetsCollector = new FacetsCollector(); searcher.IndexSearcher.Search(_luceneQuery, MultiCollector.Wrap(topDocsCollector, facetsCollector)); @@ -136,10 +136,10 @@ public ISearchResults Execute() } } - private IDictionary ExtractFacets(FacetsCollector facetsCollector, ISearcherReference searcher) + private IReadOnlyDictionary ExtractFacets(FacetsCollector facetsCollector, ISearcherReference searcher) { - var facets = new Dictionary(); - if (facetsCollector == null || _facetFields.Count == 0) + var facets = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + if (facetsCollector == null || !_facetFields.Any()) { return facets; } diff --git a/src/Examine.Lucene/Search/LuceneSearchResults.cs b/src/Examine.Lucene/Search/LuceneSearchResults.cs index 10b518125..f319f2762 100644 --- a/src/Examine.Lucene/Search/LuceneSearchResults.cs +++ b/src/Examine.Lucene/Search/LuceneSearchResults.cs @@ -1,16 +1,17 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; namespace Examine.Lucene.Search { public class LuceneSearchResults : ISearchResults, IFacetResults { - public static LuceneSearchResults Empty { get; } = new LuceneSearchResults(Array.Empty(), 0, new Dictionary()); + public static LuceneSearchResults Empty { get; } = new LuceneSearchResults(Array.Empty(), 0, new ReadOnlyDictionary(new Dictionary())); private readonly IReadOnlyCollection _results; - public LuceneSearchResults(IReadOnlyCollection results, int totalItemCount, IDictionary facets) + public LuceneSearchResults(IReadOnlyCollection results, int totalItemCount, IReadOnlyDictionary facets) { _results = results; TotalItemCount = totalItemCount; @@ -19,7 +20,7 @@ public LuceneSearchResults(IReadOnlyCollection results, int total public long TotalItemCount { get; } - public IDictionary Facets { get; } + public IReadOnlyDictionary Facets { get; } public IEnumerator GetEnumerator() => _results.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); From 7b50317e5273f94f74835c2caa80e05b3dd6554d Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Tue, 13 Dec 2022 08:38:08 +0100 Subject: [PATCH 09/42] refactor: Use params in facets --- src/Examine.Core/Search/IQuery.cs | 16 +++------- src/Examine.Lucene/Search/LuceneQuery.cs | 9 ++---- .../Search/LuceneSearchQuery.cs | 32 ++++++++++++------- .../Search/LuceneSearchQueryBase.cs | 8 ++--- 4 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/Examine.Core/Search/IQuery.cs b/src/Examine.Core/Search/IQuery.cs index 96eaa3e7f..a4c5d9e74 100644 --- a/src/Examine.Core/Search/IQuery.cs +++ b/src/Examine.Core/Search/IQuery.cs @@ -144,29 +144,21 @@ public interface IQuery IFacetQueryField Facet(string field); /// - /// Add a facet string to the current query, filtered by value - /// - /// - /// - /// - IFacetQueryField Facet(string field, string value); - - /// - /// Add a facet string to the current query, filtered by multiple values + /// Add a facet string to the current query, filtered by a single value or multiple values /// /// /// /// - IFacetQueryField Facet(string field, string[] values); + IFacetQueryField Facet(string field, params string[] values); /// /// Add a range facet to the current query /// - IFacetDoubleRangeQueryField Facet(string field, DoubleRange[] doubleRanges); + IFacetDoubleRangeQueryField Facet(string field, params DoubleRange[] doubleRanges); /// /// Add a range facet to the current query /// - IFacetLongRangeQueryField Facet(string field, Int64Range[] longRanges); + IFacetLongRangeQueryField Facet(string field, params Int64Range[] longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneQuery.cs b/src/Examine.Lucene/Search/LuceneQuery.cs index ac7e145b1..7747c48d0 100644 --- a/src/Examine.Lucene/Search/LuceneQuery.cs +++ b/src/Examine.Lucene/Search/LuceneQuery.cs @@ -110,16 +110,13 @@ INestedBooleanOperation INestedQuery.RangeQuery(string[] fields, T? min, T? m public IFacetQueryField Facet(string field) => _search.FacetInternal(field); - public IFacetQueryField Facet(string field, string value) - => _search.FacetInternal(field, value); - - public IFacetQueryField Facet(string field, string[] values) + public IFacetQueryField Facet(string field, params string[] values) => _search.FacetInternal(field, values); - public IFacetDoubleRangeQueryField Facet(string field, DoubleRange[] doubleRanges) + public IFacetDoubleRangeQueryField Facet(string field, params DoubleRange[] doubleRanges) => _search.FacetInternal(field, doubleRanges); - public IFacetLongRangeQueryField Facet(string field, Int64Range[] longRanges) + public IFacetLongRangeQueryField Facet(string field, params Int64Range[] longRanges) => _search.FacetInternal(field, longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchQuery.cs b/src/Examine.Lucene/Search/LuceneSearchQuery.cs index a2c1af867..5df295d68 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQuery.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQuery.cs @@ -313,22 +313,22 @@ public IBooleanOperation SelectAllFieldsInternal() public override IFacetQueryField Facet(string field) => FacetInternal(field); - public override IFacetQueryField Facet(string field, string value) => FacetInternal(field, value); + public override IFacetQueryField Facet(string field, params string[] values) => FacetInternal(field, values); - public override IFacetQueryField Facet(string field, string[] values) => FacetInternal(field, values); + public override IFacetDoubleRangeQueryField Facet(string field, params DoubleRange[] doubleRanges) => FacetInternal(field, doubleRanges); - public override IFacetDoubleRangeQueryField Facet(string field, DoubleRange[] doubleRanges) => FacetInternal(field, doubleRanges); - - public override IFacetLongRangeQueryField Facet(string field, Int64Range[] longRanges) => FacetInternal(field, longRanges); + public override IFacetLongRangeQueryField Facet(string field, params Int64Range[] longRanges) => FacetInternal(field, longRanges); internal IFacetQueryField FacetInternal(string field) => FacetInternal(field, Array.Empty()); - internal IFacetQueryField FacetInternal(string field, string value) - => FacetInternal(field, new[] { value }); - - internal IFacetQueryField FacetInternal(string field, string[] values) + internal IFacetQueryField FacetInternal(string field, params string[] values) { + if(values == null) + { + values = Array.Empty(); + } + var facet = new FacetFullTextField(field, values); _facetFields.Add(facet); @@ -337,8 +337,13 @@ internal IFacetQueryField FacetInternal(string field, string[] values) } - internal IFacetDoubleRangeQueryField FacetInternal(string field, DoubleRange[] doubleRanges) + internal IFacetDoubleRangeQueryField FacetInternal(string field, params DoubleRange[] doubleRanges) { + if(doubleRanges == null) + { + doubleRanges = Array.Empty(); + } + var facet = new FacetDoubleField(field, doubleRanges); _facetFields.Add(facet); @@ -346,8 +351,13 @@ internal IFacetDoubleRangeQueryField FacetInternal(string field, DoubleRange[] d return new FacetDoubleRangeQueryField(this, facet); } - internal IFacetLongRangeQueryField FacetInternal(string field, Int64Range[] longRanges) + internal IFacetLongRangeQueryField FacetInternal(string field, params Int64Range[] longRanges) { + if(longRanges == null) + { + longRanges = Array.Empty(); + } + var facet = new FacetLongField(field, longRanges); _facetFields.Add(facet); diff --git a/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs b/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs index c59f0dbb0..e74e88be3 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs @@ -508,12 +508,10 @@ public override string ToString() public abstract IFacetQueryField Facet(string field); - public abstract IFacetQueryField Facet(string field, string value); + public abstract IFacetQueryField Facet(string field, params string[] values); - public abstract IFacetQueryField Facet(string field, string[] values); + public abstract IFacetDoubleRangeQueryField Facet(string field, params DoubleRange[] doubleRanges); - public abstract IFacetDoubleRangeQueryField Facet(string field, DoubleRange[] doubleRanges); - - public abstract IFacetLongRangeQueryField Facet(string field, Int64Range[] longRanges); + public abstract IFacetLongRangeQueryField Facet(string field, params Int64Range[] longRanges); } } From 45986b5274b6b6a081bc4efde7582e63b41bd952 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Tue, 13 Dec 2022 08:59:15 +0100 Subject: [PATCH 10/42] refactor: Facet to WithFacet --- src/Examine.Core/Search/IQuery.cs | 8 +- src/Examine.Lucene/Search/LuceneQuery.cs | 8 +- .../Search/LuceneSearchQuery.cs | 8 +- .../Search/LuceneSearchQueryBase.cs | 8 +- .../Search/FacetFluentApiTests.cs | 170 +++++++++--------- 5 files changed, 101 insertions(+), 101 deletions(-) diff --git a/src/Examine.Core/Search/IQuery.cs b/src/Examine.Core/Search/IQuery.cs index a4c5d9e74..59d525263 100644 --- a/src/Examine.Core/Search/IQuery.cs +++ b/src/Examine.Core/Search/IQuery.cs @@ -141,7 +141,7 @@ public interface IQuery ///
/// /// - IFacetQueryField Facet(string field); + IFacetQueryField WithFacet(string field); /// /// Add a facet string to the current query, filtered by a single value or multiple values @@ -149,16 +149,16 @@ public interface IQuery /// /// /// - IFacetQueryField Facet(string field, params string[] values); + IFacetQueryField WithFacet(string field, params string[] values); /// /// Add a range facet to the current query /// - IFacetDoubleRangeQueryField Facet(string field, params DoubleRange[] doubleRanges); + IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); /// /// Add a range facet to the current query /// - IFacetLongRangeQueryField Facet(string field, params Int64Range[] longRanges); + IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneQuery.cs b/src/Examine.Lucene/Search/LuceneQuery.cs index 7747c48d0..2f410438b 100644 --- a/src/Examine.Lucene/Search/LuceneQuery.cs +++ b/src/Examine.Lucene/Search/LuceneQuery.cs @@ -107,16 +107,16 @@ INestedBooleanOperation INestedQuery.ManagedQuery(string query, string[] fields) INestedBooleanOperation INestedQuery.RangeQuery(string[] fields, T? min, T? max, bool minInclusive, bool maxInclusive) => _search.RangeQueryInternal(fields, min, max, minInclusive: minInclusive, maxInclusive: maxInclusive, _occurrence); - public IFacetQueryField Facet(string field) + public IFacetQueryField WithFacet(string field) => _search.FacetInternal(field); - public IFacetQueryField Facet(string field, params string[] values) + public IFacetQueryField WithFacet(string field, params string[] values) => _search.FacetInternal(field, values); - public IFacetDoubleRangeQueryField Facet(string field, params DoubleRange[] doubleRanges) + public IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges) => _search.FacetInternal(field, doubleRanges); - public IFacetLongRangeQueryField Facet(string field, params Int64Range[] longRanges) + public IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges) => _search.FacetInternal(field, longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchQuery.cs b/src/Examine.Lucene/Search/LuceneSearchQuery.cs index 5df295d68..a29ae0ba1 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQuery.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQuery.cs @@ -311,13 +311,13 @@ public IBooleanOperation SelectAllFieldsInternal() protected override LuceneBooleanOperationBase CreateOp() => new LuceneBooleanOperation(this); - public override IFacetQueryField Facet(string field) => FacetInternal(field); + public override IFacetQueryField WithFacet(string field) => FacetInternal(field); - public override IFacetQueryField Facet(string field, params string[] values) => FacetInternal(field, values); + public override IFacetQueryField WithFacet(string field, params string[] values) => FacetInternal(field, values); - public override IFacetDoubleRangeQueryField Facet(string field, params DoubleRange[] doubleRanges) => FacetInternal(field, doubleRanges); + public override IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges) => FacetInternal(field, doubleRanges); - public override IFacetLongRangeQueryField Facet(string field, params Int64Range[] longRanges) => FacetInternal(field, longRanges); + public override IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges) => FacetInternal(field, longRanges); internal IFacetQueryField FacetInternal(string field) => FacetInternal(field, Array.Empty()); diff --git a/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs b/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs index e74e88be3..da6acfe56 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs @@ -506,12 +506,12 @@ public override string ToString() return $"{{ Category: {Category}, LuceneQuery: {Query} }}"; } - public abstract IFacetQueryField Facet(string field); + public abstract IFacetQueryField WithFacet(string field); - public abstract IFacetQueryField Facet(string field, params string[] values); + public abstract IFacetQueryField WithFacet(string field, params string[] values); - public abstract IFacetDoubleRangeQueryField Facet(string field, params DoubleRange[] doubleRanges); + public abstract IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); - public abstract IFacetLongRangeQueryField Facet(string field, params Int64Range[] longRanges); + public abstract IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges); } } diff --git a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs index 265b22d5e..16266aa22 100644 --- a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs @@ -56,7 +56,7 @@ public void Allow_Leading_Wildcards_Facet() AllowLeadingWildcard = true }).NativeQuery("*dney") .And() - .Facet("nodeName"); + .WithFacet("nodeName"); Assert.Throws(() => searcher.CreateQuery( @@ -102,7 +102,7 @@ public void NativeQuery_Single_Word_Facet() var query = searcher.CreateQuery("content") .NativeQuery("sydney") .And() - .Facet("nodeName"); + .WithFacet("nodeName"); Console.WriteLine(query); @@ -138,7 +138,7 @@ public void Uppercase_Category_Facet() var searcher = indexer.Searcher; var query = searcher.CreateQuery("cOntent") - .Facet("nodeName") + .WithFacet("nodeName") .And() .All(); @@ -174,7 +174,7 @@ public void NativeQuery_Phrase_Facet() var searcher = indexer.Searcher; - var query = searcher.CreateQuery("content").NativeQuery("\"town called\"").And().Facet("bodyText"); + var query = searcher.CreateQuery("content").NativeQuery("\"town called\"").And().WithFacet("bodyText"); Console.WriteLine(query); Assert.AreEqual("{ Category: content, LuceneQuery: +(nodeName:\"town called\" bodyText:\"town called\") }", query.ToString()); @@ -231,7 +231,7 @@ public void Managed_Range_Date_Facet() var numberSortedCriteria = searcher.CreateQuery() .RangeQuery(new[] { "created" }, new DateTime(2000, 01, 02), new DateTime(2000, 01, 05)) .And() - .Facet("created", new Int64Range[] + .WithFacet("created", new Int64Range[] { new Int64Range("First days", new DateTime(2000, 01, 01).Ticks, true, new DateTime(2000, 01, 03).Ticks, true), new Int64Range("Last days", new DateTime(2000, 01, 04).Ticks, true, new DateTime(2000, 01, 06).Ticks, true) @@ -274,7 +274,7 @@ public void Managed_Full_Text_Facet() var result = searcher.CreateQuery() .ManagedQuery("darkness") .And() - .Facet("item1") + .WithFacet("item1") .Execute(); var facetResults = result.GetFacet("item1"); @@ -291,7 +291,7 @@ public void Managed_Full_Text_Facet() result = searcher.CreateQuery() .ManagedQuery("total darkness") .And() - .Facet("item1") + .WithFacet("item1") .Execute(); facetResults = result.GetFacet("item1"); @@ -325,7 +325,7 @@ public void Managed_Full_Text_With_Bool_Facet() var searcher = indexer1.Searcher; - var qry = searcher.CreateQuery().ManagedQuery("darkness").And().Field("item1", "value1").And().Facet("item1"); + var qry = searcher.CreateQuery().ManagedQuery("darkness").And().Field("item1", "value1").And().WithFacet("item1"); Console.WriteLine(qry); var result = qry.Execute(); @@ -342,7 +342,7 @@ public void Managed_Full_Text_With_Bool_Facet() qry = searcher.CreateQuery().ManagedQuery("darkness") .And(query => query.Field("item1", "value1").Or().Field("item1", "value2"), BooleanOperation.Or) .And() - .Facet("item1"); + .WithFacet("item1"); Console.WriteLine(qry); result = qry.Execute(); @@ -383,7 +383,7 @@ public void Not_Managed_Full_Text_Facet() .Field("item1", "value1") .Not().ManagedQuery("darkness") .And() - .Facet("item1"); + .WithFacet("item1"); Console.WriteLine(qry); var result = qry.Execute(); @@ -444,7 +444,7 @@ public void Managed_Range_Int_Facet() var numberSortedCriteria = searcher.CreateQuery() .RangeQuery(new[] { "parentID" }, 122, 124) .And() - .Facet("parentID", new Int64Range[] + .WithFacet("parentID", new Int64Range[] { new Int64Range("120-122", 120, true, 122, true), new Int64Range("123-125", 123, true, 125, true) @@ -503,7 +503,7 @@ public void Legacy_ParentId_Facet() var numberSortedCriteria = searcher.CreateQuery() .Field("parentID", 123) .And() - .Facet("parentID") + .WithFacet("parentID") .OrderBy(new SortableField("sortOrder", SortType.Int)); var numberSortedResult = numberSortedCriteria.Execute(); @@ -554,7 +554,7 @@ public void Grouped_Or_Examiness_Facet() var searcher = indexer.Searcher; //paths contain punctuation, we'll escape it and ensure an exact match - var criteria = searcher.CreateQuery("content").Facet("nodeTypeAlias").And(); + var criteria = searcher.CreateQuery("content").WithFacet("nodeTypeAlias").And(); //get all node type aliases starting with CWS_Home OR and all nodees starting with "About" var filter = criteria.GroupedOr( @@ -592,35 +592,35 @@ public void Grouped_Or_Query_Output_Facet() Console.WriteLine("GROUPED OR - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet"); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet"); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet"); + criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3 blahID:1 blahID:2 blahID:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().Facet("SomeFacet"); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 parentID:1)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }).And().Facet("SomeFacet"); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1)", criteria.Query.ToString()); @@ -643,7 +643,7 @@ public void Grouped_And_Query_Output_Facet() Console.WriteLine("GROUPED AND - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", "SomeValue"); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", "SomeValue"); Console.WriteLine(criteria.Query); //We used to assert this, but it must be allowed to do an add on the same field multiple times //Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); @@ -652,7 +652,7 @@ public void Grouped_And_Query_Output_Facet() Console.WriteLine("GROUPED AND - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", "SomeValue"); + criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", "SomeValue"); Console.WriteLine(criteria.Query); //The field/value array lengths are equal so we will match the key/value pairs Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2 +blahID:3)", criteria.Query.ToString()); @@ -660,7 +660,7 @@ public void Grouped_And_Query_Output_Facet() Console.WriteLine("GROUPED AND - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", "SomeValue"); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", "SomeValue"); Console.WriteLine(criteria.Query); //There are more than one field and there are more values than fields, in this case we align the key/value pairs Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2)", criteria.Query.ToString()); @@ -668,14 +668,14 @@ public void Grouped_And_Query_Output_Facet() Console.WriteLine("GROUPED AND - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().Facet("SomeFacet", "SomeValue"); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet", "SomeValue"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:1)", criteria.Query.ToString()); Console.WriteLine("GROUPED AND - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }).And().Facet("SomeFacet", "SomeValue"); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet", "SomeValue"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); } @@ -697,35 +697,35 @@ public void Grouped_Not_Query_Output_Facet() Console.WriteLine("GROUPED NOT - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().Facet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3 -blahID:1 -blahID:2 -blahID:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().Facet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -parentID:1", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }).And().Facet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1", criteria.Query.ToString()); } @@ -752,7 +752,7 @@ public void Grouped_Not_Single_Field_Single_Value_Facet() var searcher = indexer.Searcher; var query = (LuceneSearchQuery)searcher.CreateQuery("content"); - query.GroupedNot(new[] { "umbracoNaviHide" }, 1.ToString()).And().Facet("nodeName"); + query.GroupedNot(new[] { "umbracoNaviHide" }, 1.ToString()).And().WithFacet("nodeName"); Console.WriteLine(query.Query); var results = query.Execute(); @@ -787,7 +787,7 @@ public void Grouped_Not_Multi_Field_Single_Value_Facet() var searcher = indexer.Searcher; - var query = searcher.CreateQuery("content").GroupedNot(new[] { "umbracoNaviHide", "show" }, 1.ToString()).And().Facet("nodeName"); + var query = searcher.CreateQuery("content").GroupedNot(new[] { "umbracoNaviHide", "show" }, 1.ToString()).And().WithFacet("nodeName"); Console.WriteLine(query); var results = query.Execute(); @@ -826,7 +826,7 @@ public void Grouped_Or_With_Not_Facet() //paths contain punctuation, we'll escape it and ensure an exact match var criteria = searcher.CreateQuery("content"); - var filter = criteria.GroupedOr(new[] { "nodeName", "bodyText", "headerText" }, "ipsum").Not().Field("umbracoNaviHide", "1").And().Facet("headerText"); + var filter = criteria.GroupedOr(new[] { "nodeName", "bodyText", "headerText" }, "ipsum").Not().Field("umbracoNaviHide", "1").And().WithFacet("headerText"); var results = filter.Execute(); var facetResults = results.GetFacet("headerText"); @@ -856,7 +856,7 @@ public void And_Grouped_Not_Single_Value_Facet() .Field("nodeName", "name") .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString() }) - .And().Facet("nodeName"); + .And().WithFacet("nodeName"); Console.WriteLine(query); var results = query.Execute(); @@ -888,7 +888,7 @@ public void And_Grouped_Not_Multi_Value_Facet() .Field("nodeName", "name") .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString(), 2.ToString() }) - .And().Facet("nodeName"); + .And().WithFacet("nodeName"); Console.WriteLine(query); var results = query.Execute(); @@ -919,7 +919,7 @@ public void And_Not_Single_Field_Facet() .Field("nodeName", "name") .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) .Not().Field("umbracoNaviHide", 1.ToString()) - .And().Facet("nodeName"); + .And().WithFacet("nodeName"); Console.WriteLine(query); var results = query.Execute(); @@ -952,7 +952,7 @@ public void AndNot_Nested_Facet() .Field("nodeName", "name") .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) .AndNot(x => x.Field("umbracoNaviHide", 1.ToString())) - .And().Facet("nodeName"); + .And().WithFacet("nodeName"); // TODO: This results in { Category: content, LuceneQuery: +nodeName:name +(bodyText:ficus bodyText:ipsum) -(+umbracoNaviHide:1) } // Which I don't think is right with the -(+ syntax but it still seems to work. @@ -987,7 +987,7 @@ public void And_Not_Added_Later_Facet() query = query .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString(), 2.ToString() }) - .And().Facet("nodeName"); + .And().WithFacet("nodeName"); // Results in { Category: content, LuceneQuery: +nodeName:name -umbracoNaviHide:1 -umbracoNaviHide:2 } @@ -1019,7 +1019,7 @@ public void Not_Range_Facet() var query = searcher.CreateQuery("content") .Field("nodeName", "name") .Not().Field("start", 200) - .And().Facet("start", new Int64Range[] { new Int64Range("Label", 100, false, 200, false) }); + .And().WithFacet("start", new Int64Range[] { new Int64Range("Label", 100, false, 200, false) }); Console.WriteLine(query); var results = query.Execute(); @@ -1067,7 +1067,7 @@ public void Match_By_Path_Facet() //paths contain punctuation, we'll escape it and ensure an exact match var criteria = searcher.CreateQuery("content"); - var filter = criteria.Field("__Path", "-1,123,456,789").And().Facet("nodeName"); + var filter = criteria.Field("__Path", "-1,123,456,789").And().WithFacet("nodeName"); var results1 = filter.Execute(); var facetResults1 = results1.GetFacet("nodeName"); Assert.AreEqual(1, results1.TotalItemCount); @@ -1075,7 +1075,7 @@ public void Match_By_Path_Facet() //now escape it var exactcriteria = searcher.CreateQuery("content"); - var exactfilter = exactcriteria.Field("__Path", "-1,123,456,789".Escape()).And().Facet("nodeName"); + var exactfilter = exactcriteria.Field("__Path", "-1,123,456,789".Escape()).And().WithFacet("nodeName"); var results2 = exactfilter.Execute(); var facetResults2 = results2.GetFacet("nodeName"); Assert.AreEqual(1, results2.TotalItemCount); @@ -1083,7 +1083,7 @@ public void Match_By_Path_Facet() //now try with native var nativeCriteria = searcher.CreateQuery(); - var nativeFilter = nativeCriteria.NativeQuery("__Path:\\-1,123,456,789").And().Facet("nodeName"); + var nativeFilter = nativeCriteria.NativeQuery("__Path:\\-1,123,456,789").And().WithFacet("nodeName"); Console.WriteLine(nativeFilter); var results5 = nativeFilter.Execute(); var facetResults5 = results5.GetFacet("nodeName"); @@ -1092,14 +1092,14 @@ public void Match_By_Path_Facet() //now try wildcards var wildcardcriteria = searcher.CreateQuery("content"); - var wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,456,".MultipleCharacterWildcard()).And().Facet("nodeName"); + var wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,456,".MultipleCharacterWildcard()).And().WithFacet("nodeName"); var results3 = wildcardfilter.Execute(); var facetResults3 = results3.GetFacet("nodeName"); Assert.AreEqual(2, results3.TotalItemCount); Assert.AreEqual(2, facetResults3.Count()); //not found wildcardcriteria = searcher.CreateQuery("content"); - wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,457,".MultipleCharacterWildcard()).And().Facet("nodeName"); + wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,457,".MultipleCharacterWildcard()).And().WithFacet("nodeName"); results3 = wildcardfilter.Execute(); facetResults3 = results3.GetFacet("nodeName"); Assert.AreEqual(0, results3.TotalItemCount); @@ -1131,7 +1131,7 @@ public void Find_By_ParentId_Facet() var searcher = indexer.Searcher; var criteria = searcher.CreateQuery("content"); - var filter = criteria.Field("parentID", 1139).And().Facet("nodeName"); + var filter = criteria.Field("parentID", 1139).And().WithFacet("nodeName"); var results = filter.Execute(); @@ -1163,7 +1163,7 @@ public void Find_By_ParentId_Native_Query_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery("content").Facet("parentID").And(); + var criteria = searcher.CreateQuery("content").WithFacet("parentID").And(); //NOTE: This will not work :/ // It seems that this answer is along the lines of why: https://stackoverflow.com/questions/45516870/apache-lucene-6-queryparser-range-query-is-not-working-with-intpoint @@ -1228,7 +1228,7 @@ public void Find_By_NodeTypeAlias_Facet() var searcher = indexer.Searcher; var criteria = searcher.CreateQuery("content"); - var filter = criteria.Field("nodeTypeAlias", "CWS_Home".Escape()).And().Facet("nodeName"); + var filter = criteria.Field("nodeTypeAlias", "CWS_Home".Escape()).And().WithFacet("nodeName"); var results = filter.Execute(); @@ -1266,7 +1266,7 @@ public void Search_With_Stop_Words_Facet() var filter = criteria.Field("bodyText", "into") .Or().Field("nodeName", "into") - .And().Facet("nodeName"); + .And().WithFacet("nodeName"); Console.WriteLine(filter); @@ -1318,7 +1318,7 @@ public void Search_Native_Query_Facet() var criteria = searcher.CreateQuery("content"); - var results = criteria.NativeQuery("nodeTypeAlias:CWS_Home").And().Facet("nodeTypeAlias").Execute(); + var results = criteria.NativeQuery("nodeTypeAlias:CWS_Home").And().WithFacet("nodeTypeAlias").Execute(); var facetResults = results.GetFacet("nodeTypeAlias"); @@ -1353,7 +1353,7 @@ public void Find_Only_Image_Media_Facet() var searcher = indexer.Searcher; var criteria = searcher.CreateQuery("media"); - var filter = criteria.Field("nodeTypeAlias", "image").And().Facet("nodeTypeAlias"); + var filter = criteria.Field("nodeTypeAlias", "image").And().WithFacet("nodeTypeAlias"); var results = filter.Execute(); @@ -1391,7 +1391,7 @@ public void Find_Both_Media_And_Content_Facet() .Or() .Field(ExamineFieldNames.CategoryFieldName, "content") .And() - .Facet("nodeName"); + .WithFacet("nodeName"); var results = filter.Execute(); @@ -1427,9 +1427,9 @@ public void Sort_Result_By_Number_Field_Facet() var searcher = indexer.Searcher; var sc = searcher.CreateQuery("content") - .Facet("sortOrder") + .WithFacet("sortOrder") .And() - .Facet("parentID") + .WithFacet("parentID") .And(); var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("sortOrder", SortType.Int)); @@ -1482,9 +1482,9 @@ public void Sort_Result_By_Date_Field_Facet() var searcher = indexer.Searcher; var sc = searcher.CreateQuery("content") - .Facet("updateDate") + .WithFacet("updateDate") .And() - .Facet("parentID") + .WithFacet("parentID") .And(); //note: dates internally are stored as Long, see DateTimeType var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("updateDate", SortType.Long)); @@ -1533,12 +1533,12 @@ public void Sort_Result_By_Single_Field_Facet() var searcher = indexer.Searcher; var sc = searcher.CreateQuery("content") - .Facet("nodeName").And(); + .WithFacet("nodeName").And(); var sc1 = sc.Field("writerName", "administrator") .OrderBy(new SortableField("nodeName", SortType.String)); sc = searcher.CreateQuery("content") - .Facet("nodeName").And(); + .WithFacet("nodeName").And(); var sc2 = sc.Field("writerName", "administrator") .OrderByDescending(new SortableField("nodeName", SortType.String)); @@ -1586,11 +1586,11 @@ public void Sort_Result_By_Double_Fields_Facet(string fieldType, SortType sortTy var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content").Facet("field1").And(); + var sc = searcher.CreateQuery("content").WithFacet("field1").And(); var sc1 = sc.All() .OrderBy(new SortableField("field1", sortType)); - sc = searcher.CreateQuery("content").Facet("field1").And(); + sc = searcher.CreateQuery("content").WithFacet("field1").And(); var sc2 = sc.All() .OrderByDescending(new SortableField("field1", sortType)); @@ -1647,7 +1647,7 @@ public void Sort_Result_By_Multiple_Fields_Facet() var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content").Facet("field1").And().Facet("field2").And(); + var sc = searcher.CreateQuery("content").WithFacet("field1").And().WithFacet("field2").And(); var sc1 = sc.All() .OrderByDescending(new SortableField("field2", SortType.Int)) .OrderBy(new SortableField("field1", SortType.Double)); @@ -1690,7 +1690,7 @@ public void Standard_Results_Sorted_By_Score_Facet() var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content", BooleanOperation.Or).Facet("bodyText").And(); + var sc = searcher.CreateQuery("content", BooleanOperation.Or).WithFacet("bodyText").And(); var sc1 = sc.Field("nodeName", "umbraco").Or().Field("headerText", "umbraco").Or().Field("bodyText", "umbraco"); var results = sc1.Execute(); @@ -1735,7 +1735,7 @@ public void Skip_Results_Returns_Different_Results_Facet() var searcher = indexer.Searcher; //Arrange - var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().Facet("nodeName"); + var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().WithFacet("nodeName"); //Act var results = sc.Execute(); @@ -1769,7 +1769,7 @@ public void Escaping_Includes_All_Words_Facet() var searcher = indexer.Searcher; //Arrange - var sc = searcher.CreateQuery("content").Field("nodeName", "codegarden 09".Escape()).And().Facet("nodeName"); + var sc = searcher.CreateQuery("content").Field("nodeName", "codegarden 09".Escape()).And().WithFacet("nodeName"); Console.WriteLine(sc.ToString()); @@ -1808,7 +1808,7 @@ public void Grouped_And_Examiness_Facet() var searcher = indexer.Searcher; //Arrange - var criteria = searcher.CreateQuery("content").Facet("nodeName").And(); + var criteria = searcher.CreateQuery("content").WithFacet("nodeName").And(); //get all node type aliases starting with CWS and all nodees starting with "A" var filter = criteria.GroupedAnd( @@ -1848,7 +1848,7 @@ public void Examiness_Proximity_Facet() var searcher = indexer.Searcher; //Arrange - var criteria = searcher.CreateQuery("content").Facet("nodeName").And(); + var criteria = searcher.CreateQuery("content").WithFacet("nodeName").And(); //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)); @@ -1900,14 +1900,14 @@ public void Float_Range_SimpleIndexSet() var searcher = indexer.Searcher; //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery().Facet("SomeFloat", new DoubleRange[] + var criteria1 = searcher.CreateQuery().WithFacet("SomeFloat", new DoubleRange[] { new DoubleRange("1", 0, true, 12, true), new DoubleRange("2", 13, true, 250, true) }).IsFloat(true).And(); var filter1 = criteria1.RangeQuery(new[] { "SomeFloat" }, 0f, 100f, true, true); - var criteria2 = searcher.CreateQuery().Facet("SomeFloat", new DoubleRange[] + var criteria2 = searcher.CreateQuery().WithFacet("SomeFloat", new DoubleRange[] { new DoubleRange("1", 0, true, 12, true), new DoubleRange("2", 13, true, 250, true) @@ -1963,10 +1963,10 @@ public void Number_Range_SimpleIndexSet_Facet() var searcher = indexer.Searcher; //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery().Facet("SomeNumber").MaxCount(1).And(); + var criteria1 = searcher.CreateQuery().WithFacet("SomeNumber").MaxCount(1).And(); var filter1 = criteria1.RangeQuery(new[] { "SomeNumber" }, 0, 100, true, true); - var criteria2 = searcher.CreateQuery().Facet("SomeNumber").MaxCount(1).And(); + var criteria2 = searcher.CreateQuery().WithFacet("SomeNumber").MaxCount(1).And(); var filter2 = criteria2.RangeQuery(new[] { "SomeNumber" }, 101, 200, true, true); //Act @@ -2014,14 +2014,14 @@ public void Double_Range_SimpleIndexSet_Facet() var searcher = indexer.Searcher; //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery().Facet("SomeDouble", new DoubleRange[] + var criteria1 = searcher.CreateQuery().WithFacet("SomeDouble", new DoubleRange[] { new DoubleRange("1", 0, true, 100, true), new DoubleRange("2", 101, true, 200, true) }).And(); var filter1 = criteria1.RangeQuery(new[] { "SomeDouble" }, 0d, 100d, true, true); - var criteria2 = searcher.CreateQuery("content").Facet("SomeDouble", new DoubleRange[] + var criteria2 = searcher.CreateQuery("content").WithFacet("SomeDouble", new DoubleRange[] { new DoubleRange("1", 0, true, 100, true), new DoubleRange("2", 101, true, 200, true) @@ -2073,14 +2073,14 @@ public void Long_Range_SimpleIndexSet_Facet() var searcher = indexer.Searcher; //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery().Facet("SomeLong", new Int64Range[] + var criteria1 = searcher.CreateQuery().WithFacet("SomeLong", new Int64Range[] { new Int64Range("1", 0L, true, 100L, true), new Int64Range("2", 101L, true, 200L, true) }).And(); var filter1 = criteria1.RangeQuery(new[] { "SomeLong" }, 0L, 100L, true, true); - var criteria2 = searcher.CreateQuery().Facet("SomeLong", new Int64Range[] + var criteria2 = searcher.CreateQuery().WithFacet("SomeLong", new Int64Range[] { new Int64Range("1", 0L, true, 100L, true), new Int64Range("2", 101L, true, 200L, true) @@ -2134,14 +2134,14 @@ public void Date_Range_SimpleIndexSet_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery().Facet("DateCreated", new Int64Range[] + var criteria = searcher.CreateQuery().WithFacet("DateCreated", new Int64Range[] { new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) }).And(); var filter = criteria.RangeQuery(new[] { "DateCreated" }, reIndexDateTime, DateTime.Now, true, true); - var criteria2 = searcher.CreateQuery().Facet("DateCreated", new Int64Range[] + var criteria2 = searcher.CreateQuery().WithFacet("DateCreated", new Int64Range[] { new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) @@ -2187,10 +2187,10 @@ public void Fuzzy_Search_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery().Facet("Content").And(); + var criteria = searcher.CreateQuery().WithFacet("Content").And(); var filter = criteria.Field("Content", "think".Fuzzy(0.1F)); - var criteria2 = searcher.CreateQuery().Facet("Content").And(); + var criteria2 = searcher.CreateQuery().WithFacet("Content").And(); var filter2 = criteria2.Field("Content", "thought".Fuzzy()); Console.WriteLine(filter); @@ -2245,7 +2245,7 @@ public void Inner_Or_Query_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery().Facet("Type").And(); + var criteria = searcher.CreateQuery().WithFacet("Type").And(); //Query = // +Type:type1 +(Content:world Content:something) @@ -2290,7 +2290,7 @@ public void Inner_And_Query_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery().Facet("Type").And(); + var criteria = searcher.CreateQuery().WithFacet("Type").And(); //Query = // +Type:type1 +(+Content:world +Content:hello) @@ -2335,7 +2335,7 @@ public void Inner_Not_Query_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery().Facet("Type").And(); + var criteria = searcher.CreateQuery().WithFacet("Type").And(); //Query = // +Type:type1 +(+Content:world -Content:something) @@ -2392,7 +2392,7 @@ public void Complex_Or_Group_Nested_Query_Facet() // required so that the type2 query is required BooleanOperation.And) .And() - .Facet("Type"); + .WithFacet("Type"); Console.WriteLine(filter); @@ -2424,7 +2424,7 @@ public void Custom_Lucene_Query_With_Native_Facet() var criteria = (LuceneSearchQuery)searcher.CreateQuery(); //combine a custom lucene query with raw lucene query - var op = criteria.NativeQuery("hello:world").And().Facet("SomeFacet").And(); + var op = criteria.NativeQuery("hello:world").And().WithFacet("SomeFacet").And(); criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); @@ -2461,7 +2461,7 @@ public void Select_Field() }); var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content").Facet("nodeName").And(); + var sc = searcher.CreateQuery("content").WithFacet("nodeName").And(); var sc1 = sc.Field("nodeName", "my name 1").SelectField("__Path"); var results = sc1.Execute(); @@ -2504,7 +2504,7 @@ public void Select_Fields_Facet() }); var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content").Facet("nodeName").And(); + var sc = searcher.CreateQuery("content").WithFacet("nodeName").And(); var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new[] { "nodeName", "bodyText", "id", "__NodeId" })); var results = sc1.Execute(); @@ -2548,7 +2548,7 @@ public void Select_Fields_HashSet_Facet() }); var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content").Facet("nodeName").And(); + var sc = searcher.CreateQuery("content").WithFacet("nodeName").And(); var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new string[] { "nodeName", "bodyText" })); var results = sc1.Execute(); @@ -2589,7 +2589,7 @@ public void Paging_With_Skip_Take_Facet() //Arrange - var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().Facet("writerName"); + var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().WithFacet("writerName"); int pageIndex = 0; int pageSize = 2; @@ -2660,7 +2660,7 @@ public void Given_SkipTake_Returns_ExpectedTotals_Facet(int skip, int take, int //Arrange - var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().Facet("nodeName"); + var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().WithFacet("nodeName"); //Act From 4d7cdddcc937b26c4e06bd17ca55a442f3bdc849 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Tue, 13 Dec 2022 09:01:32 +0100 Subject: [PATCH 11/42] docs: Update Facet to WithFacet --- docs/searching.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/searching.md b/docs/searching.md index 360e6029b..59edcd21a 100644 --- a/docs/searching.md +++ b/docs/searching.md @@ -261,7 +261,7 @@ var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") .And() - .Facet("Address") // Get facets of the Address field + .WithFacet("Address") // Get facets of the Address field .Execute(); var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -272,7 +272,7 @@ var addressFacetResults = results.GetFacet("Address"); // Returns the facets for * Label: Hollywood, Value: 10 */ -var hillsValue = addressFacetResults.Facet("Hills"); // Gets the IFacetValue for the facet Hills +var hillsValue = addressFacetResults.WithFacet("Hills"); // Gets the IFacetValue for the facet Hills ``` Filtered value example @@ -291,7 +291,7 @@ var addressFacetResults = results.GetFacet("Address"); // Returns the facets for * Label: Hills, Value: 2 <-- As Hills was the only filtered value we will only get this facet */ -var hillsValue = addressFacetResults.Facet("Hills"); // Gets the IFacetValue for the facet Hills +var hillsValue = addressFacetResults.WithFacet("Hills"); // Gets the IFacetValue for the facet Hills ``` MaxCount example @@ -300,7 +300,7 @@ var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") .And() - .Facet("Address") // Get facets of the Address field + .WithFacet("Address") // Get facets of the Address field .Execute(); var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -315,7 +315,7 @@ var addressFacetResults = results.GetFacet("Address"); // Returns the facets for results = searcher.CreateQuery() .Field("Address", "Hills") .And() - .Facet("Address") // Get facets of the Address field + .WithFacet("Address") // Get facets of the Address field .MaxCount(2) // Gets the top 2 results (The facets with the highest value) .Execute(); @@ -352,7 +352,7 @@ var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") .And() - .Facet("Address") // Get facets of the Address field + .WithFacet("Address") // Get facets of the Address field .FacetField("address_facet") .Execute(); @@ -375,7 +375,7 @@ Double range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() - .Facet("Price", new DoubleRange[] { + .WithFacet("Price", new DoubleRange[] { new DoubleRange("0-10", 0, true, 10, true), new DoubleRange("11-20", 11, true, 20, true) }) // Get facets of the price field @@ -391,14 +391,14 @@ var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the * Label: 11-20, Value: 10 */ -var firstRangeValue = priceFacetResults.Facet("0-10"); // Gets the IFacetValue for the facet "0-10" +var firstRangeValue = priceFacetResults.WithFacet("0-10"); // Gets the IFacetValue for the facet "0-10" ``` Float range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() - .Facet("Price", new DoubleRange[] { + .WithFacet("Price", new DoubleRange[] { new DoubleRange("0-10", 0, true, 10, true), new DoubleRange("11-20", 11, true, 20, true) }) // Get facets of the price field @@ -415,14 +415,14 @@ var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the * Label: 11-20, Value: 10 */ -var firstRangeValue = priceFacetResults.Facet("0-10"); // Gets the IFacetValue for the facet "0-10" +var firstRangeValue = priceFacetResults.WithFacet("0-10"); // Gets the IFacetValue for the facet "0-10" ``` Int/Long range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() - .Facet("Price", new Int64Range[] { + .WithFacet("Price", new Int64Range[] { new Int64Range("0-10", 0, true, 10, true), new Int64Range("11-20", 11, true, 20, true) }) // Get facets of the price field @@ -438,14 +438,14 @@ var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the * Label: 11-20, Value: 10 */ -var firstRangeValue = priceFacetResults.Facet("0-10"); // Gets the IFacetValue for the facet "0-10" +var firstRangeValue = priceFacetResults.WithFacet("0-10"); // Gets the IFacetValue for the facet "0-10" ``` DateTime range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() - .Facet("Created", new Int64Range[] { + .WithFacet("Created", new Int64Range[] { new Int64Range("first", DateTime.UtcNow.AddDays(-1).Ticks, true, DateTime.UtcNow.Ticks, true), new Int64Range("last", DateTime.UtcNow.AddDays(1).Ticks, true, DateTime.UtcNow.AddDays(2).Ticks, true) }) // Get facets of the price field @@ -461,4 +461,4 @@ var createdFacetResults = results.GetFacet("Created"); // Returns the facets for * Label: last, Value: 10 */ -var firstRangeValue = createdFacetResults.Facet("first"); // Gets the IFacetValue for the facet "first" \ No newline at end of file +var firstRangeValue = createdFacetResults.WithFacet("first"); // Gets the IFacetValue for the facet "first" \ No newline at end of file From 43f73a7f66ce6a314a0a6c495d1721285e0e9742 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Tue, 13 Dec 2022 09:48:41 +0100 Subject: [PATCH 12/42] refactor: Move facet operations to different interface --- src/Examine.Core/Search/IFacetAppending.cs | 14 + .../Search/IFacetDoubleRangeQueryField.cs | 2 +- .../Search/IFacetLongRangeQueryField.cs | 2 +- src/Examine.Core/Search/IFacetQueryField.cs | 2 +- src/Examine.Core/Search/IFaceting.cs | 40 +++ src/Examine.Core/Search/IOrdering.cs | 2 +- src/Examine.Core/Search/IQuery.cs | 25 -- .../Search/FacetDoubleRangeQueryField.cs | 9 +- .../Search/FacetLongRangeQueryField.cs | 10 +- src/Examine.Lucene/Search/FacetQueryField.cs | 9 +- .../Search/LuceneBooleanOperation.cs | 10 + .../Search/LuceneBooleanOperationBase.cs | 9 + src/Examine.Lucene/Search/LuceneQuery.cs | 12 - .../Search/LuceneSearchQuery.cs | 11 - .../Search/LuceneSearchQueryBase.cs | 8 - .../Search/FacetFluentApiTests.cs | 258 +++++++++--------- 16 files changed, 225 insertions(+), 198 deletions(-) create mode 100644 src/Examine.Core/Search/IFacetAppending.cs create mode 100644 src/Examine.Core/Search/IFaceting.cs diff --git a/src/Examine.Core/Search/IFacetAppending.cs b/src/Examine.Core/Search/IFacetAppending.cs new file mode 100644 index 000000000..831e89bba --- /dev/null +++ b/src/Examine.Core/Search/IFacetAppending.cs @@ -0,0 +1,14 @@ +namespace Examine.Search +{ + /// + /// Allows for appending more operations + /// + public interface IFacetAppending + { + /// + /// Allows for adding more operations + /// + /// + IOrdering And(); + } +} diff --git a/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs b/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs index f62e0a7d8..e68300d70 100644 --- a/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs +++ b/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs @@ -1,6 +1,6 @@ namespace Examine.Search { - public interface IFacetDoubleRangeQueryField : IBooleanOperation + public interface IFacetDoubleRangeQueryField : IFacetAppending, IQueryExecutor { /// /// Sets if the range query is on values diff --git a/src/Examine.Core/Search/IFacetLongRangeQueryField.cs b/src/Examine.Core/Search/IFacetLongRangeQueryField.cs index 9f8f6d5a3..824c8a363 100644 --- a/src/Examine.Core/Search/IFacetLongRangeQueryField.cs +++ b/src/Examine.Core/Search/IFacetLongRangeQueryField.cs @@ -1,6 +1,6 @@ namespace Examine.Search { - public interface IFacetLongRangeQueryField : IBooleanOperation + public interface IFacetLongRangeQueryField : IFacetAppending, IQueryExecutor { } } diff --git a/src/Examine.Core/Search/IFacetQueryField.cs b/src/Examine.Core/Search/IFacetQueryField.cs index 79a3bf51a..b62741738 100644 --- a/src/Examine.Core/Search/IFacetQueryField.cs +++ b/src/Examine.Core/Search/IFacetQueryField.cs @@ -1,6 +1,6 @@ namespace Examine.Search { - public interface IFacetQueryField : IBooleanOperation + public interface IFacetQueryField : IFacetAppending, IQueryExecutor { /// /// Maximum number of terms to return diff --git a/src/Examine.Core/Search/IFaceting.cs b/src/Examine.Core/Search/IFaceting.cs new file mode 100644 index 000000000..463848c74 --- /dev/null +++ b/src/Examine.Core/Search/IFaceting.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Lucene.Net.Facet.Range; + +namespace Examine.Search +{ + /// + /// Faceting operations + /// + public interface IFaceting + { + /// + /// Add a facet string to the current query + /// + /// + /// + IFacetQueryField WithFacet(string field); + + /// + /// Add a facet string to the current query, filtered by a single value or multiple values + /// + /// + /// + /// + IFacetQueryField WithFacet(string field, params string[] values); + + /// + /// Add a range facet to the current query + /// + IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); + + /// + /// Add a range facet to the current query + /// + IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges); + } +} diff --git a/src/Examine.Core/Search/IOrdering.cs b/src/Examine.Core/Search/IOrdering.cs index 1c5586dc2..c9ab028f6 100644 --- a/src/Examine.Core/Search/IOrdering.cs +++ b/src/Examine.Core/Search/IOrdering.cs @@ -2,7 +2,7 @@ namespace Examine.Search { - public interface IOrdering : IQueryExecutor + public interface IOrdering : IQueryExecutor, IFaceting { /// /// Orders the results by the specified fields diff --git a/src/Examine.Core/Search/IQuery.cs b/src/Examine.Core/Search/IQuery.cs index 59d525263..68bf669d1 100644 --- a/src/Examine.Core/Search/IQuery.cs +++ b/src/Examine.Core/Search/IQuery.cs @@ -135,30 +135,5 @@ public interface IQuery /// /// IBooleanOperation RangeQuery(string[] fields, T? min, T? max, bool minInclusive = true, bool maxInclusive = true) where T : struct; - - /// - /// Add a facet string to the current query - /// - /// - /// - IFacetQueryField WithFacet(string field); - - /// - /// Add a facet string to the current query, filtered by a single value or multiple values - /// - /// - /// - /// - IFacetQueryField WithFacet(string field, params string[] values); - - /// - /// Add a range facet to the current query - /// - IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); - - /// - /// Add a range facet to the current query - /// - IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges); } } diff --git a/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs b/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs index 41de2d229..d0751c4a1 100644 --- a/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs +++ b/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs @@ -2,15 +2,20 @@ namespace Examine.Lucene.Search { - public class FacetDoubleRangeQueryField : LuceneBooleanOperation, IFacetDoubleRangeQueryField + public class FacetDoubleRangeQueryField : IFacetDoubleRangeQueryField { + private readonly LuceneSearchQuery _search; private readonly FacetDoubleField _field; - public FacetDoubleRangeQueryField(LuceneSearchQuery search, FacetDoubleField field) : base(search) + public FacetDoubleRangeQueryField(LuceneSearchQuery search, FacetDoubleField field) { + _search = search; _field = field; } + public IOrdering And() => new LuceneBooleanOperation(_search); + public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); + public IFacetDoubleRangeQueryField IsFloat(bool isFloat) { _field.IsFloat = isFloat; diff --git a/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs b/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs index 7cb8b84fc..2e8602e57 100644 --- a/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs +++ b/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs @@ -2,10 +2,16 @@ namespace Examine.Lucene.Search { - public class FacetLongRangeQueryField : LuceneBooleanOperation, IFacetLongRangeQueryField + public class FacetLongRangeQueryField : IFacetLongRangeQueryField { - public FacetLongRangeQueryField(LuceneSearchQuery search, FacetLongField _) : base(search) + private readonly LuceneSearchQuery _search; + + public FacetLongRangeQueryField(LuceneSearchQuery search, FacetLongField _) { + _search = search; } + + public IOrdering And() => new LuceneBooleanOperation(_search); + public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); } } diff --git a/src/Examine.Lucene/Search/FacetQueryField.cs b/src/Examine.Lucene/Search/FacetQueryField.cs index 1cd1975ed..d9ed7a965 100644 --- a/src/Examine.Lucene/Search/FacetQueryField.cs +++ b/src/Examine.Lucene/Search/FacetQueryField.cs @@ -2,15 +2,20 @@ namespace Examine.Search { - public class FacetQueryField : LuceneBooleanOperation, IFacetQueryField + public class FacetQueryField : IFacetQueryField { + private readonly LuceneSearchQuery _search; private readonly FacetFullTextField _field; - public FacetQueryField(LuceneSearchQuery search, FacetFullTextField field) : base(search) + public FacetQueryField(LuceneSearchQuery search, FacetFullTextField field) { + _search = search; _field = field; } + public IOrdering And() => new LuceneBooleanOperation(_search); + public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); + public IFacetQueryField FacetField(string fieldName) { _field.FacetField = fieldName; diff --git a/src/Examine.Lucene/Search/LuceneBooleanOperation.cs b/src/Examine.Lucene/Search/LuceneBooleanOperation.cs index b38dd8422..6af43e7fe 100644 --- a/src/Examine.Lucene/Search/LuceneBooleanOperation.cs +++ b/src/Examine.Lucene/Search/LuceneBooleanOperation.cs @@ -1,7 +1,9 @@ +using System; using System.Collections.Generic; using System.Diagnostics; using Examine.Lucene.Providers; using Examine.Search; +using Lucene.Net.Facet.Range; using Lucene.Net.Search; namespace Examine.Lucene.Search @@ -65,5 +67,13 @@ public LuceneBooleanOperation(LuceneSearchQuery search) #endregion public override string ToString() => _search.ToString(); + + public override IFacetQueryField WithFacet(string field) => _search.FacetInternal(field, Array.Empty()); + + public override IFacetQueryField WithFacet(string field, params string[] values) => _search.FacetInternal(field, values); + + public override IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges) => _search.FacetInternal(field, doubleRanges); + + public override IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges) => _search.FacetInternal(field, longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs index 2769479ad..bd336a468 100644 --- a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs +++ b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Examine.Search; +using Lucene.Net.Facet.Range; using Lucene.Net.Search; namespace Examine.Lucene.Search @@ -77,5 +78,13 @@ protected internal LuceneBooleanOperationBase Op( public abstract IOrdering SelectFields(ISet fieldNames); public abstract IOrdering SelectField(string fieldName); public abstract IOrdering SelectAllFields(); + + public abstract IFacetQueryField WithFacet(string field); + + public abstract IFacetQueryField WithFacet(string field, params string[] values); + + public abstract IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); + + public abstract IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneQuery.cs b/src/Examine.Lucene/Search/LuceneQuery.cs index 2f410438b..bb0c77ef0 100644 --- a/src/Examine.Lucene/Search/LuceneQuery.cs +++ b/src/Examine.Lucene/Search/LuceneQuery.cs @@ -106,17 +106,5 @@ INestedBooleanOperation INestedQuery.ManagedQuery(string query, string[] fields) INestedBooleanOperation INestedQuery.RangeQuery(string[] fields, T? min, T? max, bool minInclusive, bool maxInclusive) => _search.RangeQueryInternal(fields, min, max, minInclusive: minInclusive, maxInclusive: maxInclusive, _occurrence); - - public IFacetQueryField WithFacet(string field) - => _search.FacetInternal(field); - - public IFacetQueryField WithFacet(string field, params string[] values) - => _search.FacetInternal(field, values); - - public IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges) - => _search.FacetInternal(field, doubleRanges); - - public IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges) - => _search.FacetInternal(field, longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchQuery.cs b/src/Examine.Lucene/Search/LuceneSearchQuery.cs index a29ae0ba1..bd5600f0f 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQuery.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQuery.cs @@ -311,17 +311,6 @@ public IBooleanOperation SelectAllFieldsInternal() protected override LuceneBooleanOperationBase CreateOp() => new LuceneBooleanOperation(this); - public override IFacetQueryField WithFacet(string field) => FacetInternal(field); - - public override IFacetQueryField WithFacet(string field, params string[] values) => FacetInternal(field, values); - - public override IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges) => FacetInternal(field, doubleRanges); - - public override IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges) => FacetInternal(field, longRanges); - - internal IFacetQueryField FacetInternal(string field) - => FacetInternal(field, Array.Empty()); - internal IFacetQueryField FacetInternal(string field, params string[] values) { if(values == null) diff --git a/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs b/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs index da6acfe56..6995701a9 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQueryBase.cs @@ -505,13 +505,5 @@ public override string ToString() { return $"{{ Category: {Category}, LuceneQuery: {Query} }}"; } - - public abstract IFacetQueryField WithFacet(string field); - - public abstract IFacetQueryField WithFacet(string field, params string[] values); - - public abstract IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); - - public abstract IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges); } } diff --git a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs index 16266aa22..f4b2a1cde 100644 --- a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs @@ -55,7 +55,6 @@ public void Allow_Leading_Wildcards_Facet() { AllowLeadingWildcard = true }).NativeQuery("*dney") - .And() .WithFacet("nodeName"); Assert.Throws(() => @@ -101,7 +100,6 @@ public void NativeQuery_Single_Word_Facet() var query = searcher.CreateQuery("content") .NativeQuery("sydney") - .And() .WithFacet("nodeName"); Console.WriteLine(query); @@ -138,9 +136,8 @@ public void Uppercase_Category_Facet() var searcher = indexer.Searcher; var query = searcher.CreateQuery("cOntent") - .WithFacet("nodeName") - .And() - .All(); + .All() + .WithFacet("nodeName"); Console.WriteLine(query); @@ -174,7 +171,7 @@ public void NativeQuery_Phrase_Facet() var searcher = indexer.Searcher; - var query = searcher.CreateQuery("content").NativeQuery("\"town called\"").And().WithFacet("bodyText"); + var query = searcher.CreateQuery("content").NativeQuery("\"town called\"").WithFacet("bodyText"); Console.WriteLine(query); Assert.AreEqual("{ Category: content, LuceneQuery: +(nodeName:\"town called\" bodyText:\"town called\") }", query.ToString()); @@ -230,7 +227,6 @@ public void Managed_Range_Date_Facet() var numberSortedCriteria = searcher.CreateQuery() .RangeQuery(new[] { "created" }, new DateTime(2000, 01, 02), new DateTime(2000, 01, 05)) - .And() .WithFacet("created", new Int64Range[] { new Int64Range("First days", new DateTime(2000, 01, 01).Ticks, true, new DateTime(2000, 01, 03).Ticks, true), @@ -273,7 +269,6 @@ public void Managed_Full_Text_Facet() var result = searcher.CreateQuery() .ManagedQuery("darkness") - .And() .WithFacet("item1") .Execute(); @@ -290,7 +285,6 @@ public void Managed_Full_Text_Facet() result = searcher.CreateQuery() .ManagedQuery("total darkness") - .And() .WithFacet("item1") .Execute(); facetResults = result.GetFacet("item1"); @@ -325,7 +319,7 @@ public void Managed_Full_Text_With_Bool_Facet() var searcher = indexer1.Searcher; - var qry = searcher.CreateQuery().ManagedQuery("darkness").And().Field("item1", "value1").And().WithFacet("item1"); + var qry = searcher.CreateQuery().ManagedQuery("darkness").And().Field("item1", "value1").WithFacet("item1"); Console.WriteLine(qry); var result = qry.Execute(); @@ -341,7 +335,6 @@ public void Managed_Full_Text_With_Bool_Facet() qry = searcher.CreateQuery().ManagedQuery("darkness") .And(query => query.Field("item1", "value1").Or().Field("item1", "value2"), BooleanOperation.Or) - .And() .WithFacet("item1"); Console.WriteLine(qry); result = qry.Execute(); @@ -382,7 +375,6 @@ public void Not_Managed_Full_Text_Facet() var qry = searcher.CreateQuery() .Field("item1", "value1") .Not().ManagedQuery("darkness") - .And() .WithFacet("item1"); Console.WriteLine(qry); @@ -443,7 +435,6 @@ public void Managed_Range_Int_Facet() var numberSortedCriteria = searcher.CreateQuery() .RangeQuery(new[] { "parentID" }, 122, 124) - .And() .WithFacet("parentID", new Int64Range[] { new Int64Range("120-122", 120, true, 122, true), @@ -502,8 +493,8 @@ public void Legacy_ParentId_Facet() var numberSortedCriteria = searcher.CreateQuery() .Field("parentID", 123) - .And() .WithFacet("parentID") + .And() .OrderBy(new SortableField("sortOrder", SortType.Int)); var numberSortedResult = numberSortedCriteria.Execute(); @@ -554,7 +545,7 @@ public void Grouped_Or_Examiness_Facet() var searcher = indexer.Searcher; //paths contain punctuation, we'll escape it and ensure an exact match - var criteria = searcher.CreateQuery("content").WithFacet("nodeTypeAlias").And(); + var criteria = searcher.CreateQuery("content"); //get all node type aliases starting with CWS_Home OR and all nodees starting with "About" var filter = criteria.GroupedOr( @@ -563,7 +554,7 @@ public void Grouped_Or_Examiness_Facet() Console.WriteLine(filter); - var results = filter.Execute(); + var results = filter.WithFacet("nodeTypeAlias").Execute(); var facetResults = results.GetFacet("nodeTypeAlias"); @@ -592,35 +583,35 @@ public void Grouped_Or_Query_Output_Facet() Console.WriteLine("GROUPED OR - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet"); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet"); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet"); + criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3 blahID:1 blahID:2 blahID:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet"); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 parentID:1)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet"); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1)", criteria.Query.ToString()); @@ -643,7 +634,7 @@ public void Grouped_And_Query_Output_Facet() Console.WriteLine("GROUPED AND - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", "SomeValue"); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", "SomeValue"); Console.WriteLine(criteria.Query); //We used to assert this, but it must be allowed to do an add on the same field multiple times //Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); @@ -652,7 +643,7 @@ public void Grouped_And_Query_Output_Facet() Console.WriteLine("GROUPED AND - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", "SomeValue"); + criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", "SomeValue"); Console.WriteLine(criteria.Query); //The field/value array lengths are equal so we will match the key/value pairs Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2 +blahID:3)", criteria.Query.ToString()); @@ -660,7 +651,7 @@ public void Grouped_And_Query_Output_Facet() Console.WriteLine("GROUPED AND - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", "SomeValue"); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", "SomeValue"); Console.WriteLine(criteria.Query); //There are more than one field and there are more values than fields, in this case we align the key/value pairs Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2)", criteria.Query.ToString()); @@ -668,14 +659,14 @@ public void Grouped_And_Query_Output_Facet() Console.WriteLine("GROUPED AND - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet", "SomeValue"); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet", "SomeValue"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:1)", criteria.Query.ToString()); Console.WriteLine("GROUPED AND - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet", "SomeValue"); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet", "SomeValue"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); } @@ -697,35 +688,35 @@ public void Grouped_Not_Query_Output_Facet() Console.WriteLine("GROUPED NOT - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).And().WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3 -blahID:1 -blahID:2 -blahID:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -parentID:1", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }).And().WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1", criteria.Query.ToString()); } @@ -752,7 +743,7 @@ public void Grouped_Not_Single_Field_Single_Value_Facet() var searcher = indexer.Searcher; var query = (LuceneSearchQuery)searcher.CreateQuery("content"); - query.GroupedNot(new[] { "umbracoNaviHide" }, 1.ToString()).And().WithFacet("nodeName"); + query.GroupedNot(new[] { "umbracoNaviHide" }, 1.ToString()).WithFacet("nodeName"); Console.WriteLine(query.Query); var results = query.Execute(); @@ -787,7 +778,7 @@ public void Grouped_Not_Multi_Field_Single_Value_Facet() var searcher = indexer.Searcher; - var query = searcher.CreateQuery("content").GroupedNot(new[] { "umbracoNaviHide", "show" }, 1.ToString()).And().WithFacet("nodeName"); + var query = searcher.CreateQuery("content").GroupedNot(new[] { "umbracoNaviHide", "show" }, 1.ToString()).WithFacet("nodeName"); Console.WriteLine(query); var results = query.Execute(); @@ -826,7 +817,7 @@ public void Grouped_Or_With_Not_Facet() //paths contain punctuation, we'll escape it and ensure an exact match var criteria = searcher.CreateQuery("content"); - var filter = criteria.GroupedOr(new[] { "nodeName", "bodyText", "headerText" }, "ipsum").Not().Field("umbracoNaviHide", "1").And().WithFacet("headerText"); + var filter = criteria.GroupedOr(new[] { "nodeName", "bodyText", "headerText" }, "ipsum").Not().Field("umbracoNaviHide", "1").WithFacet("headerText"); var results = filter.Execute(); var facetResults = results.GetFacet("headerText"); @@ -856,7 +847,7 @@ public void And_Grouped_Not_Single_Value_Facet() .Field("nodeName", "name") .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString() }) - .And().WithFacet("nodeName"); + .WithFacet("nodeName"); Console.WriteLine(query); var results = query.Execute(); @@ -888,7 +879,7 @@ public void And_Grouped_Not_Multi_Value_Facet() .Field("nodeName", "name") .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString(), 2.ToString() }) - .And().WithFacet("nodeName"); + .WithFacet("nodeName"); Console.WriteLine(query); var results = query.Execute(); @@ -919,7 +910,7 @@ public void And_Not_Single_Field_Facet() .Field("nodeName", "name") .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) .Not().Field("umbracoNaviHide", 1.ToString()) - .And().WithFacet("nodeName"); + .WithFacet("nodeName"); Console.WriteLine(query); var results = query.Execute(); @@ -952,7 +943,7 @@ public void AndNot_Nested_Facet() .Field("nodeName", "name") .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) .AndNot(x => x.Field("umbracoNaviHide", 1.ToString())) - .And().WithFacet("nodeName"); + .WithFacet("nodeName"); // TODO: This results in { Category: content, LuceneQuery: +nodeName:name +(bodyText:ficus bodyText:ipsum) -(+umbracoNaviHide:1) } // Which I don't think is right with the -(+ syntax but it still seems to work. @@ -986,13 +977,13 @@ public void And_Not_Added_Later_Facet() .Field("nodeName", "name"); query = query - .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString(), 2.ToString() }) - .And().WithFacet("nodeName"); + .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString(), 2.ToString() }); // Results in { Category: content, LuceneQuery: +nodeName:name -umbracoNaviHide:1 -umbracoNaviHide:2 } Console.WriteLine(query); - var results = query.Execute(); + var results = query + .WithFacet("nodeName").Execute(); var facetResults = results.GetFacet("nodeName"); Assert.AreEqual(1, results.TotalItemCount); @@ -1019,7 +1010,7 @@ public void Not_Range_Facet() var query = searcher.CreateQuery("content") .Field("nodeName", "name") .Not().Field("start", 200) - .And().WithFacet("start", new Int64Range[] { new Int64Range("Label", 100, false, 200, false) }); + .WithFacet("start", new Int64Range[] { new Int64Range("Label", 100, false, 200, false) }); Console.WriteLine(query); var results = query.Execute(); @@ -1067,7 +1058,7 @@ public void Match_By_Path_Facet() //paths contain punctuation, we'll escape it and ensure an exact match var criteria = searcher.CreateQuery("content"); - var filter = criteria.Field("__Path", "-1,123,456,789").And().WithFacet("nodeName"); + var filter = criteria.Field("__Path", "-1,123,456,789").WithFacet("nodeName"); var results1 = filter.Execute(); var facetResults1 = results1.GetFacet("nodeName"); Assert.AreEqual(1, results1.TotalItemCount); @@ -1075,7 +1066,7 @@ public void Match_By_Path_Facet() //now escape it var exactcriteria = searcher.CreateQuery("content"); - var exactfilter = exactcriteria.Field("__Path", "-1,123,456,789".Escape()).And().WithFacet("nodeName"); + var exactfilter = exactcriteria.Field("__Path", "-1,123,456,789".Escape()).WithFacet("nodeName"); var results2 = exactfilter.Execute(); var facetResults2 = results2.GetFacet("nodeName"); Assert.AreEqual(1, results2.TotalItemCount); @@ -1083,7 +1074,7 @@ public void Match_By_Path_Facet() //now try with native var nativeCriteria = searcher.CreateQuery(); - var nativeFilter = nativeCriteria.NativeQuery("__Path:\\-1,123,456,789").And().WithFacet("nodeName"); + var nativeFilter = nativeCriteria.NativeQuery("__Path:\\-1,123,456,789").WithFacet("nodeName"); Console.WriteLine(nativeFilter); var results5 = nativeFilter.Execute(); var facetResults5 = results5.GetFacet("nodeName"); @@ -1092,14 +1083,14 @@ public void Match_By_Path_Facet() //now try wildcards var wildcardcriteria = searcher.CreateQuery("content"); - var wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,456,".MultipleCharacterWildcard()).And().WithFacet("nodeName"); + var wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,456,".MultipleCharacterWildcard()).WithFacet("nodeName"); var results3 = wildcardfilter.Execute(); var facetResults3 = results3.GetFacet("nodeName"); Assert.AreEqual(2, results3.TotalItemCount); Assert.AreEqual(2, facetResults3.Count()); //not found wildcardcriteria = searcher.CreateQuery("content"); - wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,457,".MultipleCharacterWildcard()).And().WithFacet("nodeName"); + wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,457,".MultipleCharacterWildcard()).WithFacet("nodeName"); results3 = wildcardfilter.Execute(); facetResults3 = results3.GetFacet("nodeName"); Assert.AreEqual(0, results3.TotalItemCount); @@ -1131,7 +1122,7 @@ public void Find_By_ParentId_Facet() var searcher = indexer.Searcher; var criteria = searcher.CreateQuery("content"); - var filter = criteria.Field("parentID", 1139).And().WithFacet("nodeName"); + var filter = criteria.Field("parentID", 1139).WithFacet("nodeName"); var results = filter.Execute(); @@ -1163,7 +1154,7 @@ public void Find_By_ParentId_Native_Query_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery("content").WithFacet("parentID").And(); + var criteria = searcher.CreateQuery("content"); //NOTE: This will not work :/ // It seems that this answer is along the lines of why: https://stackoverflow.com/questions/45516870/apache-lucene-6-queryparser-range-query-is-not-working-with-intpoint @@ -1177,7 +1168,7 @@ public void Find_By_ParentId_Native_Query_Facet() //We can use a Lucene query directly instead: //((LuceneSearchQuery)criteria).LuceneQuery(NumericRangeQuery) - var results = filter.Execute(); + var results = filter.WithFacet("parentID").Execute(); var facetResults = results.GetFacet("parentID"); @@ -1228,7 +1219,7 @@ public void Find_By_NodeTypeAlias_Facet() var searcher = indexer.Searcher; var criteria = searcher.CreateQuery("content"); - var filter = criteria.Field("nodeTypeAlias", "CWS_Home".Escape()).And().WithFacet("nodeName"); + var filter = criteria.Field("nodeTypeAlias", "CWS_Home".Escape()).WithFacet("nodeName"); var results = filter.Execute(); @@ -1266,7 +1257,7 @@ public void Search_With_Stop_Words_Facet() var filter = criteria.Field("bodyText", "into") .Or().Field("nodeName", "into") - .And().WithFacet("nodeName"); + .WithFacet("nodeName"); Console.WriteLine(filter); @@ -1318,7 +1309,7 @@ public void Search_Native_Query_Facet() var criteria = searcher.CreateQuery("content"); - var results = criteria.NativeQuery("nodeTypeAlias:CWS_Home").And().WithFacet("nodeTypeAlias").Execute(); + var results = criteria.NativeQuery("nodeTypeAlias:CWS_Home").WithFacet("nodeTypeAlias").Execute(); var facetResults = results.GetFacet("nodeTypeAlias"); @@ -1353,7 +1344,7 @@ public void Find_Only_Image_Media_Facet() var searcher = indexer.Searcher; var criteria = searcher.CreateQuery("media"); - var filter = criteria.Field("nodeTypeAlias", "image").And().WithFacet("nodeTypeAlias"); + var filter = criteria.Field("nodeTypeAlias", "image").WithFacet("nodeTypeAlias"); var results = filter.Execute(); @@ -1390,7 +1381,6 @@ public void Find_Both_Media_And_Content_Facet() .Field(ExamineFieldNames.CategoryFieldName, "media") .Or() .Field(ExamineFieldNames.CategoryFieldName, "content") - .And() .WithFacet("nodeName"); var results = filter.Execute(); @@ -1426,14 +1416,13 @@ public void Sort_Result_By_Number_Field_Facet() var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content") - .WithFacet("sortOrder") - .And() - .WithFacet("parentID") - .And(); + var sc = searcher.CreateQuery("content"); var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("sortOrder", SortType.Int)); - var results1 = sc1.Execute(); + var results1 = sc1 + .WithFacet("sortOrder") + .And() + .WithFacet("parentID").Execute(); var facetResults = results1.GetFacets(); @@ -1481,13 +1470,13 @@ public void Sort_Result_By_Date_Field_Facet() var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content") + var sc = searcher.CreateQuery("content"); + //note: dates internally are stored as Long, see DateTimeType + var sc1 = sc.Field("parentID", 1143) .WithFacet("updateDate") .And() .WithFacet("parentID") - .And(); - //note: dates internally are stored as Long, see DateTimeType - var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("updateDate", SortType.Long)); + .And().OrderBy(new SortableField("updateDate", SortType.Long)); var results1 = sc1.Execute(); @@ -1532,14 +1521,13 @@ public void Sort_Result_By_Single_Field_Facet() var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content") - .WithFacet("nodeName").And(); - var sc1 = sc.Field("writerName", "administrator") + var sc = searcher.CreateQuery("content"); + var sc1 = sc.Field("writerName", "administrator").WithFacet("nodeName").And() .OrderBy(new SortableField("nodeName", SortType.String)); - sc = searcher.CreateQuery("content") - .WithFacet("nodeName").And(); + sc = searcher.CreateQuery("content"); var sc2 = sc.Field("writerName", "administrator") + .WithFacet("nodeName").And() .OrderByDescending(new SortableField("nodeName", SortType.String)); var results1 = sc1.Execute(); @@ -1586,12 +1574,12 @@ public void Sort_Result_By_Double_Fields_Facet(string fieldType, SortType sortTy var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content").WithFacet("field1").And(); - var sc1 = sc.All() + var sc = searcher.CreateQuery("content"); + var sc1 = sc.All().WithFacet("field1").And() .OrderBy(new SortableField("field1", sortType)); - sc = searcher.CreateQuery("content").WithFacet("field1").And(); - var sc2 = sc.All() + sc = searcher.CreateQuery("content"); + var sc2 = sc.All().WithFacet("field1").And() .OrderByDescending(new SortableField("field1", sortType)); var results1 = sc1.Execute(); @@ -1647,8 +1635,8 @@ public void Sort_Result_By_Multiple_Fields_Facet() var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content").WithFacet("field1").And().WithFacet("field2").And(); - var sc1 = sc.All() + var sc = searcher.CreateQuery("content"); + var sc1 = sc.All().WithFacet("field1").And().WithFacet("field2").And() .OrderByDescending(new SortableField("field2", SortType.Int)) .OrderBy(new SortableField("field1", SortType.Double)); @@ -1690,10 +1678,10 @@ public void Standard_Results_Sorted_By_Score_Facet() var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content", BooleanOperation.Or).WithFacet("bodyText").And(); + var sc = searcher.CreateQuery("content", BooleanOperation.Or); var sc1 = sc.Field("nodeName", "umbraco").Or().Field("headerText", "umbraco").Or().Field("bodyText", "umbraco"); - var results = sc1.Execute(); + var results = sc1.WithFacet("bodyText").And().Execute(); var facetResults = results.GetFacet("bodyText"); @@ -1735,7 +1723,7 @@ public void Skip_Results_Returns_Different_Results_Facet() var searcher = indexer.Searcher; //Arrange - var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().WithFacet("nodeName"); + var sc = searcher.CreateQuery("content").Field("writerName", "administrator").WithFacet("nodeName"); //Act var results = sc.Execute(); @@ -1769,7 +1757,7 @@ public void Escaping_Includes_All_Words_Facet() var searcher = indexer.Searcher; //Arrange - var sc = searcher.CreateQuery("content").Field("nodeName", "codegarden 09".Escape()).And().WithFacet("nodeName"); + var sc = searcher.CreateQuery("content").Field("nodeName", "codegarden 09".Escape()).WithFacet("nodeName"); Console.WriteLine(sc.ToString()); @@ -1808,7 +1796,7 @@ public void Grouped_And_Examiness_Facet() var searcher = indexer.Searcher; //Arrange - var criteria = searcher.CreateQuery("content").WithFacet("nodeName").And(); + var criteria = searcher.CreateQuery("content"); //get all node type aliases starting with CWS and all nodees starting with "A" var filter = criteria.GroupedAnd( @@ -1817,7 +1805,7 @@ public void Grouped_And_Examiness_Facet() //Act - var results = filter.Execute(); + var results = filter.WithFacet("nodeName").Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1848,13 +1836,13 @@ public void Examiness_Proximity_Facet() var searcher = indexer.Searcher; //Arrange - var criteria = searcher.CreateQuery("content").WithFacet("nodeName").And(); + 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)); //Act - var results = filter.Execute(); + var results = filter.WithFacet("nodeName").And().Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1900,19 +1888,19 @@ public void Float_Range_SimpleIndexSet() var searcher = indexer.Searcher; //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery().WithFacet("SomeFloat", new DoubleRange[] + var criteria1 = searcher.CreateQuery(); + var filter1 = criteria1.RangeQuery(new[] { "SomeFloat" }, 0f, 100f, true, true).WithFacet("SomeFloat", new DoubleRange[] { new DoubleRange("1", 0, true, 12, true), new DoubleRange("2", 13, true, 250, true) - }).IsFloat(true).And(); - var filter1 = criteria1.RangeQuery(new[] { "SomeFloat" }, 0f, 100f, true, true); + }).IsFloat(true); - var criteria2 = searcher.CreateQuery().WithFacet("SomeFloat", new DoubleRange[] + var criteria2 = searcher.CreateQuery(); + var filter2 = criteria2.RangeQuery(new[] { "SomeFloat" }, 101f, 200f, true, true).WithFacet("SomeFloat", new DoubleRange[] { new DoubleRange("1", 0, true, 12, true), new DoubleRange("2", 13, true, 250, true) - }).IsFloat(true).And(); - var filter2 = criteria2.RangeQuery(new[] { "SomeFloat" }, 101f, 200f, true, true); + }).IsFloat(true); //Act var results1 = filter1.Execute(); @@ -1963,11 +1951,13 @@ public void Number_Range_SimpleIndexSet_Facet() var searcher = indexer.Searcher; //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery().WithFacet("SomeNumber").MaxCount(1).And(); - var filter1 = criteria1.RangeQuery(new[] { "SomeNumber" }, 0, 100, true, true); + var criteria1 = searcher.CreateQuery(); + var filter1 = criteria1.RangeQuery(new[] { "SomeNumber" }, 0, 100, true, true) + .WithFacet("SomeNumber").MaxCount(1); - var criteria2 = searcher.CreateQuery().WithFacet("SomeNumber").MaxCount(1).And(); - var filter2 = criteria2.RangeQuery(new[] { "SomeNumber" }, 101, 200, true, true); + var criteria2 = searcher.CreateQuery(); + var filter2 = criteria2.RangeQuery(new[] { "SomeNumber" }, 101, 200, true, true) + .WithFacet("SomeNumber").MaxCount(1).And(); //Act var results1 = filter1.Execute(); @@ -2014,19 +2004,19 @@ public void Double_Range_SimpleIndexSet_Facet() var searcher = indexer.Searcher; //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery().WithFacet("SomeDouble", new DoubleRange[] + var criteria1 = searcher.CreateQuery(); + var filter1 = criteria1.RangeQuery(new[] { "SomeDouble" }, 0d, 100d, true, true).WithFacet("SomeDouble", new DoubleRange[] { new DoubleRange("1", 0, true, 100, true), new DoubleRange("2", 101, true, 200, true) - }).And(); - var filter1 = criteria1.RangeQuery(new[] { "SomeDouble" }, 0d, 100d, true, true); + }); - var criteria2 = searcher.CreateQuery("content").WithFacet("SomeDouble", new DoubleRange[] + var criteria2 = searcher.CreateQuery("content"); + var filter2 = criteria2.RangeQuery(new[] { "SomeDouble" }, 101d, 200d, true, true).WithFacet("SomeDouble", new DoubleRange[] { new DoubleRange("1", 0, true, 100, true), new DoubleRange("2", 101, true, 200, true) - }).And(); - var filter2 = criteria2.RangeQuery(new[] { "SomeDouble" }, 101d, 200d, true, true); + }); //Act var results1 = filter1.Execute(); @@ -2073,19 +2063,19 @@ public void Long_Range_SimpleIndexSet_Facet() var searcher = indexer.Searcher; //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery().WithFacet("SomeLong", new Int64Range[] + var criteria1 = searcher.CreateQuery(); + var filter1 = criteria1.RangeQuery(new[] { "SomeLong" }, 0L, 100L, true, true).WithFacet("SomeLong", new Int64Range[] { new Int64Range("1", 0L, true, 100L, true), new Int64Range("2", 101L, true, 200L, true) - }).And(); - var filter1 = criteria1.RangeQuery(new[] { "SomeLong" }, 0L, 100L, true, true); + }); - var criteria2 = searcher.CreateQuery().WithFacet("SomeLong", new Int64Range[] + var criteria2 = searcher.CreateQuery(); + var filter2 = criteria2.RangeQuery(new[] { "SomeLong" }, 101L, 200L, true, true).WithFacet("SomeLong", new Int64Range[] { new Int64Range("1", 0L, true, 100L, true), new Int64Range("2", 101L, true, 200L, true) - }).And(); - var filter2 = criteria2.RangeQuery(new[] { "SomeLong" }, 101L, 200L, true, true); + }); //Act var results1 = filter1.Execute(); @@ -2134,19 +2124,20 @@ public void Date_Range_SimpleIndexSet_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery().WithFacet("DateCreated", new Int64Range[] + var criteria = searcher.CreateQuery(); + var filter = criteria.RangeQuery(new[] { "DateCreated" }, reIndexDateTime, DateTime.Now, true, true).WithFacet("DateCreated", new Int64Range[] { new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) - }).And(); - var filter = criteria.RangeQuery(new[] { "DateCreated" }, reIndexDateTime, DateTime.Now, true, true); + }); - var criteria2 = searcher.CreateQuery().WithFacet("DateCreated", new Int64Range[] + var criteria2 = searcher.CreateQuery(); + var filter2 = criteria2.RangeQuery(new[] { "DateCreated" }, reIndexDateTime.AddDays(-1), reIndexDateTime.AddSeconds(-1), true, true) + .WithFacet("DateCreated", new Int64Range[] { new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) - }).And(); - var filter2 = criteria2.RangeQuery(new[] { "DateCreated" }, reIndexDateTime.AddDays(-1), reIndexDateTime.AddSeconds(-1), true, true); + }); ////Act var results = filter.Execute(); @@ -2187,11 +2178,13 @@ public void Fuzzy_Search_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery().WithFacet("Content").And(); - var filter = criteria.Field("Content", "think".Fuzzy(0.1F)); + var criteria = searcher.CreateQuery(); + var filter = criteria.Field("Content", "think".Fuzzy(0.1F)) + .WithFacet("Content"); - var criteria2 = searcher.CreateQuery().WithFacet("Content").And(); - var filter2 = criteria2.Field("Content", "thought".Fuzzy()); + var criteria2 = searcher.CreateQuery(); + var filter2 = criteria2.Field("Content", "thought".Fuzzy()) + .WithFacet("Content"); Console.WriteLine(filter); Console.WriteLine(filter2); @@ -2245,13 +2238,14 @@ public void Inner_Or_Query_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery().WithFacet("Type").And(); + var criteria = searcher.CreateQuery(); //Query = // +Type:type1 +(Content:world Content:something) var filter = criteria.Field("Type", "type1") - .And(query => query.Field("Content", "world").Or().Field("Content", "something"), BooleanOperation.Or); + .And(query => query.Field("Content", "world").Or().Field("Content", "something"), BooleanOperation.Or) + .WithFacet("Type"); //Act var results = filter.Execute(); @@ -2290,13 +2284,14 @@ public void Inner_And_Query_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery().WithFacet("Type").And(); + var criteria = searcher.CreateQuery(); //Query = // +Type:type1 +(+Content:world +Content:hello) var filter = criteria.Field("Type", "type1") - .And(query => query.Field("Content", "world").And().Field("Content", "hello")); + .And(query => query.Field("Content", "world").And().Field("Content", "hello")) + .WithFacet("Type"); //Act var results = filter.Execute(); @@ -2335,7 +2330,7 @@ public void Inner_Not_Query_Facet() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery().WithFacet("Type").And(); + var criteria = searcher.CreateQuery(); //Query = // +Type:type1 +(+Content:world -Content:something) @@ -2344,7 +2339,7 @@ public void Inner_Not_Query_Facet() .And(query => query.Field("Content", "world").Not().Field("Content", "something")); //Act - var results = filter.Execute(); + var results = filter.WithFacet("Type").Execute(); var facetResults = results.GetFacet("Type"); @@ -2391,7 +2386,6 @@ public void Complex_Or_Group_Nested_Query_Facet() .And(query => query.Field("Content", "world").And().Field("Content", "cruel")), // required so that the type2 query is required BooleanOperation.And) - .And() .WithFacet("Type"); Console.WriteLine(filter); @@ -2424,7 +2418,7 @@ public void Custom_Lucene_Query_With_Native_Facet() var criteria = (LuceneSearchQuery)searcher.CreateQuery(); //combine a custom lucene query with raw lucene query - var op = criteria.NativeQuery("hello:world").And().WithFacet("SomeFacet").And(); + var op = criteria.NativeQuery("hello:world").WithFacet("SomeFacet").And(); criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); @@ -2461,10 +2455,10 @@ public void Select_Field() }); var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content").WithFacet("nodeName").And(); + var sc = searcher.CreateQuery("content"); var sc1 = sc.Field("nodeName", "my name 1").SelectField("__Path"); - var results = sc1.Execute(); + var results = sc1.WithFacet("nodeName").Execute(); var facetResults = results.GetFacet("nodeName"); var expectedLoadedFields = new string[] { "__Path" }; @@ -2504,10 +2498,10 @@ public void Select_Fields_Facet() }); var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content").WithFacet("nodeName").And(); + var sc = searcher.CreateQuery("content"); var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new[] { "nodeName", "bodyText", "id", "__NodeId" })); - var results = sc1.Execute(); + var results = sc1.WithFacet("nodeName").Execute(); var facetResults = results.GetFacet("nodeName"); var expectedLoadedFields = new string[] { "nodeName", "bodyText", "id", "__NodeId" }; @@ -2548,10 +2542,10 @@ public void Select_Fields_HashSet_Facet() }); var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content").WithFacet("nodeName").And(); + var sc = searcher.CreateQuery("content"); var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new string[] { "nodeName", "bodyText" })); - var results = sc1.Execute(); + var results = sc1.WithFacet("nodeName").Execute(); var facetResults = results.GetFacet("nodeName"); var expectedLoadedFields = new string[] { "nodeName", "bodyText" }; @@ -2589,7 +2583,7 @@ public void Paging_With_Skip_Take_Facet() //Arrange - var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().WithFacet("writerName"); + var sc = searcher.CreateQuery("content").Field("writerName", "administrator").WithFacet("writerName"); int pageIndex = 0; int pageSize = 2; @@ -2660,7 +2654,7 @@ public void Given_SkipTake_Returns_ExpectedTotals_Facet(int skip, int take, int //Arrange - var sc = searcher.CreateQuery("content").Field("writerName", "administrator").And().WithFacet("nodeName"); + var sc = searcher.CreateQuery("content").Field("writerName", "administrator").WithFacet("nodeName"); //Act From e4db510ac184755fe4f5efc288a7be6a16b61702 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Tue, 13 Dec 2022 09:58:50 +0100 Subject: [PATCH 13/42] test: Fix test --- src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs index f4b2a1cde..056a35867 100644 --- a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs @@ -171,12 +171,12 @@ public void NativeQuery_Phrase_Facet() var searcher = indexer.Searcher; - var query = searcher.CreateQuery("content").NativeQuery("\"town called\"").WithFacet("bodyText"); + var query = searcher.CreateQuery("content").NativeQuery("\"town called\""); Console.WriteLine(query); Assert.AreEqual("{ Category: content, LuceneQuery: +(nodeName:\"town called\" bodyText:\"town called\") }", query.ToString()); - var results = query.Execute(); + var results = query.WithFacet("bodyText").Execute(); var facetResults = results.GetFacet("bodyText"); From f68878f11c4c985b1d70f064fcf45d2b025e30a6 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 14 Dec 2022 08:12:53 +0100 Subject: [PATCH 14/42] test: Refactor facets tests to be part of fluentApiTests --- .../Search/FacetFluentApiTests.cs | 2672 ----------------- .../Examine.Lucene/Search/FluentApiTests.cs | 2182 +++++++++++--- 2 files changed, 1714 insertions(+), 3140 deletions(-) delete mode 100644 src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs diff --git a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs deleted file mode 100644 index 056a35867..000000000 --- a/src/Examine.Test/Examine.Lucene/Search/FacetFluentApiTests.cs +++ /dev/null @@ -1,2672 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Examine.Lucene; -using Examine.Lucene.Providers; -using Examine.Lucene.Search; -using Examine.Search; -using Lucene.Net.Analysis.En; -using Lucene.Net.Analysis.Standard; -using Lucene.Net.Facet.Range; -using Lucene.Net.QueryParsers.Classic; -using Lucene.Net.Search; -using NUnit.Framework; - - - -namespace Examine.Test.Examine.Lucene.Search -{ - [TestFixture] - public class FacetFluentApiTests : ExamineBaseTest - { - - /* - * Below is a copy of the tests from FluentApiTests - * With faceted search added to ensure that faceting - * doesn't break the search when used - */ - - [Test] - public void Allow_Leading_Wildcards_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "location 1", bodyText = "Zanzibar is in Africa"}), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "location 2", bodyText = "In Canada there is a town called Sydney in Nova Scotia"}), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "location 3", bodyText = "Sydney is the capital of NSW in Australia"}) - }); - - var searcher = (BaseLuceneSearcher)indexer.Searcher; - - var query1 = searcher.CreateQuery( - "content", - BooleanOperation.And, - searcher.LuceneAnalyzer, - new LuceneSearchOptions - { - AllowLeadingWildcard = true - }).NativeQuery("*dney") - .WithFacet("nodeName"); - - Assert.Throws(() => - searcher.CreateQuery( - "content", - BooleanOperation.And, - searcher.LuceneAnalyzer, - new LuceneSearchOptions - { - AllowLeadingWildcard = false - }).NativeQuery("*dney")); - - var results1 = query1.Execute(); - - var facetResults = results1.GetFacet("nodeName"); - - Assert.AreEqual(2, results1.TotalItemCount); - Assert.AreEqual(2, facetResults.Count()); - Assert.AreEqual(1, facetResults.First().Value); - } - } - - [Test] - public void NativeQuery_Single_Word_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "location 1", bodyText = "Zanzibar is in Africa"}), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "location 2", bodyText = "In Canada there is a town called Sydney in Nova Scotia"}), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "location 3", bodyText = "Sydney is the capital of NSW in Australia"}) - }); - - var searcher = indexer.Searcher; - - var query = searcher.CreateQuery("content") - .NativeQuery("sydney") - .WithFacet("nodeName"); - - Console.WriteLine(query); - - var results = query.Execute(); - - var facetResults = results.GetFacet("nodeName"); - - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(2, facetResults.Count()); - Assert.AreEqual(1, facetResults.Last().Value); - } - } - - [Test] - public void Uppercase_Category_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "cOntent", - new { nodeName = "location 1", bodyText = "Zanzibar is in Africa"}), - ValueSet.FromObject(2.ToString(), "cOntent", - new { nodeName = "location 2", bodyText = "In Canada there is a town called Sydney in Nova Scotia"}), - ValueSet.FromObject(3.ToString(), "cOntent", - new { nodeName = "location 3", bodyText = "Sydney is the capital of NSW in Australia"}) - }); - - var searcher = indexer.Searcher; - - var query = searcher.CreateQuery("cOntent") - .All() - .WithFacet("nodeName"); - - Console.WriteLine(query); - - var results = query.Execute(); - - var facetResults = results.GetFacet("nodeName"); - - Assert.AreEqual(3, results.TotalItemCount); - Assert.AreEqual(3, facetResults.Count()); - } - } - - [Test] - public void NativeQuery_Phrase_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("bodyText", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "location 1", bodyText = "Zanzibar is in Africa"}), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "location 2", bodyText = "In Canada there is a town called Sydney in Nova Scotia"}), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "location 3", bodyText = "In Australia there is a town called Bateau Bay in NSW"}) - }); - - var searcher = indexer.Searcher; - - var query = searcher.CreateQuery("content").NativeQuery("\"town called\""); - - Console.WriteLine(query); - Assert.AreEqual("{ Category: content, LuceneQuery: +(nodeName:\"town called\" bodyText:\"town called\") }", query.ToString()); - - var results = query.WithFacet("bodyText").Execute(); - - var facetResults = results.GetFacet("bodyText"); - - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(2, facetResults.Count()); - } - } - - [Test] - public void Managed_Range_Date_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("created", FieldDefinitionTypes.FacetDateTime)))) - { - - - indexer.IndexItems(new[] - { - ValueSet.FromObject(123.ToString(), "content", - new - { - created = new DateTime(2000, 01, 02), - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Home" - }), - ValueSet.FromObject(2123.ToString(), "content", - new - { - created = new DateTime(2000, 01, 04), - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Test" - }), - ValueSet.FromObject(3123.ToString(), "content", - new - { - created = new DateTime(2000, 01, 05), - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Page" - }) - }); - - - var searcher = indexer.Searcher; - - var numberSortedCriteria = searcher.CreateQuery() - .RangeQuery(new[] { "created" }, new DateTime(2000, 01, 02), new DateTime(2000, 01, 05)) - .WithFacet("created", new Int64Range[] - { - new Int64Range("First days", new DateTime(2000, 01, 01).Ticks, true, new DateTime(2000, 01, 03).Ticks, true), - new Int64Range("Last days", new DateTime(2000, 01, 04).Ticks, true, new DateTime(2000, 01, 06).Ticks, true) - }); - - var numberSortedResult = numberSortedCriteria.Execute(); - - var facetResult = numberSortedResult.GetFacet("created"); - - Assert.AreEqual(3, numberSortedResult.TotalItemCount); - Assert.AreEqual(2, facetResult.Count()); - Assert.AreEqual(1, facetResult.First().Value); - Assert.AreEqual("First days", facetResult.First().Label); - Assert.AreEqual(2, facetResult.Last().Value); - Assert.AreEqual("Last days", facetResult.Last().Label); - } - } - - [Test] - public void Managed_Full_Text_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - - using (var luceneDir1 = new RandomIdRAMDirectory()) - using (var indexer1 = GetTestIndex( - luceneDir1, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("item1", FieldDefinitionTypes.FacetFullText)))) - { - indexer1.IndexItem(ValueSet.FromObject("1", "content", new { item1 = "value1", item2 = "The agitated zebras gallop back and forth in short, panicky dashes, then skitter off into the total absolute darkness." })); - indexer1.IndexItem(ValueSet.FromObject("2", "content", new { item1 = "value2", item2 = "The festival lasts five days and celebrates the victory of good over evil, light over darkness, and knowledge over ignorance." })); - indexer1.IndexItem(ValueSet.FromObject("3", "content", new { item1 = "value3", item2 = "They are expected to confront the darkness and show evidence that they have done so in their papers" })); - indexer1.IndexItem(ValueSet.FromObject("4", "content", new { item1 = "value4", item2 = "Scientists believe the lake could be home to cold-loving microbial life adapted to living in total darkness." })); - indexer1.IndexItem(ValueSet.FromObject("5", "content", new { item1 = "value3", item2 = "Scotch scotch scotch, i love scotch" })); - indexer1.IndexItem(ValueSet.FromObject("6", "content", new { item1 = "value4", item2 = "60% of the time, it works everytime" })); - indexer1.IndexItem(ValueSet.FromObject("7", "content", new { SomeField = "value5", AnotherField = "another value" })); - - var searcher = indexer1.Searcher; - - var result = searcher.CreateQuery() - .ManagedQuery("darkness") - .WithFacet("item1") - .Execute(); - - var facetResults = result.GetFacet("item1"); - - Assert.AreEqual(4, result.TotalItemCount); - Assert.AreEqual(4, facetResults.Count()); - - Console.WriteLine("Search 1:"); - foreach (var r in result) - { - Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); - } - - result = searcher.CreateQuery() - .ManagedQuery("total darkness") - .WithFacet("item1") - .Execute(); - facetResults = result.GetFacet("item1"); - - Assert.AreEqual(2, result.TotalItemCount); - Assert.AreEqual(2, facetResults.Count()); - Console.WriteLine("Search 2:"); - foreach (var r in result) - { - Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); - } - } - } - - [Test] - public void Managed_Full_Text_With_Bool_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - - using (var luceneDir1 = new RandomIdRAMDirectory()) - using (var indexer1 = GetTestIndex( - luceneDir1, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("item1", FieldDefinitionTypes.FacetFullText)))) - { - indexer1.IndexItem(ValueSet.FromObject("1", "content", new { item1 = "value1", item2 = "The agitated zebras gallop back and forth in short, panicky dashes, then skitter off into the total absolute darkness." })); - indexer1.IndexItem(ValueSet.FromObject("2", "content", new { item1 = "value2", item2 = "The festival lasts five days and celebrates the victory of good over evil, light over darkness, and knowledge over ignorance." })); - indexer1.IndexItem(ValueSet.FromObject("3", "content", new { item1 = "value3", item2 = "They are expected to confront the darkness and show evidence that they have done so in their papers" })); - indexer1.IndexItem(ValueSet.FromObject("4", "content", new { item1 = "value4", item2 = "Scientists believe the lake could be home to cold-loving microbial life adapted to living in total darkness." })); - indexer1.IndexItem(ValueSet.FromObject("5", "content", new { item1 = "value3", item2 = "Scotch scotch scotch, i love scotch" })); - indexer1.IndexItem(ValueSet.FromObject("6", "content", new { item1 = "value4", item2 = "60% of the time, it works everytime" })); - - var searcher = indexer1.Searcher; - - var qry = searcher.CreateQuery().ManagedQuery("darkness").And().Field("item1", "value1").WithFacet("item1"); - Console.WriteLine(qry); - var result = qry.Execute(); - - var facetResults = result.GetFacet("item1"); - - Assert.AreEqual(1, result.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - Console.WriteLine("Search 1:"); - foreach (var r in result) - { - Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); - } - - qry = searcher.CreateQuery().ManagedQuery("darkness") - .And(query => query.Field("item1", "value1").Or().Field("item1", "value2"), BooleanOperation.Or) - .WithFacet("item1"); - Console.WriteLine(qry); - result = qry.Execute(); - - facetResults = result.GetFacet("item1"); - - Assert.AreEqual(2, result.TotalItemCount); - Assert.AreEqual(2, facetResults.Count()); - Console.WriteLine("Search 2:"); - foreach (var r in result) - { - Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); - } - } - } - - [Test] - public void Not_Managed_Full_Text_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - - using (var luceneDir1 = new RandomIdRAMDirectory()) - using (var indexer1 = GetTestIndex( - luceneDir1, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("item1", FieldDefinitionTypes.FacetFullText)) - )) - { - indexer1.IndexItem(ValueSet.FromObject("1", "content", new { item1 = "value1", item2 = "The agitated zebras gallop back and forth in short, panicky dashes, then skitter off into the total absolute chaos." })); - indexer1.IndexItem(ValueSet.FromObject("2", "content", new { item1 = "value1", item2 = "The festival lasts five days and celebrates the victory of good over evil, light over darkness, and knowledge over ignorance." })); - indexer1.IndexItem(ValueSet.FromObject("3", "content", new { item1 = "value3", item2 = "They are expected to confront the darkness and show evidence that they have done so in their papers" })); - indexer1.IndexItem(ValueSet.FromObject("4", "content", new { item1 = "value4", item2 = "Scientists believe the lake could be home to cold-loving microbial life adapted to living in total darkness." })); - indexer1.IndexItem(ValueSet.FromObject("5", "content", new { item1 = "value3", item2 = "Scotch scotch scotch, i love scotch" })); - indexer1.IndexItem(ValueSet.FromObject("6", "content", new { item1 = "value4", item2 = "60% of the time, it works everytime" })); - - var searcher = indexer1.Searcher; - - var qry = searcher.CreateQuery() - .Field("item1", "value1") - .Not().ManagedQuery("darkness") - .WithFacet("item1"); - - Console.WriteLine(qry); - var result = qry.Execute(); - - var facetResults = result.GetFacet("item1"); - - Assert.AreEqual(1, result.TotalItemCount); - Assert.AreEqual("1", result.ElementAt(0).Id); - Assert.AreEqual(1, facetResults.Count()); - - Console.WriteLine("Search 1:"); - foreach (var r in result) - { - Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); - } - } - } - - [Test] - public void Managed_Range_Int_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)))) - { - - - indexer.IndexItems(new[] - { - ValueSet.FromObject(123.ToString(), "content", - new - { - parentID = 121, - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Home" - }), - ValueSet.FromObject(2.ToString(), "content", - new - { - parentID = 123, - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Test" - }), - ValueSet.FromObject(3.ToString(), "content", - new - { - parentID = 124, - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Page" - }) - }); - - var searcher = indexer.Searcher; - - var numberSortedCriteria = searcher.CreateQuery() - .RangeQuery(new[] { "parentID" }, 122, 124) - .WithFacet("parentID", new Int64Range[] - { - new Int64Range("120-122", 120, true, 122, true), - new Int64Range("123-125", 123, true, 125, true) - }); - - var numberSortedResult = numberSortedCriteria.Execute(); - - var facetResults = numberSortedResult.GetFacet("parentID"); - - Assert.AreEqual(2, numberSortedResult.TotalItemCount); - Assert.AreEqual(2, facetResults.Count()); - Assert.AreEqual(0, facetResults.First(result => result.Label == "120-122").Value); - Assert.AreEqual(2, facetResults.First(result => result.Label == "123-125").Value); - } - } - - [Test] - public void Legacy_ParentId_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)))) - { - - - indexer.IndexItems(new[] - { - ValueSet.FromObject(123.ToString(), "content", - new - { - nodeName = "my name 1", - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Home" - }), - ValueSet.FromObject(2.ToString(), "content", - new - { - parentID = 123, - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Test" - }), - ValueSet.FromObject(3.ToString(), "content", - new - { - parentID = 123, - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Page" - }) - }); - - var searcher = indexer.Searcher; - - var numberSortedCriteria = searcher.CreateQuery() - .Field("parentID", 123) - .WithFacet("parentID") - .And() - .OrderBy(new SortableField("sortOrder", SortType.Int)); - - var numberSortedResult = numberSortedCriteria.Execute(); - - var facetResults = numberSortedResult.GetFacet("parentID"); - - Assert.AreEqual(2, numberSortedResult.TotalItemCount); - } - - - } - - [Test] - public void Grouped_Or_Examiness_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] - { - ValueSet.FromObject(1.ToString(), "content", - new - { - nodeName = "my name 1", - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Home" - }), - ValueSet.FromObject(2.ToString(), "content", - new - { - nodeName = "About us", - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Test" - }), - ValueSet.FromObject(3.ToString(), "content", - new - { - nodeName = "my name 3", - bodyText = "lorem ipsum", - nodeTypeAlias = "CWS_Page" - }) - }); - - var searcher = indexer.Searcher; - - //paths contain punctuation, we'll escape it and ensure an exact match - var criteria = searcher.CreateQuery("content"); - - //get all node type aliases starting with CWS_Home OR and all nodees starting with "About" - var filter = criteria.GroupedOr( - new[] { "nodeTypeAlias", "nodeName" }, - new[] { "CWS_Home".Boost(10), "About".MultipleCharacterWildcard() }); - - Console.WriteLine(filter); - - var results = filter.WithFacet("nodeTypeAlias").Execute(); - - var facetResults = results.GetFacet("nodeTypeAlias"); - - foreach (var r in results) - { - Console.WriteLine($"Id = {r.Id}"); - } - - Assert.AreEqual(2, results.TotalItemCount); - - Assert.AreEqual(2, facetResults.Count()); - - } - } - - [Test] - public void Grouped_Or_Query_Output_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) - - { - var searcher = indexer.Searcher; - - Console.WriteLine("GROUPED OR - SINGLE FIELD, MULTI VAL"); - var criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3)", criteria.Query.ToString()); - - Console.WriteLine("GROUPED OR - MULTI FIELD, MULTI VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3)", criteria.Query.ToString()); - - Console.WriteLine("GROUPED OR - MULTI FIELD, EQUAL MULTI VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3 blahID:1 blahID:2 blahID:3)", criteria.Query.ToString()); - - Console.WriteLine("GROUPED OR - MULTI FIELD, SINGLE VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 parentID:1)", criteria.Query.ToString()); - - Console.WriteLine("GROUPED OR - SINGLE FIELD, SINGLE VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1)", criteria.Query.ToString()); - - } - - - } - - [Test] - public void Grouped_And_Query_Output_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) - - { - var searcher = indexer.Searcher; - //new LuceneSearcher("testSearcher", luceneDir, analyzer); - - Console.WriteLine("GROUPED AND - SINGLE FIELD, MULTI VAL"); - var criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", "SomeValue"); - Console.WriteLine(criteria.Query); - //We used to assert this, but it must be allowed to do an add on the same field multiple times - //Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +id:2 +id:3)", criteria.Query.ToString()); - - Console.WriteLine("GROUPED AND - MULTI FIELD, EQUAL MULTI VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", "SomeValue"); - Console.WriteLine(criteria.Query); - //The field/value array lengths are equal so we will match the key/value pairs - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2 +blahID:3)", criteria.Query.ToString()); - - Console.WriteLine("GROUPED AND - MULTI FIELD, MULTI VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", "SomeValue"); - Console.WriteLine(criteria.Query); - //There are more than one field and there are more values than fields, in this case we align the key/value pairs - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2)", criteria.Query.ToString()); - - Console.WriteLine("GROUPED AND - MULTI FIELD, SINGLE VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet", "SomeValue"); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:1)", criteria.Query.ToString()); - - Console.WriteLine("GROUPED AND - SINGLE FIELD, SINGLE VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet", "SomeValue"); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); - } - } - - /// - /// CANNOT BE A MUST WITH NOT i.e. +(-id:1 -id:2 -id:3) --> That will not work with the "+" - /// - [Test] - public void Grouped_Not_Query_Output_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) - - { - var searcher = indexer.Searcher; - - Console.WriteLine("GROUPED NOT - SINGLE FIELD, MULTI VAL"); - var criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3", criteria.Query.ToString()); - - Console.WriteLine("GROUPED NOT - MULTI FIELD, MULTI VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3", criteria.Query.ToString()); - - Console.WriteLine("GROUPED NOT - MULTI FIELD, EQUAL MULTI VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3 -blahID:1 -blahID:2 -blahID:3", criteria.Query.ToString()); - - Console.WriteLine("GROUPED NOT - MULTI FIELD, SINGLE VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -parentID:1", criteria.Query.ToString()); - - Console.WriteLine("GROUPED NOT - SINGLE FIELD, SINGLE VAL"); - criteria = (LuceneSearchQuery)searcher.CreateQuery(); - criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet", new Int64Range[] { new Int64Range("Label", 0, true, 120, true) }); - Console.WriteLine(criteria.Query); - Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1", criteria.Query.ToString()); - } - } - - [Test] - public void Grouped_Not_Single_Field_Single_Value_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "lorem ficus", headerText = "header 2", umbracoNaviHide = "0" }) - }); - - var searcher = indexer.Searcher; - - var query = (LuceneSearchQuery)searcher.CreateQuery("content"); - query.GroupedNot(new[] { "umbracoNaviHide" }, 1.ToString()).WithFacet("nodeName"); - Console.WriteLine(query.Query); - var results = query.Execute(); - - var facetResults = results.GetFacet("nodeName"); - - Assert.AreEqual(1, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - } - } - - [Test] - public void Grouped_Not_Multi_Field_Single_Value_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", bodyText = "lorem ficus", show = "1", umbracoNaviHide = "1" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "lorem ficus", show = "2", umbracoNaviHide = "0" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "my name 3", bodyText = "lorem ficus", show = "1", umbracoNaviHide = "0" }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "my name 4", bodyText = "lorem ficus", show = "0", umbracoNaviHide = "1" }) - }); - - var searcher = indexer.Searcher; - - var query = searcher.CreateQuery("content").GroupedNot(new[] { "umbracoNaviHide", "show" }, 1.ToString()).WithFacet("nodeName"); - Console.WriteLine(query); - var results = query.Execute(); - - var facetResults = results.GetFacet("nodeName"); - - Assert.AreEqual(1, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - Assert.AreEqual(1, facetResults.Facet("my name 2").Value); - } - } - - [Test] - public void Grouped_Or_With_Not_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - - //TODO: Making this a number makes the query fail - i wonder how to make it work correctly? - // It's because the searching is NOT using a managed search - //new[] { new FieldDefinition("umbracoNaviHide", FieldDefinitionTypes.Integer) }, - - luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("headerText", FieldDefinitionTypes.FacetFullText)))) - { - - - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", bodyText = "lorem ipsum", headerText = "header 1", umbracoNaviHide = "1" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) - }); - - var searcher = indexer.Searcher; - - //paths contain punctuation, we'll escape it and ensure an exact match - var criteria = searcher.CreateQuery("content"); - var filter = criteria.GroupedOr(new[] { "nodeName", "bodyText", "headerText" }, "ipsum").Not().Field("umbracoNaviHide", "1").WithFacet("headerText"); - var results = filter.Execute(); - - var facetResults = results.GetFacet("headerText"); - - Assert.AreEqual(1, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Facet("header 2").Value); - } - } - - [Test] - public void And_Grouped_Not_Single_Value_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) - }); - - var searcher = indexer.Searcher; - - var query = searcher.CreateQuery("content") - .Field("nodeName", "name") - .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) - .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString() }) - .WithFacet("nodeName"); - - Console.WriteLine(query); - var results = query.Execute(); - - var facetResults = results.GetFacet("nodeName"); - - Assert.AreEqual(1, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - } - } - - [Test] - public void And_Grouped_Not_Multi_Value_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) - }); - - var searcher = indexer.Searcher; - - var query = searcher.CreateQuery("content") - .Field("nodeName", "name") - .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) - .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString(), 2.ToString() }) - .WithFacet("nodeName"); - - Console.WriteLine(query); - var results = query.Execute(); - var facetResults = results.GetFacet("nodeName"); - - Assert.AreEqual(1, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - } - } - - [Test] - public void And_Not_Single_Field_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) - }); - - var searcher = indexer.Searcher; - - var query = searcher.CreateQuery("content") - .Field("nodeName", "name") - .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) - .Not().Field("umbracoNaviHide", 1.ToString()) - .WithFacet("nodeName"); - - Console.WriteLine(query); - var results = query.Execute(); - - var facetResults = results.GetFacets(); - - Assert.AreEqual(1, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - Assert.AreEqual(1, facetResults.First().Count()); - } - } - - [Test] - public void AndNot_Nested_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) - }); - - var searcher = indexer.Searcher; - - var query = searcher.CreateQuery("content") - .Field("nodeName", "name") - .And().GroupedOr(new[] { "bodyText" }, new[] { "ficus", "ipsum" }) - .AndNot(x => x.Field("umbracoNaviHide", 1.ToString())) - .WithFacet("nodeName"); - - // TODO: This results in { Category: content, LuceneQuery: +nodeName:name +(bodyText:ficus bodyText:ipsum) -(+umbracoNaviHide:1) } - // Which I don't think is right with the -(+ syntax but it still seems to work. - - Console.WriteLine(query); - var results = query.Execute(); - - var facetResults = results.GetFacet("nodeName"); - Assert.AreEqual(1, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - } - } - - [Test] - public void And_Not_Added_Later_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", umbracoNaviHide = "1" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", umbracoNaviHide = "0" }) - }); - - var searcher = indexer.Searcher; - - var query = searcher.CreateQuery("content") - .Field("nodeName", "name"); - - query = query - .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString(), 2.ToString() }); - - // Results in { Category: content, LuceneQuery: +nodeName:name -umbracoNaviHide:1 -umbracoNaviHide:2 } - - Console.WriteLine(query); - var results = query - .WithFacet("nodeName").Execute(); - - var facetResults = results.GetFacet("nodeName"); - Assert.AreEqual(1, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - } - } - - [Test] - public void Not_Range_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("start", FieldDefinitionTypes.FacetInteger)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", bodyText = "lorem ficus", headerText = "header 1", start = 100 }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "lorem ipsum", headerText = "header 2", start = 200 }) - }); - - var searcher = indexer.Searcher; - - var query = searcher.CreateQuery("content") - .Field("nodeName", "name") - .Not().Field("start", 200) - .WithFacet("start", new Int64Range[] { new Int64Range("Label", 100, false, 200, false) }); - - Console.WriteLine(query); - var results = query.Execute(); - - var facetResults = results.GetFacet("start"); - Assert.AreEqual(1, results.TotalItemCount); - Assert.AreEqual(results.First().Id, 1.ToString()); - Assert.AreEqual(0, facetResults.Facet("Label").Value); - } - } - - [Test] - public void Match_By_Path_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("__Path", "raw"), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - - - indexer.IndexItems(new[] { - new ValueSet(1.ToString(), "content", - new Dictionary - { - {"nodeName", "my name 1"}, - {"bodyText", "lorem ipsum"}, - {"__Path", "-1,123,456,789"} - }), - new ValueSet(2.ToString(), "content", - new Dictionary - { - {"nodeName", "my name 2"}, - {"bodyText", "lorem ipsum"}, - {"__Path", "-1,123,456,987"} - }) - }); - - - - var searcher = indexer.Searcher; - - //paths contain punctuation, we'll escape it and ensure an exact match - var criteria = searcher.CreateQuery("content"); - var filter = criteria.Field("__Path", "-1,123,456,789").WithFacet("nodeName"); - var results1 = filter.Execute(); - var facetResults1 = results1.GetFacet("nodeName"); - Assert.AreEqual(1, results1.TotalItemCount); - Assert.AreEqual(1, facetResults1.Count()); - - //now escape it - var exactcriteria = searcher.CreateQuery("content"); - var exactfilter = exactcriteria.Field("__Path", "-1,123,456,789".Escape()).WithFacet("nodeName"); - var results2 = exactfilter.Execute(); - var facetResults2 = results2.GetFacet("nodeName"); - Assert.AreEqual(1, results2.TotalItemCount); - Assert.AreEqual(1, facetResults2.Count()); - - //now try with native - var nativeCriteria = searcher.CreateQuery(); - var nativeFilter = nativeCriteria.NativeQuery("__Path:\\-1,123,456,789").WithFacet("nodeName"); - Console.WriteLine(nativeFilter); - var results5 = nativeFilter.Execute(); - var facetResults5 = results5.GetFacet("nodeName"); - Assert.AreEqual(1, results5.TotalItemCount); - Assert.AreEqual(1, facetResults5.Count()); - - //now try wildcards - var wildcardcriteria = searcher.CreateQuery("content"); - var wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,456,".MultipleCharacterWildcard()).WithFacet("nodeName"); - var results3 = wildcardfilter.Execute(); - var facetResults3 = results3.GetFacet("nodeName"); - Assert.AreEqual(2, results3.TotalItemCount); - Assert.AreEqual(2, facetResults3.Count()); - //not found - wildcardcriteria = searcher.CreateQuery("content"); - wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,457,".MultipleCharacterWildcard()).WithFacet("nodeName"); - results3 = wildcardfilter.Execute(); - facetResults3 = results3.GetFacet("nodeName"); - Assert.AreEqual(0, results3.TotalItemCount); - Assert.AreEqual(0, facetResults3?.Count() ?? 0); - } - - - } - - [Test] - public void Find_By_ParentId_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", bodyText = "lorem ipsum", parentID = "1235" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "lorem ipsum", parentID = "1139" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "my name 3", bodyText = "lorem ipsum", parentID = "1139" }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery("content"); - var filter = criteria.Field("parentID", 1139).WithFacet("nodeName"); - - var results = filter.Execute(); - - var facetResults = results.GetFacet("nodeName"); - - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(2, facetResults.Count()); - } - } - - [Test] - public void Find_By_ParentId_Native_Query_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", bodyText = "lorem ipsum", parentID = "1235" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "lorem ipsum", parentID = "1139" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "my name 3", bodyText = "lorem ipsum", parentID = "1139" }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery("content"); - - //NOTE: This will not work :/ - // It seems that this answer is along the lines of why: https://stackoverflow.com/questions/45516870/apache-lucene-6-queryparser-range-query-is-not-working-with-intpoint - // because the field is numeric, this range query will generate a TermRangeQuery which isn't compatible with numerics and what is annoying - // is the query parser docs uses a numerical figure as examples: https://lucene.apache.org/core/2_9_4/queryparsersyntax.html#Range%20Searches - // BUT looking closely, those numeric figures are actually dates stored in a specific way that this will work. - var filter = criteria.NativeQuery("parentID:[1139 TO 1139]"); - - //This thread says we could potentially make this work by overriding the query parser: https://stackoverflow.com/questions/5026185/how-do-i-make-the-queryparser-in-lucene-handle-numeric-ranges - - //We can use a Lucene query directly instead: - //((LuceneSearchQuery)criteria).LuceneQuery(NumericRangeQuery) - - var results = filter.WithFacet("parentID").Execute(); - - var facetResults = results.GetFacet("parentID"); - - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - Assert.AreEqual(2, facetResults.Facet("1139").Value); - } - } - - [Test] - public void Find_By_NodeTypeAlias_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", "raw"), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - - - indexer.IndexItems(new[] { - new ValueSet(1.ToString(), "content", - new Dictionary - { - {"nodeName", "my name 1"}, - {"nodeTypeAlias", "CWS_Home"} - //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Home"} - }), - new ValueSet(2.ToString(), "content", - new Dictionary - { - {"nodeName", "my name 2"}, - {"nodeTypeAlias", "CWS_Home"} - //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Home"} - }), - new ValueSet(3.ToString(), "content", - new Dictionary - { - {"nodeName", "my name 3"}, - {"nodeTypeAlias", "CWS_Page"} - //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Page"} - }) - }); - - - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery("content"); - var filter = criteria.Field("nodeTypeAlias", "CWS_Home".Escape()).WithFacet("nodeName"); - - var results = filter.Execute(); - - var facetResults = results.GetFacet("nodeName"); - - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(2, facetResults.Count()); - } - } - - [Test] - public void Search_With_Stop_Words_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - - - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "into 1", bodyText = "It was one thing to bring Carmen into it, but Jonathan was another story." }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", bodyText = "Hands shoved backwards into his back pockets, he took slow deliberate steps, as if he had something on his mind." }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "my name 3", bodyText = "Slowly carrying the full cups into the living room, she handed one to Alex." }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery(); - - // TODO: This isn't testing correctly because the search parser is actually removing stop words to generate the search so we actually - // end up with an empty search and then by fluke this test passes. - - var filter = criteria.Field("bodyText", "into") - .Or().Field("nodeName", "into") - .WithFacet("nodeName"); - - Console.WriteLine(filter); - - var results = filter.Execute(); - - var facetResults = results.GetFacet("nodeName"); - - Assert.AreEqual(0, results.TotalItemCount); - Assert.AreEqual(0, facetResults?.Count() ?? 0); - } - } - - [Test] - public void Search_Native_Query_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", FieldDefinitionTypes.FacetFullText)))) - - - { - - - indexer.IndexItems(new[] { - new ValueSet(1.ToString(), "content", - new Dictionary - { - {"nodeName", "my name 1"}, - {"nodeTypeAlias", "CWS_Home"} - //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Home"} - }), - new ValueSet(2.ToString(), "content", - new Dictionary - { - {"nodeName", "my name 2"}, - {"nodeTypeAlias", "CWS_Home"} - //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Home"} - }), - new ValueSet(3.ToString(), "content", - new Dictionary - { - {"nodeName", "my name 3"}, - {"nodeTypeAlias", "CWS_Page"} - //{UmbracoContentIndexer.NodeTypeAliasFieldName, "CWS_Page"} - }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery("content"); - - var results = criteria.NativeQuery("nodeTypeAlias:CWS_Home").WithFacet("nodeTypeAlias").Execute(); - - var facetResults = results.GetFacet("nodeTypeAlias"); - - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - Assert.AreEqual(2, facetResults.Facet("CWS_Home").Value); - } - - } - - - [Test] - public void Find_Only_Image_Media_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", FieldDefinitionTypes.FacetFullText)))) - - - { - - - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "media", - new { nodeName = "my name 1", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), - ValueSet.FromObject(2.ToString(), "media", - new { nodeName = "my name 2", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), - ValueSet.FromObject(3.ToString(), "media", - new { nodeName = "my name 3", bodyText = "lorem ipsum", nodeTypeAlias = "file" }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery("media"); - var filter = criteria.Field("nodeTypeAlias", "image").WithFacet("nodeTypeAlias"); - - var results = filter.Execute(); - - var facetResults = results.GetFacet("nodeTypeAlias"); - - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - Assert.AreEqual(2, facetResults.Facet("image").Value); - } - } - - [Test] - public void Find_Both_Media_And_Content_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "media", - new { nodeName = "my name 1", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), - ValueSet.FromObject(2.ToString(), "media", - new { nodeName = "my name 2", bodyText = "lorem ipsum", nodeTypeAlias = "image" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "my name 3", bodyText = "lorem ipsum", nodeTypeAlias = "file" }), - ValueSet.FromObject(4.ToString(), "other", - new { nodeName = "my name 4", bodyText = "lorem ipsum", nodeTypeAlias = "file" }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery(defaultOperation: BooleanOperation.Or); - var filter = criteria - .Field(ExamineFieldNames.CategoryFieldName, "media") - .Or() - .Field(ExamineFieldNames.CategoryFieldName, "content") - .WithFacet("nodeName"); - - var results = filter.Execute(); - - var facetResults = results.GetFacet("nodeName"); - - Assert.AreEqual(3, results.TotalItemCount); - Assert.AreEqual(3, facetResults.Count()); - } - } - - [Test] - public void Sort_Result_By_Number_Field_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - //Ensure it's set to a number, otherwise it's not sortable - new FieldDefinitionCollection(new FieldDefinition("sortOrder", FieldDefinitionTypes.FacetInteger), new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", sortOrder = "3", parentID = "1143" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", sortOrder = "1", parentID = "1143" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "my name 3", sortOrder = "2", parentID = "1143" }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "my name 4", bodyText = "lorem ipsum", parentID = "2222" }) - }); - - var searcher = indexer.Searcher; - - var sc = searcher.CreateQuery("content"); - var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("sortOrder", SortType.Int)); - - var results1 = sc1 - .WithFacet("sortOrder") - .And() - .WithFacet("parentID").Execute(); - - var facetResults = results1.GetFacets(); - - Assert.AreEqual(3, results1.Count()); - Assert.AreEqual(2, facetResults.Count()); - Assert.AreEqual(3, facetResults.First().Count()); - Assert.AreEqual(1, facetResults.Last().Count()); - Assert.AreEqual(3, facetResults.Last().Facet("1143").Value); - - var results2 = results1.ToArray(); - var currSort = 0; - for (var i = 0; i < results2.Length; i++) - { - Assert.GreaterOrEqual(int.Parse(results2[i].Values["sortOrder"]), currSort); - currSort = int.Parse(results2[i].Values["sortOrder"]); - } - } - } - - [Test] - public void Sort_Result_By_Date_Field_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - //Ensure it's set to a date, otherwise it's not sortable - new FieldDefinitionCollection(new FieldDefinition("updateDate", FieldDefinitionTypes.FacetDateTime), new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)))) - { - - - var now = DateTime.Now; - - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", updateDate = now.AddDays(2).ToString("yyyy-MM-dd"), parentID = "1143" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", updateDate = now.ToString("yyyy-MM-dd"), parentID = 1143 }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "my name 3", updateDate = now.AddDays(1).ToString("yyyy-MM-dd"), parentID = 1143 }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "my name 4", updateDate = now, parentID = "2222" }) - }); - - var searcher = indexer.Searcher; - - var sc = searcher.CreateQuery("content"); - //note: dates internally are stored as Long, see DateTimeType - var sc1 = sc.Field("parentID", 1143) - .WithFacet("updateDate") - .And() - .WithFacet("parentID") - .And().OrderBy(new SortableField("updateDate", SortType.Long)); - - var results1 = sc1.Execute(); - - var facetResults = results1.GetFacet("updateDate"); - var facetReuslts2 = results1.GetFacet("parentID"); - - Assert.AreEqual(3, results1.Count()); - Assert.AreEqual(3, facetResults.Count()); - Assert.AreEqual(1, facetReuslts2.Count()); - - var results2 = results1.ToArray(); - double currSort = 0; - for (var i = 0; i < results2.Length; i++) - { - Assert.GreaterOrEqual(double.Parse(results2[i].Values["updateDate"]), currSort); - currSort = double.Parse(results2[i].Values["updateDate"]); - } - } - } - - [Test] - public void Sort_Result_By_Single_Field_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - //Ensure it's set to a fulltextsortable, otherwise it's not sortable - new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullTextSortable)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "my name 1", writerName = "administrator", parentID = "1143" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "my name 2", writerName = "administrator", parentID = "1143" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "my name 3", writerName = "administrator", parentID = "1143" }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "my name 4", writerName = "writer", parentID = "2222" }) - }); - - var searcher = indexer.Searcher; - - var sc = searcher.CreateQuery("content"); - var sc1 = sc.Field("writerName", "administrator").WithFacet("nodeName").And() - .OrderBy(new SortableField("nodeName", SortType.String)); - - sc = searcher.CreateQuery("content"); - var sc2 = sc.Field("writerName", "administrator") - .WithFacet("nodeName").And() - .OrderByDescending(new SortableField("nodeName", SortType.String)); - - var results1 = sc1.Execute(); - var results2 = sc2.Execute(); - - var facetResults1 = results1.GetFacet("nodeName"); - var facetResults2 = results2.GetFacet("nodeName"); - - Assert.AreNotEqual(results1.First().Id, results2.First().Id); - - Assert.AreEqual(3, facetResults1.Count()); - Assert.AreEqual(3, facetResults2.Count()); - } - - - } - - [TestCase(FieldDefinitionTypes.FacetDouble, SortType.Double)] - [TestCase(FieldDefinitionTypes.FacetFullText, SortType.Double)] - [TestCase(FieldDefinitionTypes.FacetFullText, SortType.String)] - [TestCase(FieldDefinitionTypes.FacetFullTextSortable, SortType.Double)] - [TestCase(FieldDefinitionTypes.FacetFullTextSortable, SortType.String)] - public void Sort_Result_By_Double_Fields_Facet(string fieldType, SortType sortType) - { - // See: https://github.com/Shazwazza/Examine/issues/242 - - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection( - new FieldDefinition("field1", fieldType)))) - { - indexer.IndexItems(new[] - { - ValueSet.FromObject(1.ToString(), "content", new { field1 = 5.0 }), - ValueSet.FromObject(2.ToString(), "content", new { field1 = 4.9 }), - ValueSet.FromObject(3.ToString(), "content", new { field1 = 4.5 }), - ValueSet.FromObject(4.ToString(), "content", new { field1 = 3.9 }), - ValueSet.FromObject(5.ToString(), "content", new { field1 = 3.8 }), - ValueSet.FromObject(6.ToString(), "content", new { field1 = 2.6 }), - }); - - var searcher = indexer.Searcher; - - var sc = searcher.CreateQuery("content"); - var sc1 = sc.All().WithFacet("field1").And() - .OrderBy(new SortableField("field1", sortType)); - - sc = searcher.CreateQuery("content"); - var sc2 = sc.All().WithFacet("field1").And() - .OrderByDescending(new SortableField("field1", sortType)); - - var results1 = sc1.Execute(); - var results2 = sc2.Execute(); - - var facetResults1 = results1.GetFacet("field1"); - var facetResults2 = results2.GetFacet("field1"); - - var results3 = results1.ToList(); - var results4 = results2.ToList(); - - Assert.AreEqual(2.6, double.Parse(results3[0].Values["field1"])); - Assert.AreEqual(3.8, double.Parse(results3[1].Values["field1"])); - Assert.AreEqual(3.9, double.Parse(results3[2].Values["field1"])); - Assert.AreEqual(4.5, double.Parse(results3[3].Values["field1"])); - Assert.AreEqual(4.9, double.Parse(results3[4].Values["field1"])); - Assert.AreEqual(5.0, double.Parse(results3[5].Values["field1"])); - - - Assert.AreEqual(2.6, double.Parse(results4[5].Values["field1"])); - Assert.AreEqual(3.8, double.Parse(results4[4].Values["field1"])); - Assert.AreEqual(3.9, double.Parse(results4[3].Values["field1"])); - Assert.AreEqual(4.5, double.Parse(results4[2].Values["field1"])); - Assert.AreEqual(4.9, double.Parse(results4[1].Values["field1"])); - Assert.AreEqual(5.0, double.Parse(results4[0].Values["field1"])); - - Assert.AreEqual(6, facetResults1.Count()); - Assert.AreEqual(6, facetResults2.Count()); - } - } - - [Test] - public void Sort_Result_By_Multiple_Fields_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection( - new FieldDefinition("field1", FieldDefinitionTypes.FacetDouble), - new FieldDefinition("field2", FieldDefinitionTypes.FacetInteger)))) - { - indexer.IndexItems(new[] - { - ValueSet.FromObject(1.ToString(), "content", new { field1 = 5.0, field2 = 2 }), - ValueSet.FromObject(2.ToString(), "content", new { field1 = 4.9, field2 = 2 }), - ValueSet.FromObject(3.ToString(), "content", new { field1 = 4.5, field2 = 2 }), - ValueSet.FromObject(4.ToString(), "content", new { field1 = 3.9, field2 = 1 }), - ValueSet.FromObject(5.ToString(), "content", new { field1 = 3.8, field2 = 1 }), - ValueSet.FromObject(6.ToString(), "content", new { field1 = 2.6, field2 = 1 }), - }); - - var searcher = indexer.Searcher; - - var sc = searcher.CreateQuery("content"); - var sc1 = sc.All().WithFacet("field1").And().WithFacet("field2").And() - .OrderByDescending(new SortableField("field2", SortType.Int)) - .OrderBy(new SortableField("field1", SortType.Double)); - - var results1 = sc1.Execute(); - - var facetResults = results1.GetFacet("field1"); - var facetResults2 = results1.GetFacet("field2"); - - var results2 = results1.ToList(); - Assert.AreEqual("3", results2[0].Id); - Assert.AreEqual("2", results2[1].Id); - Assert.AreEqual("1", results2[2].Id); - Assert.AreEqual("6", results2[3].Id); - Assert.AreEqual("5", results2[4].Id); - Assert.AreEqual("4", results2[5].Id); - - Assert.AreEqual(6, facetResults.Count()); - Assert.AreEqual(2, facetResults2.Count()); - } - } - - [Test] - public void Standard_Results_Sorted_By_Score_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("bodyText", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "umbraco", headerText = "world", bodyText = "blah" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "umbraco", headerText = "umbraco", bodyText = "blah" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "umbraco", headerText = "umbraco", bodyText = "umbraco" }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "hello", headerText = "world", bodyText = "blah" }) - }); - - var searcher = indexer.Searcher; - - var sc = searcher.CreateQuery("content", BooleanOperation.Or); - var sc1 = sc.Field("nodeName", "umbraco").Or().Field("headerText", "umbraco").Or().Field("bodyText", "umbraco"); - - var results = sc1.WithFacet("bodyText").And().Execute(); - - var facetResults = results.GetFacet("bodyText"); - - Assert.AreEqual(2, facetResults.Count()); - - //Assert - for (int i = 0; i < results.TotalItemCount - 1; i++) - { - var curr = results.ElementAt(i); - var next = results.ElementAtOrDefault(i + 1); - - if (next == null) - break; - - Assert.IsTrue(curr.Score >= next.Score, string.Format("Result at index {0} must have a higher score than result at index {1}", i, i + 1)); - } - } - - } - - [Test] - public void Skip_Results_Returns_Different_Results_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "umbraco", headerText = "world", writerName = "administrator" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "hello", headerText = "world", writerName = "blah" }) - }); - - var searcher = indexer.Searcher; - - //Arrange - var sc = searcher.CreateQuery("content").Field("writerName", "administrator").WithFacet("nodeName"); - - //Act - var results = sc.Execute(); - - var facetResults = results.GetFacet("nodeName"); - - //Assert - Assert.AreNotEqual(results.First(), results.Skip(2).First(), "Third result should be different"); - Assert.AreEqual(1, facetResults.Count()); - } - } - - [Test] - public void Escaping_Includes_All_Words_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "codegarden09", headerText = "world", writerName = "administrator" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "codegarden 09", headerText = "umbraco", writerName = "administrator" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "codegarden 09", headerText = "umbraco", writerName = "administrator" }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "codegarden 090", headerText = "world", writerName = "blah" }) - }); - - var searcher = indexer.Searcher; - - //Arrange - var sc = searcher.CreateQuery("content").Field("nodeName", "codegarden 09".Escape()).WithFacet("nodeName"); - - Console.WriteLine(sc.ToString()); - - //Act - var results = sc.Execute(); - - var facetResults = results.GetFacet("nodeName"); - - //Assert - //NOTE: The result is 2 because the double space is removed with the analyzer - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(2, facetResults.Count()); - } - - - } - - [Test] - public void Grouped_And_Examiness_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "Aloha", nodeTypeAlias = "CWS_Hello" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "Helo", nodeTypeAlias = "CWS_World" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "Another node", nodeTypeAlias = "SomethingElse" }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "Always consider this", nodeTypeAlias = "CWS_World" }) - }); - - var searcher = indexer.Searcher; - - //Arrange - var criteria = searcher.CreateQuery("content"); - - //get all node type aliases starting with CWS and all nodees starting with "A" - var filter = criteria.GroupedAnd( - new[] { "nodeTypeAlias", "nodeName" }, - new[] { "CWS".MultipleCharacterWildcard(), "A".MultipleCharacterWildcard() }); - - - //Act - var results = filter.WithFacet("nodeName").Execute(); - - var facetResults = results.GetFacet("nodeName"); - - //Assert - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(2, facetResults.Count()); - } - } - - [Test] - public void Examiness_Proximity_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "Aloha", metaKeywords = "Warren is likely to be creative" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "Helo", metaKeywords = "Creative is Warren middle name" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "Another node", metaKeywords = "If Warren were creative... well, he actually is" }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "Always consider this", metaKeywords = "Warren is a very talented individual and quite creative" }) - }); - - var searcher = indexer.Searcher; - - //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)); - - //Act - var results = filter.WithFacet("nodeName").And().Execute(); - - var facetResults = results.GetFacet("nodeName"); - - foreach (var r in results) - { - Console.WriteLine($"Id = {r.Id}"); - } - - //Assert - Assert.AreEqual(3, results.TotalItemCount); - Assert.AreEqual(3, facetResults.Count()); - } - } - - /// - /// test range query with a Float structure - /// - [Test] - public void Float_Range_SimpleIndexSet() - { - - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - //Ensure it's set to a float - new FieldDefinitionCollection(new FieldDefinition("SomeFloat", FieldDefinitionTypes.FacetFloat)))) - { - - - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "Aloha", SomeFloat = 1 }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "Helo", SomeFloat = 123 }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "Another node", SomeFloat = 12 }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "Always consider this", SomeFloat = 25 }) - }); - - var searcher = indexer.Searcher; - - //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery(); - var filter1 = criteria1.RangeQuery(new[] { "SomeFloat" }, 0f, 100f, true, true).WithFacet("SomeFloat", new DoubleRange[] - { - new DoubleRange("1", 0, true, 12, true), - new DoubleRange("2", 13, true, 250, true) - }).IsFloat(true); - - var criteria2 = searcher.CreateQuery(); - var filter2 = criteria2.RangeQuery(new[] { "SomeFloat" }, 101f, 200f, true, true).WithFacet("SomeFloat", new DoubleRange[] - { - new DoubleRange("1", 0, true, 12, true), - new DoubleRange("2", 13, true, 250, true) - }).IsFloat(true); - - //Act - var results1 = filter1.Execute(); - var results2 = filter2.Execute(); - - var facetResults1 = results1.GetFacet("SomeFloat"); - var facetResults2 = results2.GetFacet("SomeFloat"); - - //Assert - Assert.AreEqual(3, results1.TotalItemCount); - Assert.AreEqual(1, results2.TotalItemCount); - Assert.AreEqual(2, facetResults1.Facet("1").Value); - Assert.AreEqual(1, facetResults1.Facet("2").Value); - Assert.AreEqual(0, facetResults2.Facet("1").Value); - Assert.AreEqual(1, facetResults2.Facet("2").Value); - } - - - } - - /// - /// test range query with a Number structure - /// - [Test] - public void Number_Range_SimpleIndexSet_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - //Ensure it's set to a float - new FieldDefinitionCollection(new FieldDefinition("SomeNumber", FieldDefinitionTypes.FacetInteger)))) - { - - - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "Aloha", SomeNumber = 1 }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "Helo", SomeNumber = 123 }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "Another node", SomeNumber = 12 }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "Always consider this", SomeNumber = 25 }) - }); - - var searcher = indexer.Searcher; - - //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery(); - var filter1 = criteria1.RangeQuery(new[] { "SomeNumber" }, 0, 100, true, true) - .WithFacet("SomeNumber").MaxCount(1); - - var criteria2 = searcher.CreateQuery(); - var filter2 = criteria2.RangeQuery(new[] { "SomeNumber" }, 101, 200, true, true) - .WithFacet("SomeNumber").MaxCount(1).And(); - - //Act - var results1 = filter1.Execute(); - var results2 = filter2.Execute(); - - var facetResults1 = results1.GetFacet("SomeNumber"); - var facetResults2 = results2.GetFacet("SomeNumber"); - - //Assert - Assert.AreEqual(3, results1.TotalItemCount); - Assert.AreEqual(1, results2.TotalItemCount); - Assert.AreEqual(1, facetResults1.Count()); - Assert.AreEqual(1, facetResults2.Count()); - } - } - - /// - /// test range query with a Number structure - /// - [Test] - public void Double_Range_SimpleIndexSet_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - //Ensure it's set to a float - new FieldDefinitionCollection(new FieldDefinition("SomeDouble", FieldDefinitionTypes.FacetDouble)))) - { - - - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "Aloha", SomeDouble = 1d }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "Helo", SomeDouble = 123d }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "Another node", SomeDouble = 12d }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "Always consider this", SomeDouble = 25d }) - }); - - var searcher = indexer.Searcher; - - //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery(); - var filter1 = criteria1.RangeQuery(new[] { "SomeDouble" }, 0d, 100d, true, true).WithFacet("SomeDouble", new DoubleRange[] - { - new DoubleRange("1", 0, true, 100, true), - new DoubleRange("2", 101, true, 200, true) - }); - - var criteria2 = searcher.CreateQuery("content"); - var filter2 = criteria2.RangeQuery(new[] { "SomeDouble" }, 101d, 200d, true, true).WithFacet("SomeDouble", new DoubleRange[] - { - new DoubleRange("1", 0, true, 100, true), - new DoubleRange("2", 101, true, 200, true) - }); - - //Act - var results1 = filter1.Execute(); - var results2 = filter2.Execute(); - - var facetResults1 = results1.GetFacet("SomeDouble"); - var facetResults2 = results2.GetFacet("SomeDouble"); - - //Assert - Assert.AreEqual(3, results1.TotalItemCount); - Assert.AreEqual(1, results2.TotalItemCount); - Assert.AreEqual(3, facetResults1.Facet("1").Value); - Assert.AreEqual(0, facetResults1.Facet("2").Value); - Assert.AreEqual(0, facetResults2.Facet("1").Value); - Assert.AreEqual(1, facetResults2.Facet("2").Value); - } - } - - /// - /// test range query with a Double structure - /// - [Test] - public void Long_Range_SimpleIndexSet_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - //Ensure it's set to a float - new FieldDefinitionCollection(new FieldDefinition("SomeLong", FieldDefinitionTypes.FacetLong)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "Aloha", SomeLong = 1L }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "Helo", SomeLong = 123L }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "Another node", SomeLong = 12L }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "Always consider this", SomeLong = 25L }) - }); - - var searcher = indexer.Searcher; - - //all numbers should be between 0 and 100 based on the data source - var criteria1 = searcher.CreateQuery(); - var filter1 = criteria1.RangeQuery(new[] { "SomeLong" }, 0L, 100L, true, true).WithFacet("SomeLong", new Int64Range[] - { - new Int64Range("1", 0L, true, 100L, true), - new Int64Range("2", 101L, true, 200L, true) - }); - - var criteria2 = searcher.CreateQuery(); - var filter2 = criteria2.RangeQuery(new[] { "SomeLong" }, 101L, 200L, true, true).WithFacet("SomeLong", new Int64Range[] - { - new Int64Range("1", 0L, true, 100L, true), - new Int64Range("2", 101L, true, 200L, true) - }); - - //Act - var results1 = filter1.Execute(); - var results2 = filter2.Execute(); - - var facetResults1 = results1.GetFacet("SomeLong"); - var facetResults2 = results2.GetFacet("SomeLong"); - - //Assert - Assert.AreEqual(3, results1.TotalItemCount); - Assert.AreEqual(1, results2.TotalItemCount); - Assert.AreEqual(3, facetResults1.Facet("1").Value); - Assert.AreEqual(0, facetResults1.Facet("2").Value); - Assert.AreEqual(0, facetResults2.Facet("1").Value); - Assert.AreEqual(1, facetResults2.Facet("2").Value); - } - } - - - - /// - /// Test range query with a DateTime structure - /// - [Test] - public void Date_Range_SimpleIndexSet_Facet() - { - var reIndexDateTime = DateTime.Now; - - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, - analyzer, - new FieldDefinitionCollection(new FieldDefinition("DateCreated", FieldDefinitionTypes.FacetDateTime)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { DateCreated = reIndexDateTime }), - ValueSet.FromObject(2.ToString(), "content", - new { DateCreated = reIndexDateTime }), - ValueSet.FromObject(3.ToString(), "content", - new { DateCreated = reIndexDateTime.AddMonths(-10) }), - ValueSet.FromObject(4.ToString(), "content", - new { DateCreated = reIndexDateTime }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery(); - var filter = criteria.RangeQuery(new[] { "DateCreated" }, reIndexDateTime, DateTime.Now, true, true).WithFacet("DateCreated", new Int64Range[] - { - new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), - new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) - }); - - var criteria2 = searcher.CreateQuery(); - var filter2 = criteria2.RangeQuery(new[] { "DateCreated" }, reIndexDateTime.AddDays(-1), reIndexDateTime.AddSeconds(-1), true, true) - .WithFacet("DateCreated", new Int64Range[] - { - new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), - new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) - }); - - ////Act - var results = filter.Execute(); - var results2 = filter2.Execute(); - - var facetResults1 = results.GetFacet("DateCreated"); - var facetResults2 = results2.GetFacet("DateCreated"); - - ////Assert - Assert.IsTrue(results.TotalItemCount > 0); - Assert.IsTrue(results2.TotalItemCount == 0); - Assert.AreEqual(3, facetResults1.Facet("1").Value); - Assert.AreEqual(0, facetResults1.Facet("2").Value); - Assert.AreEqual(0, facetResults2.Facet("1").Value); - Assert.AreEqual(0, facetResults2.Facet("2").Value); - } - - - } - - [Test] - public void Fuzzy_Search_Facet() - { - var analyzer = new EnglishAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("Content", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { Content = "I'm thinking here" }), - ValueSet.FromObject(2.ToString(), "content", - new { Content = "I'm a thinker" }), - ValueSet.FromObject(3.ToString(), "content", - new { Content = "I am pretty thoughtful" }), - ValueSet.FromObject(4.ToString(), "content", - new { Content = "I thought you were cool" }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery(); - var filter = criteria.Field("Content", "think".Fuzzy(0.1F)) - .WithFacet("Content"); - - var criteria2 = searcher.CreateQuery(); - var filter2 = criteria2.Field("Content", "thought".Fuzzy()) - .WithFacet("Content"); - - Console.WriteLine(filter); - Console.WriteLine(filter2); - - ////Act - var results = filter.Execute(); - var results2 = filter2.Execute(); - - var facetResults1 = results.GetFacet("Content"); - var facetResults2 = results2.GetFacet("Content"); - - foreach (var r in results) - { - Console.WriteLine($"Result Id: {r.Id}"); - } - - foreach (var r in results2) - { - Console.WriteLine($"Result2 Id: {r.Id}"); - } - - ////Assert - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(2, results2.TotalItemCount); - Assert.AreEqual(2, facetResults1.Count()); - Assert.AreEqual(2, facetResults2.Count()); - } - } - - [Test] - public void Inner_Or_Query_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)))) - - - { - - - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { Content = "hello world", Type = "type1" }), - ValueSet.FromObject(2.ToString(), "content", - new { Content = "hello something or other", Type = "type1" }), - ValueSet.FromObject(3.ToString(), "content", - new { Content = "hello you cruel world", Type = "type2" }), - ValueSet.FromObject(4.ToString(), "content", - new { Content = "hi there, hello world", Type = "type2" }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery(); - - //Query = - // +Type:type1 +(Content:world Content:something) - - var filter = criteria.Field("Type", "type1") - .And(query => query.Field("Content", "world").Or().Field("Content", "something"), BooleanOperation.Or) - .WithFacet("Type"); - - //Act - var results = filter.Execute(); - - var facetResults = results.GetFacet("Type"); - - //Assert - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - } - } - - [Test] - public void Inner_And_Query_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)))) - - - { - - - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { Content = "hello world", Type = "type1" }), - ValueSet.FromObject(2.ToString(), "content", - new { Content = "hello something or other", Type = "type1" }), - ValueSet.FromObject(3.ToString(), "content", - new { Content = "hello something or world", Type = "type1" }), - ValueSet.FromObject(4.ToString(), "content", - new { Content = "hello you cruel world", Type = "type2" }), - ValueSet.FromObject(5.ToString(), "content", - new { Content = "hi there, hello world", Type = "type2" }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery(); - - //Query = - // +Type:type1 +(+Content:world +Content:hello) - - var filter = criteria.Field("Type", "type1") - .And(query => query.Field("Content", "world").And().Field("Content", "hello")) - .WithFacet("Type"); - - //Act - var results = filter.Execute(); - - var facetResults = results.GetFacet("Type"); - - //Assert - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - } - } - - [Test] - public void Inner_Not_Query_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)))) - - - { - - - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { Content = "hello world", Type = "type1" }), - ValueSet.FromObject(2.ToString(), "content", - new { Content = "hello something or other", Type = "type1" }), - ValueSet.FromObject(3.ToString(), "content", - new { Content = "hello something or world", Type = "type1" }), - ValueSet.FromObject(4.ToString(), "content", - new { Content = "hello you cruel world", Type = "type2" }), - ValueSet.FromObject(5.ToString(), "content", - new { Content = "hi there, hello world", Type = "type2" }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery(); - - //Query = - // +Type:type1 +(+Content:world -Content:something) - - var filter = criteria.Field("Type", "type1") - .And(query => query.Field("Content", "world").Not().Field("Content", "something")); - - //Act - var results = filter.WithFacet("Type").Execute(); - - var facetResults = results.GetFacet("Type"); - - //Assert - Assert.AreEqual(1, results.TotalItemCount); - Assert.AreEqual(1, facetResults.Count()); - } - } - - [Test] - public void Complex_Or_Group_Nested_Query_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { Content = "hello world", Type = "type1" }), - ValueSet.FromObject(2.ToString(), "content", - new { Content = "hello something or other", Type = "type1" }), - ValueSet.FromObject(3.ToString(), "content", - new { Content = "hello you guys", Type = "type1" }), - ValueSet.FromObject(4.ToString(), "content", - new { Content = "hello you cruel world", Type = "type2" }), - ValueSet.FromObject(5.ToString(), "content", - new { Content = "hi there, hello world", Type = "type2" }) - }); - - var searcher = indexer.Searcher; - - var criteria = searcher.CreateQuery(defaultOperation: BooleanOperation.Or); - - //Query = - // (+Type:type1 +(Content:world Content:something)) (+Type:type2 +(+Content:world +Content:cruel)) - - var filter = criteria - .Group(group => group.Field("Type", "type1") - .And(query => query.Field("Content", "world").Or().Field("Content", "something"), BooleanOperation.Or), - // required so that the type1 query is required - BooleanOperation.And) - .Or() - .Group(group => group.Field("Type", "type2") - .And(query => query.Field("Content", "world").And().Field("Content", "cruel")), - // required so that the type2 query is required - BooleanOperation.And) - .WithFacet("Type"); - - Console.WriteLine(filter); - - //Act - var results = filter.Execute(); - - var facetResults = results.GetFacet("Type"); - - //Assert - foreach (var r in results) - { - Console.WriteLine($"Result Id: {r.Id}"); - } - Assert.AreEqual(3, results.TotalItemCount); - - Assert.AreEqual(2, facetResults.Count()); - } - } - - - [Test] - public void Custom_Lucene_Query_With_Native_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) - { - var searcher = indexer.Searcher; - var criteria = (LuceneSearchQuery)searcher.CreateQuery(); - - //combine a custom lucene query with raw lucene query - var op = criteria.NativeQuery("hello:world").WithFacet("SomeFacet").And(); - - criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); - - Console.WriteLine(criteria.Query); - Assert.AreEqual("+hello:world +numTest:[4 TO 5]", criteria.Query.ToString()); - } - } - - [Test] - public void Select_Field() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - - { - indexer.IndexItems(new[] { - new ValueSet(1.ToString(), "content", - new Dictionary - { - {"id","1" }, - {"nodeName", "my name 1"}, - {"bodyText", "lorem ipsum"}, - {"__Path", "-1,123,456,789"} - }), - new ValueSet(2.ToString(), "content", - new Dictionary - { - {"id","2" }, - {"nodeName", "my name 2"}, - {"bodyText", "lorem ipsum"}, - {"__Path", "-1,123,456,987"} - }) - }); - - var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content"); - var sc1 = sc.Field("nodeName", "my name 1").SelectField("__Path"); - - var results = sc1.WithFacet("nodeName").Execute(); - var facetResults = results.GetFacet("nodeName"); - - var expectedLoadedFields = new string[] { "__Path" }; - var keys = results.First().Values.Keys.ToArray(); - Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); - Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); - - Assert.AreEqual(2, facetResults.Count()); - } - } - - [Test] - public void Select_Fields_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - - { - indexer.IndexItems(new[] { - new ValueSet(1.ToString(), "content", - new Dictionary - { - {"id","1" }, - {"nodeName", "my name 1"}, - {"bodyText", "lorem ipsum"}, - {"__Path", "-1,123,456,789"} - }), - new ValueSet(2.ToString(), "content", - new Dictionary - { - {"id","2" }, - {"nodeName", "my name 2"}, - {"bodyText", "lorem ipsum"}, - {"__Path", "-1,123,456,987"} - }) - }); - - var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content"); - var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new[] { "nodeName", "bodyText", "id", "__NodeId" })); - - var results = sc1.WithFacet("nodeName").Execute(); - var facetResults = results.GetFacet("nodeName"); - - var expectedLoadedFields = new string[] { "nodeName", "bodyText", "id", "__NodeId" }; - var keys = results.First().Values.Keys.ToArray(); - Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); - Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); - - Assert.AreEqual(2, facetResults.Count()); - } - - } - - [Test] - public void Select_Fields_HashSet_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - - { - indexer.IndexItems(new[] { - new ValueSet(1.ToString(), "content", - new Dictionary - { - {"id","1" }, - {"nodeName", "my name 1"}, - {"bodyText", "lorem ipsum"}, - {"__Path", "-1,123,456,789"} - }), - new ValueSet(2.ToString(), "content", - new Dictionary - { - {"id","2" }, - {"nodeName", "my name 2"}, - {"bodyText", "lorem ipsum"}, - {"__Path", "-1,123,456,987"} - }) - }); - - var searcher = indexer.Searcher; - var sc = searcher.CreateQuery("content"); - var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new string[] { "nodeName", "bodyText" })); - - var results = sc1.WithFacet("nodeName").Execute(); - var facetResults = results.GetFacet("nodeName"); - - var expectedLoadedFields = new string[] { "nodeName", "bodyText" }; - var keys = results.First().Values.Keys.ToArray(); - Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); - Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); - - Assert.AreEqual(2, facetResults.Count()); - } - } - - [Test] - public void Paging_With_Skip_Take_Facet() - { - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("writerName", FieldDefinitionTypes.FacetFullText)))) - { - indexer.IndexItems(new[] { - ValueSet.FromObject(1.ToString(), "content", - new { nodeName = "umbraco", headerText = "world", writerName = "administrator" }), - ValueSet.FromObject(2.ToString(), "content", - new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - ValueSet.FromObject(3.ToString(), "content", - new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - ValueSet.FromObject(4.ToString(), "content", - new { nodeName = "hello", headerText = "world", writerName = "blah" }), - ValueSet.FromObject(5.ToString(), "content", - new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }), - ValueSet.FromObject(6.ToString(), "content", - new { nodeName = "umbraco", headerText = "umbraco", writerName = "administrator" }) - }); - - var searcher = indexer.Searcher; - - //Arrange - - var sc = searcher.CreateQuery("content").Field("writerName", "administrator").WithFacet("writerName"); - int pageIndex = 0; - int pageSize = 2; - - //Act - - var results = sc - .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); - Assert.AreEqual(2, results.Count()); - var facetResults = results.GetFacet("writerName"); - Assert.AreEqual(1, facetResults.Count()); - Assert.AreEqual(5, facetResults.Facet("administrator").Value); - - pageIndex++; - - results = sc - .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); - Assert.AreEqual(2, results.Count()); - facetResults = results.GetFacet("writerName"); - Assert.AreEqual(1, facetResults.Count()); - Assert.AreEqual(5, facetResults.Facet("administrator").Value); - - pageIndex++; - - results = sc - .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); - Assert.AreEqual(1, results.Count()); - facetResults = results.GetFacet("writerName"); - Assert.AreEqual(1, facetResults.Count()); - Assert.AreEqual(5, facetResults.Facet("administrator").Value); - - pageIndex++; - - results = sc - .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); - Assert.AreEqual(0, results.Count()); - facetResults = results.GetFacet("writerName"); - Assert.AreEqual(1, facetResults.Count()); - Assert.AreEqual(5, facetResults.Facet("administrator").Value); - } - } - - [TestCase(0, 1, 1)] - [TestCase(0, 2, 2)] - [TestCase(0, 3, 3)] - [TestCase(0, 4, 4)] - [TestCase(0, 5, 5)] - [TestCase(0, 100, 5)] - [TestCase(1, 1, 1)] - [TestCase(1, 2, 2)] - [TestCase(1, 3, 3)] - [TestCase(1, 4, 4)] - [TestCase(1, 5, 4)] - [TestCase(2, 2, 2)] - [TestCase(2, 5, 3)] - public void Given_SkipTake_Returns_ExpectedTotals_Facet(int skip, int take, int expectedResults) - { - const int indexSize = 5; - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); - using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)))) - { - var items = Enumerable.Range(0, indexSize).Select(x => ValueSet.FromObject(x.ToString(), "content", - new { nodeName = "umbraco", headerText = "world", writerName = "administrator" })); - - indexer.IndexItems(items); - - var searcher = indexer.Searcher; - - //Arrange - - var sc = searcher.CreateQuery("content").Field("writerName", "administrator").WithFacet("nodeName"); - - //Act - - var results = sc.Execute(QueryOptions.SkipTake(skip, take)); - - var facetResults = results.GetFacet("nodeName"); - - Assert.AreEqual(indexSize, results.TotalItemCount); - Assert.AreEqual(expectedResults, results.Count()); - Assert.AreEqual(1, facetResults.Count()); - Assert.AreEqual(5, facetResults.Facet("umbraco").Value); - } - } - } -} diff --git a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs index 8aac33f6f..76718397a 100644 --- a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; +using Examine.Lucene; using Examine.Lucene.Providers; using Examine.Lucene.Search; using Examine.Search; using J2N; using Lucene.Net.Analysis.En; using Lucene.Net.Analysis.Standard; +using Lucene.Net.Facet; +using Lucene.Net.Facet.Range; using Lucene.Net.QueryParsers.Classic; using Lucene.Net.Search; using NUnit.Framework; @@ -18,12 +21,16 @@ namespace Examine.Test.Examine.Lucene.Search [TestFixture] public class FluentApiTests : ExamineBaseTest { - [Test] - public void Allow_Leading_Wildcards() + [TestCase(true)] + [TestCase(false)] + public void Allow_Leading_Wildcards(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -55,21 +62,38 @@ public void Allow_Leading_Wildcards() AllowLeadingWildcard = false }).NativeQuery("*dney")); - var results1 = query1.Execute(); - - Assert.AreEqual(2, results1.TotalItemCount); + if (withFacets) + { + var results1 = query1.WithFacet("nodeName").Execute(); + + var facetResults = results1.GetFacet("nodeName"); + + Assert.AreEqual(2, results1.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + Assert.AreEqual(1, facetResults.First().Value); + } + else + { + var results1 = query1.Execute(); + + Assert.AreEqual(2, results1.TotalItemCount); + } } } - [Test] - public void NativeQuery_Single_Word() + [TestCase(true)] + [TestCase(false)] + public void NativeQuery_Single_Word(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) + fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -86,21 +110,38 @@ public void NativeQuery_Single_Word() Console.WriteLine(query); - var results = query.Execute(); + if (withFacets) + { + var results = query.WithFacet("nodeName").Execute(); + + var facetResults = results.GetFacet("nodeName"); - Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + Assert.AreEqual(1, facetResults.Last().Value); + } + else + { + var results = query.Execute(); + + Assert.AreEqual(2, results.TotalItemCount); + } } } - [Test] - public void Uppercase_Category() + [TestCase(true)] + [TestCase(false)] + public void Uppercase_Category(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) + fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "cOntent", @@ -117,21 +158,37 @@ public void Uppercase_Category() Console.WriteLine(query); - var results = query.Execute(); + if (withFacets) + { + var results = query.WithFacet("nodeName").Execute(); - Assert.AreEqual(3, results.TotalItemCount); + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(3, results.TotalItemCount); + Assert.AreEqual(3, facetResults.Count()); + } + else + { + var results = query.Execute(); + + Assert.AreEqual(3, results.TotalItemCount); + } } } - [Test] - public void NativeQuery_Phrase() + [TestCase(true)] + [TestCase(false)] + public void NativeQuery_Phrase(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("bodyText", FieldDefinitionTypes.FacetFullText)) + : new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) + fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -149,21 +206,37 @@ public void NativeQuery_Phrase() Console.WriteLine(query); Assert.AreEqual("{ Category: content, LuceneQuery: +(nodeName:\"town called\" bodyText:\"town called\") }", query.ToString()); - var results = query.Execute(); + if (withFacets) + { + var results = query.WithFacet("bodyText").Execute(); + + var facetResults = results.GetFacet("bodyText"); + + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + } + else + { + var results = query.Execute(); - Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, results.TotalItemCount); + } } } - [Test] - public void Managed_Range_Date() + [TestCase(true)] + [TestCase(false)] + public void Managed_Range_Date(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("created", "datetime"), new FieldDefinition("created", FieldDefinitionTypes.FacetDateTime)) + : new FieldDefinitionCollection(new FieldDefinition("created", "datetime")); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("created", "datetime")))) + fieldDefinitionCollection)) { @@ -198,19 +271,43 @@ public void Managed_Range_Date() var numberSortedCriteria = searcher.CreateQuery() .RangeQuery(new[] { "created" }, new DateTime(2000, 01, 02), new DateTime(2000, 01, 05), maxInclusive: false); - var numberSortedResult = numberSortedCriteria.Execute(); + if(withFacets) + { + var numberSortedResult = numberSortedCriteria.WithFacet("created", new Int64Range[] + { + new Int64Range("First days", new DateTime(2000, 01, 01).Ticks, true, new DateTime(2000, 01, 03).Ticks, true), + new Int64Range("Last days", new DateTime(2000, 01, 04).Ticks, true, new DateTime(2000, 01, 06).Ticks, true) + }).Execute(); + + var facetResult = numberSortedResult.GetFacet("created"); + + Assert.AreEqual(2, numberSortedResult.TotalItemCount); + Assert.AreEqual(2, facetResult.Count()); + Assert.AreEqual(1, facetResult.First().Value); + Assert.AreEqual("First days", facetResult.First().Label); + Assert.AreEqual(1, facetResult.Last().Value); + Assert.AreEqual("Last days", facetResult.Last().Label); + } + else + { + var numberSortedResult = numberSortedCriteria.Execute(); - Assert.AreEqual(2, numberSortedResult.TotalItemCount); + Assert.AreEqual(2, numberSortedResult.TotalItemCount); + } } } - [Test] - public void Managed_Full_Text() + [TestCase(true)] + [TestCase(false)] + public void Managed_Full_Text(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("item1", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir1 = new RandomIdRAMDirectory()) - using (var indexer1 = GetTestIndex(luceneDir1, analyzer)) + using (var indexer1 = GetTestIndex(luceneDir1, analyzer, fieldDefinitionCollection)) { indexer1.IndexItem(ValueSet.FromObject("1", "content", new { item1 = "value1", item2 = "The agitated zebras gallop back and forth in short, panicky dashes, then skitter off into the total absolute darkness." })); indexer1.IndexItem(ValueSet.FromObject("2", "content", new { item1 = "value2", item2 = "The festival lasts five days and celebrates the victory of good over evil, light over darkness, and knowledge over ignorance." })); @@ -222,32 +319,71 @@ public void Managed_Full_Text() var searcher = indexer1.Searcher; - var result = searcher.Search("darkness"); - - Assert.AreEqual(4, result.TotalItemCount); - Console.WriteLine("Search 1:"); - foreach (var r in result) + if (withFacets) { - Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); - } + var result = searcher.CreateQuery() + .ManagedQuery("darkness") + .WithFacet("item1") + .Execute(); + + var facetResults = result.GetFacet("item1"); - result = searcher.Search("total darkness"); - Assert.AreEqual(2, result.TotalItemCount); - Console.WriteLine("Search 2:"); - foreach (var r in result) + Assert.AreEqual(4, result.TotalItemCount); + Assert.AreEqual(4, facetResults.Count()); + + Console.WriteLine("Search 1:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + + result = searcher.CreateQuery() + .ManagedQuery("total darkness") + .WithFacet("item1") + .Execute(); + facetResults = result.GetFacet("item1"); + + Assert.AreEqual(2, result.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + Console.WriteLine("Search 2:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + } + else { - Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + var result = searcher.Search("darkness"); + + Assert.AreEqual(4, result.TotalItemCount); + Console.WriteLine("Search 1:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + + result = searcher.Search("total darkness"); + Assert.AreEqual(2, result.TotalItemCount); + Console.WriteLine("Search 2:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } } } } - [Test] - public void Managed_Full_Text_With_Bool() + [TestCase(true)] + [TestCase(false)] + public void Managed_Full_Text_With_Bool(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("item1", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir1 = new RandomIdRAMDirectory()) - using (var indexer1 = GetTestIndex(luceneDir1, analyzer)) + using (var indexer1 = GetTestIndex(luceneDir1, analyzer, fieldDefinitionCollection)) { indexer1.IndexItem(ValueSet.FromObject("1", "content", new { item1 = "value1", item2 = "The agitated zebras gallop back and forth in short, panicky dashes, then skitter off into the total absolute darkness." })); indexer1.IndexItem(ValueSet.FromObject("2", "content", new { item1 = "value2", item2 = "The festival lasts five days and celebrates the victory of good over evil, light over darkness, and knowledge over ignorance." })); @@ -260,36 +396,73 @@ public void Managed_Full_Text_With_Bool() var qry = searcher.CreateQuery().ManagedQuery("darkness").And().Field("item1", "value1"); Console.WriteLine(qry); - var result = qry.Execute(); - Assert.AreEqual(1, result.TotalItemCount); - Console.WriteLine("Search 1:"); - foreach (var r in result) + if (withFacets) { - Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); - } + var result = qry.WithFacet("item1").Execute(); - qry = searcher.CreateQuery().ManagedQuery("darkness") - .And(query => query.Field("item1", "value1").Or().Field("item1", "value2"), BooleanOperation.Or); - Console.WriteLine(qry); - result = qry.Execute(); + var facetResults = result.GetFacet("item1"); + + Assert.AreEqual(1, result.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Console.WriteLine("Search 1:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + + qry = searcher.CreateQuery().ManagedQuery("darkness") + .And(query => query.Field("item1", "value1").Or().Field("item1", "value2"), BooleanOperation.Or); + Console.WriteLine(qry); + result = qry.WithFacet("item1").Execute(); - Assert.AreEqual(2, result.TotalItemCount); - Console.WriteLine("Search 2:"); - foreach (var r in result) + facetResults = result.GetFacet("item1"); + + Assert.AreEqual(2, result.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + Console.WriteLine("Search 2:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + } + else { - Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + var result = qry.Execute(); + + Assert.AreEqual(1, result.TotalItemCount); + Console.WriteLine("Search 1:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + + qry = searcher.CreateQuery().ManagedQuery("darkness") + .And(query => query.Field("item1", "value1").Or().Field("item1", "value2"), BooleanOperation.Or); + Console.WriteLine(qry); + result = qry.Execute(); + + Assert.AreEqual(2, result.TotalItemCount); + Console.WriteLine("Search 2:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } } } } - [Test] - public void Not_Managed_Full_Text() + [TestCase(true)] + [TestCase(false)] + public void Not_Managed_Full_Text(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("item1", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir1 = new RandomIdRAMDirectory()) - using (var indexer1 = GetTestIndex(luceneDir1, analyzer)) + using (var indexer1 = GetTestIndex(luceneDir1, analyzer, fieldDefinitionCollection)) { indexer1.IndexItem(ValueSet.FromObject("1", "content", new { item1 = "value1", item2 = "The agitated zebras gallop back and forth in short, panicky dashes, then skitter off into the total absolute chaos." })); indexer1.IndexItem(ValueSet.FromObject("2", "content", new { item1 = "value1", item2 = "The festival lasts five days and celebrates the victory of good over evil, light over darkness, and knowledge over ignorance." })); @@ -305,28 +478,52 @@ public void Not_Managed_Full_Text() .Not().ManagedQuery("darkness"); Console.WriteLine(qry); - var result = qry.Execute(); - Assert.AreEqual(1, result.TotalItemCount); - Assert.AreEqual("1", result.ElementAt(0).Id); + if (withFacets) + { + var result = qry.WithFacet("item1").Execute(); + + var facetResults = result.GetFacet("item1"); + + Assert.AreEqual(1, result.TotalItemCount); + Assert.AreEqual("1", result.ElementAt(0).Id); + Assert.AreEqual(1, facetResults.Count()); - Console.WriteLine("Search 1:"); - foreach (var r in result) + Console.WriteLine("Search 1:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } + } + else { - Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + var result = qry.Execute(); + + Assert.AreEqual(1, result.TotalItemCount); + Assert.AreEqual("1", result.ElementAt(0).Id); + + Console.WriteLine("Search 1:"); + foreach (var r in result) + { + Console.WriteLine($"Id = {r.Id}, Score = {r.Score}"); + } } } } - [Test] - public void Managed_Range_Int() + [TestCase(true)] + [TestCase(false)] + public void Managed_Range_Int(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)) + : new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) + fieldDefinitionCollection)) { @@ -360,21 +557,44 @@ public void Managed_Range_Int() var numberSortedCriteria = searcher.CreateQuery() .RangeQuery(new[] { "parentID" }, 122, 124); - var numberSortedResult = numberSortedCriteria.Execute(); + if (withFacets) + { + var numberSortedResult = numberSortedCriteria + .WithFacet("parentID", new Int64Range[] + { + new Int64Range("120-122", 120, true, 122, true), + new Int64Range("123-125", 123, true, 125, true) + }).Execute(); - Assert.AreEqual(2, numberSortedResult.TotalItemCount); + var facetResults = numberSortedResult.GetFacet("parentID"); + + Assert.AreEqual(2, numberSortedResult.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + Assert.AreEqual(0, facetResults.First(result => result.Label == "120-122").Value); + Assert.AreEqual(2, facetResults.First(result => result.Label == "123-125").Value); + } + else + { + var numberSortedResult = numberSortedCriteria.Execute(); + + Assert.AreEqual(2, numberSortedResult.TotalItemCount); + } } } - [Test] - public void Legacy_ParentId() + [TestCase(true)] + [TestCase(false)] + public void Legacy_ParentId(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)) + : new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) + fieldDefinitionCollection)) { @@ -409,20 +629,37 @@ public void Legacy_ParentId() .Field("parentID", 123) .OrderBy(new SortableField("sortOrder", SortType.Int)); - var numberSortedResult = numberSortedCriteria.Execute(); + if (withFacets) + { + var numberSortedResult = numberSortedCriteria.WithFacet("parentID").Execute(); + + var facetResults = numberSortedResult.GetFacet("parentID"); - Assert.AreEqual(2, numberSortedResult.TotalItemCount); + Assert.AreEqual(2, numberSortedResult.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(2, facetResults.Facet("123").Value); + } + else + { + var numberSortedResult = numberSortedCriteria.Execute(); + + Assert.AreEqual(2, numberSortedResult.TotalItemCount); + } } } - [Test] - public void Grouped_Or_Examiness() + [TestCase(true)] + [TestCase(false)] + public void Grouped_Or_Examiness(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { @@ -461,14 +698,32 @@ public void Grouped_Or_Examiness() Console.WriteLine(filter); - var results = filter.Execute(); - - foreach (var r in results) + if (withFacets) { - Console.WriteLine($"Id = {r.Id}"); + var results = filter.WithFacet("nodeTypeAlias").Execute(); + + var facetResults = results.GetFacet("nodeTypeAlias"); + + foreach (var r in results) + { + Console.WriteLine($"Id = {r.Id}"); + } + + Assert.AreEqual(2, results.TotalItemCount); + + Assert.AreEqual(2, facetResults.Count()); } + else + { + var results = filter.Execute(); - Assert.AreEqual(2, results.TotalItemCount); + foreach (var r in results) + { + Console.WriteLine($"Id = {r.Id}"); + } + + Assert.AreEqual(2, results.TotalItemCount); + } } } @@ -486,35 +741,35 @@ public void Grouped_Or_Query_Output() Console.WriteLine("GROUPED OR - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }); + criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3 blahID:1 blahID:2 blahID:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 parentID:1)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1)", criteria.Query.ToString()); @@ -537,7 +792,7 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); //We used to assert this, but it must be allowed to do an add on the same field multiple times //Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); @@ -546,7 +801,7 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }); + criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); //The field/value array lengths are equal so we will match the key/value pairs Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2 +blahID:3)", criteria.Query.ToString()); @@ -554,7 +809,7 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); //There are more than one field and there are more values than fields, in this case we align the key/value pairs Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2)", criteria.Query.ToString()); @@ -562,14 +817,14 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:1)", criteria.Query.ToString()); Console.WriteLine("GROUPED AND - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); } @@ -591,47 +846,50 @@ public void Grouped_Not_Query_Output() Console.WriteLine("GROUPED NOT - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }); + criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3 -blahID:1 -blahID:2 -blahID:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -parentID:1", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1", criteria.Query.ToString()); } } - [Test] - public void Grouped_Not_Single_Field_Single_Value() + [TestCase(true)] + [TestCase(false)] + public void Grouped_Not_Single_Field_Single_Value(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { @@ -646,18 +904,34 @@ public void Grouped_Not_Single_Field_Single_Value() var query = (LuceneSearchQuery)searcher.CreateQuery("content"); query.GroupedNot(new[] { "umbracoNaviHide" }, 1.ToString()); Console.WriteLine(query.Query); - var results = query.Execute(); - Assert.AreEqual(1, results.TotalItemCount); + + if (withFacets) + { + var results = query.All().WithFacet("nodeName").Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + else + { + var results = query.Execute(); + Assert.AreEqual(1, results.TotalItemCount); + } } } - [Test] - public void Grouped_Not_Multi_Field_Single_Value() + [TestCase(true)] + [TestCase(false)] + public void Grouped_Not_Multi_Field_Single_Value(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex( - luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { @@ -675,14 +949,32 @@ public void Grouped_Not_Multi_Field_Single_Value() var query = searcher.CreateQuery("content").GroupedNot(new[] { "umbracoNaviHide", "show" }, 1.ToString()); Console.WriteLine(query); - var results = query.Execute(); - Assert.AreEqual(1, results.TotalItemCount); + + if (withFacets) + { + var results = query.WithFacet("nodeName").Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(1, facetResults.Facet("my name 2").Value); + } + else + { + var results = query.Execute(); + Assert.AreEqual(1, results.TotalItemCount); + } } } - [Test] - public void Grouped_Or_With_Not() + [TestCase(true)] + [TestCase(false)] + public void Grouped_Or_With_Not(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("headerText", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( @@ -691,7 +983,7 @@ public void Grouped_Or_With_Not() // It's because the searching is NOT using a managed search //new[] { new FieldDefinition("umbracoNaviHide", FieldDefinitionTypes.Integer) }, - luceneDir, analyzer)) + luceneDir, analyzer, fieldDefinitionCollection)) { @@ -707,17 +999,34 @@ public void Grouped_Or_With_Not() //paths contain punctuation, we'll escape it and ensure an exact match var criteria = searcher.CreateQuery("content"); var filter = criteria.GroupedOr(new[] { "nodeName", "bodyText", "headerText" }, "ipsum").Not().Field("umbracoNaviHide", "1"); - var results = filter.Execute(); - Assert.AreEqual(1, results.TotalItemCount); + + if (withFacets) + { + var results = filter.WithFacet("headerText").Execute(); + + var facetResults = results.GetFacet("headerText"); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Facet("header 2").Value); + } + else + { + var results = filter.Execute(); + Assert.AreEqual(1, results.TotalItemCount); + } } } - [Test] - public void And_Grouped_Not_Single_Value() + [TestCase(true)] + [TestCase(false)] + public void And_Grouped_Not_Single_Value(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -734,17 +1043,34 @@ public void And_Grouped_Not_Single_Value() .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString() }); Console.WriteLine(query); - var results = query.Execute(); - Assert.AreEqual(1, results.TotalItemCount); + + if (withFacets) + { + var results = query.WithFacet("nodeName").Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + else + { + var results = query.Execute(); + Assert.AreEqual(1, results.TotalItemCount); + } } } - [Test] - public void And_Grouped_Not_Multi_Value() + [TestCase(true)] + [TestCase(false)] + public void And_Grouped_Not_Multi_Value(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -761,17 +1087,33 @@ public void And_Grouped_Not_Multi_Value() .And().GroupedNot(new[] { "umbracoNaviHide" }, new[] { 1.ToString(), 2.ToString() }); Console.WriteLine(query); - var results = query.Execute(); - Assert.AreEqual(1, results.TotalItemCount); + + if (withFacets) + { + var results = query.WithFacet("nodeName").Execute(); + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + else + { + var results = query.Execute(); + Assert.AreEqual(1, results.TotalItemCount); + } } } - [Test] - public void And_Not_Single_Field() + [TestCase(true)] + [TestCase(false)] + public void And_Not_Single_Field(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -788,17 +1130,35 @@ public void And_Not_Single_Field() .Not().Field("umbracoNaviHide", 1.ToString()); Console.WriteLine(query); - var results = query.Execute(); - Assert.AreEqual(1, results.TotalItemCount); + + if (withFacets) + { + var results = query.WithFacet("nodeName").Execute(); + + var facetResults = results.GetFacets(); + + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(1, facetResults.First().Count()); + } + else + { + var results = query.Execute(); + Assert.AreEqual(1, results.TotalItemCount); + } } } - [Test] - public void AndNot_Nested() + [TestCase(true)] + [TestCase(false)] + public void AndNot_Nested(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -818,17 +1178,33 @@ public void AndNot_Nested() // Which I don't think is right with the -(+ syntax but it still seems to work. Console.WriteLine(query); - var results = query.Execute(); - Assert.AreEqual(1, results.TotalItemCount); + + if (withFacets) + { + var results = query.WithFacet("nodeName").Execute(); + + var facetResults = results.GetFacet("nodeName"); + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + else + { + var results = query.Execute(); + Assert.AreEqual(1, results.TotalItemCount); + } } } - [Test] - public void And_Not_Added_Later() + [TestCase(true)] + [TestCase(false)] + public void And_Not_Added_Later(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -848,17 +1224,33 @@ public void And_Not_Added_Later() // Results in { Category: content, LuceneQuery: +nodeName:name -umbracoNaviHide:1 -umbracoNaviHide:2 } Console.WriteLine(query); - var results = query.Execute(); - Assert.AreEqual(1, results.TotalItemCount); + + if (withFacets) + { + var results = query.WithFacet("nodeName").Execute(); + + var facetResults = results.GetFacet("nodeName"); + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + else + { + var results = query.Execute(); + Assert.AreEqual(1, results.TotalItemCount); + } } } - [Test] - public void Not_Range() + [TestCase(true)] + [TestCase(false)] + public void Not_Range(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("start", FieldDefinitionTypes.FacetInteger)) + : new FieldDefinitionCollection(new FieldDefinition("start", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer, new FieldDefinitionCollection(new FieldDefinition("start", FieldDefinitionTypes.Integer)))) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -874,22 +1266,42 @@ public void Not_Range() .Not().Field("start", 200); Console.WriteLine(query); - var results = query.Execute(); - Assert.AreEqual(1, results.TotalItemCount); - Assert.AreEqual(results.First().Id, 1.ToString()); + + if (withFacets) + { + + var results = query + .WithFacet("start", new Int64Range("Label", 100, false, 200, false)) + .Execute(); + + var facetResults = results.GetFacet("start"); + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(results.First().Id, 1.ToString()); + Assert.AreEqual(0, facetResults.Facet("Label").Value); + } + else + { + var results = query.Execute(); + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(results.First().Id, 1.ToString()); + } } } - [Test] - public void Match_By_Path() + [TestCase(true)] + [TestCase(false)] + public void Match_By_Path(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("__Path", "raw"), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : new FieldDefinitionCollection(new FieldDefinition("__Path", "raw")); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("__Path", "raw")))) + fieldDefinitionCollection)) { @@ -917,46 +1329,107 @@ public void Match_By_Path() //paths contain punctuation, we'll escape it and ensure an exact match var criteria = searcher.CreateQuery("content"); var filter = criteria.Field("__Path", "-1,123,456,789"); - var results1 = filter.Execute(); - Assert.AreEqual(1, results1.TotalItemCount); + + if (withFacets) + { + var results1 = filter.WithFacet("nodeName").Execute(); + var facetResults1 = results1.GetFacet("nodeName"); + Assert.AreEqual(1, results1.TotalItemCount); + Assert.AreEqual(1, facetResults1.Count()); + } + else + { + var results1 = filter.Execute(); + Assert.AreEqual(1, results1.TotalItemCount); + } //now escape it var exactcriteria = searcher.CreateQuery("content"); var exactfilter = exactcriteria.Field("__Path", "-1,123,456,789".Escape()); - var results2 = exactfilter.Execute(); - Assert.AreEqual(1, results2.TotalItemCount); + + if (withFacets) + { + var results2 = exactfilter.WithFacet("nodeName").Execute(); + var facetResults2 = results2.GetFacet("nodeName"); + Assert.AreEqual(1, results2.TotalItemCount); + Assert.AreEqual(1, facetResults2.Count()); + } + else + { + var results2 = exactfilter.Execute(); + Assert.AreEqual(1, results2.TotalItemCount); + } //now try with native var nativeCriteria = searcher.CreateQuery(); var nativeFilter = nativeCriteria.NativeQuery("__Path:\\-1,123,456,789"); Console.WriteLine(nativeFilter); - var results5 = nativeFilter.Execute(); - Assert.AreEqual(1, results5.TotalItemCount); + + if (withFacets) + { + + var results5 = nativeFilter.WithFacet("nodeName").Execute(); + var facetResults5 = results5.GetFacet("nodeName"); + Assert.AreEqual(1, results5.TotalItemCount); + Assert.AreEqual(1, facetResults5.Count()); + } + else + { + var results5 = nativeFilter.Execute(); + Assert.AreEqual(1, results5.TotalItemCount); + } //now try wildcards var wildcardcriteria = searcher.CreateQuery("content"); var wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,456,".MultipleCharacterWildcard()); - var results3 = wildcardfilter.Execute(); - Assert.AreEqual(2, results3.TotalItemCount); + + if (withFacets) + { + var results3 = wildcardfilter.WithFacet("nodeName").Execute(); + var facetResults3 = results3.GetFacet("nodeName"); + Assert.AreEqual(2, results3.TotalItemCount); + Assert.AreEqual(2, facetResults3.Count()); + } + else + { + var results3 = wildcardfilter.Execute(); + Assert.AreEqual(2, results3.TotalItemCount); + } + //not found wildcardcriteria = searcher.CreateQuery("content"); wildcardfilter = wildcardcriteria.Field("__Path", "-1,123,457,".MultipleCharacterWildcard()); - results3 = wildcardfilter.Execute(); - Assert.AreEqual(0, results3.TotalItemCount); + + if (withFacets) + { + var results3 = wildcardfilter.WithFacet("nodeName").Execute(); + var facetResults3 = results3.GetFacet("nodeName"); + Assert.AreEqual(0, results3.TotalItemCount); + Assert.AreEqual(0, facetResults3?.Count() ?? 0); + } + else + { + var results3 = wildcardfilter.Execute(); + Assert.AreEqual(0, results3.TotalItemCount); + } } } - [Test] - public void Find_By_ParentId() + [TestCase(true)] + [TestCase(false)] + public void Find_By_ParentId(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) + fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -972,21 +1445,37 @@ public void Find_By_ParentId() var criteria = searcher.CreateQuery("content"); var filter = criteria.Field("parentID", 1139); - var results = filter.Execute(); + if (withFacets) + { + var results = filter.WithFacet("nodeName").Execute(); + + var facetResults = results.GetFacet("nodeName"); - Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + } + else + { + var results = filter.Execute(); + + Assert.AreEqual(2, results.TotalItemCount); + } } } - [Test] - public void Find_By_ParentId_Native_Query() + [TestCase(true)] + [TestCase(false)] + public void Find_By_ParentId_Native_Query(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)) + : new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) + fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -1013,21 +1502,38 @@ public void Find_By_ParentId_Native_Query() //We can use a Lucene query directly instead: //((LuceneSearchQuery)criteria).LuceneQuery(NumericRangeQuery) - var results = filter.Execute(); + if (withFacets) + { + var results = filter.WithFacet("parentID").Execute(); + + var facetResults = results.GetFacet("parentID"); - Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(2, facetResults.Facet("1139").Value); + } + else + { + var results = filter.Execute(); + + Assert.AreEqual(2, results.TotalItemCount); + } } } - [Test] - public void Find_By_NodeTypeAlias() + [TestCase(true)] + [TestCase(false)] + public void Find_By_NodeTypeAlias(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", "raw"), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", "raw")); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", "raw")))) + fieldDefinitionCollection)) { @@ -1062,19 +1568,34 @@ public void Find_By_NodeTypeAlias() var criteria = searcher.CreateQuery("content"); var filter = criteria.Field("nodeTypeAlias", "CWS_Home".Escape()); - var results = filter.Execute(); + if (withFacets) + { + var results = filter.WithFacet("nodeName").Execute(); + var facetResults = results.GetFacet("nodeName"); - Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + } + else + { + var results = filter.Execute(); + + Assert.AreEqual(2, results.TotalItemCount); + } } } - [Test] - public void Search_With_Stop_Words() + [TestCase(true)] + [TestCase(false)] + public void Search_With_Stop_Words(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { @@ -1099,18 +1620,34 @@ public void Search_With_Stop_Words() Console.WriteLine(filter); - var results = filter.Execute(); + if (withFacets) + { + var results = filter.WithFacet("nodeName").Execute(); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(0, results.TotalItemCount); + Assert.AreEqual(0, facetResults?.Count() ?? 0); + } + else + { + var results = filter.Execute(); - Assert.AreEqual(0, results.TotalItemCount); + Assert.AreEqual(0, results.TotalItemCount); + } } } - [Test] - public void Search_Native_Query() + [TestCase(true)] + [TestCase(false)] + public void Search_Native_Query(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { @@ -1142,22 +1679,39 @@ public void Search_Native_Query() var searcher = indexer.Searcher; - var criteria = searcher.CreateQuery("content"); + var criteria = searcher.CreateQuery("content").NativeQuery("nodeTypeAlias:CWS_Home"); - var results = criteria.NativeQuery("nodeTypeAlias:CWS_Home").Execute(); + if (withFacets) + { + var results = criteria.WithFacet("nodeTypeAlias").Execute(); - Assert.AreEqual(2, results.TotalItemCount); + var facetResults = results.GetFacet("nodeTypeAlias"); + + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(2, facetResults.Facet("CWS_Home").Value); + } + else + { + var results = criteria.Execute(); + + Assert.AreEqual(2, results.TotalItemCount); + } } } - [Test] - public void Find_Only_Image_Media() + [TestCase(true)] + [TestCase(false)] + public void Find_Only_Image_Media(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeTypeAlias", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { @@ -1177,18 +1731,35 @@ public void Find_Only_Image_Media() var criteria = searcher.CreateQuery("media"); var filter = criteria.Field("nodeTypeAlias", "image"); - var results = filter.Execute(); + if (withFacets) + { + var results = filter.WithFacet("nodeTypeAlias").Execute(); + + var facetResults = results.GetFacet("nodeTypeAlias"); - Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(2, facetResults.Facet("image").Value); + } + else + { + var results = filter.Execute(); + + Assert.AreEqual(2, results.TotalItemCount); + } } } - [Test] - public void Find_Both_Media_And_Content() + [TestCase(true)] + [TestCase(false)] + public void Find_Both_Media_And_Content(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "media", @@ -1209,22 +1780,38 @@ public void Find_Both_Media_And_Content() .Or() .Field(ExamineFieldNames.CategoryFieldName, "content"); - var results = filter.Execute(); + if (withFacets) + { + var results = filter.WithFacet("nodeName").Execute(); - Assert.AreEqual(3, results.TotalItemCount); + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(3, results.TotalItemCount); + Assert.AreEqual(3, facetResults.Count()); + } + else + { + var results = filter.Execute(); + + Assert.AreEqual(3, results.TotalItemCount); + } } } - [Test] - public void Sort_Result_By_Number_Field() + [TestCase(true)] + [TestCase(false)] + public void Sort_Result_By_Number_Field(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("sortOrder", FieldDefinitionTypes.FacetInteger), new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)) + : new FieldDefinitionCollection(new FieldDefinition("sortOrder", FieldDefinitionTypes.Integer), new FieldDefinition("parentID", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, //Ensure it's set to a number, otherwise it's not sortable - new FieldDefinitionCollection(new FieldDefinition("sortOrder", FieldDefinitionTypes.Integer), new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) + fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -1242,29 +1829,59 @@ public void Sort_Result_By_Number_Field() var sc = searcher.CreateQuery("content"); var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("sortOrder", SortType.Int)); - var results1 = sc1.Execute().ToArray(); + if (withFacets) + { + var results1 = sc1 + .WithFacet("sortOrder") + .And() + .WithFacet("parentID") + .Execute(); + + var facetResults = results1.GetFacet("sortOrder"); + var facetReuslts2 = results1.GetFacet("parentID"); + + Assert.AreEqual(3, results1.Count()); + Assert.AreEqual(3, facetResults.Count()); + Assert.AreEqual(1, facetReuslts2.Count()); + + var results2 = results1.ToArray(); + double currSort = 0; + for (var i = 0; i < results2.Length; i++) + { + Assert.GreaterOrEqual(double.Parse(results2[i].Values["sortOrder"]), currSort); + currSort = double.Parse(results2[i].Values["sortOrder"]); + } + } + else + { + var results1 = sc1.Execute().ToArray(); - Assert.AreEqual(3, results1.Length); + Assert.AreEqual(3, results1.Length); - var currSort = 0; - for (var i = 0; i < results1.Length; i++) - { - Assert.GreaterOrEqual(int.Parse(results1[i].Values["sortOrder"]), currSort); - currSort = int.Parse(results1[i].Values["sortOrder"]); + var currSort = 0; + for (var i = 0; i < results1.Length; i++) + { + Assert.GreaterOrEqual(int.Parse(results1[i].Values["sortOrder"]), currSort); + currSort = int.Parse(results1[i].Values["sortOrder"]); + } } } } - [Test] - public void Sort_Result_By_Date_Field() + [TestCase(true)] + [TestCase(false)] + public void Sort_Result_By_Date_Field(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("updateDate", FieldDefinitionTypes.FacetDateTime), new FieldDefinition("parentID", FieldDefinitionTypes.FacetInteger)) + : new FieldDefinitionCollection(new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime), new FieldDefinition("parentID", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, //Ensure it's set to a date, otherwise it's not sortable - new FieldDefinitionCollection(new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime), new FieldDefinition("parentID", FieldDefinitionTypes.Integer)))) + fieldDefinitionCollection)) { @@ -1287,29 +1904,59 @@ public void Sort_Result_By_Date_Field() //note: dates internally are stored as Long, see DateTimeType var sc1 = sc.Field("parentID", 1143).OrderBy(new SortableField("updateDate", SortType.Long)); - var results1 = sc1.Execute().ToArray(); + if (withFacets) + { + var results1 = sc1 + .WithFacet("updateDate") + .And() + .WithFacet("parentID") + .Execute(); + + var facetResults = results1.GetFacet("updateDate"); + var facetReuslts2 = results1.GetFacet("parentID"); + + Assert.AreEqual(3, results1.Count()); + Assert.AreEqual(3, facetResults.Count()); + Assert.AreEqual(1, facetReuslts2.Count()); + + var results2 = results1.ToArray(); + double currSort = 0; + for (var i = 0; i < results2.Length; i++) + { + Assert.GreaterOrEqual(double.Parse(results2[i].Values["updateDate"]), currSort); + currSort = double.Parse(results2[i].Values["updateDate"]); + } + } + else + { + var results1 = sc1.Execute().ToArray(); - Assert.AreEqual(3, results1.Length); + Assert.AreEqual(3, results1.Length); - double currSort = 0; - for (var i = 0; i < results1.Length; i++) - { - Assert.GreaterOrEqual(double.Parse(results1[i].Values["updateDate"]), currSort); - currSort = double.Parse(results1[i].Values["updateDate"]); + double currSort = 0; + for (var i = 0; i < results1.Length; i++) + { + Assert.GreaterOrEqual(double.Parse(results1[i].Values["updateDate"]), currSort); + currSort = double.Parse(results1[i].Values["updateDate"]); + } } } } - [Test] - public void Sort_Result_By_Single_Field() + [TestCase(true)] + [TestCase(false)] + public void Sort_Result_By_Single_Field(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullTextSortable)) + : new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FullTextSortable)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, //Ensure it's set to a fulltextsortable, otherwise it's not sortable - new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FullTextSortable)))) + fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -1332,32 +1979,52 @@ public void Sort_Result_By_Single_Field() var sc2 = sc.Field("writerName", "administrator") .OrderByDescending(new SortableField("nodeName", SortType.String)); - var results1 = sc1.Execute(); - var results2 = sc2.Execute(); + if (withFacets) + { + var results1 = sc1.WithFacet("nodeName").Execute(); + var results2 = sc2.WithFacet("nodeName").Execute(); + + var facetResults1 = results1.GetFacet("nodeName"); + var facetResults2 = results2.GetFacet("nodeName"); + + Assert.AreNotEqual(results1.First().Id, results2.First().Id); - Assert.AreNotEqual(results1.First().Id, results2.First().Id); + Assert.AreEqual(3, facetResults1.Count()); + Assert.AreEqual(3, facetResults2.Count()); + } + else + { + var results1 = sc1.Execute(); + var results2 = sc2.Execute(); + + Assert.AreNotEqual(results1.First().Id, results2.First().Id); + } } } - [TestCase(FieldDefinitionTypes.Double, SortType.Double)] - //[TestCase(FieldDefinitionTypes.Double, SortType.String)] // This differs from Lucene 3.x, if string is specified it will still sort like as string - [TestCase(FieldDefinitionTypes.FullText, SortType.Double)] - [TestCase(FieldDefinitionTypes.FullText, SortType.String)] - [TestCase(FieldDefinitionTypes.FullTextSortable, SortType.Double)] - [TestCase(FieldDefinitionTypes.FullTextSortable, SortType.String)] - public void Sort_Result_By_Double_Fields(string fieldType, SortType sortType) + [TestCase(FieldDefinitionTypes.FacetDouble, SortType.Double, true)] + //[TestCase(FieldDefinitionTypes.FacetDouble, SortType.String, true)] // This differs from Lucene 3.x, if string is specified it will still sort like as string + [TestCase(FieldDefinitionTypes.FacetFullText, SortType.Double, true)] + [TestCase(FieldDefinitionTypes.FacetFullText, SortType.String, true)] + [TestCase(FieldDefinitionTypes.FacetFullTextSortable, SortType.Double, true)] + [TestCase(FieldDefinitionTypes.FacetFullTextSortable, SortType.String, true)] + [TestCase(FieldDefinitionTypes.Double, SortType.Double, false)] + //[TestCase(FieldDefinitionTypes.Double, SortType.String, false)] // This differs from Lucene 3.x, if string is specified it will still sort like as string + [TestCase(FieldDefinitionTypes.FullText, SortType.Double, false)] + [TestCase(FieldDefinitionTypes.FullText, SortType.String, false)] + [TestCase(FieldDefinitionTypes.FullTextSortable, SortType.Double, false)] + [TestCase(FieldDefinitionTypes.FullTextSortable, SortType.String, false)] + public void Sort_Result_By_Double_Fields(string fieldType, SortType sortType, bool withFacets) { // See: https://github.com/Shazwazza/Examine/issues/242 - var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection( - new FieldDefinition("field1", fieldType)))) + new FieldDefinitionCollection(new FieldDefinition("field1", fieldType)))) { indexer.IndexItems(new[] { @@ -1379,37 +2046,75 @@ public void Sort_Result_By_Double_Fields(string fieldType, SortType sortType) var sc2 = sc.All() .OrderByDescending(new SortableField("field1", sortType)); - var results1 = sc1.Execute().ToList(); - var results2 = sc2.Execute().ToList(); + if (withFacets) + { + var results1 = sc1.WithFacet("field1").Execute(); + var results2 = sc2.WithFacet("field1").Execute(); + + var facetResults1 = results1.GetFacet("field1"); + var facetResults2 = results2.GetFacet("field1"); - Assert.AreEqual(2.6, double.Parse(results1[0].Values["field1"])); - Assert.AreEqual(3.8, double.Parse(results1[1].Values["field1"])); - Assert.AreEqual(3.9, double.Parse(results1[2].Values["field1"])); - Assert.AreEqual(4.5, double.Parse(results1[3].Values["field1"])); - Assert.AreEqual(4.9, double.Parse(results1[4].Values["field1"])); - Assert.AreEqual(5.0, double.Parse(results1[5].Values["field1"])); + var results3 = results1.ToList(); + var results4 = results2.ToList(); + Assert.AreEqual(2.6, double.Parse(results3[0].Values["field1"])); + Assert.AreEqual(3.8, double.Parse(results3[1].Values["field1"])); + Assert.AreEqual(3.9, double.Parse(results3[2].Values["field1"])); + Assert.AreEqual(4.5, double.Parse(results3[3].Values["field1"])); + Assert.AreEqual(4.9, double.Parse(results3[4].Values["field1"])); + Assert.AreEqual(5.0, double.Parse(results3[5].Values["field1"])); - Assert.AreEqual(2.6, double.Parse(results2[5].Values["field1"])); - Assert.AreEqual(3.8, double.Parse(results2[4].Values["field1"])); - Assert.AreEqual(3.9, double.Parse(results2[3].Values["field1"])); - Assert.AreEqual(4.5, double.Parse(results2[2].Values["field1"])); - Assert.AreEqual(4.9, double.Parse(results2[1].Values["field1"])); - Assert.AreEqual(5.0, double.Parse(results2[0].Values["field1"])); + + Assert.AreEqual(2.6, double.Parse(results4[5].Values["field1"])); + Assert.AreEqual(3.8, double.Parse(results4[4].Values["field1"])); + Assert.AreEqual(3.9, double.Parse(results4[3].Values["field1"])); + Assert.AreEqual(4.5, double.Parse(results4[2].Values["field1"])); + Assert.AreEqual(4.9, double.Parse(results4[1].Values["field1"])); + Assert.AreEqual(5.0, double.Parse(results4[0].Values["field1"])); + + Assert.AreEqual(6, facetResults1.Count()); + Assert.AreEqual(6, facetResults2.Count()); + } + else + { + var results1 = sc1.Execute().ToList(); + var results2 = sc2.Execute().ToList(); + + Assert.AreEqual(2.6, double.Parse(results1[0].Values["field1"])); + Assert.AreEqual(3.8, double.Parse(results1[1].Values["field1"])); + Assert.AreEqual(3.9, double.Parse(results1[2].Values["field1"])); + Assert.AreEqual(4.5, double.Parse(results1[3].Values["field1"])); + Assert.AreEqual(4.9, double.Parse(results1[4].Values["field1"])); + Assert.AreEqual(5.0, double.Parse(results1[5].Values["field1"])); + + + Assert.AreEqual(2.6, double.Parse(results2[5].Values["field1"])); + Assert.AreEqual(3.8, double.Parse(results2[4].Values["field1"])); + Assert.AreEqual(3.9, double.Parse(results2[3].Values["field1"])); + Assert.AreEqual(4.5, double.Parse(results2[2].Values["field1"])); + Assert.AreEqual(4.9, double.Parse(results2[1].Values["field1"])); + Assert.AreEqual(5.0, double.Parse(results2[0].Values["field1"])); + } } } - [Test] - public void Sort_Result_By_Multiple_Fields() + [TestCase(true)] + [TestCase(false)] + public void Sort_Result_By_Multiple_Fields(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection( + new FieldDefinition("field1", FieldDefinitionTypes.FacetDouble), + new FieldDefinition("field2", FieldDefinitionTypes.FacetInteger)) + : new FieldDefinitionCollection( + new FieldDefinition("field1", FieldDefinitionTypes.Double), + new FieldDefinition("field2", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection( - new FieldDefinition("field1", FieldDefinitionTypes.Double), - new FieldDefinition("field2", FieldDefinitionTypes.Integer)))) + fieldDefinitionCollection)) { indexer.IndexItems(new[] { @@ -1428,23 +2133,48 @@ public void Sort_Result_By_Multiple_Fields() .OrderByDescending(new SortableField("field2", SortType.Int)) .OrderBy(new SortableField("field1", SortType.Double)); - var results1 = sc1.Execute().ToList(); + if (withFacets) + { + var results1 = sc1.WithFacet("field1").And().WithFacet("field2").Execute(); + + var facetResults = results1.GetFacet("field1"); + var facetResults2 = results1.GetFacet("field2"); - Assert.AreEqual("3", results1[0].Id); - Assert.AreEqual("2", results1[1].Id); - Assert.AreEqual("1", results1[2].Id); - Assert.AreEqual("6", results1[3].Id); - Assert.AreEqual("5", results1[4].Id); - Assert.AreEqual("4", results1[5].Id); + var results2 = results1.ToList(); + Assert.AreEqual("3", results2[0].Id); + Assert.AreEqual("2", results2[1].Id); + Assert.AreEqual("1", results2[2].Id); + Assert.AreEqual("6", results2[3].Id); + Assert.AreEqual("5", results2[4].Id); + Assert.AreEqual("4", results2[5].Id); + + Assert.AreEqual(6, facetResults.Count()); + Assert.AreEqual(2, facetResults2.Count()); + } + else + { + var results1 = sc1.Execute().ToList(); + + Assert.AreEqual("3", results1[0].Id); + Assert.AreEqual("2", results1[1].Id); + Assert.AreEqual("1", results1[2].Id); + Assert.AreEqual("6", results1[3].Id); + Assert.AreEqual("5", results1[4].Id); + Assert.AreEqual("4", results1[5].Id); + } } } - [Test] - public void Standard_Results_Sorted_By_Score() + [TestCase(true)] + [TestCase(false)] + public void Standard_Results_Sorted_By_Score(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("bodyText", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -1462,29 +2192,56 @@ public void Standard_Results_Sorted_By_Score() var sc = searcher.CreateQuery("content", BooleanOperation.Or); var sc1 = sc.Field("nodeName", "umbraco").Or().Field("headerText", "umbraco").Or().Field("bodyText", "umbraco"); - var results = sc1.Execute(); + if (withFacets) + { + var results = sc1.WithFacet("bodyText").And().Execute(); - //Assert - for (int i = 0; i < results.TotalItemCount - 1; i++) + var facetResults = results.GetFacet("bodyText"); + + Assert.AreEqual(2, facetResults.Count()); + + //Assert + for (int i = 0; i < results.TotalItemCount - 1; i++) + { + var curr = results.ElementAt(i); + var next = results.ElementAtOrDefault(i + 1); + + if (next == null) + break; + + Assert.IsTrue(curr.Score >= next.Score, string.Format("Result at index {0} must have a higher score than result at index {1}", i, i + 1)); + } + } + else { - var curr = results.ElementAt(i); - var next = results.ElementAtOrDefault(i + 1); + var results = sc1.Execute(); + + //Assert + for (int i = 0; i < results.TotalItemCount - 1; i++) + { + var curr = results.ElementAt(i); + var next = results.ElementAtOrDefault(i + 1); - if (next == null) - break; + if (next == null) + break; - Assert.IsTrue(curr.Score >= next.Score, string.Format("Result at index {0} must have a higher score than result at index {1}", i, i + 1)); + Assert.IsTrue(curr.Score >= next.Score, string.Format("Result at index {0} must have a higher score than result at index {1}", i, i + 1)); + } } } } - [Test] - public void Skip_Results_Returns_Different_Results() + [TestCase(true)] + [TestCase(false)] + public void Skip_Results_Returns_Different_Results(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -1502,20 +2259,38 @@ public void Skip_Results_Returns_Different_Results() //Arrange var sc = searcher.CreateQuery("content").Field("writerName", "administrator"); - //Act - var results = sc.Execute(); + if (withFacets) + { + //Act + var results = sc.WithFacet("nodeName").Execute(); - //Assert - Assert.AreNotEqual(results.First(), results.Skip(2).First(), "Third result should be different"); + var facetResults = results.GetFacet("nodeName"); + + //Assert + Assert.AreNotEqual(results.First(), results.Skip(2).First(), "Third result should be different"); + Assert.AreEqual(1, facetResults.Count()); + } + else + { + //Act + var results = sc.Execute(); + + //Assert + Assert.AreNotEqual(results.First(), results.Skip(2).First(), "Third result should be different"); + } } } - [Test] - public void Escaping_Includes_All_Words() + [TestCase(true)] + [TestCase(false)] + public void Escaping_Includes_All_Words(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -1535,23 +2310,42 @@ public void Escaping_Includes_All_Words() Console.WriteLine(sc.ToString()); - //Act - var results = sc.Execute(); + if (withFacets) + { + //Act + var results = sc.WithFacet("nodeName").Execute(); - //Assert - //NOTE: The result is 2 because the double space is removed with the analyzer - Assert.AreEqual(2, results.TotalItemCount); + var facetResults = results.GetFacet("nodeName"); + + //Assert + //NOTE: The result is 2 because the double space is removed with the analyzer + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + } + else + { + //Act + var results = sc.Execute(); + + //Assert + //NOTE: The result is 2 because the double space is removed with the analyzer + Assert.AreEqual(2, results.TotalItemCount); + } } } - [Test] - public void Grouped_And_Examiness() + [TestCase(true)] + [TestCase(false)] + public void Grouped_And_Examiness(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -1575,20 +2369,38 @@ public void Grouped_And_Examiness() new[] { "CWS".MultipleCharacterWildcard(), "A".MultipleCharacterWildcard() }); - //Act - var results = filter.Execute(); + if (withFacets) + { + //Act + var results = filter.WithFacet("nodeName").Execute(); - //Assert - Assert.AreEqual(2, results.TotalItemCount); + var facetResults = results.GetFacet("nodeName"); + + //Assert + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, facetResults.Count()); + } + else + { + //Act + var results = filter.Execute(); + + //Assert + Assert.AreEqual(2, results.TotalItemCount); + } } } - [Test] - public void Examiness_Proximity() + [TestCase(true)] + [TestCase(false)] + public void Examiness_Proximity(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -1609,33 +2421,56 @@ public void Examiness_Proximity() //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)); - //Act - var results = filter.Execute(); - - foreach (var r in results) + if (withFacets) { - Console.WriteLine($"Id = {r.Id}"); + //Act + var results = filter.WithFacet("nodeName").And().Execute(); + + var facetResults = results.GetFacet("nodeName"); + + foreach (var r in results) + { + Console.WriteLine($"Id = {r.Id}"); + } + + //Assert + Assert.AreEqual(3, results.TotalItemCount); + Assert.AreEqual(3, facetResults.Count()); } + else + { + //Act + var results = filter.Execute(); - //Assert - Assert.AreEqual(3, results.TotalItemCount); + foreach (var r in results) + { + Console.WriteLine($"Id = {r.Id}"); + } + + //Assert + Assert.AreEqual(3, results.TotalItemCount); + } } } /// /// test range query with a Float structure /// - [Test] - public void Float_Range_SimpleIndexSet() + [TestCase(true)] + [TestCase(false)] + public void Float_Range_SimpleIndexSet(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("SomeFloat", FieldDefinitionTypes.FacetFloat)) + : new FieldDefinitionCollection(new FieldDefinition("SomeFloat", FieldDefinitionTypes.Float)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, //Ensure it's set to a float - new FieldDefinitionCollection(new FieldDefinition("SomeFloat", FieldDefinitionTypes.Float)))) + fieldDefinitionCollection)) { @@ -1659,13 +2494,42 @@ public void Float_Range_SimpleIndexSet() var criteria2 = searcher.CreateQuery(); var filter2 = criteria2.RangeQuery(new[] { "SomeFloat" }, 101f, 200f, true, true); - //Act - var results1 = filter1.Execute(); - var results2 = filter2.Execute(); + if (withFacets) + { - //Assert - Assert.AreEqual(3, results1.TotalItemCount); - Assert.AreEqual(1, results2.TotalItemCount); + //Act + var results1 = filter1.WithFacet("SomeFloat", new DoubleRange[] + { + new DoubleRange("1", 0, true, 12, true), + new DoubleRange("2", 13, true, 250, true) + }).IsFloat(true).Execute(); + var results2 = filter2.WithFacet("SomeFloat", new DoubleRange[] + { + new DoubleRange("1", 0, true, 12, true), + new DoubleRange("2", 13, true, 250, true) + }).IsFloat(true).Execute(); + + var facetResults1 = results1.GetFacet("SomeFloat"); + var facetResults2 = results2.GetFacet("SomeFloat"); + + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + Assert.AreEqual(2, facetResults1.Facet("1").Value); + Assert.AreEqual(1, facetResults1.Facet("2").Value); + Assert.AreEqual(0, facetResults2.Facet("1").Value); + Assert.AreEqual(1, facetResults2.Facet("2").Value); + } + else + { + //Act + var results1 = filter1.Execute(); + var results2 = filter2.Execute(); + + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + } } @@ -1674,16 +2538,20 @@ public void Float_Range_SimpleIndexSet() /// /// test range query with a Number structure /// - [Test] - public void Number_Range_SimpleIndexSet() + [TestCase(true)] + [TestCase(false)] + public void Number_Range_SimpleIndexSet(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("SomeNumber", FieldDefinitionTypes.FacetInteger)) + : new FieldDefinitionCollection(new FieldDefinition("SomeNumber", FieldDefinitionTypes.Integer)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, //Ensure it's set to a float - new FieldDefinitionCollection(new FieldDefinition("SomeNumber", FieldDefinitionTypes.Integer)))) + fieldDefinitionCollection)) { @@ -1707,29 +2575,51 @@ public void Number_Range_SimpleIndexSet() var criteria2 = searcher.CreateQuery(); var filter2 = criteria2.RangeQuery(new[] { "SomeNumber" }, 101, 200, true, true); - //Act - var results1 = filter1.Execute(); - var results2 = filter2.Execute(); + if (withFacets) + { + //Act + var results1 = filter1.WithFacet("SomeNumber").MaxCount(1).Execute(); + var results2 = filter2.WithFacet("SomeNumber").MaxCount(1).Execute(); + + var facetResults1 = results1.GetFacet("SomeNumber"); + var facetResults2 = results2.GetFacet("SomeNumber"); + + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + Assert.AreEqual(1, facetResults1.Count()); + Assert.AreEqual(1, facetResults2.Count()); + } + else + { + //Act + var results1 = filter1.Execute(); + var results2 = filter2.Execute(); - //Assert - Assert.AreEqual(3, results1.TotalItemCount); - Assert.AreEqual(1, results2.TotalItemCount); + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + } } } /// /// test range query with a Number structure /// - [Test] - public void Double_Range_SimpleIndexSet() + [TestCase(true)] + [TestCase(false)] + public void Double_Range_SimpleIndexSet(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("SomeDouble", FieldDefinitionTypes.FacetDouble)) + : new FieldDefinitionCollection(new FieldDefinition("SomeDouble", FieldDefinitionTypes.Double)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, //Ensure it's set to a float - new FieldDefinitionCollection(new FieldDefinition("SomeDouble", FieldDefinitionTypes.Double)))) + fieldDefinitionCollection)) { @@ -1753,29 +2643,61 @@ public void Double_Range_SimpleIndexSet() var criteria2 = searcher.CreateQuery("content"); var filter2 = criteria2.RangeQuery(new[] { "SomeDouble" }, 101d, 200d, true, true); - //Act - var results1 = filter1.Execute(); - var results2 = filter2.Execute(); + if (withFacets) + { + //Act + var results1 = filter1.WithFacet("SomeDouble", new DoubleRange[] + { + new DoubleRange("1", 0, true, 100, true), + new DoubleRange("2", 101, true, 200, true) + }).Execute(); + var results2 = filter2.WithFacet("SomeDouble", new DoubleRange[] + { + new DoubleRange("1", 0, true, 100, true), + new DoubleRange("2", 101, true, 200, true) + }).Execute(); + + var facetResults1 = results1.GetFacet("SomeDouble"); + var facetResults2 = results2.GetFacet("SomeDouble"); + + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + Assert.AreEqual(3, facetResults1.Facet("1").Value); + Assert.AreEqual(0, facetResults1.Facet("2").Value); + Assert.AreEqual(0, facetResults2.Facet("1").Value); + Assert.AreEqual(1, facetResults2.Facet("2").Value); + } + else + { + //Act + var results1 = filter1.Execute(); + var results2 = filter2.Execute(); - //Assert - Assert.AreEqual(3, results1.TotalItemCount); - Assert.AreEqual(1, results2.TotalItemCount); + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + } } } /// /// test range query with a Double structure /// - [Test] - public void Long_Range_SimpleIndexSet() + [TestCase(true)] + [TestCase(false)] + public void Long_Range_SimpleIndexSet(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("SomeLong", FieldDefinitionTypes.FacetLong)) + : new FieldDefinitionCollection(new FieldDefinition("SomeLong", FieldDefinitionTypes.Long)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, //Ensure it's set to a float - new FieldDefinitionCollection(new FieldDefinition("SomeLong", "long")))) + fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -1797,13 +2719,41 @@ public void Long_Range_SimpleIndexSet() var criteria2 = searcher.CreateQuery(); var filter2 = criteria2.RangeQuery(new[] { "SomeLong" }, 101L, 200L, true, true); - //Act - var results1 = filter1.Execute(); - var results2 = filter2.Execute(); + if (withFacets) + { + //Act + var results1 = filter1.WithFacet("SomeLong", new Int64Range[] + { + new Int64Range("1", 0L, true, 100L, true), + new Int64Range("2", 101L, true, 200L, true) + }).Execute(); + var results2 = filter2.WithFacet("SomeLong", new Int64Range[] + { + new Int64Range("1", 0L, true, 100L, true), + new Int64Range("2", 101L, true, 200L, true) + }).Execute(); + + var facetResults1 = results1.GetFacet("SomeLong"); + var facetResults2 = results2.GetFacet("SomeLong"); + + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + Assert.AreEqual(3, facetResults1.Facet("1").Value); + Assert.AreEqual(0, facetResults1.Facet("2").Value); + Assert.AreEqual(0, facetResults2.Facet("1").Value); + Assert.AreEqual(1, facetResults2.Facet("2").Value); + } + else + { + //Act + var results1 = filter1.Execute(); + var results2 = filter2.Execute(); - //Assert - Assert.AreEqual(3, results1.TotalItemCount); - Assert.AreEqual(1, results2.TotalItemCount); + //Assert + Assert.AreEqual(3, results1.TotalItemCount); + Assert.AreEqual(1, results2.TotalItemCount); + } } } @@ -1812,9 +2762,13 @@ public void Long_Range_SimpleIndexSet() /// /// Test range query with a DateTime structure /// - [Test] - public void Date_Range_SimpleIndexSet() + [TestCase(true)] + [TestCase(false)] + public void Date_Range_SimpleIndexSet(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("DateCreated", FieldDefinitionTypes.FacetDateTime)) + : new FieldDefinitionCollection(new FieldDefinition("DateCreated", FieldDefinitionTypes.DateTime)); var reIndexDateTime = DateTime.Now; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); @@ -1822,7 +2776,7 @@ public void Date_Range_SimpleIndexSet() using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("DateCreated", "datetime")))) + fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -1843,24 +2797,56 @@ public void Date_Range_SimpleIndexSet() var criteria2 = searcher.CreateQuery(); var filter2 = criteria2.RangeQuery(new[] { "DateCreated" }, reIndexDateTime.AddDays(-1), reIndexDateTime.AddSeconds(-1), true, true); - ////Act - var results = filter.Execute(); - var results2 = filter2.Execute(); + if (withFacets) + { + ////Act + var results = filter.WithFacet("DateCreated", new Int64Range[] + { + new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), + new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) + }).Execute(); + var results2 = filter2.WithFacet("DateCreated", new Int64Range[] + { + new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), + new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) + }).Execute(); + + var facetResults1 = results.GetFacet("DateCreated"); + var facetResults2 = results2.GetFacet("DateCreated"); + + ////Assert + Assert.IsTrue(results.TotalItemCount > 0); + Assert.IsTrue(results2.TotalItemCount == 0); + Assert.AreEqual(3, facetResults1.Facet("1").Value); + Assert.AreEqual(0, facetResults1.Facet("2").Value); + Assert.AreEqual(0, facetResults2.Facet("1").Value); + Assert.AreEqual(0, facetResults2.Facet("2").Value); + } + else + { + ////Act + var results = filter.Execute(); + var results2 = filter2.Execute(); - ////Assert - Assert.IsTrue(results.TotalItemCount > 0); - Assert.IsTrue(results2.TotalItemCount == 0); + ////Assert + Assert.IsTrue(results.TotalItemCount > 0); + Assert.IsTrue(results2.TotalItemCount == 0); + } } } - [Test] - public void Fuzzy_Search() + [TestCase(true)] + [TestCase(false)] + public void Fuzzy_Search(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("Content", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new EnglishAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -1884,23 +2870,51 @@ public void Fuzzy_Search() Console.WriteLine(filter); Console.WriteLine(filter2); - ////Act - var results = filter.Execute(); - var results2 = filter2.Execute(); - - foreach (var r in results) + if (withFacets) { - Console.WriteLine($"Result Id: {r.Id}"); - } + ////Act + var results = filter.WithFacet("Content").Execute(); + var results2 = filter2.WithFacet("Content").Execute(); - foreach (var r in results2) - { - Console.WriteLine($"Result2 Id: {r.Id}"); + var facetResults1 = results.GetFacet("Content"); + var facetResults2 = results2.GetFacet("Content"); + + foreach (var r in results) + { + Console.WriteLine($"Result Id: {r.Id}"); + } + + foreach (var r in results2) + { + Console.WriteLine($"Result2 Id: {r.Id}"); + } + + ////Assert + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, results2.TotalItemCount); + Assert.AreEqual(2, facetResults1.Count()); + Assert.AreEqual(2, facetResults2.Count()); } + else + { + ////Act + var results = filter.Execute(); + var results2 = filter2.Execute(); + + foreach (var r in results) + { + Console.WriteLine($"Result Id: {r.Id}"); + } + + foreach (var r in results2) + { + Console.WriteLine($"Result2 Id: {r.Id}"); + } - ////Assert - Assert.AreEqual(2, results.TotalItemCount); - Assert.AreEqual(2, results2.TotalItemCount); + ////Assert + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(2, results2.TotalItemCount); + } } } @@ -1969,12 +2983,16 @@ public void Execute_With_Take_Max_Results() } - [Test] - public void Inner_Or_Query() + [TestCase(true)] + [TestCase(false)] + public void Inner_Or_Query(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { @@ -2001,20 +3019,38 @@ public void Inner_Or_Query() var filter = criteria.Field("Type", "type1") .And(query => query.Field("Content", "world").Or().Field("Content", "something"), BooleanOperation.Or); - //Act - var results = filter.Execute(); + if (withFacets) + { + //Act + var results = filter.WithFacet("Type").Execute(); - //Assert - Assert.AreEqual(2, results.TotalItemCount); + var facetResults = results.GetFacet("Type"); + + //Assert + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + else + { + //Act + var results = filter.Execute(); + + //Assert + Assert.AreEqual(2, results.TotalItemCount); + } } } - [Test] - public void Inner_And_Query() + [TestCase(true)] + [TestCase(false)] + public void Inner_And_Query(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { @@ -2043,20 +3079,38 @@ public void Inner_And_Query() var filter = criteria.Field("Type", "type1") .And(query => query.Field("Content", "world").And().Field("Content", "hello")); - //Act - var results = filter.Execute(); + if (withFacets) + { + //Act + var results = filter.WithFacet("Type").Execute(); - //Assert - Assert.AreEqual(2, results.TotalItemCount); + var facetResults = results.GetFacet("Type"); + + //Assert + Assert.AreEqual(2, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + else + { + //Act + var results = filter.Execute(); + + //Assert + Assert.AreEqual(2, results.TotalItemCount); + } } } - [Test] - public void Inner_Not_Query() + [TestCase(true)] + [TestCase(false)] + public void Inner_Not_Query(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { @@ -2085,20 +3139,38 @@ public void Inner_Not_Query() var filter = criteria.Field("Type", "type1") .And(query => query.Field("Content", "world").Not().Field("Content", "something")); - //Act - var results = filter.Execute(); + if (withFacets) + { + //Act + var results = filter.WithFacet("Type").Execute(); - //Assert - Assert.AreEqual(1, results.TotalItemCount); + var facetResults = results.GetFacet("Type"); + + //Assert + Assert.AreEqual(1, results.TotalItemCount); + Assert.AreEqual(1, facetResults.Count()); + } + else + { + //Act + var results = filter.Execute(); + + //Assert + Assert.AreEqual(1, results.TotalItemCount); + } } } - [Test] - public void Complex_Or_Group_Nested_Query() + [TestCase(true)] + [TestCase(false)] + public void Complex_Or_Group_Nested_Query(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("Type", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -2133,21 +3205,42 @@ public void Complex_Or_Group_Nested_Query() Console.WriteLine(filter); - //Act - var results = filter.Execute(); + if (withFacets) + { - //Assert - foreach (var r in results) + //Act + var results = filter.WithFacet("Type").Execute(); + + var facetResults = results.GetFacet("Type"); + + //Assert + foreach (var r in results) + { + Console.WriteLine($"Result Id: {r.Id}"); + } + Assert.AreEqual(3, results.TotalItemCount); + + Assert.AreEqual(2, facetResults.Count()); + } + else { - Console.WriteLine($"Result Id: {r.Id}"); + //Act + var results = filter.Execute(); + + //Assert + foreach (var r in results) + { + Console.WriteLine($"Result Id: {r.Id}"); + } + Assert.AreEqual(3, results.TotalItemCount); } - Assert.AreEqual(3, results.TotalItemCount); } } - [Test] - public void Custom_Lucene_Query_With_Native() + [TestCase(true)] + [TestCase(false)] + public void Custom_Lucene_Query_With_Native(bool withFacets) { var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) @@ -2159,7 +3252,14 @@ public void Custom_Lucene_Query_With_Native() //combine a custom lucene query with raw lucene query var op = criteria.NativeQuery("hello:world").And(); - criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); + if (withFacets) + { + criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)).WithFacet("SomeFacet"); + } + else + { + criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); + } Console.WriteLine(criteria.Query); Assert.AreEqual("+hello:world +numTest:[4 TO 5]", criteria.Query.ToString()); @@ -2263,12 +3363,16 @@ public void Category() //} - [Test] - public void Select_Field() + [TestCase(true)] + [TestCase(false)] + public void Select_Field(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { @@ -2294,20 +3398,39 @@ public void Select_Field() var sc = searcher.CreateQuery("content"); var sc1 = sc.Field("nodeName", "my name 1").SelectField("__Path"); - var results = sc1.Execute(); - var expectedLoadedFields = new string[] { "__Path" }; - var keys = results.First().Values.Keys.ToArray(); - Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); - Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + if (withFacets) + { + var results = sc1.WithFacet("nodeName").Execute(); + var facetResults = results.GetFacet("nodeName"); + + var expectedLoadedFields = new string[] { "__Path" }; + var keys = results.First().Values.Keys.ToArray(); + Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + + Assert.AreEqual(2, facetResults.Count()); + } + else + { + var results = sc1.Execute(); + var expectedLoadedFields = new string[] { "__Path" }; + var keys = results.First().Values.Keys.ToArray(); + Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + } } } - [Test] - public void Select_Fields() + [TestCase(true)] + [TestCase(false)] + public void Select_Fields(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { @@ -2333,21 +3456,40 @@ public void Select_Fields() var sc = searcher.CreateQuery("content"); var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new[] { "nodeName", "bodyText", "id", "__NodeId" })); - var results = sc1.Execute(); - var expectedLoadedFields = new string[] { "nodeName", "bodyText", "id", "__NodeId" }; - var keys = results.First().Values.Keys.ToArray(); - Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); - Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + if (withFacets) + { + var results = sc1.WithFacet("nodeName").Execute(); + var facetResults = results.GetFacet("nodeName"); + + var expectedLoadedFields = new string[] { "nodeName", "bodyText", "id", "__NodeId" }; + var keys = results.First().Values.Keys.ToArray(); + Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + + Assert.AreEqual(2, facetResults.Count()); + } + else + { + var results = sc1.Execute(); + var expectedLoadedFields = new string[] { "nodeName", "bodyText", "id", "__NodeId" }; + var keys = results.First().Values.Keys.ToArray(); + Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + } } } - [Test] - public void Select_Fields_HashSet() + [TestCase(true)] + [TestCase(false)] + public void Select_Fields_HashSet(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { @@ -2373,11 +3515,26 @@ public void Select_Fields_HashSet() var sc = searcher.CreateQuery("content"); var sc1 = sc.Field("nodeName", "my name 1").SelectFields(new HashSet(new string[] { "nodeName", "bodyText" })); - var results = sc1.Execute(); - var expectedLoadedFields = new string[] { "nodeName", "bodyText" }; - var keys = results.First().Values.Keys.ToArray(); - Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); - Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + if (withFacets) + { + var results = sc1.WithFacet("nodeName").Execute(); + var facetResults = results.GetFacet("nodeName"); + + var expectedLoadedFields = new string[] { "nodeName", "bodyText" }; + var keys = results.First().Values.Keys.ToArray(); + Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + + Assert.AreEqual(2, facetResults.Count()); + } + else + { + var results = sc1.Execute(); + var expectedLoadedFields = new string[] { "nodeName", "bodyText" }; + var keys = results.First().Values.Keys.ToArray(); + Assert.True(keys.All(x => expectedLoadedFields.Contains(x))); + Assert.True(expectedLoadedFields.All(x => keys.Contains(x))); + } } } @@ -2456,12 +3613,16 @@ public void Can_Skip() } } - [Test] - public void Paging_With_Skip_Take() + [TestCase(true)] + [TestCase(false)] + public void Paging_With_Skip_Take(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("writerName", FieldDefinitionTypes.FacetFullText)) + : null; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { indexer.IndexItems(new[] { ValueSet.FromObject(1.ToString(), "content", @@ -2487,54 +3648,109 @@ public void Paging_With_Skip_Take() int pageSize = 2; //Act + if (withFacets) + { - var results = sc - .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) - .ToList(); - Assert.AreEqual(2, results.Count); + var results = sc.WithFacet("writerName") + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); + Assert.AreEqual(2, results.Count()); + var facetResults = results.GetFacet("writerName"); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(5, facetResults.Facet("administrator").Value); + + pageIndex++; + + results = sc + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); + Assert.AreEqual(2, results.Count()); + facetResults = results.GetFacet("writerName"); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(5, facetResults.Facet("administrator").Value); + + pageIndex++; + + results = sc + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); + Assert.AreEqual(1, results.Count()); + facetResults = results.GetFacet("writerName"); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(5, facetResults.Facet("administrator").Value); + + pageIndex++; + + results = sc + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); + Assert.AreEqual(0, results.Count()); + facetResults = results.GetFacet("writerName"); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(5, facetResults.Facet("administrator").Value); + } + else + { + var results = sc + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) + .ToList(); + Assert.AreEqual(2, results.Count); - pageIndex++; + pageIndex++; - results = sc - .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) - .ToList(); - Assert.AreEqual(2, results.Count); + results = sc + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) + .ToList(); + Assert.AreEqual(2, results.Count); - pageIndex++; + pageIndex++; - results = sc - .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) - .ToList(); - Assert.AreEqual(1, results.Count); + results = sc + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) + .ToList(); + Assert.AreEqual(1, results.Count); - pageIndex++; + pageIndex++; - results = sc - .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) - .ToList(); - Assert.AreEqual(0, results.Count); + results = sc + .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)) + .ToList(); + Assert.AreEqual(0, results.Count); + } } } - [TestCase(0, 1, 1)] - [TestCase(0, 2, 2)] - [TestCase(0, 3, 3)] - [TestCase(0, 4, 4)] - [TestCase(0, 5, 5)] - [TestCase(0, 100, 5)] - [TestCase(1, 1, 1)] - [TestCase(1, 2, 2)] - [TestCase(1, 3, 3)] - [TestCase(1, 4, 4)] - [TestCase(1, 5, 4)] - [TestCase(2, 2, 2)] - [TestCase(2, 5, 3)] - public void Given_SkipTake_Returns_ExpectedTotals(int skip, int take, int expectedResults) - { + [TestCase(0, 1, 1, true)] + [TestCase(0, 2, 2, true)] + [TestCase(0, 3, 3, true)] + [TestCase(0, 4, 4, true)] + [TestCase(0, 5, 5, true)] + [TestCase(0, 100, 5, true)] + [TestCase(1, 1, 1, true)] + [TestCase(1, 2, 2, true)] + [TestCase(1, 3, 3, true)] + [TestCase(1, 4, 4, true)] + [TestCase(1, 5, 4, true)] + [TestCase(2, 2, 2, true)] + [TestCase(2, 5, 3, true)] + [TestCase(0, 1, 1, false)] + [TestCase(0, 2, 2, false)] + [TestCase(0, 3, 3, false)] + [TestCase(0, 4, 4, false)] + [TestCase(0, 5, 5, false)] + [TestCase(0, 100, 5, false)] + [TestCase(1, 1, 1, false)] + [TestCase(1, 2, 2, false)] + [TestCase(1, 3, 3, false)] + [TestCase(1, 4, 4, false)] + [TestCase(1, 5, 4, false)] + [TestCase(2, 2, 2, false)] + [TestCase(2, 5, 3, false)] + public void Given_SkipTake_Returns_ExpectedTotals(int skip, int take, int expectedResults, bool withFacets) + { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)) + : null; const int indexSize = 5; var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) - using (var indexer = GetTestIndex(luceneDir, analyzer)) + using (var indexer = GetTestIndex(luceneDir, analyzer, fieldDefinitionCollection)) { var items = Enumerable.Range(0, indexSize).Select(x => ValueSet.FromObject(x.ToString(), "content", new { nodeName = "umbraco", headerText = "world", writerName = "administrator" })); @@ -2549,23 +3765,41 @@ public void Given_SkipTake_Returns_ExpectedTotals(int skip, int take, int expect //Act - var results = sc.Execute(QueryOptions.SkipTake(skip, take)); + if (withFacets) + { + var results = sc.WithFacet("nodeName").Execute(QueryOptions.SkipTake(skip, take)); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(indexSize, results.TotalItemCount); + Assert.AreEqual(expectedResults, results.Count()); + Assert.AreEqual(1, facetResults.Count()); + Assert.AreEqual(5, facetResults.Facet("umbraco").Value); + } + else + { + var results = sc.Execute(QueryOptions.SkipTake(skip, take)); - Assert.AreEqual(indexSize, results.TotalItemCount); - Assert.AreEqual(expectedResults, results.Count()); + Assert.AreEqual(indexSize, results.TotalItemCount); + Assert.AreEqual(expectedResults, results.Count()); + } } } #if NET6_0_OR_GREATER - [Test] - public void Range_DateOnly() + [TestCase(true)] + [TestCase(false)] + public void Range_DateOnly(bool withFacets) { + var fieldDefinitionCollection = withFacets ? + new FieldDefinitionCollection(new FieldDefinition("created", FieldDefinitionTypes.FacetDateTime)) + : new FieldDefinitionCollection(new FieldDefinition("created", FieldDefinitionTypes.DateTime)); var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) using (var indexer = GetTestIndex( luceneDir, analyzer, - new FieldDefinitionCollection(new FieldDefinition("created", "datetime")))) + fieldDefinitionCollection)) { @@ -2600,9 +3834,20 @@ public void Range_DateOnly() var numberSortedCriteria = searcher.CreateQuery() .RangeQuery(new[] { "created" }, new DateOnly(2000, 01, 02), new DateOnly(2000, 01, 05), maxInclusive: false); - var numberSortedResult = numberSortedCriteria.Execute(); + if (withFacets) + { + var numberSortedResult = numberSortedCriteria.WithFacet("created").Execute(); + var facetResult = numberSortedResult.GetFacet("created"); + + Assert.AreEqual(2, numberSortedResult.TotalItemCount); + Assert.AreEqual(2, facetResult.Count()); + } + else + { + var numberSortedResult = numberSortedCriteria.Execute(); - Assert.AreEqual(2, numberSortedResult.TotalItemCount); + Assert.AreEqual(2, numberSortedResult.TotalItemCount); + } } } @@ -2655,8 +3900,9 @@ public void Range_DateOnly_Min_And_Max_Inclusive() } } - [Test] - public void Range_DateOnly_No_Inclusive() + [TestCase(true)] + [TestCase(false)] + public void Range_DateOnly_No_Inclusive(bool withFacets) { var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) From 8d7a9d958287e63e08b48b77b6cef8f4902330af Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Thu, 15 Dec 2022 09:46:55 +0100 Subject: [PATCH 15/42] docs: Update code examples --- docs/searching.md | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/docs/searching.md b/docs/searching.md index 59edcd21a..af30118bc 100644 --- a/docs/searching.md +++ b/docs/searching.md @@ -260,7 +260,6 @@ Basic example var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") - .And() .WithFacet("Address") // Get facets of the Address field .Execute(); @@ -280,8 +279,7 @@ Filtered value example var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") - .And() - .Facet("Address", "Hills") // Get facets of the Address field + .WithFacet("Address", "Hills") // Get facets of the Address field .Execute(); var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -291,7 +289,7 @@ var addressFacetResults = results.GetFacet("Address"); // Returns the facets for * Label: Hills, Value: 2 <-- As Hills was the only filtered value we will only get this facet */ -var hillsValue = addressFacetResults.WithFacet("Hills"); // Gets the IFacetValue for the facet Hills +var hillsValue = addressFacetResults.Facet("Hills"); // Gets the IFacetValue for the facet Hills ``` MaxCount example @@ -299,7 +297,6 @@ MaxCount example var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") - .And() .WithFacet("Address") // Get facets of the Address field .Execute(); @@ -314,7 +311,6 @@ var addressFacetResults = results.GetFacet("Address"); // Returns the facets for results = searcher.CreateQuery() .Field("Address", "Hills") - .And() .WithFacet("Address") // Get facets of the Address field .MaxCount(2) // Gets the top 2 results (The facets with the highest value) .Execute(); @@ -351,7 +347,6 @@ services.AddExamineLuceneIndex("MyIndex", var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") - .And() .WithFacet("Address") // Get facets of the Address field .FacetField("address_facet") .Execute(); @@ -375,12 +370,11 @@ Double range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() + .All() .WithFacet("Price", new DoubleRange[] { new DoubleRange("0-10", 0, true, 10, true), new DoubleRange("11-20", 11, true, 20, true) }) // Get facets of the price field - .And() - .All() .Execute(); var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the specific field Price @@ -391,20 +385,20 @@ var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the * Label: 11-20, Value: 10 */ -var firstRangeValue = priceFacetResults.WithFacet("0-10"); // Gets the IFacetValue for the facet "0-10" +var firstRangeValue = priceFacetResults.Facet("0-10"); // Gets the IFacetValue for the facet "0-10" ``` Float range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() + .All() .WithFacet("Price", new DoubleRange[] { new DoubleRange("0-10", 0, true, 10, true), new DoubleRange("11-20", 11, true, 20, true) }) // Get facets of the price field .IsFloat(true) // Marks that the underlying field is a float .And() - .All() .Execute(); var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the specific field Price @@ -415,19 +409,18 @@ var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the * Label: 11-20, Value: 10 */ -var firstRangeValue = priceFacetResults.WithFacet("0-10"); // Gets the IFacetValue for the facet "0-10" +var firstRangeValue = priceFacetResults.Facet("0-10"); // Gets the IFacetValue for the facet "0-10" ``` Int/Long range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() + .All() .WithFacet("Price", new Int64Range[] { new Int64Range("0-10", 0, true, 10, true), new Int64Range("11-20", 11, true, 20, true) }) // Get facets of the price field - .And() - .All() .Execute(); var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the specific field Price @@ -438,19 +431,18 @@ var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the * Label: 11-20, Value: 10 */ -var firstRangeValue = priceFacetResults.WithFacet("0-10"); // Gets the IFacetValue for the facet "0-10" +var firstRangeValue = priceFacetResults.Facet("0-10"); // Gets the IFacetValue for the facet "0-10" ``` DateTime range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() + .All() .WithFacet("Created", new Int64Range[] { new Int64Range("first", DateTime.UtcNow.AddDays(-1).Ticks, true, DateTime.UtcNow.Ticks, true), new Int64Range("last", DateTime.UtcNow.AddDays(1).Ticks, true, DateTime.UtcNow.AddDays(2).Ticks, true) }) // Get facets of the price field - .And() - .All() .Execute(); var createdFacetResults = results.GetFacet("Created"); // Returns the facets for the specific field Created @@ -461,4 +453,4 @@ var createdFacetResults = results.GetFacet("Created"); // Returns the facets for * Label: last, Value: 10 */ -var firstRangeValue = createdFacetResults.WithFacet("first"); // Gets the IFacetValue for the facet "first" \ No newline at end of file +var firstRangeValue = createdFacetResults.Facet("first"); // Gets the IFacetValue for the facet "first" \ No newline at end of file From 5a9cd43e84fe319996b1a29d19eea967650176fb Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Thu, 15 Dec 2022 12:09:37 +0100 Subject: [PATCH 16/42] refactor: Use default facets name constant for FacetField --- src/Examine.Core/Search/FacetDoubleField.cs | 3 ++- src/Examine.Core/Search/FacetLongField.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Examine.Core/Search/FacetDoubleField.cs b/src/Examine.Core/Search/FacetDoubleField.cs index bb9893715..65de894f4 100644 --- a/src/Examine.Core/Search/FacetDoubleField.cs +++ b/src/Examine.Core/Search/FacetDoubleField.cs @@ -12,7 +12,7 @@ public class FacetDoubleField : IFacetDoubleField public string Field { get; } - public string FacetField { get; set; } = "$facets"; + public string FacetField { get; set; } public bool IsFloat { get; set; } @@ -20,6 +20,7 @@ public FacetDoubleField(string field, DoubleRange[] doubleRanges) { Field = field; DoubleRanges = doubleRanges; + FacetField = ExamineFieldNames.DefaultFacetsName; } } } diff --git a/src/Examine.Core/Search/FacetLongField.cs b/src/Examine.Core/Search/FacetLongField.cs index 26bc8b646..5b11e1a0c 100644 --- a/src/Examine.Core/Search/FacetLongField.cs +++ b/src/Examine.Core/Search/FacetLongField.cs @@ -8,12 +8,13 @@ public class FacetLongField : IFacetLongField public Int64Range[] LongRanges { get; } - public string FacetField { get; set; } = "$facets"; + public string FacetField { get; set; } public FacetLongField(string field, Int64Range[] longRanges) { Field = field; LongRanges = longRanges; + FacetField = ExamineFieldNames.DefaultFacetsName; } } } From a079aaa510acd3d054c493c9fc722635ac21e51d Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Thu, 15 Dec 2022 12:49:49 +0100 Subject: [PATCH 17/42] refactor: Remove Lucene.Net.Facet from core project --- src/Examine.Core/Examine.Core.csproj | 1 - src/Examine.Core/Search/DoubleRange.cs | 43 +++++++++++++++++++ src/Examine.Core/Search/FacetDoubleField.cs | 1 - src/Examine.Core/Search/FacetLongField.cs | 2 - src/Examine.Core/Search/IFacetDoubleField.cs | 1 - src/Examine.Core/Search/IFacetLongField.cs | 1 - src/Examine.Core/Search/IFaceting.cs | 1 - src/Examine.Core/Search/IQuery.cs | 1 - src/Examine.Core/Search/Int64Range.cs | 43 +++++++++++++++++++ src/Examine.Lucene/FacetExtensions.cs | 31 +++++++++++++ .../Search/LuceneBooleanOperation.cs | 1 - .../Search/LuceneBooleanOperationBase.cs | 1 - .../Search/LuceneSearchExecutor.cs | 6 +-- .../Search/LuceneSearchQuery.cs | 1 - .../Examine.Lucene/Search/FluentApiTests.cs | 2 - 15 files changed, 120 insertions(+), 16 deletions(-) create mode 100644 src/Examine.Core/Search/DoubleRange.cs create mode 100644 src/Examine.Core/Search/Int64Range.cs diff --git a/src/Examine.Core/Examine.Core.csproj b/src/Examine.Core/Examine.Core.csproj index c90d38c33..d25704669 100644 --- a/src/Examine.Core/Examine.Core.csproj +++ b/src/Examine.Core/Examine.Core.csproj @@ -12,7 +12,6 @@ - diff --git a/src/Examine.Core/Search/DoubleRange.cs b/src/Examine.Core/Search/DoubleRange.cs new file mode 100644 index 000000000..0b4abfce2 --- /dev/null +++ b/src/Examine.Core/Search/DoubleRange.cs @@ -0,0 +1,43 @@ +namespace Examine.Search +{ + /// + /// Represents a range over values. + /// + public class DoubleRange + { + /// + public DoubleRange(string label, double min, bool minInclusive, double max, bool maxInclusive) + { + Label = label; + Min = min; + MinInclusive = minInclusive; + Max = max; + MaxInclusive = maxInclusive; + } + + /// + /// Label that identifies this range. + /// + public string Label { get; set; } + + /// + /// Minimum. + /// + public double Min { get; set; } + + /// + /// True if the minimum value is inclusive. + /// + public bool MinInclusive { get; set; } + + /// + /// Maximum. + /// + public double Max { get; set; } + + /// + /// True if the maximum value is inclusive. + /// + public bool MaxInclusive { get; set; } + } +} diff --git a/src/Examine.Core/Search/FacetDoubleField.cs b/src/Examine.Core/Search/FacetDoubleField.cs index 65de894f4..2d0e20fec 100644 --- a/src/Examine.Core/Search/FacetDoubleField.cs +++ b/src/Examine.Core/Search/FacetDoubleField.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text; using Examine.Lucene.Search; -using Lucene.Net.Facet.Range; namespace Examine.Search { diff --git a/src/Examine.Core/Search/FacetLongField.cs b/src/Examine.Core/Search/FacetLongField.cs index 5b11e1a0c..85d978716 100644 --- a/src/Examine.Core/Search/FacetLongField.cs +++ b/src/Examine.Core/Search/FacetLongField.cs @@ -1,5 +1,3 @@ -using Lucene.Net.Facet.Range; - namespace Examine.Search { public class FacetLongField : IFacetLongField diff --git a/src/Examine.Core/Search/IFacetDoubleField.cs b/src/Examine.Core/Search/IFacetDoubleField.cs index e78f6e3d4..2155a72cc 100644 --- a/src/Examine.Core/Search/IFacetDoubleField.cs +++ b/src/Examine.Core/Search/IFacetDoubleField.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text; using Examine.Lucene.Search; -using Lucene.Net.Facet.Range; namespace Examine.Search { diff --git a/src/Examine.Core/Search/IFacetLongField.cs b/src/Examine.Core/Search/IFacetLongField.cs index 0e8b5d538..8b4272817 100644 --- a/src/Examine.Core/Search/IFacetLongField.cs +++ b/src/Examine.Core/Search/IFacetLongField.cs @@ -1,5 +1,4 @@ using Examine.Lucene.Search; -using Lucene.Net.Facet.Range; namespace Examine.Search { diff --git a/src/Examine.Core/Search/IFaceting.cs b/src/Examine.Core/Search/IFaceting.cs index 463848c74..ab352f9cb 100644 --- a/src/Examine.Core/Search/IFaceting.cs +++ b/src/Examine.Core/Search/IFaceting.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Lucene.Net.Facet.Range; namespace Examine.Search { diff --git a/src/Examine.Core/Search/IQuery.cs b/src/Examine.Core/Search/IQuery.cs index 68bf669d1..7fdfb630c 100644 --- a/src/Examine.Core/Search/IQuery.cs +++ b/src/Examine.Core/Search/IQuery.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Lucene.Net.Facet.Range; namespace Examine.Search { diff --git a/src/Examine.Core/Search/Int64Range.cs b/src/Examine.Core/Search/Int64Range.cs new file mode 100644 index 000000000..843d94477 --- /dev/null +++ b/src/Examine.Core/Search/Int64Range.cs @@ -0,0 +1,43 @@ +namespace Examine.Search +{ + /// + /// Represents a range over values. + /// + public class Int64Range + { + /// + public Int64Range(string label, long min, bool minInclusive, long max, bool maxInclusive) + { + Label = label; + Min = min; + MinInclusive = minInclusive; + Max = max; + MaxInclusive = maxInclusive; + } + + /// + /// Label that identifies this range. + /// + public string Label { get; set; } + + /// + /// Minimum. + /// + public long Min { get; set; } + + /// + /// True if the minimum value is inclusive. + /// + public bool MinInclusive { get; set; } + + /// + /// Maximum. + /// + public long Max { get; set; } + + /// + /// True if the maximum value is inclusive. + /// + public bool MaxInclusive { get; set; } + } +} diff --git a/src/Examine.Lucene/FacetExtensions.cs b/src/Examine.Lucene/FacetExtensions.cs index 532de516a..863996c5a 100644 --- a/src/Examine.Lucene/FacetExtensions.cs +++ b/src/Examine.Lucene/FacetExtensions.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Text; using Examine.Lucene.Search; +using Lucene.Net.Facet.Range; namespace Examine.Lucene { @@ -33,5 +36,33 @@ public static IEnumerable GetFacets(this ISearchResults searchResu return facetResults.Facets.Values; } + + /// + /// Converts to the Lucene equivalent + /// + /// + /// + public static IEnumerable AsLuceneRange(this IEnumerable ranges) => ranges.Select(range => range.AsLuceneRange()); + + /// + /// Converts a to the Lucene equivalent + /// + /// + /// + public static Int64Range AsLuceneRange(this Examine.Search.Int64Range range) => new Int64Range(range.Label, range.Min, range.MinInclusive, range.Max, range.MaxInclusive); + + /// + /// Converts to the Lucene equivalent + /// + /// + /// + public static IEnumerable AsLuceneRange(this IEnumerable ranges) => ranges.Select(range => range.AsLuceneRange()); + + /// + /// Converts a to the Lucene equivalent + /// + /// + /// + public static DoubleRange AsLuceneRange(this Examine.Search.DoubleRange range) => new DoubleRange(range.Label, range.Min, range.MinInclusive, range.Max, range.MaxInclusive); } } diff --git a/src/Examine.Lucene/Search/LuceneBooleanOperation.cs b/src/Examine.Lucene/Search/LuceneBooleanOperation.cs index 6af43e7fe..b01430f6e 100644 --- a/src/Examine.Lucene/Search/LuceneBooleanOperation.cs +++ b/src/Examine.Lucene/Search/LuceneBooleanOperation.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using Examine.Lucene.Providers; using Examine.Search; -using Lucene.Net.Facet.Range; using Lucene.Net.Search; namespace Examine.Lucene.Search diff --git a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs index bd336a468..ead5ee13c 100644 --- a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs +++ b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Examine.Search; -using Lucene.Net.Facet.Range; using Lucene.Net.Search; namespace Examine.Lucene.Search diff --git a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs index 20512436b..4b20df1b0 100644 --- a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs +++ b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs @@ -156,7 +156,7 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector } else if (field is FacetLongField facetLongField) { - var longFacetCounts = new Int64RangeFacetCounts(facetLongField.Field, facetsCollector, facetLongField.LongRanges); + var longFacetCounts = new Int64RangeFacetCounts(facetLongField.Field, facetsCollector, facetLongField.LongRanges.AsLuceneRange().ToArray()); var longFacets = longFacetCounts.GetTopChildren(0, facetLongField.Field); @@ -172,11 +172,11 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector DoubleRangeFacetCounts doubleFacetCounts; if (facetDoubleField.IsFloat) { - doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, new SingleFieldSource(facetDoubleField.Field), facetsCollector, facetDoubleField.DoubleRanges); + doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, new SingleFieldSource(facetDoubleField.Field), facetsCollector, facetDoubleField.DoubleRanges.AsLuceneRange().ToArray()); } else { - doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, facetsCollector, facetDoubleField.DoubleRanges); + doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, facetsCollector, facetDoubleField.DoubleRanges.AsLuceneRange().ToArray()); } var doubleFacets = doubleFacetCounts.GetTopChildren(0, facetDoubleField.Field); diff --git a/src/Examine.Lucene/Search/LuceneSearchQuery.cs b/src/Examine.Lucene/Search/LuceneSearchQuery.cs index bd5600f0f..195973bd4 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQuery.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQuery.cs @@ -5,7 +5,6 @@ using Examine.Lucene.Indexing; using Examine.Search; using Lucene.Net.Analysis; -using Lucene.Net.Facet.Range; using Lucene.Net.Search; namespace Examine.Lucene.Search diff --git a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs index 76718397a..c4e4c684b 100644 --- a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs @@ -8,8 +8,6 @@ using J2N; using Lucene.Net.Analysis.En; using Lucene.Net.Analysis.Standard; -using Lucene.Net.Facet; -using Lucene.Net.Facet.Range; using Lucene.Net.QueryParsers.Classic; using Lucene.Net.Search; using NUnit.Framework; From 0efd647e10ed41d0838c09079339a0e5718d3ff2 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Fri, 16 Dec 2022 08:19:42 +0100 Subject: [PATCH 18/42] feat: Add FacetField methods to long and double fields --- src/Examine.Core/Search/FacetDoubleField.cs | 4 +- src/Examine.Core/Search/FacetLongField.cs | 4 +- .../Search/IFacetDoubleRangeQueryField.cs | 5 + .../Search/IFacetLongRangeQueryField.cs | 4 + .../Search/FacetDoubleRangeQueryField.cs | 8 + .../Search/FacetLongRangeQueryField.cs | 12 +- .../Examine.Lucene/Search/FluentApiTests.cs | 149 ++++++++++++++++++ src/Examine.Test/ExamineBaseTest.cs | 6 +- 8 files changed, 185 insertions(+), 7 deletions(-) diff --git a/src/Examine.Core/Search/FacetDoubleField.cs b/src/Examine.Core/Search/FacetDoubleField.cs index 2d0e20fec..ebf08fcf8 100644 --- a/src/Examine.Core/Search/FacetDoubleField.cs +++ b/src/Examine.Core/Search/FacetDoubleField.cs @@ -15,11 +15,11 @@ public class FacetDoubleField : IFacetDoubleField public bool IsFloat { get; set; } - public FacetDoubleField(string field, DoubleRange[] doubleRanges) + public FacetDoubleField(string field, DoubleRange[] doubleRanges, string facetField = ExamineFieldNames.DefaultFacetsName) { Field = field; DoubleRanges = doubleRanges; - FacetField = ExamineFieldNames.DefaultFacetsName; + FacetField = facetField; } } } diff --git a/src/Examine.Core/Search/FacetLongField.cs b/src/Examine.Core/Search/FacetLongField.cs index 85d978716..03badb747 100644 --- a/src/Examine.Core/Search/FacetLongField.cs +++ b/src/Examine.Core/Search/FacetLongField.cs @@ -8,11 +8,11 @@ public class FacetLongField : IFacetLongField public string FacetField { get; set; } - public FacetLongField(string field, Int64Range[] longRanges) + public FacetLongField(string field, Int64Range[] longRanges, string facetField = ExamineFieldNames.DefaultFacetsName) { Field = field; LongRanges = longRanges; - FacetField = ExamineFieldNames.DefaultFacetsName; + FacetField = facetField; } } } diff --git a/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs b/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs index e68300d70..6cc85d9fd 100644 --- a/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs +++ b/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs @@ -8,5 +8,10 @@ public interface IFacetDoubleRangeQueryField : IFacetAppending, IQueryExecutor /// /// IFacetDoubleRangeQueryField IsFloat(bool isFloat); + + /// + /// Sets the field where the facet information will be read from + /// + IFacetDoubleRangeQueryField FacetField(string fieldName); } } diff --git a/src/Examine.Core/Search/IFacetLongRangeQueryField.cs b/src/Examine.Core/Search/IFacetLongRangeQueryField.cs index 824c8a363..b4634c4ea 100644 --- a/src/Examine.Core/Search/IFacetLongRangeQueryField.cs +++ b/src/Examine.Core/Search/IFacetLongRangeQueryField.cs @@ -2,5 +2,9 @@ namespace Examine.Search { public interface IFacetLongRangeQueryField : IFacetAppending, IQueryExecutor { + /// + /// Sets the field where the facet information will be read from + /// + IFacetLongRangeQueryField FacetField(string fieldName); } } diff --git a/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs b/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs index d0751c4a1..8ea4d2304 100644 --- a/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs +++ b/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs @@ -22,5 +22,13 @@ public IFacetDoubleRangeQueryField IsFloat(bool isFloat) return this; } + + /// + public IFacetDoubleRangeQueryField FacetField(string fieldName) + { + _field.FacetField = fieldName; + + return this; + } } } diff --git a/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs b/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs index 2e8602e57..ea1c0664a 100644 --- a/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs +++ b/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs @@ -5,13 +5,23 @@ namespace Examine.Lucene.Search public class FacetLongRangeQueryField : IFacetLongRangeQueryField { private readonly LuceneSearchQuery _search; + private readonly FacetLongField _field; - public FacetLongRangeQueryField(LuceneSearchQuery search, FacetLongField _) + public FacetLongRangeQueryField(LuceneSearchQuery search, FacetLongField field) { _search = search; + _field = field; } public IOrdering And() => new LuceneBooleanOperation(_search); public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); + + /// + public IFacetLongRangeQueryField FacetField(string fieldName) + { + _field.FacetField = fieldName; + + return this; + } } } diff --git a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs index c4e4c684b..3bd93a244 100644 --- a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs @@ -8,6 +8,7 @@ using J2N; using Lucene.Net.Analysis.En; using Lucene.Net.Analysis.Standard; +using Lucene.Net.Facet; using Lucene.Net.QueryParsers.Classic; using Lucene.Net.Search; using NUnit.Framework; @@ -174,6 +175,154 @@ public void Uppercase_Category(bool withFacets) } } + [Test] + public void FacetsConfig_SetIndexName_FullText() + { + var fieldDefinitionCollection = new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("nodeName", FieldDefinitionTypes.FacetFullText)); + + var facetsConfig = new FacetsConfig(); + facetsConfig.SetIndexFieldName("nodeName", "facet_nodeName"); + + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + fieldDefinitionCollection, + facetsConfig: facetsConfig)) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "cOntent", + new { nodeName = "location 1", bodyText = "Zanzibar is in Africa"}), + ValueSet.FromObject(2.ToString(), "cOntent", + new { nodeName = "location 2", bodyText = "In Canada there is a town called Sydney in Nova Scotia"}), + ValueSet.FromObject(3.ToString(), "cOntent", + new { nodeName = "location 3", bodyText = "Sydney is the capital of NSW in Australia"}) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("cOntent").All(); + + Console.WriteLine(query); + + + var results = query.WithFacet("nodeName").FacetField("facet_nodeName").Execute(); + + Assert.Throws(() => + { + query.WithFacet("nodeName").Execute(); // $facets not indexed + }); + + var facetResults = results.GetFacet("nodeName"); + + Assert.AreEqual(3, results.TotalItemCount); + Assert.AreEqual(3, facetResults.Count()); + } + } + + [Test] + public void FacetsConfig_SetIndexName_Long() + { + var fieldDefinitionCollection = new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("LongValue", FieldDefinitionTypes.FacetLong)); + + var facetsConfig = new FacetsConfig(); + facetsConfig.SetIndexFieldName("LongValue", "facet_longvalue"); + + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + fieldDefinitionCollection, + facetsConfig: facetsConfig)) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "cOntent", + new { nodeName = "location 1", bodyText = "Zanzibar is in Africa", LongValue = 10L }), + ValueSet.FromObject(2.ToString(), "cOntent", + new { nodeName = "location 2", bodyText = "In Canada there is a town called Sydney in Nova Scotia", LongValue = 20L }), + ValueSet.FromObject(3.ToString(), "cOntent", + new { nodeName = "location 3", bodyText = "Sydney is the capital of NSW in Australia", LongValue = 30L }) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("cOntent").All(); + + Console.WriteLine(query); + + + var results = query.WithFacet("LongValue", new Int64Range[] + { + new Int64Range("10", 10, true, 11, true), + new Int64Range("20", 20, true, 21, true), + new Int64Range("30", 30, true, 31, true), + }).FacetField("facet_longvalue").Execute(); + + Assert.Throws(() => + { + query.WithFacet("LongValue").Execute(); // $facets not indexed + }); + + var facetResults = results.GetFacet("LongValue"); + + Assert.AreEqual(3, results.TotalItemCount); + Assert.AreEqual(3, facetResults.Count()); + } + } + + [Test] + public void FacetsConfig_SetIndexName_Double() + { + var fieldDefinitionCollection = new FieldDefinitionCollection(new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("DoubleValue", FieldDefinitionTypes.FacetDouble)); + + var facetsConfig = new FacetsConfig(); + facetsConfig.SetIndexFieldName("DoubleValue", "facet_doublevalue"); + + var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + using (var luceneDir = new RandomIdRAMDirectory()) + using (var indexer = GetTestIndex( + luceneDir, + analyzer, + fieldDefinitionCollection, + facetsConfig: facetsConfig)) + { + indexer.IndexItems(new[] { + ValueSet.FromObject(1.ToString(), "cOntent", + new { nodeName = "location 1", bodyText = "Zanzibar is in Africa", DoubleValue = 10D }), + ValueSet.FromObject(2.ToString(), "cOntent", + new { nodeName = "location 2", bodyText = "In Canada there is a town called Sydney in Nova Scotia", DoubleValue = 20D }), + ValueSet.FromObject(3.ToString(), "cOntent", + new { nodeName = "location 3", bodyText = "Sydney is the capital of NSW in Australia", DoubleValue = 30D }) + }); + + var searcher = indexer.Searcher; + + var query = searcher.CreateQuery("cOntent").All(); + + Console.WriteLine(query); + + + var results = query.WithFacet("DoubleValue", new DoubleRange[] + { + new DoubleRange("10", 10, true, 11, true), + new DoubleRange("20", 20, true, 21, true), + new DoubleRange("30", 30, true, 31, true), + }).FacetField("facet_doublevalue").Execute(); + + Assert.Throws(() => + { + query.WithFacet("DoubleValue").Execute(); // $facets not indexed + }); + + var facetResults = results.GetFacet("DoubleValue"); + + Assert.AreEqual(3, results.TotalItemCount); + Assert.AreEqual(3, facetResults.Count()); + } + } + [TestCase(true)] [TestCase(false)] public void NativeQuery_Phrase(bool withFacets) diff --git a/src/Examine.Test/ExamineBaseTest.cs b/src/Examine.Test/ExamineBaseTest.cs index 2d7b72618..525498856 100644 --- a/src/Examine.Test/ExamineBaseTest.cs +++ b/src/Examine.Test/ExamineBaseTest.cs @@ -8,6 +8,7 @@ using Moq; using Examine.Lucene.Directories; using System.Collections.Generic; +using Lucene.Net.Facet; namespace Examine.Test { @@ -25,7 +26,7 @@ public virtual void Setup() [TearDown] public virtual void TearDown() => _loggerFactory.Dispose(); - public TestIndex GetTestIndex(Directory d, Analyzer analyzer, FieldDefinitionCollection fieldDefinitions = null, IndexDeletionPolicy indexDeletionPolicy = null, IReadOnlyDictionary indexValueTypesFactory = null) + public TestIndex GetTestIndex(Directory d, Analyzer analyzer, FieldDefinitionCollection fieldDefinitions = null, IndexDeletionPolicy indexDeletionPolicy = null, IReadOnlyDictionary indexValueTypesFactory = null, FacetsConfig? facetsConfig = null) => new TestIndex( _loggerFactory, Mock.Of>(x => x.Get(TestIndex.TestIndexName) == new LuceneDirectoryIndexOptions @@ -34,7 +35,8 @@ public TestIndex GetTestIndex(Directory d, Analyzer analyzer, FieldDefinitionCol DirectoryFactory = new GenericDirectoryFactory(_ => d), Analyzer = analyzer, IndexDeletionPolicy = indexDeletionPolicy, - IndexValueTypesFactory = indexValueTypesFactory + IndexValueTypesFactory = indexValueTypesFactory, + FacetsConfig = facetsConfig ?? new FacetsConfig() })); public TestIndex GetTestIndex(IndexWriter writer) From ff35df58029702cc4960b710e76e14a3406600b8 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 10:09:53 +0100 Subject: [PATCH 19/42] refactor: Ranges immutable properties --- src/Examine.Core/Search/DoubleRange.cs | 10 +++++----- src/Examine.Core/Search/Int64Range.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Examine.Core/Search/DoubleRange.cs b/src/Examine.Core/Search/DoubleRange.cs index 0b4abfce2..ca9fd1cc4 100644 --- a/src/Examine.Core/Search/DoubleRange.cs +++ b/src/Examine.Core/Search/DoubleRange.cs @@ -18,26 +18,26 @@ public DoubleRange(string label, double min, bool minInclusive, double max, bool /// /// Label that identifies this range. /// - public string Label { get; set; } + public string Label { get; } /// /// Minimum. /// - public double Min { get; set; } + public double Min { get; } /// /// True if the minimum value is inclusive. /// - public bool MinInclusive { get; set; } + public bool MinInclusive { get; } /// /// Maximum. /// - public double Max { get; set; } + public double Max { get; } /// /// True if the maximum value is inclusive. /// - public bool MaxInclusive { get; set; } + public bool MaxInclusive { get; } } } diff --git a/src/Examine.Core/Search/Int64Range.cs b/src/Examine.Core/Search/Int64Range.cs index 843d94477..5389bf747 100644 --- a/src/Examine.Core/Search/Int64Range.cs +++ b/src/Examine.Core/Search/Int64Range.cs @@ -18,26 +18,26 @@ public Int64Range(string label, long min, bool minInclusive, long max, bool maxI /// /// Label that identifies this range. /// - public string Label { get; set; } + public string Label { get; } /// /// Minimum. /// - public long Min { get; set; } + public long Min { get; } /// /// True if the minimum value is inclusive. /// - public bool MinInclusive { get; set; } + public bool MinInclusive { get; } /// /// Maximum. /// - public long Max { get; set; } + public long Max { get; } /// /// True if the maximum value is inclusive. /// - public bool MaxInclusive { get; set; } + public bool MaxInclusive { get; } } } From 3974b06d4b8f83081331f9d335131ce7607fe8f5 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 10:18:37 +0100 Subject: [PATCH 20/42] refactor Range to readonly structs --- src/Examine.Core/Search/DoubleRange.cs | 2 +- src/Examine.Core/Search/Int64Range.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Examine.Core/Search/DoubleRange.cs b/src/Examine.Core/Search/DoubleRange.cs index ca9fd1cc4..365d27002 100644 --- a/src/Examine.Core/Search/DoubleRange.cs +++ b/src/Examine.Core/Search/DoubleRange.cs @@ -3,7 +3,7 @@ namespace Examine.Search /// /// Represents a range over values. /// - public class DoubleRange + public readonly struct DoubleRange { /// public DoubleRange(string label, double min, bool minInclusive, double max, bool maxInclusive) diff --git a/src/Examine.Core/Search/Int64Range.cs b/src/Examine.Core/Search/Int64Range.cs index 5389bf747..44caf027d 100644 --- a/src/Examine.Core/Search/Int64Range.cs +++ b/src/Examine.Core/Search/Int64Range.cs @@ -3,7 +3,7 @@ namespace Examine.Search /// /// Represents a range over values. /// - public class Int64Range + public readonly struct Int64Range { /// public Int64Range(string label, long min, bool minInclusive, long max, bool maxInclusive) From a4c5fa8c57117a8bb16462aee8821e731e055271 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 11:11:11 +0100 Subject: [PATCH 21/42] refactor: FloatRange, immutable properties & structs --- src/Examine.Core/Search/FacetDoubleField.cs | 2 - src/Examine.Core/Search/FacetFloatField.cs | 18 ++++++++ src/Examine.Core/Search/FacetFullTextField.cs | 4 +- src/Examine.Core/Search/FacetValue.cs | 2 +- src/Examine.Core/Search/FloatRange.cs | 43 +++++++++++++++++++ src/Examine.Core/Search/IFacetDoubleField.cs | 2 - .../Search/IFacetDoubleRangeQueryField.cs | 7 --- src/Examine.Core/Search/IFacetField.cs | 2 +- src/Examine.Core/Search/IFacetFloatField.cs | 12 ++++++ .../Search/IFacetFloatRangeQueryField.cs | 10 +++++ .../Search/IFacetFullTextField.cs | 4 +- src/Examine.Core/Search/IFaceting.cs | 5 +++ src/Examine.Lucene/FacetExtensions.cs | 27 +++++++++--- .../Search/FacetDoubleRangeQueryField.cs | 7 --- .../Search/FacetFloatRangeQueryField.cs | 27 ++++++++++++ .../Search/LuceneBooleanOperation.cs | 2 + .../Search/LuceneBooleanOperationBase.cs | 2 + .../Search/LuceneSearchExecutor.cs | 31 +++++++------ .../Search/LuceneSearchQuery.cs | 14 ++++++ .../Examine.Lucene/Search/FluentApiTests.cs | 16 +++---- 20 files changed, 185 insertions(+), 52 deletions(-) create mode 100644 src/Examine.Core/Search/FacetFloatField.cs create mode 100644 src/Examine.Core/Search/FloatRange.cs create mode 100644 src/Examine.Core/Search/IFacetFloatField.cs create mode 100644 src/Examine.Core/Search/IFacetFloatRangeQueryField.cs create mode 100644 src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs diff --git a/src/Examine.Core/Search/FacetDoubleField.cs b/src/Examine.Core/Search/FacetDoubleField.cs index ebf08fcf8..abef962bc 100644 --- a/src/Examine.Core/Search/FacetDoubleField.cs +++ b/src/Examine.Core/Search/FacetDoubleField.cs @@ -13,8 +13,6 @@ public class FacetDoubleField : IFacetDoubleField public string FacetField { get; set; } - public bool IsFloat { get; set; } - public FacetDoubleField(string field, DoubleRange[] doubleRanges, string facetField = ExamineFieldNames.DefaultFacetsName) { Field = field; diff --git a/src/Examine.Core/Search/FacetFloatField.cs b/src/Examine.Core/Search/FacetFloatField.cs new file mode 100644 index 000000000..5a6b2fbbf --- /dev/null +++ b/src/Examine.Core/Search/FacetFloatField.cs @@ -0,0 +1,18 @@ +namespace Examine.Search +{ + public class FacetFloatField : IFacetFloatField + { + public FloatRange[] FloatRanges { get; } + + public string Field { get; } + + public string FacetField { get; set; } + + public FacetFloatField(string field, FloatRange[] floatRanges, string facetField = ExamineFieldNames.DefaultFacetsName) + { + Field = field; + FloatRanges = floatRanges; + FacetField = facetField; + } + } +} diff --git a/src/Examine.Core/Search/FacetFullTextField.cs b/src/Examine.Core/Search/FacetFullTextField.cs index 1a64fb10f..0877a710d 100644 --- a/src/Examine.Core/Search/FacetFullTextField.cs +++ b/src/Examine.Core/Search/FacetFullTextField.cs @@ -4,9 +4,9 @@ public class FacetFullTextField : IFacetFullTextField { public int MaxCount { get; set; } - public string[] Values { get; set; } + public string[] Values { get; } - public string Field { get; set; } + public string Field { get; } public string FacetField { get; set; } diff --git a/src/Examine.Core/Search/FacetValue.cs b/src/Examine.Core/Search/FacetValue.cs index 05cc0c5aa..c5bd9d5d5 100644 --- a/src/Examine.Core/Search/FacetValue.cs +++ b/src/Examine.Core/Search/FacetValue.cs @@ -5,7 +5,7 @@ namespace Examine.Search { - public class FacetValue : IFacetValue + public readonly struct FacetValue : IFacetValue { public string Label { get; } diff --git a/src/Examine.Core/Search/FloatRange.cs b/src/Examine.Core/Search/FloatRange.cs new file mode 100644 index 000000000..393bc2fad --- /dev/null +++ b/src/Examine.Core/Search/FloatRange.cs @@ -0,0 +1,43 @@ +namespace Examine.Search +{ + /// + /// Represents a range over values. + /// + public readonly struct FloatRange + { + /// + public FloatRange(string label, float min, bool minInclusive, float max, bool maxInclusive) + { + Label = label; + Min = min; + MinInclusive = minInclusive; + Max = max; + MaxInclusive = maxInclusive; + } + + /// + /// Label that identifies this range. + /// + public string Label { get; } + + /// + /// Minimum. + /// + public float Min { get; } + + /// + /// True if the minimum value is inclusive. + /// + public bool MinInclusive { get; } + + /// + /// Maximum. + /// + public float Max { get; } + + /// + /// True if the maximum value is inclusive. + /// + public bool MaxInclusive { get; } + } +} diff --git a/src/Examine.Core/Search/IFacetDoubleField.cs b/src/Examine.Core/Search/IFacetDoubleField.cs index 2155a72cc..afd2324ce 100644 --- a/src/Examine.Core/Search/IFacetDoubleField.cs +++ b/src/Examine.Core/Search/IFacetDoubleField.cs @@ -8,7 +8,5 @@ namespace Examine.Search public interface IFacetDoubleField : IFacetField { DoubleRange[] DoubleRanges { get; } - - bool IsFloat { get; set; } } } diff --git a/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs b/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs index 6cc85d9fd..4a767e2dd 100644 --- a/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs +++ b/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs @@ -2,13 +2,6 @@ namespace Examine.Search { public interface IFacetDoubleRangeQueryField : IFacetAppending, IQueryExecutor { - /// - /// Sets if the range query is on values - /// - /// - /// - IFacetDoubleRangeQueryField IsFloat(bool isFloat); - /// /// Sets the field where the facet information will be read from /// diff --git a/src/Examine.Core/Search/IFacetField.cs b/src/Examine.Core/Search/IFacetField.cs index fe3eedaf8..ece4464a7 100644 --- a/src/Examine.Core/Search/IFacetField.cs +++ b/src/Examine.Core/Search/IFacetField.cs @@ -10,6 +10,6 @@ public interface IFacetField /// /// The field to get the facet field from /// - string FacetField { get; set; } + string FacetField { get; } } } diff --git a/src/Examine.Core/Search/IFacetFloatField.cs b/src/Examine.Core/Search/IFacetFloatField.cs new file mode 100644 index 000000000..fcd13ee74 --- /dev/null +++ b/src/Examine.Core/Search/IFacetFloatField.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Examine.Lucene.Search; + +namespace Examine.Search +{ + public interface IFacetFloatField : IFacetField + { + FloatRange[] FloatRanges { get; } + } +} diff --git a/src/Examine.Core/Search/IFacetFloatRangeQueryField.cs b/src/Examine.Core/Search/IFacetFloatRangeQueryField.cs new file mode 100644 index 000000000..f53efe9a6 --- /dev/null +++ b/src/Examine.Core/Search/IFacetFloatRangeQueryField.cs @@ -0,0 +1,10 @@ +namespace Examine.Search +{ + public interface IFacetFloatRangeQueryField : IFacetAppending, IQueryExecutor + { + /// + /// Sets the field where the facet information will be read from + /// + IFacetFloatRangeQueryField FacetField(string fieldName); + } +} diff --git a/src/Examine.Core/Search/IFacetFullTextField.cs b/src/Examine.Core/Search/IFacetFullTextField.cs index 7a2188d17..568976155 100644 --- a/src/Examine.Core/Search/IFacetFullTextField.cs +++ b/src/Examine.Core/Search/IFacetFullTextField.cs @@ -7,11 +7,11 @@ public interface IFacetFullTextField : IFacetField /// /// Maximum number of terms to return /// - int MaxCount { get; set; } + int MaxCount { get; } /// /// Filter values /// - string[] Values { get; set; } + string[] Values { get; } } } diff --git a/src/Examine.Core/Search/IFaceting.cs b/src/Examine.Core/Search/IFaceting.cs index ab352f9cb..c0bdfc5f5 100644 --- a/src/Examine.Core/Search/IFaceting.cs +++ b/src/Examine.Core/Search/IFaceting.cs @@ -31,6 +31,11 @@ public interface IFaceting /// IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); + /// + /// Add a range facet to the current query + /// + IFacetFloatRangeQueryField WithFacet(string field, params FloatRange[] floatRanges); + /// /// Add a range facet to the current query /// diff --git a/src/Examine.Lucene/FacetExtensions.cs b/src/Examine.Lucene/FacetExtensions.cs index 863996c5a..72b5bc568 100644 --- a/src/Examine.Lucene/FacetExtensions.cs +++ b/src/Examine.Lucene/FacetExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using Examine.Lucene.Search; using Lucene.Net.Facet.Range; @@ -16,7 +15,7 @@ public static IFacetResult GetFacet(this ISearchResults searchResults, string fi { if (!(searchResults is IFacetResults facetResults)) { - throw new ArgumentException("Result does not support facets"); + throw new NotSupportedException("Result does not support facets"); } facetResults.Facets.TryGetValue(field, out IFacetResult facet); @@ -31,7 +30,7 @@ public static IEnumerable GetFacets(this ISearchResults searchResu { if (!(searchResults is IFacetResults facetResults)) { - throw new ArgumentException("Result does not support facets"); + throw new NotSupportedException("Result does not support facets"); } return facetResults.Facets.Values; @@ -42,27 +41,41 @@ public static IEnumerable GetFacets(this ISearchResults searchResu /// /// /// - public static IEnumerable AsLuceneRange(this IEnumerable ranges) => ranges.Select(range => range.AsLuceneRange()); + internal static IEnumerable AsLuceneRange(this IEnumerable ranges) => ranges.Select(range => range.AsLuceneRange()); /// /// Converts a to the Lucene equivalent /// /// /// - public static Int64Range AsLuceneRange(this Examine.Search.Int64Range range) => new Int64Range(range.Label, range.Min, range.MinInclusive, range.Max, range.MaxInclusive); + internal static Int64Range AsLuceneRange(this Examine.Search.Int64Range range) => new Int64Range(range.Label, range.Min, range.MinInclusive, range.Max, range.MaxInclusive); /// /// Converts to the Lucene equivalent /// /// /// - public static IEnumerable AsLuceneRange(this IEnumerable ranges) => ranges.Select(range => range.AsLuceneRange()); + internal static IEnumerable AsLuceneRange(this IEnumerable ranges) => ranges.Select(range => range.AsLuceneRange()); /// /// Converts a to the Lucene equivalent /// /// /// - public static DoubleRange AsLuceneRange(this Examine.Search.DoubleRange range) => new DoubleRange(range.Label, range.Min, range.MinInclusive, range.Max, range.MaxInclusive); + internal static DoubleRange AsLuceneRange(this Examine.Search.DoubleRange range) => new DoubleRange(range.Label, range.Min, range.MinInclusive, range.Max, range.MaxInclusive); + + /// + /// Converts to the Lucene equivalent + /// + /// + /// + internal static IEnumerable AsLuceneRange(this IEnumerable ranges) => ranges.Select(range => range.AsLuceneRange()); + + /// + /// Converts a to the Lucene equivalent + /// + /// + /// + internal static DoubleRange AsLuceneRange(this Examine.Search.FloatRange range) => new DoubleRange(range.Label, range.Min, range.MinInclusive, range.Max, range.MaxInclusive); } } diff --git a/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs b/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs index 8ea4d2304..d5c28c860 100644 --- a/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs +++ b/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs @@ -16,13 +16,6 @@ public FacetDoubleRangeQueryField(LuceneSearchQuery search, FacetDoubleField fie public IOrdering And() => new LuceneBooleanOperation(_search); public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); - public IFacetDoubleRangeQueryField IsFloat(bool isFloat) - { - _field.IsFloat = isFloat; - - return this; - } - /// public IFacetDoubleRangeQueryField FacetField(string fieldName) { diff --git a/src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs b/src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs new file mode 100644 index 000000000..092ac1d58 --- /dev/null +++ b/src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs @@ -0,0 +1,27 @@ +using Examine.Search; + +namespace Examine.Lucene.Search +{ + public class FacetFloatRangeQueryField : IFacetFloatRangeQueryField + { + private readonly LuceneSearchQuery _search; + private readonly FacetFloatField _field; + + public FacetFloatRangeQueryField(LuceneSearchQuery search, FacetFloatField field) + { + _search = search; + _field = field; + } + + public IOrdering And() => new LuceneBooleanOperation(_search); + public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); + + /// + public IFacetFloatRangeQueryField FacetField(string fieldName) + { + _field.FacetField = fieldName; + + return this; + } + } +} diff --git a/src/Examine.Lucene/Search/LuceneBooleanOperation.cs b/src/Examine.Lucene/Search/LuceneBooleanOperation.cs index b01430f6e..36dc7c8f8 100644 --- a/src/Examine.Lucene/Search/LuceneBooleanOperation.cs +++ b/src/Examine.Lucene/Search/LuceneBooleanOperation.cs @@ -73,6 +73,8 @@ public LuceneBooleanOperation(LuceneSearchQuery search) public override IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges) => _search.FacetInternal(field, doubleRanges); + public override IFacetFloatRangeQueryField WithFacet(string field, params FloatRange[] floatRanges) => _search.FacetInternal(field, floatRanges); + public override IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges) => _search.FacetInternal(field, longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs index ead5ee13c..bbe7a5d64 100644 --- a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs +++ b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs @@ -84,6 +84,8 @@ protected internal LuceneBooleanOperationBase Op( public abstract IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); + public abstract IFacetFloatRangeQueryField WithFacet(string field, params FloatRange[] floatRanges); + public abstract IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs index 4b20df1b0..d342ef4e8 100644 --- a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs +++ b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs @@ -165,28 +165,33 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector continue; } - facets.Add(facetLongField.Field, new Examine.Search.FacetResult(longFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value)))); + facets.Add(facetLongField.Field, new Examine.Search.FacetResult(longFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } else if (field is FacetDoubleField facetDoubleField) { - DoubleRangeFacetCounts doubleFacetCounts; - if (facetDoubleField.IsFloat) - { - doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, new SingleFieldSource(facetDoubleField.Field), facetsCollector, facetDoubleField.DoubleRanges.AsLuceneRange().ToArray()); - } - else + var doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, facetsCollector, facetDoubleField.DoubleRanges.AsLuceneRange().ToArray()); + + var doubleFacets = doubleFacetCounts.GetTopChildren(0, facetDoubleField.Field); + + if(doubleFacets == null) { - doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, facetsCollector, facetDoubleField.DoubleRanges.AsLuceneRange().ToArray()); + continue; } - var doubleFacets = doubleFacetCounts.GetTopChildren(0, facetDoubleField.Field); + facets.Add(facetDoubleField.Field, new Examine.Search.FacetResult(doubleFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); + } + else if(field is FacetFloatField facetFloatField) + { + var floatFacetCounts = new DoubleRangeFacetCounts(facetFloatField.Field, new SingleFieldSource(facetFloatField.Field), facetsCollector, facetFloatField.FloatRanges.AsLuceneRange().ToArray()); + + var floatFacets = floatFacetCounts.GetTopChildren(0, facetFloatField.Field); - if(doubleFacets == null) + if (floatFacets == null) { continue; } - facets.Add(facetDoubleField.Field, new Examine.Search.FacetResult(doubleFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value)))); + facets.Add(facetFloatField.Field, new Examine.Search.FacetResult(floatFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } } @@ -210,7 +215,7 @@ private static void ExtractFullTextFacets(FacetsCollector facetsCollector, ISear var value = sortedFacetsCounts.GetSpecificValue(facetFullTextField.Field, label); facetValues.Add(new FacetValue(label, value)); } - facets.Add(facetFullTextField.Field, new Examine.Search.FacetResult(facetValues.OrderBy(value => value.Value).Take(facetFullTextField.MaxCount))); + facets.Add(facetFullTextField.Field, new Examine.Search.FacetResult(facetValues.OrderBy(value => value.Value).Take(facetFullTextField.MaxCount).OfType())); } else { @@ -221,7 +226,7 @@ private static void ExtractFullTextFacets(FacetsCollector facetsCollector, ISear return; } - facets.Add(facetFullTextField.Field, new Examine.Search.FacetResult(sortedFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value)))); + facets.Add(facetFullTextField.Field, new Examine.Search.FacetResult(sortedFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchQuery.cs b/src/Examine.Lucene/Search/LuceneSearchQuery.cs index 195973bd4..a6f84412e 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQuery.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQuery.cs @@ -339,6 +339,20 @@ internal IFacetDoubleRangeQueryField FacetInternal(string field, params DoubleRa return new FacetDoubleRangeQueryField(this, facet); } + internal IFacetFloatRangeQueryField FacetInternal(string field, params FloatRange[] floatRanges) + { + if (floatRanges == null) + { + floatRanges = Array.Empty(); + } + + var facet = new FacetFloatField(field, floatRanges); + + _facetFields.Add(facet); + + return new FacetFloatRangeQueryField(this, facet); + } + internal IFacetLongRangeQueryField FacetInternal(string field, params Int64Range[] longRanges) { if(longRanges == null) diff --git a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs index 3bd93a244..6dc004890 100644 --- a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs @@ -2645,16 +2645,16 @@ public void Float_Range_SimpleIndexSet(bool withFacets) { //Act - var results1 = filter1.WithFacet("SomeFloat", new DoubleRange[] + var results1 = filter1.WithFacet("SomeFloat", new FloatRange[] { - new DoubleRange("1", 0, true, 12, true), - new DoubleRange("2", 13, true, 250, true) - }).IsFloat(true).Execute(); - var results2 = filter2.WithFacet("SomeFloat", new DoubleRange[] + new FloatRange("1", 0, true, 12, true), + new FloatRange("2", 13, true, 250, true) + }).Execute(); + var results2 = filter2.WithFacet("SomeFloat", new FloatRange[] { - new DoubleRange("1", 0, true, 12, true), - new DoubleRange("2", 13, true, 250, true) - }).IsFloat(true).Execute(); + new FloatRange("1", 0, true, 12, true), + new FloatRange("2", 13, true, 250, true) + }).Execute(); var facetResults1 = results1.GetFacet("SomeFloat"); var facetResults2 = results2.GetFacet("SomeFloat"); From f2f9bc9f0ae2017bc2f8517a7e3b354b569caa36 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 13:31:45 +0100 Subject: [PATCH 22/42] refactor: Encapsulate facets in query Added parallelizable to FluentApiTests saves about 2 min when running tests. --- src/Examine.Core/Search/IFacetAppending.cs | 4 +- src/Examine.Core/Search/IFaceting.cs | 2 +- src/Examine.Core/Search/IOrdering.cs | 10 +- .../Search/FacetDoubleRangeQueryField.cs | 2 +- .../Search/FacetFloatRangeQueryField.cs | 2 +- .../Search/FacetLongRangeQueryField.cs | 2 +- src/Examine.Lucene/Search/FacetQueryField.cs | 2 +- .../Search/LuceneBooleanOperation.cs | 15 +- .../Search/LuceneBooleanOperationBase.cs | 10 +- .../Search/LuceneFacetOperation.cs | 38 +++ .../Search/LuceneFacetOperationBase.cs | 24 ++ .../Examine.Lucene/Search/FluentApiTests.cs | 221 +++++++++--------- src/Examine.Test/ExamineBaseTest.cs | 23 +- 13 files changed, 209 insertions(+), 146 deletions(-) create mode 100644 src/Examine.Lucene/Search/LuceneFacetOperation.cs create mode 100644 src/Examine.Lucene/Search/LuceneFacetOperationBase.cs diff --git a/src/Examine.Core/Search/IFacetAppending.cs b/src/Examine.Core/Search/IFacetAppending.cs index 831e89bba..167b818d1 100644 --- a/src/Examine.Core/Search/IFacetAppending.cs +++ b/src/Examine.Core/Search/IFacetAppending.cs @@ -1,4 +1,4 @@ -namespace Examine.Search +namespace Examine.Search { /// /// Allows for appending more operations @@ -9,6 +9,6 @@ public interface IFacetAppending /// Allows for adding more operations /// /// - IOrdering And(); + IFaceting And(); } } diff --git a/src/Examine.Core/Search/IFaceting.cs b/src/Examine.Core/Search/IFaceting.cs index c0bdfc5f5..adb4223ee 100644 --- a/src/Examine.Core/Search/IFaceting.cs +++ b/src/Examine.Core/Search/IFaceting.cs @@ -9,7 +9,7 @@ namespace Examine.Search /// /// Faceting operations /// - public interface IFaceting + public interface IFaceting : IQueryExecutor { /// /// Add a facet string to the current query diff --git a/src/Examine.Core/Search/IOrdering.cs b/src/Examine.Core/Search/IOrdering.cs index c9ab028f6..ffedeba4b 100644 --- a/src/Examine.Core/Search/IOrdering.cs +++ b/src/Examine.Core/Search/IOrdering.cs @@ -1,8 +1,9 @@ +using System; using System.Collections.Generic; namespace Examine.Search { - public interface IOrdering : IQueryExecutor, IFaceting + public interface IOrdering : IQueryExecutor { /// /// Orders the results by the specified fields @@ -37,5 +38,12 @@ public interface IOrdering : IQueryExecutor, IFaceting /// /// IOrdering SelectAllFields(); + + /// + /// Allows for selecting facets to return in your query + /// + /// + /// + IQueryExecutor WithFacets(Action facets); } } diff --git a/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs b/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs index d5c28c860..fc2278436 100644 --- a/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs +++ b/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs @@ -13,7 +13,7 @@ public FacetDoubleRangeQueryField(LuceneSearchQuery search, FacetDoubleField fie _field = field; } - public IOrdering And() => new LuceneBooleanOperation(_search); + public IFaceting And() => new LuceneFacetOperation(_search); public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); /// diff --git a/src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs b/src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs index 092ac1d58..151518825 100644 --- a/src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs +++ b/src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs @@ -13,7 +13,7 @@ public FacetFloatRangeQueryField(LuceneSearchQuery search, FacetFloatField field _field = field; } - public IOrdering And() => new LuceneBooleanOperation(_search); + public IFaceting And() => new LuceneFacetOperation(_search); public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); /// diff --git a/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs b/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs index ea1c0664a..36c218ea1 100644 --- a/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs +++ b/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs @@ -13,7 +13,7 @@ public FacetLongRangeQueryField(LuceneSearchQuery search, FacetLongField field) _field = field; } - public IOrdering And() => new LuceneBooleanOperation(_search); + public IFaceting And() => new LuceneFacetOperation(_search); public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); /// diff --git a/src/Examine.Lucene/Search/FacetQueryField.cs b/src/Examine.Lucene/Search/FacetQueryField.cs index d9ed7a965..7f000b5de 100644 --- a/src/Examine.Lucene/Search/FacetQueryField.cs +++ b/src/Examine.Lucene/Search/FacetQueryField.cs @@ -13,7 +13,7 @@ public FacetQueryField(LuceneSearchQuery search, FacetFullTextField field) _field = field; } - public IOrdering And() => new LuceneBooleanOperation(_search); + public IFaceting And() => new LuceneFacetOperation(_search); public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); public IFacetQueryField FacetField(string fieldName) diff --git a/src/Examine.Lucene/Search/LuceneBooleanOperation.cs b/src/Examine.Lucene/Search/LuceneBooleanOperation.cs index 36dc7c8f8..ac03df096 100644 --- a/src/Examine.Lucene/Search/LuceneBooleanOperation.cs +++ b/src/Examine.Lucene/Search/LuceneBooleanOperation.cs @@ -67,14 +67,11 @@ public LuceneBooleanOperation(LuceneSearchQuery search) public override string ToString() => _search.ToString(); - public override IFacetQueryField WithFacet(string field) => _search.FacetInternal(field, Array.Empty()); - - public override IFacetQueryField WithFacet(string field, params string[] values) => _search.FacetInternal(field, values); - - public override IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges) => _search.FacetInternal(field, doubleRanges); - - public override IFacetFloatRangeQueryField WithFacet(string field, params FloatRange[] floatRanges) => _search.FacetInternal(field, floatRanges); - - public override IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges) => _search.FacetInternal(field, longRanges); + public override IQueryExecutor WithFacets(Action facets) + { + var luceneFacetOperation = new LuceneFacetOperation(_search); + facets.Invoke(luceneFacetOperation); + return luceneFacetOperation; + } } } diff --git a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs index bbe7a5d64..5440ae01f 100644 --- a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs +++ b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs @@ -78,14 +78,6 @@ protected internal LuceneBooleanOperationBase Op( public abstract IOrdering SelectField(string fieldName); public abstract IOrdering SelectAllFields(); - public abstract IFacetQueryField WithFacet(string field); - - public abstract IFacetQueryField WithFacet(string field, params string[] values); - - public abstract IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); - - public abstract IFacetFloatRangeQueryField WithFacet(string field, params FloatRange[] floatRanges); - - public abstract IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges); + public abstract IQueryExecutor WithFacets(Action facets); } } diff --git a/src/Examine.Lucene/Search/LuceneFacetOperation.cs b/src/Examine.Lucene/Search/LuceneFacetOperation.cs new file mode 100644 index 000000000..2d5481a54 --- /dev/null +++ b/src/Examine.Lucene/Search/LuceneFacetOperation.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Examine.Search; + +namespace Examine.Lucene.Search +{ + /// + /// An implementation of the fluent API boolean operations + /// + [DebuggerDisplay("{_search}")] + public class LuceneFacetOperation : LuceneFacetOperationBase + { + private readonly LuceneSearchQuery _search; + + public LuceneFacetOperation(LuceneSearchQuery search) + { + _search = search; + } + + public override ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); + + public override IFacetQueryField WithFacet(string field) => _search.FacetInternal(field, Array.Empty()); + + public override IFacetQueryField WithFacet(string field, params string[] values) => _search.FacetInternal(field, values); + + public override IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges) => _search.FacetInternal(field, doubleRanges); + + public override IFacetFloatRangeQueryField WithFacet(string field, params FloatRange[] floatRanges) => _search.FacetInternal(field, floatRanges); + + public override IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges) => _search.FacetInternal(field, longRanges); + + public override string ToString() => _search.ToString(); + } +} diff --git a/src/Examine.Lucene/Search/LuceneFacetOperationBase.cs b/src/Examine.Lucene/Search/LuceneFacetOperationBase.cs new file mode 100644 index 000000000..5f85c32a5 --- /dev/null +++ b/src/Examine.Lucene/Search/LuceneFacetOperationBase.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Examine.Search; + +namespace Examine.Lucene.Search +{ + public abstract class LuceneFacetOperationBase : IFaceting + { + public abstract ISearchResults Execute(QueryOptions options = null); + + public abstract IFacetQueryField WithFacet(string field); + + public abstract IFacetQueryField WithFacet(string field, params string[] values); + + public abstract IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); + + public abstract IFacetFloatRangeQueryField WithFacet(string field, params FloatRange[] floatRanges); + + public abstract IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges); + } +} diff --git a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs index 6dc004890..7da24f1ed 100644 --- a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs @@ -18,6 +18,7 @@ namespace Examine.Test.Examine.Lucene.Search { [TestFixture] + [Parallelizable(ParallelScope.All)] public class FluentApiTests : ExamineBaseTest { [TestCase(true)] @@ -63,7 +64,7 @@ public void Allow_Leading_Wildcards(bool withFacets) if (withFacets) { - var results1 = query1.WithFacet("nodeName").Execute(); + var results1 = query1.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results1.GetFacet("nodeName"); @@ -111,7 +112,7 @@ public void NativeQuery_Single_Word(bool withFacets) if (withFacets) { - var results = query.WithFacet("nodeName").Execute(); + var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -159,7 +160,7 @@ public void Uppercase_Category(bool withFacets) if (withFacets) { - var results = query.WithFacet("nodeName").Execute(); + var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -207,11 +208,11 @@ public void FacetsConfig_SetIndexName_FullText() Console.WriteLine(query); - var results = query.WithFacet("nodeName").FacetField("facet_nodeName").Execute(); + var results = query.WithFacets(facets => facets.WithFacet("nodeName").FacetField("facet_nodeName")).Execute(); Assert.Throws(() => { - query.WithFacet("nodeName").Execute(); // $facets not indexed + query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); // $facets not indexed }); var facetResults = results.GetFacet("nodeName"); @@ -253,16 +254,16 @@ public void FacetsConfig_SetIndexName_Long() Console.WriteLine(query); - var results = query.WithFacet("LongValue", new Int64Range[] + var results = query.WithFacets(facets => facets.WithFacet("LongValue", new Int64Range[] { new Int64Range("10", 10, true, 11, true), new Int64Range("20", 20, true, 21, true), new Int64Range("30", 30, true, 31, true), - }).FacetField("facet_longvalue").Execute(); + }).FacetField("facet_longvalue")).Execute(); Assert.Throws(() => { - query.WithFacet("LongValue").Execute(); // $facets not indexed + query.WithFacets(facets => facets.WithFacet("LongValue")).Execute(); // $facets not indexed }); var facetResults = results.GetFacet("LongValue"); @@ -304,16 +305,16 @@ public void FacetsConfig_SetIndexName_Double() Console.WriteLine(query); - var results = query.WithFacet("DoubleValue", new DoubleRange[] + var results = query.WithFacets(factes => factes.WithFacet("DoubleValue", new DoubleRange[] { new DoubleRange("10", 10, true, 11, true), new DoubleRange("20", 20, true, 21, true), new DoubleRange("30", 30, true, 31, true), - }).FacetField("facet_doublevalue").Execute(); + }).FacetField("facet_doublevalue")).Execute(); Assert.Throws(() => { - query.WithFacet("DoubleValue").Execute(); // $facets not indexed + query.WithFacets(facets => facets.WithFacet("DoubleValue")).Execute(); // $facets not indexed }); var facetResults = results.GetFacet("DoubleValue"); @@ -355,7 +356,7 @@ public void NativeQuery_Phrase(bool withFacets) if (withFacets) { - var results = query.WithFacet("bodyText").Execute(); + var results = query.WithFacets(facets => facets.WithFacet("bodyText")).Execute(); var facetResults = results.GetFacet("bodyText"); @@ -420,11 +421,11 @@ public void Managed_Range_Date(bool withFacets) if(withFacets) { - var numberSortedResult = numberSortedCriteria.WithFacet("created", new Int64Range[] + var numberSortedResult = numberSortedCriteria.WithFacets(facets => facets.WithFacet("created", new Int64Range[] { new Int64Range("First days", new DateTime(2000, 01, 01).Ticks, true, new DateTime(2000, 01, 03).Ticks, true), new Int64Range("Last days", new DateTime(2000, 01, 04).Ticks, true, new DateTime(2000, 01, 06).Ticks, true) - }).Execute(); + })).Execute(); var facetResult = numberSortedResult.GetFacet("created"); @@ -470,7 +471,7 @@ public void Managed_Full_Text(bool withFacets) { var result = searcher.CreateQuery() .ManagedQuery("darkness") - .WithFacet("item1") + .WithFacets(facets => facets.WithFacet("item1")) .Execute(); var facetResults = result.GetFacet("item1"); @@ -486,7 +487,7 @@ public void Managed_Full_Text(bool withFacets) result = searcher.CreateQuery() .ManagedQuery("total darkness") - .WithFacet("item1") + .WithFacets(facets => facets.WithFacet("item1")) .Execute(); facetResults = result.GetFacet("item1"); @@ -546,7 +547,7 @@ public void Managed_Full_Text_With_Bool(bool withFacets) if (withFacets) { - var result = qry.WithFacet("item1").Execute(); + var result = qry.WithFacets(facets => facets.WithFacet("item1")).Execute(); var facetResults = result.GetFacet("item1"); @@ -561,7 +562,7 @@ public void Managed_Full_Text_With_Bool(bool withFacets) qry = searcher.CreateQuery().ManagedQuery("darkness") .And(query => query.Field("item1", "value1").Or().Field("item1", "value2"), BooleanOperation.Or); Console.WriteLine(qry); - result = qry.WithFacet("item1").Execute(); + result = qry.WithFacets(facets => facets.WithFacet("item1")).Execute(); facetResults = result.GetFacet("item1"); @@ -628,7 +629,7 @@ public void Not_Managed_Full_Text(bool withFacets) if (withFacets) { - var result = qry.WithFacet("item1").Execute(); + var result = qry.WithFacets(facets => facets.WithFacet("item1")).Execute(); var facetResults = result.GetFacet("item1"); @@ -707,11 +708,12 @@ public void Managed_Range_Int(bool withFacets) if (withFacets) { var numberSortedResult = numberSortedCriteria - .WithFacet("parentID", new Int64Range[] + .WithFacets(facets => facets.WithFacet("parentID", new Int64Range[] { new Int64Range("120-122", 120, true, 122, true), new Int64Range("123-125", 123, true, 125, true) - }).Execute(); + })) + .Execute(); var facetResults = numberSortedResult.GetFacet("parentID"); @@ -778,7 +780,7 @@ public void Legacy_ParentId(bool withFacets) if (withFacets) { - var numberSortedResult = numberSortedCriteria.WithFacet("parentID").Execute(); + var numberSortedResult = numberSortedCriteria.WithFacets(facets => facets.WithFacet("parentID")).Execute(); var facetResults = numberSortedResult.GetFacet("parentID"); @@ -847,7 +849,7 @@ public void Grouped_Or_Examiness(bool withFacets) if (withFacets) { - var results = filter.WithFacet("nodeTypeAlias").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("nodeTypeAlias")).Execute(); var facetResults = results.GetFacet("nodeTypeAlias"); @@ -888,35 +890,35 @@ public void Grouped_Or_Query_Output() Console.WriteLine("GROUPED OR - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); + criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3 blahID:1 blahID:2 blahID:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 parentID:1)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1)", criteria.Query.ToString()); @@ -939,7 +941,7 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); //We used to assert this, but it must be allowed to do an add on the same field multiple times //Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); @@ -948,7 +950,7 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); + criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); //The field/value array lengths are equal so we will match the key/value pairs Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2 +blahID:3)", criteria.Query.ToString()); @@ -956,7 +958,7 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); //There are more than one field and there are more values than fields, in this case we align the key/value pairs Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2)", criteria.Query.ToString()); @@ -964,14 +966,14 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:1)", criteria.Query.ToString()); Console.WriteLine("GROUPED AND - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); } @@ -993,35 +995,35 @@ public void Grouped_Not_Query_Output() Console.WriteLine("GROUPED NOT - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacet("SomeFacet"); + criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3 -blahID:1 -blahID:2 -blahID:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -parentID:1", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }).WithFacet("SomeFacet"); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1", criteria.Query.ToString()); } @@ -1054,7 +1056,7 @@ public void Grouped_Not_Single_Field_Single_Value(bool withFacets) if (withFacets) { - var results = query.All().WithFacet("nodeName").Execute(); + var results = query.All().WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1099,7 +1101,7 @@ public void Grouped_Not_Multi_Field_Single_Value(bool withFacets) if (withFacets) { - var results = query.WithFacet("nodeName").Execute(); + var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1149,7 +1151,7 @@ public void Grouped_Or_With_Not(bool withFacets) if (withFacets) { - var results = filter.WithFacet("headerText").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("headerText")).Execute(); var facetResults = results.GetFacet("headerText"); @@ -1193,7 +1195,7 @@ public void And_Grouped_Not_Single_Value(bool withFacets) if (withFacets) { - var results = query.WithFacet("nodeName").Execute(); + var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1237,7 +1239,7 @@ public void And_Grouped_Not_Multi_Value(bool withFacets) if (withFacets) { - var results = query.WithFacet("nodeName").Execute(); + var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); Assert.AreEqual(1, results.TotalItemCount); @@ -1280,7 +1282,7 @@ public void And_Not_Single_Field(bool withFacets) if (withFacets) { - var results = query.WithFacet("nodeName").Execute(); + var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacets(); @@ -1328,7 +1330,7 @@ public void AndNot_Nested(bool withFacets) if (withFacets) { - var results = query.WithFacet("nodeName").Execute(); + var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); Assert.AreEqual(1, results.TotalItemCount); @@ -1374,7 +1376,7 @@ public void And_Not_Added_Later(bool withFacets) if (withFacets) { - var results = query.WithFacet("nodeName").Execute(); + var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); Assert.AreEqual(1, results.TotalItemCount); @@ -1418,7 +1420,7 @@ public void Not_Range(bool withFacets) { var results = query - .WithFacet("start", new Int64Range("Label", 100, false, 200, false)) + .WithFacets(facets => facets.WithFacet("start", new Int64Range("Label", 100, false, 200, false))) .Execute(); var facetResults = results.GetFacet("start"); @@ -1479,7 +1481,7 @@ public void Match_By_Path(bool withFacets) if (withFacets) { - var results1 = filter.WithFacet("nodeName").Execute(); + var results1 = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults1 = results1.GetFacet("nodeName"); Assert.AreEqual(1, results1.TotalItemCount); Assert.AreEqual(1, facetResults1.Count()); @@ -1496,7 +1498,7 @@ public void Match_By_Path(bool withFacets) if (withFacets) { - var results2 = exactfilter.WithFacet("nodeName").Execute(); + var results2 = exactfilter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults2 = results2.GetFacet("nodeName"); Assert.AreEqual(1, results2.TotalItemCount); Assert.AreEqual(1, facetResults2.Count()); @@ -1515,7 +1517,7 @@ public void Match_By_Path(bool withFacets) if (withFacets) { - var results5 = nativeFilter.WithFacet("nodeName").Execute(); + var results5 = nativeFilter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults5 = results5.GetFacet("nodeName"); Assert.AreEqual(1, results5.TotalItemCount); Assert.AreEqual(1, facetResults5.Count()); @@ -1532,7 +1534,7 @@ public void Match_By_Path(bool withFacets) if (withFacets) { - var results3 = wildcardfilter.WithFacet("nodeName").Execute(); + var results3 = wildcardfilter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults3 = results3.GetFacet("nodeName"); Assert.AreEqual(2, results3.TotalItemCount); Assert.AreEqual(2, facetResults3.Count()); @@ -1549,7 +1551,7 @@ public void Match_By_Path(bool withFacets) if (withFacets) { - var results3 = wildcardfilter.WithFacet("nodeName").Execute(); + var results3 = wildcardfilter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults3 = results3.GetFacet("nodeName"); Assert.AreEqual(0, results3.TotalItemCount); Assert.AreEqual(0, facetResults3?.Count() ?? 0); @@ -1594,7 +1596,7 @@ public void Find_By_ParentId(bool withFacets) if (withFacets) { - var results = filter.WithFacet("nodeName").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1651,7 +1653,7 @@ public void Find_By_ParentId_Native_Query(bool withFacets) if (withFacets) { - var results = filter.WithFacet("parentID").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("parentID")).Execute(); var facetResults = results.GetFacet("parentID"); @@ -1717,7 +1719,7 @@ public void Find_By_NodeTypeAlias(bool withFacets) if (withFacets) { - var results = filter.WithFacet("nodeName").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1769,7 +1771,7 @@ public void Search_With_Stop_Words(bool withFacets) if (withFacets) { - var results = filter.WithFacet("nodeName").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1830,7 +1832,7 @@ public void Search_Native_Query(bool withFacets) if (withFacets) { - var results = criteria.WithFacet("nodeTypeAlias").Execute(); + var results = criteria.WithFacets(facets => facets.WithFacet("nodeTypeAlias")).Execute(); var facetResults = results.GetFacet("nodeTypeAlias"); @@ -1880,7 +1882,7 @@ public void Find_Only_Image_Media(bool withFacets) if (withFacets) { - var results = filter.WithFacet("nodeTypeAlias").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("nodeTypeAlias")).Execute(); var facetResults = results.GetFacet("nodeTypeAlias"); @@ -1929,7 +1931,7 @@ public void Find_Both_Media_And_Content(bool withFacets) if (withFacets) { - var results = filter.WithFacet("nodeName").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1979,9 +1981,10 @@ public void Sort_Result_By_Number_Field(bool withFacets) if (withFacets) { var results1 = sc1 - .WithFacet("sortOrder") - .And() - .WithFacet("parentID") + .WithFacets(facets => facets + .WithFacet("sortOrder") + .And() + .WithFacet("parentID")) .Execute(); var facetResults = results1.GetFacet("sortOrder"); @@ -2054,9 +2057,10 @@ public void Sort_Result_By_Date_Field(bool withFacets) if (withFacets) { var results1 = sc1 - .WithFacet("updateDate") - .And() - .WithFacet("parentID") + .WithFacets(facets => facets + .WithFacet("updateDate") + .And() + .WithFacet("parentID")) .Execute(); var facetResults = results1.GetFacet("updateDate"); @@ -2128,8 +2132,8 @@ public void Sort_Result_By_Single_Field(bool withFacets) if (withFacets) { - var results1 = sc1.WithFacet("nodeName").Execute(); - var results2 = sc2.WithFacet("nodeName").Execute(); + var results1 = sc1.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results2 = sc2.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults1 = results1.GetFacet("nodeName"); var facetResults2 = results2.GetFacet("nodeName"); @@ -2195,8 +2199,8 @@ public void Sort_Result_By_Double_Fields(string fieldType, SortType sortType, bo if (withFacets) { - var results1 = sc1.WithFacet("field1").Execute(); - var results2 = sc2.WithFacet("field1").Execute(); + var results1 = sc1.WithFacets(facets => facets.WithFacet("field1")).Execute(); + var results2 = sc2.WithFacets(facets => facets.WithFacet("field1")).Execute(); var facetResults1 = results1.GetFacet("field1"); var facetResults2 = results2.GetFacet("field1"); @@ -2282,7 +2286,7 @@ public void Sort_Result_By_Multiple_Fields(bool withFacets) if (withFacets) { - var results1 = sc1.WithFacet("field1").And().WithFacet("field2").Execute(); + var results1 = sc1.WithFacets(facets => facets.WithFacet("field1").And().WithFacet("field2")).Execute(); var facetResults = results1.GetFacet("field1"); var facetResults2 = results1.GetFacet("field2"); @@ -2341,7 +2345,7 @@ public void Standard_Results_Sorted_By_Score(bool withFacets) if (withFacets) { - var results = sc1.WithFacet("bodyText").And().Execute(); + var results = sc1.WithFacets(facets => facets.WithFacet("bodyText")).Execute(); var facetResults = results.GetFacet("bodyText"); @@ -2409,7 +2413,7 @@ public void Skip_Results_Returns_Different_Results(bool withFacets) if (withFacets) { //Act - var results = sc.WithFacet("nodeName").Execute(); + var results = sc.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -2460,7 +2464,7 @@ public void Escaping_Includes_All_Words(bool withFacets) if (withFacets) { //Act - var results = sc.WithFacet("nodeName").Execute(); + var results = sc.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -2519,7 +2523,7 @@ public void Grouped_And_Examiness(bool withFacets) if (withFacets) { //Act - var results = filter.WithFacet("nodeName").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -2571,7 +2575,7 @@ public void Examiness_Proximity(bool withFacets) if (withFacets) { //Act - var results = filter.WithFacet("nodeName").And().Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -2645,16 +2649,16 @@ public void Float_Range_SimpleIndexSet(bool withFacets) { //Act - var results1 = filter1.WithFacet("SomeFloat", new FloatRange[] + var results1 = filter1.WithFacets(facets => facets.WithFacet("SomeFloat", new FloatRange[] { new FloatRange("1", 0, true, 12, true), new FloatRange("2", 13, true, 250, true) - }).Execute(); - var results2 = filter2.WithFacet("SomeFloat", new FloatRange[] + })).Execute(); + var results2 = filter2.WithFacets(facets => facets.WithFacet("SomeFloat", new FloatRange[] { new FloatRange("1", 0, true, 12, true), new FloatRange("2", 13, true, 250, true) - }).Execute(); + })).Execute(); var facetResults1 = results1.GetFacet("SomeFloat"); var facetResults2 = results2.GetFacet("SomeFloat"); @@ -2725,8 +2729,8 @@ public void Number_Range_SimpleIndexSet(bool withFacets) if (withFacets) { //Act - var results1 = filter1.WithFacet("SomeNumber").MaxCount(1).Execute(); - var results2 = filter2.WithFacet("SomeNumber").MaxCount(1).Execute(); + var results1 = filter1.WithFacets(facets => facets.WithFacet("SomeNumber").MaxCount(1)).Execute(); + var results2 = filter2.WithFacets(facets => facets.WithFacet("SomeNumber").MaxCount(1)).Execute(); var facetResults1 = results1.GetFacet("SomeNumber"); var facetResults2 = results2.GetFacet("SomeNumber"); @@ -2793,16 +2797,16 @@ public void Double_Range_SimpleIndexSet(bool withFacets) if (withFacets) { //Act - var results1 = filter1.WithFacet("SomeDouble", new DoubleRange[] + var results1 = filter1.WithFacets(facets => facets.WithFacet("SomeDouble", new DoubleRange[] { new DoubleRange("1", 0, true, 100, true), new DoubleRange("2", 101, true, 200, true) - }).Execute(); - var results2 = filter2.WithFacet("SomeDouble", new DoubleRange[] + })).Execute(); + var results2 = filter2.WithFacets(facets => facets.WithFacet("SomeDouble", new DoubleRange[] { new DoubleRange("1", 0, true, 100, true), new DoubleRange("2", 101, true, 200, true) - }).Execute(); + })).Execute(); var facetResults1 = results1.GetFacet("SomeDouble"); var facetResults2 = results2.GetFacet("SomeDouble"); @@ -2869,16 +2873,16 @@ public void Long_Range_SimpleIndexSet(bool withFacets) if (withFacets) { //Act - var results1 = filter1.WithFacet("SomeLong", new Int64Range[] + var results1 = filter1.WithFacets(facets => facets.WithFacet("SomeLong", new Int64Range[] { new Int64Range("1", 0L, true, 100L, true), new Int64Range("2", 101L, true, 200L, true) - }).Execute(); - var results2 = filter2.WithFacet("SomeLong", new Int64Range[] + })).Execute(); + var results2 = filter2.WithFacets(facets => facets.WithFacet("SomeLong", new Int64Range[] { new Int64Range("1", 0L, true, 100L, true), new Int64Range("2", 101L, true, 200L, true) - }).Execute(); + })).Execute(); var facetResults1 = results1.GetFacet("SomeLong"); var facetResults2 = results2.GetFacet("SomeLong"); @@ -2947,16 +2951,16 @@ public void Date_Range_SimpleIndexSet(bool withFacets) if (withFacets) { ////Act - var results = filter.WithFacet("DateCreated", new Int64Range[] + var results = filter.WithFacets(facets => facets.WithFacet("DateCreated", new Int64Range[] { new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) - }).Execute(); - var results2 = filter2.WithFacet("DateCreated", new Int64Range[] + })).Execute(); + var results2 = filter2.WithFacets(facets => facets.WithFacet("DateCreated", new Int64Range[] { new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) - }).Execute(); + })).Execute(); var facetResults1 = results.GetFacet("DateCreated"); var facetResults2 = results2.GetFacet("DateCreated"); @@ -3020,8 +3024,8 @@ public void Fuzzy_Search(bool withFacets) if (withFacets) { ////Act - var results = filter.WithFacet("Content").Execute(); - var results2 = filter2.WithFacet("Content").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("Content")).Execute(); + var results2 = filter2.WithFacets(facets => facets.WithFacet("Content")).Execute(); var facetResults1 = results.GetFacet("Content"); var facetResults2 = results2.GetFacet("Content"); @@ -3169,7 +3173,7 @@ public void Inner_Or_Query(bool withFacets) if (withFacets) { //Act - var results = filter.WithFacet("Type").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("Type")).Execute(); var facetResults = results.GetFacet("Type"); @@ -3229,7 +3233,7 @@ public void Inner_And_Query(bool withFacets) if (withFacets) { //Act - var results = filter.WithFacet("Type").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("Type")).Execute(); var facetResults = results.GetFacet("Type"); @@ -3289,7 +3293,7 @@ public void Inner_Not_Query(bool withFacets) if (withFacets) { //Act - var results = filter.WithFacet("Type").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("Type")).Execute(); var facetResults = results.GetFacet("Type"); @@ -3356,7 +3360,7 @@ public void Complex_Or_Group_Nested_Query(bool withFacets) { //Act - var results = filter.WithFacet("Type").Execute(); + var results = filter.WithFacets(facets => facets.WithFacet("Type")).Execute(); var facetResults = results.GetFacet("Type"); @@ -3401,7 +3405,7 @@ public void Custom_Lucene_Query_With_Native(bool withFacets) if (withFacets) { - criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)).WithFacet("SomeFacet"); + criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)).WithFacets(facets => facets.WithFacet("SomeFacet")); } else { @@ -3547,7 +3551,7 @@ public void Select_Field(bool withFacets) if (withFacets) { - var results = sc1.WithFacet("nodeName").Execute(); + var results = sc1.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); var expectedLoadedFields = new string[] { "__Path" }; @@ -3605,7 +3609,7 @@ public void Select_Fields(bool withFacets) if (withFacets) { - var results = sc1.WithFacet("nodeName").Execute(); + var results = sc1.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); var expectedLoadedFields = new string[] { "nodeName", "bodyText", "id", "__NodeId" }; @@ -3664,7 +3668,7 @@ public void Select_Fields_HashSet(bool withFacets) if (withFacets) { - var results = sc1.WithFacet("nodeName").Execute(); + var results = sc1.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); var expectedLoadedFields = new string[] { "nodeName", "bodyText" }; @@ -3798,7 +3802,7 @@ public void Paging_With_Skip_Take(bool withFacets) if (withFacets) { - var results = sc.WithFacet("writerName") + var results = sc.WithFacets(facets => facets.WithFacet("writerName")) .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); Assert.AreEqual(2, results.Count()); var facetResults = results.GetFacet("writerName"); @@ -3914,7 +3918,7 @@ public void Given_SkipTake_Returns_ExpectedTotals(int skip, int take, int expect if (withFacets) { - var results = sc.WithFacet("nodeName").Execute(QueryOptions.SkipTake(skip, take)); + var results = sc.WithFacets(facets => facets.WithFacet("nodeName")).Execute(QueryOptions.SkipTake(skip, take)); var facetResults = results.GetFacet("nodeName"); @@ -3983,7 +3987,7 @@ public void Range_DateOnly(bool withFacets) if (withFacets) { - var numberSortedResult = numberSortedCriteria.WithFacet("created").Execute(); + var numberSortedResult = numberSortedCriteria.WithFacets(facets => facets.WithFacet("created")).Execute(); var facetResult = numberSortedResult.GetFacet("created"); Assert.AreEqual(2, numberSortedResult.TotalItemCount); @@ -4047,9 +4051,8 @@ public void Range_DateOnly_Min_And_Max_Inclusive() } } - [TestCase(true)] - [TestCase(false)] - public void Range_DateOnly_No_Inclusive(bool withFacets) + [Test] + public void Range_DateOnly_No_Inclusive() { var analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); using (var luceneDir = new RandomIdRAMDirectory()) diff --git a/src/Examine.Test/ExamineBaseTest.cs b/src/Examine.Test/ExamineBaseTest.cs index 525498856..8128c4d53 100644 --- a/src/Examine.Test/ExamineBaseTest.cs +++ b/src/Examine.Test/ExamineBaseTest.cs @@ -14,21 +14,18 @@ namespace Examine.Test { public abstract class ExamineBaseTest { - private ILoggerFactory _loggerFactory; - [SetUp] public virtual void Setup() { - _loggerFactory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug)); - _loggerFactory.CreateLogger(typeof(ExamineBaseTest)).LogDebug("Initializing test"); + var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug)); + loggerFactory.CreateLogger(typeof(ExamineBaseTest)).LogDebug("Initializing test"); } - [TearDown] - public virtual void TearDown() => _loggerFactory.Dispose(); - public TestIndex GetTestIndex(Directory d, Analyzer analyzer, FieldDefinitionCollection fieldDefinitions = null, IndexDeletionPolicy indexDeletionPolicy = null, IReadOnlyDictionary indexValueTypesFactory = null, FacetsConfig? facetsConfig = null) - => new TestIndex( - _loggerFactory, + { + var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug)); + return new TestIndex( + loggerFactory, Mock.Of>(x => x.Get(TestIndex.TestIndexName) == new LuceneDirectoryIndexOptions { FieldDefinitions = fieldDefinitions, @@ -38,11 +35,15 @@ public TestIndex GetTestIndex(Directory d, Analyzer analyzer, FieldDefinitionCol IndexValueTypesFactory = indexValueTypesFactory, FacetsConfig = facetsConfig ?? new FacetsConfig() })); + } public TestIndex GetTestIndex(IndexWriter writer) - => new TestIndex( - _loggerFactory, + { + var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug)); + return new TestIndex( + loggerFactory, Mock.Of>(x => x.Get(TestIndex.TestIndexName) == new LuceneIndexOptions()), writer); + } } } From e62b35521643f07234385fb8584e732b27fe8285 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 13:55:59 +0100 Subject: [PATCH 23/42] docs: Update facet docs with refactored code --- docs/searching.md | 83 +++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 43 deletions(-) diff --git a/docs/searching.md b/docs/searching.md index af30118bc..c3c878013 100644 --- a/docs/searching.md +++ b/docs/searching.md @@ -260,7 +260,7 @@ Basic example var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") - .WithFacet("Address") // Get facets of the Address field + .WithFacets(facets => facets.WithFacet("Address")) // Get facets of the Address field .Execute(); var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -271,16 +271,16 @@ var addressFacetResults = results.GetFacet("Address"); // Returns the facets for * Label: Hollywood, Value: 10 */ -var hillsValue = addressFacetResults.WithFacet("Hills"); // Gets the IFacetValue for the facet Hills +var hillsValue = addressFacetResults.Facet("Hills"); // Gets the IFacetValue for the facet Hills ``` Filtered value example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() - .Field("Address", "Hills") - .WithFacet("Address", "Hills") // Get facets of the Address field - .Execute(); + .Field("Address", "Hills") + .WithFacets(facets => facets.WithFacet("Address", "Hills")) // Get facets of the Address field + .Execute(); var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -296,9 +296,9 @@ MaxCount example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() - .Field("Address", "Hills") - .WithFacet("Address") // Get facets of the Address field - .Execute(); + .Field("Address", "Hills") + .WithFacets(facets => facets.WithFacet("Address")) // Get facets of the Address field + .Execute(); var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -310,10 +310,9 @@ var addressFacetResults = results.GetFacet("Address"); // Returns the facets for */ results = searcher.CreateQuery() - .Field("Address", "Hills") - .WithFacet("Address") // Get facets of the Address field - .MaxCount(2) // Gets the top 2 results (The facets with the highest value) - .Execute(); + .Field("Address", "Hills") + .WithFacets(facets => facets.WithFacet("Address").MaxCount(2)) // Get facets of the Address field & Gets the top 2 results (The facets with the highest value) + .Execute(); addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -346,10 +345,9 @@ services.AddExamineLuceneIndex("MyIndex", var searcher = myIndex.Searcher; var results = searcher.CreateQuery() - .Field("Address", "Hills") - .WithFacet("Address") // Get facets of the Address field - .FacetField("address_facet") - .Execute(); + .Field("Address", "Hills") + .WithFacets(facets => facets.WithFacet("Address").FacetField("address_facet")) // Get facets of the Address field from the facet field address_facet + .Execute(); var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -370,12 +368,12 @@ Double range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() - .All() - .WithFacet("Price", new DoubleRange[] { - new DoubleRange("0-10", 0, true, 10, true), - new DoubleRange("11-20", 11, true, 20, true) - }) // Get facets of the price field - .Execute(); + .All() + .WithFacets(facets => facets.WithFacet("Price", new DoubleRange[] { + new DoubleRange("0-10", 0, true, 10, true), + new DoubleRange("11-20", 11, true, 20, true) + })) // Get facets of the price field + .Execute(); var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the specific field Price @@ -392,14 +390,12 @@ Float range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() - .All() - .WithFacet("Price", new DoubleRange[] { - new DoubleRange("0-10", 0, true, 10, true), - new DoubleRange("11-20", 11, true, 20, true) - }) // Get facets of the price field - .IsFloat(true) // Marks that the underlying field is a float - .And() - .Execute(); + .All() + .WithFacets(facets => facets.WithFacet("Price", new FloatRange[] { + new FloatRange("0-10", 0, true, 10, true), + new FloatRange("11-20", 11, true, 20, true) + })) // Get facets of the price field + .Execute(); var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the specific field Price @@ -416,12 +412,12 @@ Int/Long range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() - .All() - .WithFacet("Price", new Int64Range[] { - new Int64Range("0-10", 0, true, 10, true), - new Int64Range("11-20", 11, true, 20, true) - }) // Get facets of the price field - .Execute(); + .All() + .WithFacets(facets => facets.WithFacet("Price", new Int64Range[] { + new Int64Range("0-10", 0, true, 10, true), + new Int64Range("11-20", 11, true, 20, true) + })) // Get facets of the price field + .Execute(); var priceFacetResults = results.GetFacet("Price"); // Returns the facets for the specific field Price @@ -438,12 +434,12 @@ DateTime range example ```csharp var searcher = myIndex.Searcher; var results = searcher.CreateQuery() - .All() - .WithFacet("Created", new Int64Range[] { - new Int64Range("first", DateTime.UtcNow.AddDays(-1).Ticks, true, DateTime.UtcNow.Ticks, true), - new Int64Range("last", DateTime.UtcNow.AddDays(1).Ticks, true, DateTime.UtcNow.AddDays(2).Ticks, true) - }) // Get facets of the price field - .Execute(); + .All() + .WithFacets(facets => facets.WithFacet("Created", new Int64Range[] { + new Int64Range("first", DateTime.UtcNow.AddDays(-1).Ticks, true, DateTime.UtcNow.Ticks, true), + new Int64Range("last", DateTime.UtcNow.AddDays(1).Ticks, true, DateTime.UtcNow.AddDays(2).Ticks, true) + })) // Get facets of the price field + .Execute(); var createdFacetResults = results.GetFacet("Created"); // Returns the facets for the specific field Created @@ -453,4 +449,5 @@ var createdFacetResults = results.GetFacet("Created"); // Returns the facets for * Label: last, Value: 10 */ -var firstRangeValue = createdFacetResults.Facet("first"); // Gets the IFacetValue for the facet "first" \ No newline at end of file +var firstRangeValue = createdFacetResults.Facet("first"); // Gets the IFacetValue for the facet "first" +``` \ No newline at end of file From afd03418b87f026995e6edff9b9bcc6012e620be Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 21:22:22 +0100 Subject: [PATCH 24/42] refactor: Refactored facetfield --- src/Examine.Core/Search/FacetDoubleField.cs | 11 +- src/Examine.Core/Search/FacetFloatField.cs | 6 +- src/Examine.Core/Search/FacetFullTextField.cs | 6 +- src/Examine.Core/Search/FacetLongField.cs | 6 +- src/Examine.Core/Search/FacetResult.cs | 2 - src/Examine.Core/Search/FacetValue.cs | 3 - src/Examine.Core/Search/IFacetAppending.cs | 14 -- src/Examine.Core/Search/IFacetDoubleField.cs | 3 - .../Search/IFacetDoubleRangeQueryField.cs | 10 - src/Examine.Core/Search/IFacetFloatField.cs | 3 - .../Search/IFacetFloatRangeQueryField.cs | 10 - .../Search/IFacetLongRangeQueryField.cs | 10 - src/Examine.Core/Search/IFacetQueryField.cs | 7 +- src/Examine.Core/Search/IFaceting.cs | 16 +- .../Providers/BaseLuceneSearcher.cs | 8 +- src/Examine.Lucene/Providers/LuceneIndex.cs | 2 +- .../Providers/LuceneSearcher.cs | 6 +- .../Providers/MultiIndexSearcher.cs | 11 +- .../Search/FacetDoubleRangeQueryField.cs | 27 --- .../Search/FacetFloatRangeQueryField.cs | 27 --- .../Search/FacetLongRangeQueryField.cs | 27 --- src/Examine.Lucene/Search/FacetQueryField.cs | 14 +- .../Search/LuceneFacetOperation.cs | 14 +- .../Search/LuceneFacetOperationBase.cs | 24 --- .../Search/LuceneSearchQuery.cs | 44 ++-- .../Examine.Lucene/Search/FluentApiTests.cs | 199 ++++++++---------- .../Examine.Lucene/Search/MultiIndexSearch.cs | 3 + .../Search/MultiIndexSearchTests.cs | 6 + 28 files changed, 175 insertions(+), 344 deletions(-) delete mode 100644 src/Examine.Core/Search/IFacetAppending.cs delete mode 100644 src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs delete mode 100644 src/Examine.Core/Search/IFacetFloatRangeQueryField.cs delete mode 100644 src/Examine.Core/Search/IFacetLongRangeQueryField.cs delete mode 100644 src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs delete mode 100644 src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs delete mode 100644 src/Examine.Lucene/Search/FacetLongRangeQueryField.cs delete mode 100644 src/Examine.Lucene/Search/LuceneFacetOperationBase.cs diff --git a/src/Examine.Core/Search/FacetDoubleField.cs b/src/Examine.Core/Search/FacetDoubleField.cs index abef962bc..f9a804da8 100644 --- a/src/Examine.Core/Search/FacetDoubleField.cs +++ b/src/Examine.Core/Search/FacetDoubleField.cs @@ -1,19 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Examine.Lucene.Search; - namespace Examine.Search { - public class FacetDoubleField : IFacetDoubleField + public readonly struct FacetDoubleField : IFacetDoubleField { public DoubleRange[] DoubleRanges { get; } public string Field { get; } - public string FacetField { get; set; } + public string FacetField { get; } - public FacetDoubleField(string field, DoubleRange[] doubleRanges, string facetField = ExamineFieldNames.DefaultFacetsName) + public FacetDoubleField(string field, DoubleRange[] doubleRanges, string facetField) { Field = field; DoubleRanges = doubleRanges; diff --git a/src/Examine.Core/Search/FacetFloatField.cs b/src/Examine.Core/Search/FacetFloatField.cs index 5a6b2fbbf..6f5e8e653 100644 --- a/src/Examine.Core/Search/FacetFloatField.cs +++ b/src/Examine.Core/Search/FacetFloatField.cs @@ -1,14 +1,14 @@ namespace Examine.Search { - public class FacetFloatField : IFacetFloatField + public readonly struct FacetFloatField : IFacetFloatField { public FloatRange[] FloatRanges { get; } public string Field { get; } - public string FacetField { get; set; } + public string FacetField { get; } - public FacetFloatField(string field, FloatRange[] floatRanges, string facetField = ExamineFieldNames.DefaultFacetsName) + public FacetFloatField(string field, FloatRange[] floatRanges, string facetField) { Field = field; FloatRanges = floatRanges; diff --git a/src/Examine.Core/Search/FacetFullTextField.cs b/src/Examine.Core/Search/FacetFullTextField.cs index 0877a710d..ed993b34a 100644 --- a/src/Examine.Core/Search/FacetFullTextField.cs +++ b/src/Examine.Core/Search/FacetFullTextField.cs @@ -8,14 +8,14 @@ public class FacetFullTextField : IFacetFullTextField public string Field { get; } - public string FacetField { get; set; } + public string FacetField { get; } - public FacetFullTextField(string field, string[] values, int maxCount = 10, string facetField = ExamineFieldNames.DefaultFacetsName) + public FacetFullTextField(string field, string[] values, string facetField, int maxCount = 10) { Field = field; Values = values; - MaxCount = maxCount; FacetField = facetField; + MaxCount = maxCount; } } } diff --git a/src/Examine.Core/Search/FacetLongField.cs b/src/Examine.Core/Search/FacetLongField.cs index 03badb747..9820077e9 100644 --- a/src/Examine.Core/Search/FacetLongField.cs +++ b/src/Examine.Core/Search/FacetLongField.cs @@ -1,14 +1,14 @@ namespace Examine.Search { - public class FacetLongField : IFacetLongField + public readonly struct FacetLongField : IFacetLongField { public string Field { get; } public Int64Range[] LongRanges { get; } - public string FacetField { get; set; } + public string FacetField { get; } - public FacetLongField(string field, Int64Range[] longRanges, string facetField = ExamineFieldNames.DefaultFacetsName) + public FacetLongField(string field, Int64Range[] longRanges, string facetField) { Field = field; LongRanges = longRanges; diff --git a/src/Examine.Core/Search/FacetResult.cs b/src/Examine.Core/Search/FacetResult.cs index e58d23bac..adb9294a5 100644 --- a/src/Examine.Core/Search/FacetResult.cs +++ b/src/Examine.Core/Search/FacetResult.cs @@ -1,8 +1,6 @@ -using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; using Examine.Lucene.Search; namespace Examine.Search diff --git a/src/Examine.Core/Search/FacetValue.cs b/src/Examine.Core/Search/FacetValue.cs index c5bd9d5d5..37ac4332e 100644 --- a/src/Examine.Core/Search/FacetValue.cs +++ b/src/Examine.Core/Search/FacetValue.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Text; using Examine.Lucene.Search; namespace Examine.Search diff --git a/src/Examine.Core/Search/IFacetAppending.cs b/src/Examine.Core/Search/IFacetAppending.cs deleted file mode 100644 index 167b818d1..000000000 --- a/src/Examine.Core/Search/IFacetAppending.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Examine.Search -{ - /// - /// Allows for appending more operations - /// - public interface IFacetAppending - { - /// - /// Allows for adding more operations - /// - /// - IFaceting And(); - } -} diff --git a/src/Examine.Core/Search/IFacetDoubleField.cs b/src/Examine.Core/Search/IFacetDoubleField.cs index afd2324ce..6be9dfdb2 100644 --- a/src/Examine.Core/Search/IFacetDoubleField.cs +++ b/src/Examine.Core/Search/IFacetDoubleField.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Text; using Examine.Lucene.Search; namespace Examine.Search diff --git a/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs b/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs deleted file mode 100644 index 4a767e2dd..000000000 --- a/src/Examine.Core/Search/IFacetDoubleRangeQueryField.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Examine.Search -{ - public interface IFacetDoubleRangeQueryField : IFacetAppending, IQueryExecutor - { - /// - /// Sets the field where the facet information will be read from - /// - IFacetDoubleRangeQueryField FacetField(string fieldName); - } -} diff --git a/src/Examine.Core/Search/IFacetFloatField.cs b/src/Examine.Core/Search/IFacetFloatField.cs index fcd13ee74..e936e9216 100644 --- a/src/Examine.Core/Search/IFacetFloatField.cs +++ b/src/Examine.Core/Search/IFacetFloatField.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Text; using Examine.Lucene.Search; namespace Examine.Search diff --git a/src/Examine.Core/Search/IFacetFloatRangeQueryField.cs b/src/Examine.Core/Search/IFacetFloatRangeQueryField.cs deleted file mode 100644 index f53efe9a6..000000000 --- a/src/Examine.Core/Search/IFacetFloatRangeQueryField.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Examine.Search -{ - public interface IFacetFloatRangeQueryField : IFacetAppending, IQueryExecutor - { - /// - /// Sets the field where the facet information will be read from - /// - IFacetFloatRangeQueryField FacetField(string fieldName); - } -} diff --git a/src/Examine.Core/Search/IFacetLongRangeQueryField.cs b/src/Examine.Core/Search/IFacetLongRangeQueryField.cs deleted file mode 100644 index b4634c4ea..000000000 --- a/src/Examine.Core/Search/IFacetLongRangeQueryField.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Examine.Search -{ - public interface IFacetLongRangeQueryField : IFacetAppending, IQueryExecutor - { - /// - /// Sets the field where the facet information will be read from - /// - IFacetLongRangeQueryField FacetField(string fieldName); - } -} diff --git a/src/Examine.Core/Search/IFacetQueryField.cs b/src/Examine.Core/Search/IFacetQueryField.cs index b62741738..b9bb1e739 100644 --- a/src/Examine.Core/Search/IFacetQueryField.cs +++ b/src/Examine.Core/Search/IFacetQueryField.cs @@ -1,15 +1,10 @@ namespace Examine.Search { - public interface IFacetQueryField : IFacetAppending, IQueryExecutor + public interface IFacetQueryField { /// /// Maximum number of terms to return /// IFacetQueryField MaxCount(int count); - - /// - /// Sets the field where the facet information will be read from - /// - IFacetQueryField FacetField(string fieldName); } } diff --git a/src/Examine.Core/Search/IFaceting.cs b/src/Examine.Core/Search/IFaceting.cs index adb4223ee..c7d6315a5 100644 --- a/src/Examine.Core/Search/IFaceting.cs +++ b/src/Examine.Core/Search/IFaceting.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Examine.Search { @@ -15,30 +11,32 @@ public interface IFaceting : IQueryExecutor /// Add a facet string to the current query /// /// + /// /// - IFacetQueryField WithFacet(string field); + IFaceting Facet(string field, Action facetConfiguration = null); /// /// Add a facet string to the current query, filtered by a single value or multiple values /// /// + /// /// /// - IFacetQueryField WithFacet(string field, params string[] values); + IFaceting Facet(string field, Action facetConfiguration = null, params string[] values); /// /// Add a range facet to the current query /// - IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); + IFaceting Facet(string field, params DoubleRange[] doubleRanges); /// /// Add a range facet to the current query /// - IFacetFloatRangeQueryField WithFacet(string field, params FloatRange[] floatRanges); + IFaceting Facet(string field, params FloatRange[] floatRanges); /// /// Add a range facet to the current query /// - IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges); + IFaceting Facet(string field, params Int64Range[] longRanges); } } diff --git a/src/Examine.Lucene/Providers/BaseLuceneSearcher.cs b/src/Examine.Lucene/Providers/BaseLuceneSearcher.cs index 0c4a2c29c..8f9b9ecd3 100644 --- a/src/Examine.Lucene/Providers/BaseLuceneSearcher.cs +++ b/src/Examine.Lucene/Providers/BaseLuceneSearcher.cs @@ -3,6 +3,7 @@ using Lucene.Net.Search; using Examine.Lucene.Search; using Examine.Search; +using Lucene.Net.Facet; namespace Examine.Lucene.Providers { @@ -11,17 +12,20 @@ namespace Examine.Lucene.Providers /// public abstract class BaseLuceneSearcher : BaseSearchProvider { + private readonly FacetsConfig _facetsConfig; + /// /// Constructor to allow for creating an indexer at runtime /// /// /// - protected BaseLuceneSearcher(string name, Analyzer analyzer) + protected BaseLuceneSearcher(string name, Analyzer analyzer, FacetsConfig facetsConfig) : base(name) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(name)); LuceneAnalyzer = analyzer; + _facetsConfig = facetsConfig; } /// @@ -48,7 +52,7 @@ public IQuery CreateQuery(string category, BooleanOperation defaultOperation, An if (luceneAnalyzer == null) throw new ArgumentNullException(nameof(luceneAnalyzer)); - return new LuceneSearchQuery(GetSearchContext(), category, luceneAnalyzer, searchOptions, defaultOperation); + return new LuceneSearchQuery(GetSearchContext(), category, luceneAnalyzer, searchOptions, defaultOperation, _facetsConfig); } /// diff --git a/src/Examine.Lucene/Providers/LuceneIndex.cs b/src/Examine.Lucene/Providers/LuceneIndex.cs index c71fa6236..f96eb98af 100644 --- a/src/Examine.Lucene/Providers/LuceneIndex.cs +++ b/src/Examine.Lucene/Providers/LuceneIndex.cs @@ -1048,7 +1048,7 @@ private LuceneSearcher CreateSearcher() // wait for most recent changes when first creating the searcher WaitForChanges(); - return new LuceneSearcher(name + "Searcher", searcherManager, FieldAnalyzer, FieldValueTypeCollection); + return new LuceneSearcher(name + "Searcher", searcherManager, FieldAnalyzer, FieldValueTypeCollection, _options.FacetsConfig); } /// diff --git a/src/Examine.Lucene/Providers/LuceneSearcher.cs b/src/Examine.Lucene/Providers/LuceneSearcher.cs index c20194a15..8166f0d3c 100644 --- a/src/Examine.Lucene/Providers/LuceneSearcher.cs +++ b/src/Examine.Lucene/Providers/LuceneSearcher.cs @@ -2,7 +2,7 @@ using Examine.Lucene.Search; using Lucene.Net.Search; using Lucene.Net.Analysis; - +using Lucene.Net.Facet; namespace Examine.Lucene.Providers { @@ -23,8 +23,8 @@ public class LuceneSearcher : BaseLuceneSearcher, IDisposable /// /// /// - public LuceneSearcher(string name, SearcherManager searcherManager, Analyzer analyzer, FieldValueTypeCollection fieldValueTypeCollection) - : base(name, analyzer) + public LuceneSearcher(string name, SearcherManager searcherManager, Analyzer analyzer, FieldValueTypeCollection fieldValueTypeCollection, FacetsConfig facetsConfig) + : base(name, analyzer, facetsConfig) { _searcherManager = searcherManager; _fieldValueTypeCollection = fieldValueTypeCollection; diff --git a/src/Examine.Lucene/Providers/MultiIndexSearcher.cs b/src/Examine.Lucene/Providers/MultiIndexSearcher.cs index 7fe503efe..7a01d9a59 100644 --- a/src/Examine.Lucene/Providers/MultiIndexSearcher.cs +++ b/src/Examine.Lucene/Providers/MultiIndexSearcher.cs @@ -4,6 +4,7 @@ using Examine.Lucene.Search; using Lucene.Net.Analysis; using Lucene.Net.Analysis.Standard; +using Lucene.Net.Facet; namespace Examine.Lucene.Providers { @@ -20,9 +21,10 @@ public class MultiIndexSearcher : BaseLuceneSearcher /// /// /// + /// Get the current by injecting or use new FacetsConfig() for an empty configuration /// - public MultiIndexSearcher(string name, IEnumerable indexes, Analyzer analyzer = null) - : base(name, analyzer ?? new StandardAnalyzer(LuceneInfo.CurrentVersion)) + public MultiIndexSearcher(string name, IEnumerable indexes, FacetsConfig facetsConfig, Analyzer analyzer = null) + : base(name, analyzer ?? new StandardAnalyzer(LuceneInfo.CurrentVersion), facetsConfig) { _searchers = new Lazy>(() => indexes.Select(x => x.Searcher)); } @@ -32,9 +34,10 @@ public MultiIndexSearcher(string name, IEnumerable indexes, Analyzer ana /// /// /// + /// Get the current by injecting or use new FacetsConfig() for an empty configuration /// - public MultiIndexSearcher(string name, Lazy> searchers, Analyzer analyzer = null) - : base(name, analyzer ?? new StandardAnalyzer(LuceneInfo.CurrentVersion)) + public MultiIndexSearcher(string name, Lazy> searchers, FacetsConfig facetsConfig, Analyzer analyzer = null) + : base(name, analyzer ?? new StandardAnalyzer(LuceneInfo.CurrentVersion), facetsConfig) { _searchers = searchers; } diff --git a/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs b/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs deleted file mode 100644 index fc2278436..000000000 --- a/src/Examine.Lucene/Search/FacetDoubleRangeQueryField.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Examine.Search; - -namespace Examine.Lucene.Search -{ - public class FacetDoubleRangeQueryField : IFacetDoubleRangeQueryField - { - private readonly LuceneSearchQuery _search; - private readonly FacetDoubleField _field; - - public FacetDoubleRangeQueryField(LuceneSearchQuery search, FacetDoubleField field) - { - _search = search; - _field = field; - } - - public IFaceting And() => new LuceneFacetOperation(_search); - public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); - - /// - public IFacetDoubleRangeQueryField FacetField(string fieldName) - { - _field.FacetField = fieldName; - - return this; - } - } -} diff --git a/src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs b/src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs deleted file mode 100644 index 151518825..000000000 --- a/src/Examine.Lucene/Search/FacetFloatRangeQueryField.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Examine.Search; - -namespace Examine.Lucene.Search -{ - public class FacetFloatRangeQueryField : IFacetFloatRangeQueryField - { - private readonly LuceneSearchQuery _search; - private readonly FacetFloatField _field; - - public FacetFloatRangeQueryField(LuceneSearchQuery search, FacetFloatField field) - { - _search = search; - _field = field; - } - - public IFaceting And() => new LuceneFacetOperation(_search); - public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); - - /// - public IFacetFloatRangeQueryField FacetField(string fieldName) - { - _field.FacetField = fieldName; - - return this; - } - } -} diff --git a/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs b/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs deleted file mode 100644 index 36c218ea1..000000000 --- a/src/Examine.Lucene/Search/FacetLongRangeQueryField.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Examine.Search; - -namespace Examine.Lucene.Search -{ - public class FacetLongRangeQueryField : IFacetLongRangeQueryField - { - private readonly LuceneSearchQuery _search; - private readonly FacetLongField _field; - - public FacetLongRangeQueryField(LuceneSearchQuery search, FacetLongField field) - { - _search = search; - _field = field; - } - - public IFaceting And() => new LuceneFacetOperation(_search); - public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); - - /// - public IFacetLongRangeQueryField FacetField(string fieldName) - { - _field.FacetField = fieldName; - - return this; - } - } -} diff --git a/src/Examine.Lucene/Search/FacetQueryField.cs b/src/Examine.Lucene/Search/FacetQueryField.cs index 7f000b5de..d74fbd886 100644 --- a/src/Examine.Lucene/Search/FacetQueryField.cs +++ b/src/Examine.Lucene/Search/FacetQueryField.cs @@ -4,25 +4,13 @@ namespace Examine.Search { public class FacetQueryField : IFacetQueryField { - private readonly LuceneSearchQuery _search; private readonly FacetFullTextField _field; - public FacetQueryField(LuceneSearchQuery search, FacetFullTextField field) + public FacetQueryField(FacetFullTextField field) { - _search = search; _field = field; } - public IFaceting And() => new LuceneFacetOperation(_search); - public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); - - public IFacetQueryField FacetField(string fieldName) - { - _field.FacetField = fieldName; - - return this; - } - public IFacetQueryField MaxCount(int count) { _field.MaxCount = count; diff --git a/src/Examine.Lucene/Search/LuceneFacetOperation.cs b/src/Examine.Lucene/Search/LuceneFacetOperation.cs index 2d5481a54..38d8233d7 100644 --- a/src/Examine.Lucene/Search/LuceneFacetOperation.cs +++ b/src/Examine.Lucene/Search/LuceneFacetOperation.cs @@ -12,7 +12,7 @@ namespace Examine.Lucene.Search /// An implementation of the fluent API boolean operations /// [DebuggerDisplay("{_search}")] - public class LuceneFacetOperation : LuceneFacetOperationBase + public class LuceneFacetOperation : IFaceting { private readonly LuceneSearchQuery _search; @@ -21,17 +21,17 @@ public LuceneFacetOperation(LuceneSearchQuery search) _search = search; } - public override ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); + public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); - public override IFacetQueryField WithFacet(string field) => _search.FacetInternal(field, Array.Empty()); + public IFaceting Facet(string field, Action facetConfiguration = null) => _search.FacetInternal(field, facetConfiguration, Array.Empty()); - public override IFacetQueryField WithFacet(string field, params string[] values) => _search.FacetInternal(field, values); + public IFaceting Facet(string field, Action facetConfiguration = null, params string[] values) => _search.FacetInternal(field, facetConfiguration, values); - public override IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges) => _search.FacetInternal(field, doubleRanges); + public IFaceting Facet(string field, params DoubleRange[] doubleRanges) => _search.FacetInternal(field, doubleRanges); - public override IFacetFloatRangeQueryField WithFacet(string field, params FloatRange[] floatRanges) => _search.FacetInternal(field, floatRanges); + public IFaceting Facet(string field, params FloatRange[] floatRanges) => _search.FacetInternal(field, floatRanges); - public override IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges) => _search.FacetInternal(field, longRanges); + public IFaceting Facet(string field, params Int64Range[] longRanges) => _search.FacetInternal(field, longRanges); public override string ToString() => _search.ToString(); } diff --git a/src/Examine.Lucene/Search/LuceneFacetOperationBase.cs b/src/Examine.Lucene/Search/LuceneFacetOperationBase.cs deleted file mode 100644 index 5f85c32a5..000000000 --- a/src/Examine.Lucene/Search/LuceneFacetOperationBase.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Examine.Search; - -namespace Examine.Lucene.Search -{ - public abstract class LuceneFacetOperationBase : IFaceting - { - public abstract ISearchResults Execute(QueryOptions options = null); - - public abstract IFacetQueryField WithFacet(string field); - - public abstract IFacetQueryField WithFacet(string field, params string[] values); - - public abstract IFacetDoubleRangeQueryField WithFacet(string field, params DoubleRange[] doubleRanges); - - public abstract IFacetFloatRangeQueryField WithFacet(string field, params FloatRange[] floatRanges); - - public abstract IFacetLongRangeQueryField WithFacet(string field, params Int64Range[] longRanges); - } -} diff --git a/src/Examine.Lucene/Search/LuceneSearchQuery.cs b/src/Examine.Lucene/Search/LuceneSearchQuery.cs index a6f84412e..10860b949 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQuery.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQuery.cs @@ -5,6 +5,7 @@ using Examine.Lucene.Indexing; using Examine.Search; using Lucene.Net.Analysis; +using Lucene.Net.Facet; using Lucene.Net.Search; namespace Examine.Lucene.Search @@ -16,15 +17,17 @@ namespace Examine.Lucene.Search public class LuceneSearchQuery : LuceneSearchQueryBase, IQueryExecutor { private readonly ISearchContext _searchContext; + private readonly FacetsConfig _facetsConfig; private ISet _fieldsToLoad = null; private readonly IList _facetFields = new List(); public LuceneSearchQuery( ISearchContext searchContext, - string category, Analyzer analyzer, LuceneSearchOptions searchOptions, BooleanOperation occurance) + string category, Analyzer analyzer, LuceneSearchOptions searchOptions, BooleanOperation occurance, FacetsConfig facetsConfig) : base(CreateQueryParser(searchContext, analyzer, searchOptions), category, searchOptions, occurance) { _searchContext = searchContext; + _facetsConfig = facetsConfig; } private static CustomMultiFieldQueryParser CreateQueryParser(ISearchContext searchContext, Analyzer analyzer, LuceneSearchOptions searchOptions) @@ -310,61 +313,74 @@ public IBooleanOperation SelectAllFieldsInternal() protected override LuceneBooleanOperationBase CreateOp() => new LuceneBooleanOperation(this); - internal IFacetQueryField FacetInternal(string field, params string[] values) + internal IFaceting FacetInternal(string field, Action facetConfiguration, params string[] values) { if(values == null) { values = Array.Empty(); } - var facet = new FacetFullTextField(field, values); + var facet = new FacetFullTextField(field, values, GetFacetField(field)); + + if(facetConfiguration != null) + { + facetConfiguration.Invoke(new FacetQueryField(facet)); + } _facetFields.Add(facet); - return new FacetQueryField(this, facet); + return new LuceneFacetOperation(this); } - - internal IFacetDoubleRangeQueryField FacetInternal(string field, params DoubleRange[] doubleRanges) + internal IFaceting FacetInternal(string field, params DoubleRange[] doubleRanges) { if(doubleRanges == null) { doubleRanges = Array.Empty(); } - var facet = new FacetDoubleField(field, doubleRanges); + var facet = new FacetDoubleField(field, doubleRanges, GetFacetField(field)); _facetFields.Add(facet); - return new FacetDoubleRangeQueryField(this, facet); + return new LuceneFacetOperation(this); } - internal IFacetFloatRangeQueryField FacetInternal(string field, params FloatRange[] floatRanges) + internal IFaceting FacetInternal(string field, params FloatRange[] floatRanges) { if (floatRanges == null) { floatRanges = Array.Empty(); } - var facet = new FacetFloatField(field, floatRanges); + var facet = new FacetFloatField(field, floatRanges, GetFacetField(field)); _facetFields.Add(facet); - return new FacetFloatRangeQueryField(this, facet); + return new LuceneFacetOperation(this); } - internal IFacetLongRangeQueryField FacetInternal(string field, params Int64Range[] longRanges) + internal IFaceting FacetInternal(string field, params Int64Range[] longRanges) { if(longRanges == null) { longRanges = Array.Empty(); } - var facet = new FacetLongField(field, longRanges); + var facet = new FacetLongField(field, longRanges, GetFacetField(field)); _facetFields.Add(facet); - return new FacetLongRangeQueryField(this, facet); + return new LuceneFacetOperation(this); + } + + private string GetFacetField(string field) + { + if (_facetsConfig.DimConfigs.ContainsKey(field)) + { + return _facetsConfig.DimConfigs[field].IndexFieldName; + } + return ExamineFieldNames.DefaultFacetsName; } } } diff --git a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs index 7da24f1ed..2d0bee137 100644 --- a/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs @@ -64,7 +64,7 @@ public void Allow_Leading_Wildcards(bool withFacets) if (withFacets) { - var results1 = query1.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results1 = query1.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results1.GetFacet("nodeName"); @@ -112,7 +112,7 @@ public void NativeQuery_Single_Word(bool withFacets) if (withFacets) { - var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = query.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -160,7 +160,7 @@ public void Uppercase_Category(bool withFacets) if (withFacets) { - var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = query.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -208,12 +208,7 @@ public void FacetsConfig_SetIndexName_FullText() Console.WriteLine(query); - var results = query.WithFacets(facets => facets.WithFacet("nodeName").FacetField("facet_nodeName")).Execute(); - - Assert.Throws(() => - { - query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); // $facets not indexed - }); + var results = query.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -254,17 +249,12 @@ public void FacetsConfig_SetIndexName_Long() Console.WriteLine(query); - var results = query.WithFacets(facets => facets.WithFacet("LongValue", new Int64Range[] + var results = query.WithFacets(facets => facets.Facet("LongValue", new Int64Range[] { new Int64Range("10", 10, true, 11, true), new Int64Range("20", 20, true, 21, true), new Int64Range("30", 30, true, 31, true), - }).FacetField("facet_longvalue")).Execute(); - - Assert.Throws(() => - { - query.WithFacets(facets => facets.WithFacet("LongValue")).Execute(); // $facets not indexed - }); + })).Execute(); var facetResults = results.GetFacet("LongValue"); @@ -305,17 +295,12 @@ public void FacetsConfig_SetIndexName_Double() Console.WriteLine(query); - var results = query.WithFacets(factes => factes.WithFacet("DoubleValue", new DoubleRange[] + var results = query.WithFacets(factes => factes.Facet("DoubleValue", new DoubleRange[] { new DoubleRange("10", 10, true, 11, true), new DoubleRange("20", 20, true, 21, true), new DoubleRange("30", 30, true, 31, true), - }).FacetField("facet_doublevalue")).Execute(); - - Assert.Throws(() => - { - query.WithFacets(facets => facets.WithFacet("DoubleValue")).Execute(); // $facets not indexed - }); + })).Execute(); var facetResults = results.GetFacet("DoubleValue"); @@ -356,7 +341,7 @@ public void NativeQuery_Phrase(bool withFacets) if (withFacets) { - var results = query.WithFacets(facets => facets.WithFacet("bodyText")).Execute(); + var results = query.WithFacets(facets => facets.Facet("bodyText")).Execute(); var facetResults = results.GetFacet("bodyText"); @@ -421,7 +406,7 @@ public void Managed_Range_Date(bool withFacets) if(withFacets) { - var numberSortedResult = numberSortedCriteria.WithFacets(facets => facets.WithFacet("created", new Int64Range[] + var numberSortedResult = numberSortedCriteria.WithFacets(facets => facets.Facet("created", new Int64Range[] { new Int64Range("First days", new DateTime(2000, 01, 01).Ticks, true, new DateTime(2000, 01, 03).Ticks, true), new Int64Range("Last days", new DateTime(2000, 01, 04).Ticks, true, new DateTime(2000, 01, 06).Ticks, true) @@ -471,7 +456,7 @@ public void Managed_Full_Text(bool withFacets) { var result = searcher.CreateQuery() .ManagedQuery("darkness") - .WithFacets(facets => facets.WithFacet("item1")) + .WithFacets(facets => facets.Facet("item1")) .Execute(); var facetResults = result.GetFacet("item1"); @@ -487,7 +472,7 @@ public void Managed_Full_Text(bool withFacets) result = searcher.CreateQuery() .ManagedQuery("total darkness") - .WithFacets(facets => facets.WithFacet("item1")) + .WithFacets(facets => facets.Facet("item1")) .Execute(); facetResults = result.GetFacet("item1"); @@ -547,7 +532,7 @@ public void Managed_Full_Text_With_Bool(bool withFacets) if (withFacets) { - var result = qry.WithFacets(facets => facets.WithFacet("item1")).Execute(); + var result = qry.WithFacets(facets => facets.Facet("item1")).Execute(); var facetResults = result.GetFacet("item1"); @@ -562,7 +547,7 @@ public void Managed_Full_Text_With_Bool(bool withFacets) qry = searcher.CreateQuery().ManagedQuery("darkness") .And(query => query.Field("item1", "value1").Or().Field("item1", "value2"), BooleanOperation.Or); Console.WriteLine(qry); - result = qry.WithFacets(facets => facets.WithFacet("item1")).Execute(); + result = qry.WithFacets(facets => facets.Facet("item1")).Execute(); facetResults = result.GetFacet("item1"); @@ -629,7 +614,7 @@ public void Not_Managed_Full_Text(bool withFacets) if (withFacets) { - var result = qry.WithFacets(facets => facets.WithFacet("item1")).Execute(); + var result = qry.WithFacets(facets => facets.Facet("item1")).Execute(); var facetResults = result.GetFacet("item1"); @@ -708,7 +693,7 @@ public void Managed_Range_Int(bool withFacets) if (withFacets) { var numberSortedResult = numberSortedCriteria - .WithFacets(facets => facets.WithFacet("parentID", new Int64Range[] + .WithFacets(facets => facets.Facet("parentID", new Int64Range[] { new Int64Range("120-122", 120, true, 122, true), new Int64Range("123-125", 123, true, 125, true) @@ -780,7 +765,7 @@ public void Legacy_ParentId(bool withFacets) if (withFacets) { - var numberSortedResult = numberSortedCriteria.WithFacets(facets => facets.WithFacet("parentID")).Execute(); + var numberSortedResult = numberSortedCriteria.WithFacets(facets => facets.Facet("parentID")).Execute(); var facetResults = numberSortedResult.GetFacet("parentID"); @@ -849,7 +834,7 @@ public void Grouped_Or_Examiness(bool withFacets) if (withFacets) { - var results = filter.WithFacets(facets => facets.WithFacet("nodeTypeAlias")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("nodeTypeAlias")).Execute(); var facetResults = results.GetFacet("nodeTypeAlias"); @@ -890,35 +875,35 @@ public void Grouped_Or_Query_Output() Console.WriteLine("GROUPED OR - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedOr(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 id:2 id:3 parentID:1 parentID:2 parentID:3 blahID:1 blahID:2 blahID:3)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedOr(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1 parentID:1)", criteria.Query.ToString()); Console.WriteLine("GROUPED OR - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedOr(new[] { "id" }.ToList(), new[] { "1" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(id:1)", criteria.Query.ToString()); @@ -941,7 +926,7 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); //We used to assert this, but it must be allowed to do an add on the same field multiple times //Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); @@ -950,7 +935,7 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedAnd(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); //The field/value array lengths are equal so we will match the key/value pairs Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2 +blahID:3)", criteria.Query.ToString()); @@ -958,7 +943,7 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); //There are more than one field and there are more values than fields, in this case we align the key/value pairs Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:2)", criteria.Query.ToString()); @@ -966,14 +951,14 @@ public void Grouped_And_Query_Output() Console.WriteLine("GROUPED AND - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedAnd(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1 +parentID:1)", criteria.Query.ToString()); Console.WriteLine("GROUPED AND - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedAnd(new[] { "id" }.ToList(), new[] { "1" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias +(+id:1)", criteria.Query.ToString()); } @@ -995,35 +980,35 @@ public void Grouped_Not_Query_Output() Console.WriteLine("GROUPED NOT - SINGLE FIELD, MULTI VAL"); var criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, EQUAL MULTI VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedNot(new[] { "id", "parentID", "blahID" }.ToList(), new[] { "1", "2", "3" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -id:2 -id:3 -parentID:1 -parentID:2 -parentID:3 -blahID:1 -blahID:2 -blahID:3", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - MULTI FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedNot(new[] { "id", "parentID" }.ToList(), new[] { "1" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1 -parentID:1", criteria.Query.ToString()); Console.WriteLine("GROUPED NOT - SINGLE FIELD, SINGLE VAL"); criteria = (LuceneSearchQuery)searcher.CreateQuery(); criteria.Field("__NodeTypeAlias", "myDocumentTypeAlias"); - criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.GroupedNot(new[] { "id" }.ToList(), new[] { "1" }).WithFacets(facets => facets.Facet("SomeFacet")); Console.WriteLine(criteria.Query); Assert.AreEqual("+__NodeTypeAlias:mydocumenttypealias -id:1", criteria.Query.ToString()); } @@ -1056,7 +1041,7 @@ public void Grouped_Not_Single_Field_Single_Value(bool withFacets) if (withFacets) { - var results = query.All().WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = query.All().WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1101,7 +1086,7 @@ public void Grouped_Not_Multi_Field_Single_Value(bool withFacets) if (withFacets) { - var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = query.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1151,7 +1136,7 @@ public void Grouped_Or_With_Not(bool withFacets) if (withFacets) { - var results = filter.WithFacets(facets => facets.WithFacet("headerText")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("headerText")).Execute(); var facetResults = results.GetFacet("headerText"); @@ -1195,7 +1180,7 @@ public void And_Grouped_Not_Single_Value(bool withFacets) if (withFacets) { - var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = query.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1239,7 +1224,7 @@ public void And_Grouped_Not_Multi_Value(bool withFacets) if (withFacets) { - var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = query.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); Assert.AreEqual(1, results.TotalItemCount); @@ -1282,7 +1267,7 @@ public void And_Not_Single_Field(bool withFacets) if (withFacets) { - var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = query.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacets(); @@ -1330,7 +1315,7 @@ public void AndNot_Nested(bool withFacets) if (withFacets) { - var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = query.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); Assert.AreEqual(1, results.TotalItemCount); @@ -1376,7 +1361,7 @@ public void And_Not_Added_Later(bool withFacets) if (withFacets) { - var results = query.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = query.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); Assert.AreEqual(1, results.TotalItemCount); @@ -1420,7 +1405,7 @@ public void Not_Range(bool withFacets) { var results = query - .WithFacets(facets => facets.WithFacet("start", new Int64Range("Label", 100, false, 200, false))) + .WithFacets(facets => facets.Facet("start", new Int64Range("Label", 100, false, 200, false))) .Execute(); var facetResults = results.GetFacet("start"); @@ -1481,7 +1466,7 @@ public void Match_By_Path(bool withFacets) if (withFacets) { - var results1 = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results1 = filter.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults1 = results1.GetFacet("nodeName"); Assert.AreEqual(1, results1.TotalItemCount); Assert.AreEqual(1, facetResults1.Count()); @@ -1498,7 +1483,7 @@ public void Match_By_Path(bool withFacets) if (withFacets) { - var results2 = exactfilter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results2 = exactfilter.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults2 = results2.GetFacet("nodeName"); Assert.AreEqual(1, results2.TotalItemCount); Assert.AreEqual(1, facetResults2.Count()); @@ -1517,7 +1502,7 @@ public void Match_By_Path(bool withFacets) if (withFacets) { - var results5 = nativeFilter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results5 = nativeFilter.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults5 = results5.GetFacet("nodeName"); Assert.AreEqual(1, results5.TotalItemCount); Assert.AreEqual(1, facetResults5.Count()); @@ -1534,7 +1519,7 @@ public void Match_By_Path(bool withFacets) if (withFacets) { - var results3 = wildcardfilter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results3 = wildcardfilter.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults3 = results3.GetFacet("nodeName"); Assert.AreEqual(2, results3.TotalItemCount); Assert.AreEqual(2, facetResults3.Count()); @@ -1551,7 +1536,7 @@ public void Match_By_Path(bool withFacets) if (withFacets) { - var results3 = wildcardfilter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results3 = wildcardfilter.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults3 = results3.GetFacet("nodeName"); Assert.AreEqual(0, results3.TotalItemCount); Assert.AreEqual(0, facetResults3?.Count() ?? 0); @@ -1596,7 +1581,7 @@ public void Find_By_ParentId(bool withFacets) if (withFacets) { - var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1653,7 +1638,7 @@ public void Find_By_ParentId_Native_Query(bool withFacets) if (withFacets) { - var results = filter.WithFacets(facets => facets.WithFacet("parentID")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("parentID")).Execute(); var facetResults = results.GetFacet("parentID"); @@ -1719,7 +1704,7 @@ public void Find_By_NodeTypeAlias(bool withFacets) if (withFacets) { - var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1771,7 +1756,7 @@ public void Search_With_Stop_Words(bool withFacets) if (withFacets) { - var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1832,7 +1817,7 @@ public void Search_Native_Query(bool withFacets) if (withFacets) { - var results = criteria.WithFacets(facets => facets.WithFacet("nodeTypeAlias")).Execute(); + var results = criteria.WithFacets(facets => facets.Facet("nodeTypeAlias")).Execute(); var facetResults = results.GetFacet("nodeTypeAlias"); @@ -1882,7 +1867,7 @@ public void Find_Only_Image_Media(bool withFacets) if (withFacets) { - var results = filter.WithFacets(facets => facets.WithFacet("nodeTypeAlias")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("nodeTypeAlias")).Execute(); var facetResults = results.GetFacet("nodeTypeAlias"); @@ -1931,7 +1916,7 @@ public void Find_Both_Media_And_Content(bool withFacets) if (withFacets) { - var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -1982,9 +1967,8 @@ public void Sort_Result_By_Number_Field(bool withFacets) { var results1 = sc1 .WithFacets(facets => facets - .WithFacet("sortOrder") - .And() - .WithFacet("parentID")) + .Facet("sortOrder") + .Facet("parentID")) .Execute(); var facetResults = results1.GetFacet("sortOrder"); @@ -2058,9 +2042,8 @@ public void Sort_Result_By_Date_Field(bool withFacets) { var results1 = sc1 .WithFacets(facets => facets - .WithFacet("updateDate") - .And() - .WithFacet("parentID")) + .Facet("updateDate") + .Facet("parentID")) .Execute(); var facetResults = results1.GetFacet("updateDate"); @@ -2132,8 +2115,8 @@ public void Sort_Result_By_Single_Field(bool withFacets) if (withFacets) { - var results1 = sc1.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); - var results2 = sc2.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results1 = sc1.WithFacets(facets => facets.Facet("nodeName")).Execute(); + var results2 = sc2.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults1 = results1.GetFacet("nodeName"); var facetResults2 = results2.GetFacet("nodeName"); @@ -2199,8 +2182,8 @@ public void Sort_Result_By_Double_Fields(string fieldType, SortType sortType, bo if (withFacets) { - var results1 = sc1.WithFacets(facets => facets.WithFacet("field1")).Execute(); - var results2 = sc2.WithFacets(facets => facets.WithFacet("field1")).Execute(); + var results1 = sc1.WithFacets(facets => facets.Facet("field1")).Execute(); + var results2 = sc2.WithFacets(facets => facets.Facet("field1")).Execute(); var facetResults1 = results1.GetFacet("field1"); var facetResults2 = results2.GetFacet("field1"); @@ -2286,7 +2269,7 @@ public void Sort_Result_By_Multiple_Fields(bool withFacets) if (withFacets) { - var results1 = sc1.WithFacets(facets => facets.WithFacet("field1").And().WithFacet("field2")).Execute(); + var results1 = sc1.WithFacets(facets => facets.Facet("field1").Facet("field2")).Execute(); var facetResults = results1.GetFacet("field1"); var facetResults2 = results1.GetFacet("field2"); @@ -2345,7 +2328,7 @@ public void Standard_Results_Sorted_By_Score(bool withFacets) if (withFacets) { - var results = sc1.WithFacets(facets => facets.WithFacet("bodyText")).Execute(); + var results = sc1.WithFacets(facets => facets.Facet("bodyText")).Execute(); var facetResults = results.GetFacet("bodyText"); @@ -2413,7 +2396,7 @@ public void Skip_Results_Returns_Different_Results(bool withFacets) if (withFacets) { //Act - var results = sc.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = sc.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -2464,7 +2447,7 @@ public void Escaping_Includes_All_Words(bool withFacets) if (withFacets) { //Act - var results = sc.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = sc.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -2523,7 +2506,7 @@ public void Grouped_And_Examiness(bool withFacets) if (withFacets) { //Act - var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -2575,7 +2558,7 @@ public void Examiness_Proximity(bool withFacets) if (withFacets) { //Act - var results = filter.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); @@ -2649,12 +2632,12 @@ public void Float_Range_SimpleIndexSet(bool withFacets) { //Act - var results1 = filter1.WithFacets(facets => facets.WithFacet("SomeFloat", new FloatRange[] + var results1 = filter1.WithFacets(facets => facets.Facet("SomeFloat", new FloatRange[] { new FloatRange("1", 0, true, 12, true), new FloatRange("2", 13, true, 250, true) })).Execute(); - var results2 = filter2.WithFacets(facets => facets.WithFacet("SomeFloat", new FloatRange[] + var results2 = filter2.WithFacets(facets => facets.Facet("SomeFloat", new FloatRange[] { new FloatRange("1", 0, true, 12, true), new FloatRange("2", 13, true, 250, true) @@ -2729,8 +2712,8 @@ public void Number_Range_SimpleIndexSet(bool withFacets) if (withFacets) { //Act - var results1 = filter1.WithFacets(facets => facets.WithFacet("SomeNumber").MaxCount(1)).Execute(); - var results2 = filter2.WithFacets(facets => facets.WithFacet("SomeNumber").MaxCount(1)).Execute(); + var results1 = filter1.WithFacets(facets => facets.Facet("SomeNumber", config => config.MaxCount(1))).Execute(); + var results2 = filter2.WithFacets(facets => facets.Facet("SomeNumber", config => config.MaxCount(1))).Execute(); var facetResults1 = results1.GetFacet("SomeNumber"); var facetResults2 = results2.GetFacet("SomeNumber"); @@ -2797,12 +2780,12 @@ public void Double_Range_SimpleIndexSet(bool withFacets) if (withFacets) { //Act - var results1 = filter1.WithFacets(facets => facets.WithFacet("SomeDouble", new DoubleRange[] + var results1 = filter1.WithFacets(facets => facets.Facet("SomeDouble", new DoubleRange[] { new DoubleRange("1", 0, true, 100, true), new DoubleRange("2", 101, true, 200, true) })).Execute(); - var results2 = filter2.WithFacets(facets => facets.WithFacet("SomeDouble", new DoubleRange[] + var results2 = filter2.WithFacets(facets => facets.Facet("SomeDouble", new DoubleRange[] { new DoubleRange("1", 0, true, 100, true), new DoubleRange("2", 101, true, 200, true) @@ -2873,12 +2856,12 @@ public void Long_Range_SimpleIndexSet(bool withFacets) if (withFacets) { //Act - var results1 = filter1.WithFacets(facets => facets.WithFacet("SomeLong", new Int64Range[] + var results1 = filter1.WithFacets(facets => facets.Facet("SomeLong", new Int64Range[] { new Int64Range("1", 0L, true, 100L, true), new Int64Range("2", 101L, true, 200L, true) })).Execute(); - var results2 = filter2.WithFacets(facets => facets.WithFacet("SomeLong", new Int64Range[] + var results2 = filter2.WithFacets(facets => facets.Facet("SomeLong", new Int64Range[] { new Int64Range("1", 0L, true, 100L, true), new Int64Range("2", 101L, true, 200L, true) @@ -2951,12 +2934,12 @@ public void Date_Range_SimpleIndexSet(bool withFacets) if (withFacets) { ////Act - var results = filter.WithFacets(facets => facets.WithFacet("DateCreated", new Int64Range[] + var results = filter.WithFacets(facets => facets.Facet("DateCreated", new Int64Range[] { new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) })).Execute(); - var results2 = filter2.WithFacets(facets => facets.WithFacet("DateCreated", new Int64Range[] + var results2 = filter2.WithFacets(facets => facets.Facet("DateCreated", new Int64Range[] { new Int64Range("1", reIndexDateTime.AddYears(-1).Ticks, true, reIndexDateTime.Ticks, true), new Int64Range("2", reIndexDateTime.AddMinutes(1).Ticks, true, reIndexDateTime.AddDays(1).Ticks, true) @@ -3024,8 +3007,8 @@ public void Fuzzy_Search(bool withFacets) if (withFacets) { ////Act - var results = filter.WithFacets(facets => facets.WithFacet("Content")).Execute(); - var results2 = filter2.WithFacets(facets => facets.WithFacet("Content")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("Content")).Execute(); + var results2 = filter2.WithFacets(facets => facets.Facet("Content")).Execute(); var facetResults1 = results.GetFacet("Content"); var facetResults2 = results2.GetFacet("Content"); @@ -3173,7 +3156,7 @@ public void Inner_Or_Query(bool withFacets) if (withFacets) { //Act - var results = filter.WithFacets(facets => facets.WithFacet("Type")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("Type")).Execute(); var facetResults = results.GetFacet("Type"); @@ -3233,7 +3216,7 @@ public void Inner_And_Query(bool withFacets) if (withFacets) { //Act - var results = filter.WithFacets(facets => facets.WithFacet("Type")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("Type")).Execute(); var facetResults = results.GetFacet("Type"); @@ -3293,7 +3276,7 @@ public void Inner_Not_Query(bool withFacets) if (withFacets) { //Act - var results = filter.WithFacets(facets => facets.WithFacet("Type")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("Type")).Execute(); var facetResults = results.GetFacet("Type"); @@ -3360,7 +3343,7 @@ public void Complex_Or_Group_Nested_Query(bool withFacets) { //Act - var results = filter.WithFacets(facets => facets.WithFacet("Type")).Execute(); + var results = filter.WithFacets(facets => facets.Facet("Type")).Execute(); var facetResults = results.GetFacet("Type"); @@ -3405,7 +3388,7 @@ public void Custom_Lucene_Query_With_Native(bool withFacets) if (withFacets) { - criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)).WithFacets(facets => facets.WithFacet("SomeFacet")); + criteria.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)).WithFacets(facets => facets.Facet("SomeFacet")); } else { @@ -3551,7 +3534,7 @@ public void Select_Field(bool withFacets) if (withFacets) { - var results = sc1.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = sc1.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); var expectedLoadedFields = new string[] { "__Path" }; @@ -3609,7 +3592,7 @@ public void Select_Fields(bool withFacets) if (withFacets) { - var results = sc1.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = sc1.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); var expectedLoadedFields = new string[] { "nodeName", "bodyText", "id", "__NodeId" }; @@ -3668,7 +3651,7 @@ public void Select_Fields_HashSet(bool withFacets) if (withFacets) { - var results = sc1.WithFacets(facets => facets.WithFacet("nodeName")).Execute(); + var results = sc1.WithFacets(facets => facets.Facet("nodeName")).Execute(); var facetResults = results.GetFacet("nodeName"); var expectedLoadedFields = new string[] { "nodeName", "bodyText" }; @@ -3802,7 +3785,7 @@ public void Paging_With_Skip_Take(bool withFacets) if (withFacets) { - var results = sc.WithFacets(facets => facets.WithFacet("writerName")) + var results = sc.WithFacets(facets => facets.Facet("writerName")) .Execute(QueryOptions.SkipTake(pageIndex * pageSize, pageSize)); Assert.AreEqual(2, results.Count()); var facetResults = results.GetFacet("writerName"); @@ -3918,7 +3901,7 @@ public void Given_SkipTake_Returns_ExpectedTotals(int skip, int take, int expect if (withFacets) { - var results = sc.WithFacets(facets => facets.WithFacet("nodeName")).Execute(QueryOptions.SkipTake(skip, take)); + var results = sc.WithFacets(facets => facets.Facet("nodeName")).Execute(QueryOptions.SkipTake(skip, take)); var facetResults = results.GetFacet("nodeName"); @@ -3987,7 +3970,7 @@ public void Range_DateOnly(bool withFacets) if (withFacets) { - var numberSortedResult = numberSortedCriteria.WithFacets(facets => facets.WithFacet("created")).Execute(); + var numberSortedResult = numberSortedCriteria.WithFacets(facets => facets.Facet("created")).Execute(); var facetResult = numberSortedResult.GetFacet("created"); Assert.AreEqual(2, numberSortedResult.TotalItemCount); diff --git a/src/Examine.Test/Examine.Lucene/Search/MultiIndexSearch.cs b/src/Examine.Test/Examine.Lucene/Search/MultiIndexSearch.cs index 8d3c50638..514c81de3 100644 --- a/src/Examine.Test/Examine.Lucene/Search/MultiIndexSearch.cs +++ b/src/Examine.Test/Examine.Lucene/Search/MultiIndexSearch.cs @@ -3,6 +3,7 @@ using Lucene.Net.Analysis.Standard; using NUnit.Framework; using Examine.Lucene.Providers; +using Lucene.Net.Facet; namespace Examine.Test.Examine.Lucene.Search { @@ -34,6 +35,7 @@ public void MultiIndex_Simple_Search() var searcher = new MultiIndexSearcher("testSearcher", new[] { indexer1, indexer2, indexer3, indexer4 }, + new FacetsConfig(), analyzer); var result = searcher.Search("darkness"); @@ -69,6 +71,7 @@ public void MultiIndex_Field_Count() var searcher = new MultiIndexSearcher("testSearcher", new[] { indexer1, indexer2, indexer3, indexer4 }, + new FacetsConfig(), analyzer); var result = searcher.GetSearchContext().SearchableFields; diff --git a/src/Examine.Test/Examine.Lucene/Search/MultiIndexSearchTests.cs b/src/Examine.Test/Examine.Lucene/Search/MultiIndexSearchTests.cs index 12d94e3b8..4f9cf3b55 100644 --- a/src/Examine.Test/Examine.Lucene/Search/MultiIndexSearchTests.cs +++ b/src/Examine.Test/Examine.Lucene/Search/MultiIndexSearchTests.cs @@ -4,6 +4,7 @@ using Lucene.Net.Analysis.Standard; using NUnit.Framework; using Lucene.Net.Analysis.Util; +using Lucene.Net.Facet; namespace Examine.Test.Examine.Lucene.Search { @@ -36,6 +37,7 @@ public void GivenCustomStopWords_WhenUsedOnlyForSearchingAndNotIndexing_TheDefau var searcher = new MultiIndexSearcher("testSearcher", new[] { indexer1, indexer2 }, + new FacetsConfig(), customAnalyzer); // Even though the custom analyzer doesn't have a stop word of 'will' @@ -63,6 +65,7 @@ public void GivenCustomStopWords_WhenUsedOnlyForIndexingAndNotForSearching_TheDe var searcher = new MultiIndexSearcher("testSearcher", new[] { indexer1, indexer2 }, + new FacetsConfig(), // The Analyzer here is used for query parsing values when // non ManagedQuery queries are executed. standardAnalyzer); @@ -94,6 +97,7 @@ public void GivenCustomStopWords_WhenUsedOnlyForIndexingAndNotForSearching_TheDe var searcher = new MultiIndexSearcher("testSearcher", new[] { indexer1, indexer2 }, + new FacetsConfig(), // The Analyzer here is used for query parsing values when // non ManagedQuery queries are executed. standardAnalyzer); @@ -133,6 +137,7 @@ public void MultiIndex_Simple_Search() var searcher = new MultiIndexSearcher("testSearcher", new[] { indexer1, indexer2, indexer3, indexer4 }, + new FacetsConfig(), analyzer); var result = searcher.Search("darkness"); @@ -169,6 +174,7 @@ public void MultiIndex_Field_Count() var searcher = new MultiIndexSearcher( "testSearcher", new[] { indexer1, indexer2, indexer3, indexer4 }, + new FacetsConfig(), analyzer); var searchContext = searcher.GetSearchContext(); From e88da28e28fdb5e046b3fdcf4d6adb544cf2ef22 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 21:28:18 +0100 Subject: [PATCH 25/42] refactor: Correct Empty in LuceneSearchResults --- src/Examine.Lucene/Search/LuceneSearchResults.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Examine.Lucene/Search/LuceneSearchResults.cs b/src/Examine.Lucene/Search/LuceneSearchResults.cs index f319f2762..019490c11 100644 --- a/src/Examine.Lucene/Search/LuceneSearchResults.cs +++ b/src/Examine.Lucene/Search/LuceneSearchResults.cs @@ -1,13 +1,12 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Collections.ObjectModel; namespace Examine.Lucene.Search { public class LuceneSearchResults : ISearchResults, IFacetResults { - public static LuceneSearchResults Empty { get; } = new LuceneSearchResults(Array.Empty(), 0, new ReadOnlyDictionary(new Dictionary())); + public static LuceneSearchResults Empty { get; } = new LuceneSearchResults(Array.Empty(), 0, new Dictionary()); private readonly IReadOnlyCollection _results; From 98049026c2178aa58be68bfad9d0ce675f25dacb Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 21:33:28 +0100 Subject: [PATCH 26/42] chore: Unused using --- src/Examine.Lucene/Search/FacetQueryField.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Examine.Lucene/Search/FacetQueryField.cs b/src/Examine.Lucene/Search/FacetQueryField.cs index d74fbd886..706268d5e 100644 --- a/src/Examine.Lucene/Search/FacetQueryField.cs +++ b/src/Examine.Lucene/Search/FacetQueryField.cs @@ -1,5 +1,3 @@ -using Examine.Lucene.Search; - namespace Examine.Search { public class FacetQueryField : IFacetQueryField From 6b19575ed22c5e9c06a374b809c3d993368b953b Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 21:49:45 +0100 Subject: [PATCH 27/42] refactor: Namespace alignment & Use interface field --- src/Examine.Core/Search/FacetResult.cs | 1 - src/Examine.Core/Search/FacetValue.cs | 2 -- src/Examine.Core/Search/IFacetDoubleField.cs | 2 -- src/Examine.Core/Search/IFacetField.cs | 2 +- src/Examine.Core/Search/IFacetFloatField.cs | 2 -- src/Examine.Core/Search/IFacetFullTextField.cs | 2 -- src/Examine.Core/Search/IFacetLongField.cs | 2 -- src/Examine.Core/Search/IFacetResult.cs | 2 +- src/Examine.Core/Search/IFacetResults.cs | 2 +- src/Examine.Core/Search/IFacetValue.cs | 2 +- src/Examine.Lucene/FacetExtensions.cs | 11 +++++------ src/Examine.Lucene/Search/FacetQueryField.cs | 4 +++- src/Examine.Lucene/Search/LuceneSearchExecutor.cs | 10 +++++----- src/Examine.Lucene/Search/LuceneSearchResults.cs | 1 + 14 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/Examine.Core/Search/FacetResult.cs b/src/Examine.Core/Search/FacetResult.cs index adb9294a5..1aebde175 100644 --- a/src/Examine.Core/Search/FacetResult.cs +++ b/src/Examine.Core/Search/FacetResult.cs @@ -1,7 +1,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using Examine.Lucene.Search; namespace Examine.Search { diff --git a/src/Examine.Core/Search/FacetValue.cs b/src/Examine.Core/Search/FacetValue.cs index 37ac4332e..799a84b84 100644 --- a/src/Examine.Core/Search/FacetValue.cs +++ b/src/Examine.Core/Search/FacetValue.cs @@ -1,5 +1,3 @@ -using Examine.Lucene.Search; - namespace Examine.Search { public readonly struct FacetValue : IFacetValue diff --git a/src/Examine.Core/Search/IFacetDoubleField.cs b/src/Examine.Core/Search/IFacetDoubleField.cs index 6be9dfdb2..be23b3108 100644 --- a/src/Examine.Core/Search/IFacetDoubleField.cs +++ b/src/Examine.Core/Search/IFacetDoubleField.cs @@ -1,5 +1,3 @@ -using Examine.Lucene.Search; - namespace Examine.Search { public interface IFacetDoubleField : IFacetField diff --git a/src/Examine.Core/Search/IFacetField.cs b/src/Examine.Core/Search/IFacetField.cs index ece4464a7..25d73e70f 100644 --- a/src/Examine.Core/Search/IFacetField.cs +++ b/src/Examine.Core/Search/IFacetField.cs @@ -1,4 +1,4 @@ -namespace Examine.Lucene.Search +namespace Examine.Search { public interface IFacetField { diff --git a/src/Examine.Core/Search/IFacetFloatField.cs b/src/Examine.Core/Search/IFacetFloatField.cs index e936e9216..e04120ae2 100644 --- a/src/Examine.Core/Search/IFacetFloatField.cs +++ b/src/Examine.Core/Search/IFacetFloatField.cs @@ -1,5 +1,3 @@ -using Examine.Lucene.Search; - namespace Examine.Search { public interface IFacetFloatField : IFacetField diff --git a/src/Examine.Core/Search/IFacetFullTextField.cs b/src/Examine.Core/Search/IFacetFullTextField.cs index 568976155..1b25b1854 100644 --- a/src/Examine.Core/Search/IFacetFullTextField.cs +++ b/src/Examine.Core/Search/IFacetFullTextField.cs @@ -1,5 +1,3 @@ -using Examine.Lucene.Search; - namespace Examine.Search { public interface IFacetFullTextField : IFacetField diff --git a/src/Examine.Core/Search/IFacetLongField.cs b/src/Examine.Core/Search/IFacetLongField.cs index 8b4272817..d912ac7e7 100644 --- a/src/Examine.Core/Search/IFacetLongField.cs +++ b/src/Examine.Core/Search/IFacetLongField.cs @@ -1,5 +1,3 @@ -using Examine.Lucene.Search; - namespace Examine.Search { public interface IFacetLongField : IFacetField diff --git a/src/Examine.Core/Search/IFacetResult.cs b/src/Examine.Core/Search/IFacetResult.cs index 81dc59547..af6236735 100644 --- a/src/Examine.Core/Search/IFacetResult.cs +++ b/src/Examine.Core/Search/IFacetResult.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Examine.Lucene.Search +namespace Examine.Search { public interface IFacetResult : IEnumerable { diff --git a/src/Examine.Core/Search/IFacetResults.cs b/src/Examine.Core/Search/IFacetResults.cs index 27f176bce..d23032dbc 100644 --- a/src/Examine.Core/Search/IFacetResults.cs +++ b/src/Examine.Core/Search/IFacetResults.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Examine.Lucene.Search +namespace Examine.Search { public interface IFacetResults { diff --git a/src/Examine.Core/Search/IFacetValue.cs b/src/Examine.Core/Search/IFacetValue.cs index 3ccfd6b28..d2d0e16c6 100644 --- a/src/Examine.Core/Search/IFacetValue.cs +++ b/src/Examine.Core/Search/IFacetValue.cs @@ -1,4 +1,4 @@ -namespace Examine.Lucene.Search +namespace Examine.Search { public interface IFacetValue { diff --git a/src/Examine.Lucene/FacetExtensions.cs b/src/Examine.Lucene/FacetExtensions.cs index 72b5bc568..932361c44 100644 --- a/src/Examine.Lucene/FacetExtensions.cs +++ b/src/Examine.Lucene/FacetExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Examine.Lucene.Search; using Lucene.Net.Facet.Range; namespace Examine.Lucene @@ -11,14 +10,14 @@ public static class FacetExtensions /// /// Get the values for a particular facet in the results /// - public static IFacetResult GetFacet(this ISearchResults searchResults, string field) + public static Examine.Search.IFacetResult GetFacet(this ISearchResults searchResults, string field) { - if (!(searchResults is IFacetResults facetResults)) + if (!(searchResults is Examine.Search.IFacetResults facetResults)) { throw new NotSupportedException("Result does not support facets"); } - facetResults.Facets.TryGetValue(field, out IFacetResult facet); + facetResults.Facets.TryGetValue(field, out Examine.Search.IFacetResult facet); return facet; } @@ -26,9 +25,9 @@ public static IFacetResult GetFacet(this ISearchResults searchResults, string fi /// /// Get all of the facets in the results /// - public static IEnumerable GetFacets(this ISearchResults searchResults) + public static IEnumerable GetFacets(this ISearchResults searchResults) { - if (!(searchResults is IFacetResults facetResults)) + if (!(searchResults is Examine.Search.IFacetResults facetResults)) { throw new NotSupportedException("Result does not support facets"); } diff --git a/src/Examine.Lucene/Search/FacetQueryField.cs b/src/Examine.Lucene/Search/FacetQueryField.cs index 706268d5e..22e461a89 100644 --- a/src/Examine.Lucene/Search/FacetQueryField.cs +++ b/src/Examine.Lucene/Search/FacetQueryField.cs @@ -1,4 +1,6 @@ -namespace Examine.Search +using Examine.Search; + +namespace Examine.Lucene.Search { public class FacetQueryField : IFacetQueryField { diff --git a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs index d342ef4e8..0fb41d2b6 100644 --- a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs +++ b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs @@ -150,11 +150,11 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector foreach(var field in facetFields) { - if (field is FacetFullTextField facetFullTextField) + if (field is IFacetFullTextField facetFullTextField) { ExtractFullTextFacets(facetsCollector, searcher, facets, sortedSetReaderState, field, facetFullTextField); } - else if (field is FacetLongField facetLongField) + else if (field is IFacetLongField facetLongField) { var longFacetCounts = new Int64RangeFacetCounts(facetLongField.Field, facetsCollector, facetLongField.LongRanges.AsLuceneRange().ToArray()); @@ -167,7 +167,7 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector facets.Add(facetLongField.Field, new Examine.Search.FacetResult(longFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } - else if (field is FacetDoubleField facetDoubleField) + else if (field is IFacetDoubleField facetDoubleField) { var doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, facetsCollector, facetDoubleField.DoubleRanges.AsLuceneRange().ToArray()); @@ -180,7 +180,7 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector facets.Add(facetDoubleField.Field, new Examine.Search.FacetResult(doubleFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } - else if(field is FacetFloatField facetFloatField) + else if(field is IFacetFloatField facetFloatField) { var floatFacetCounts = new DoubleRangeFacetCounts(facetFloatField.Field, new SingleFieldSource(facetFloatField.Field), facetsCollector, facetFloatField.FloatRanges.AsLuceneRange().ToArray()); @@ -198,7 +198,7 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector return facets; } - private static void ExtractFullTextFacets(FacetsCollector facetsCollector, ISearcherReference searcher, Dictionary facets, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field, FacetFullTextField facetFullTextField) + private static void ExtractFullTextFacets(FacetsCollector facetsCollector, ISearcherReference searcher, Dictionary facets, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field, IFacetFullTextField facetFullTextField) { if (sortedSetReaderState == null || !sortedSetReaderState.Field.Equals(field.FacetField)) { diff --git a/src/Examine.Lucene/Search/LuceneSearchResults.cs b/src/Examine.Lucene/Search/LuceneSearchResults.cs index 019490c11..1db370592 100644 --- a/src/Examine.Lucene/Search/LuceneSearchResults.cs +++ b/src/Examine.Lucene/Search/LuceneSearchResults.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using Examine.Search; namespace Examine.Lucene.Search { From 67902fdca85aba2cce73d4298e6bea7abe7c85cd Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 21:59:07 +0100 Subject: [PATCH 28/42] refactor: Move FacetQueryField concrete type to Core --- src/Examine.Core/Search/FacetFullTextField.cs | 2 +- .../Search/FacetQueryField.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) rename src/{Examine.Lucene => Examine.Core}/Search/FacetQueryField.cs (86%) diff --git a/src/Examine.Core/Search/FacetFullTextField.cs b/src/Examine.Core/Search/FacetFullTextField.cs index ed993b34a..265632a93 100644 --- a/src/Examine.Core/Search/FacetFullTextField.cs +++ b/src/Examine.Core/Search/FacetFullTextField.cs @@ -2,7 +2,7 @@ namespace Examine.Search { public class FacetFullTextField : IFacetFullTextField { - public int MaxCount { get; set; } + public int MaxCount { get; internal set; } public string[] Values { get; } diff --git a/src/Examine.Lucene/Search/FacetQueryField.cs b/src/Examine.Core/Search/FacetQueryField.cs similarity index 86% rename from src/Examine.Lucene/Search/FacetQueryField.cs rename to src/Examine.Core/Search/FacetQueryField.cs index 22e461a89..706268d5e 100644 --- a/src/Examine.Lucene/Search/FacetQueryField.cs +++ b/src/Examine.Core/Search/FacetQueryField.cs @@ -1,6 +1,4 @@ -using Examine.Search; - -namespace Examine.Lucene.Search +namespace Examine.Search { public class FacetQueryField : IFacetQueryField { From 1a58a38f4b1ce168b21ed76ab813e4297ad37452 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 22:04:30 +0100 Subject: [PATCH 29/42] refactor: Remove field interfaces --- src/Examine.Core/Search/FacetDoubleField.cs | 2 +- src/Examine.Core/Search/FacetFloatField.cs | 2 +- src/Examine.Core/Search/FacetFullTextField.cs | 2 +- src/Examine.Core/Search/FacetLongField.cs | 2 +- src/Examine.Core/Search/IFacetDoubleField.cs | 7 ------- src/Examine.Core/Search/IFacetFloatField.cs | 7 ------- src/Examine.Core/Search/IFacetFullTextField.cs | 15 --------------- src/Examine.Core/Search/IFacetLongField.cs | 7 ------- src/Examine.Lucene/Search/LuceneSearchExecutor.cs | 10 +++++----- 9 files changed, 9 insertions(+), 45 deletions(-) delete mode 100644 src/Examine.Core/Search/IFacetDoubleField.cs delete mode 100644 src/Examine.Core/Search/IFacetFloatField.cs delete mode 100644 src/Examine.Core/Search/IFacetFullTextField.cs delete mode 100644 src/Examine.Core/Search/IFacetLongField.cs diff --git a/src/Examine.Core/Search/FacetDoubleField.cs b/src/Examine.Core/Search/FacetDoubleField.cs index f9a804da8..063932c96 100644 --- a/src/Examine.Core/Search/FacetDoubleField.cs +++ b/src/Examine.Core/Search/FacetDoubleField.cs @@ -1,6 +1,6 @@ namespace Examine.Search { - public readonly struct FacetDoubleField : IFacetDoubleField + public readonly struct FacetDoubleField : IFacetField { public DoubleRange[] DoubleRanges { get; } diff --git a/src/Examine.Core/Search/FacetFloatField.cs b/src/Examine.Core/Search/FacetFloatField.cs index 6f5e8e653..0945138d0 100644 --- a/src/Examine.Core/Search/FacetFloatField.cs +++ b/src/Examine.Core/Search/FacetFloatField.cs @@ -1,6 +1,6 @@ namespace Examine.Search { - public readonly struct FacetFloatField : IFacetFloatField + public readonly struct FacetFloatField : IFacetField { public FloatRange[] FloatRanges { get; } diff --git a/src/Examine.Core/Search/FacetFullTextField.cs b/src/Examine.Core/Search/FacetFullTextField.cs index 265632a93..15b7b82c7 100644 --- a/src/Examine.Core/Search/FacetFullTextField.cs +++ b/src/Examine.Core/Search/FacetFullTextField.cs @@ -1,6 +1,6 @@ namespace Examine.Search { - public class FacetFullTextField : IFacetFullTextField + public class FacetFullTextField : IFacetField { public int MaxCount { get; internal set; } diff --git a/src/Examine.Core/Search/FacetLongField.cs b/src/Examine.Core/Search/FacetLongField.cs index 9820077e9..dc4862da4 100644 --- a/src/Examine.Core/Search/FacetLongField.cs +++ b/src/Examine.Core/Search/FacetLongField.cs @@ -1,6 +1,6 @@ namespace Examine.Search { - public readonly struct FacetLongField : IFacetLongField + public readonly struct FacetLongField : IFacetField { public string Field { get; } diff --git a/src/Examine.Core/Search/IFacetDoubleField.cs b/src/Examine.Core/Search/IFacetDoubleField.cs deleted file mode 100644 index be23b3108..000000000 --- a/src/Examine.Core/Search/IFacetDoubleField.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Examine.Search -{ - public interface IFacetDoubleField : IFacetField - { - DoubleRange[] DoubleRanges { get; } - } -} diff --git a/src/Examine.Core/Search/IFacetFloatField.cs b/src/Examine.Core/Search/IFacetFloatField.cs deleted file mode 100644 index e04120ae2..000000000 --- a/src/Examine.Core/Search/IFacetFloatField.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Examine.Search -{ - public interface IFacetFloatField : IFacetField - { - FloatRange[] FloatRanges { get; } - } -} diff --git a/src/Examine.Core/Search/IFacetFullTextField.cs b/src/Examine.Core/Search/IFacetFullTextField.cs deleted file mode 100644 index 1b25b1854..000000000 --- a/src/Examine.Core/Search/IFacetFullTextField.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Examine.Search -{ - public interface IFacetFullTextField : IFacetField - { - /// - /// Maximum number of terms to return - /// - int MaxCount { get; } - - /// - /// Filter values - /// - string[] Values { get; } - } -} diff --git a/src/Examine.Core/Search/IFacetLongField.cs b/src/Examine.Core/Search/IFacetLongField.cs deleted file mode 100644 index d912ac7e7..000000000 --- a/src/Examine.Core/Search/IFacetLongField.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Examine.Search -{ - public interface IFacetLongField : IFacetField - { - Int64Range[] LongRanges { get; } - } -} diff --git a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs index 0fb41d2b6..d342ef4e8 100644 --- a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs +++ b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs @@ -150,11 +150,11 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector foreach(var field in facetFields) { - if (field is IFacetFullTextField facetFullTextField) + if (field is FacetFullTextField facetFullTextField) { ExtractFullTextFacets(facetsCollector, searcher, facets, sortedSetReaderState, field, facetFullTextField); } - else if (field is IFacetLongField facetLongField) + else if (field is FacetLongField facetLongField) { var longFacetCounts = new Int64RangeFacetCounts(facetLongField.Field, facetsCollector, facetLongField.LongRanges.AsLuceneRange().ToArray()); @@ -167,7 +167,7 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector facets.Add(facetLongField.Field, new Examine.Search.FacetResult(longFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } - else if (field is IFacetDoubleField facetDoubleField) + else if (field is FacetDoubleField facetDoubleField) { var doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, facetsCollector, facetDoubleField.DoubleRanges.AsLuceneRange().ToArray()); @@ -180,7 +180,7 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector facets.Add(facetDoubleField.Field, new Examine.Search.FacetResult(doubleFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } - else if(field is IFacetFloatField facetFloatField) + else if(field is FacetFloatField facetFloatField) { var floatFacetCounts = new DoubleRangeFacetCounts(facetFloatField.Field, new SingleFieldSource(facetFloatField.Field), facetsCollector, facetFloatField.FloatRanges.AsLuceneRange().ToArray()); @@ -198,7 +198,7 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector return facets; } - private static void ExtractFullTextFacets(FacetsCollector facetsCollector, ISearcherReference searcher, Dictionary facets, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field, IFacetFullTextField facetFullTextField) + private static void ExtractFullTextFacets(FacetsCollector facetsCollector, ISearcherReference searcher, Dictionary facets, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field, FacetFullTextField facetFullTextField) { if (sortedSetReaderState == null || !sortedSetReaderState.Field.Equals(field.FacetField)) { From d8ab1129d282b387a4957dc58ac0cd237279ab2b Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 21 Dec 2022 22:09:02 +0100 Subject: [PATCH 30/42] docs: Update code examples from refactor --- docs/searching.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/searching.md b/docs/searching.md index c3c878013..949a83a6e 100644 --- a/docs/searching.md +++ b/docs/searching.md @@ -260,7 +260,7 @@ Basic example var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") - .WithFacets(facets => facets.WithFacet("Address")) // Get facets of the Address field + .WithFacets(facets => facets.Facet("Address")) // Get facets of the Address field .Execute(); var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -279,7 +279,7 @@ Filtered value example var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") - .WithFacets(facets => facets.WithFacet("Address", "Hills")) // Get facets of the Address field + .WithFacets(facets => facets.Facet("Address", "Hills")) // Get facets of the Address field .Execute(); var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -297,7 +297,7 @@ MaxCount example var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") - .WithFacets(facets => facets.WithFacet("Address")) // Get facets of the Address field + .WithFacets(facets => facets.Facet("Address")) // Get facets of the Address field .Execute(); var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -311,7 +311,7 @@ var addressFacetResults = results.GetFacet("Address"); // Returns the facets for results = searcher.CreateQuery() .Field("Address", "Hills") - .WithFacets(facets => facets.WithFacet("Address").MaxCount(2)) // Get facets of the Address field & Gets the top 2 results (The facets with the highest value) + .WithFacets(facets => facets.Facet("Address", config => config.MaxCount(2))) // Get facets of the Address field & Gets the top 2 results (The facets with the highest value) .Execute(); addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -346,7 +346,7 @@ services.AddExamineLuceneIndex("MyIndex", var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .Field("Address", "Hills") - .WithFacets(facets => facets.WithFacet("Address").FacetField("address_facet")) // Get facets of the Address field from the facet field address_facet + .WithFacets(facets => facets.Facet("Address")) // Get facets of the Address field from the facet field address_facet (The facet field is automatically read from the FacetsConfig) .Execute(); var addressFacetResults = results.GetFacet("Address"); // Returns the facets for the specific field Address @@ -369,7 +369,7 @@ Double range example var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .All() - .WithFacets(facets => facets.WithFacet("Price", new DoubleRange[] { + .WithFacets(facets => facets.Facet("Price", new DoubleRange[] { new DoubleRange("0-10", 0, true, 10, true), new DoubleRange("11-20", 11, true, 20, true) })) // Get facets of the price field @@ -391,7 +391,7 @@ Float range example var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .All() - .WithFacets(facets => facets.WithFacet("Price", new FloatRange[] { + .WithFacets(facets => facets.Facet("Price", new FloatRange[] { new FloatRange("0-10", 0, true, 10, true), new FloatRange("11-20", 11, true, 20, true) })) // Get facets of the price field @@ -413,7 +413,7 @@ Int/Long range example var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .All() - .WithFacets(facets => facets.WithFacet("Price", new Int64Range[] { + .WithFacets(facets => facets.Facet("Price", new Int64Range[] { new Int64Range("0-10", 0, true, 10, true), new Int64Range("11-20", 11, true, 20, true) })) // Get facets of the price field @@ -435,7 +435,7 @@ DateTime range example var searcher = myIndex.Searcher; var results = searcher.CreateQuery() .All() - .WithFacets(facets => facets.WithFacet("Created", new Int64Range[] { + .WithFacets(facets => facets.Facet("Created", new Int64Range[] { new Int64Range("first", DateTime.UtcNow.AddDays(-1).Ticks, true, DateTime.UtcNow.Ticks, true), new Int64Range("last", DateTime.UtcNow.AddDays(1).Ticks, true, DateTime.UtcNow.AddDays(2).Ticks, true) })) // Get facets of the price field From 7bddef8768de9f8597a813f4f15e63177dbd3f62 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 28 Dec 2022 22:42:20 +0100 Subject: [PATCH 31/42] perf: Add dictionary to look up facet result --- src/Examine.Core/Search/FacetResult.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Examine.Core/Search/FacetResult.cs b/src/Examine.Core/Search/FacetResult.cs index 1aebde175..1d18b9d32 100644 --- a/src/Examine.Core/Search/FacetResult.cs +++ b/src/Examine.Core/Search/FacetResult.cs @@ -7,6 +7,7 @@ namespace Examine.Search public class FacetResult : IFacetResult { private readonly IEnumerable _values; + private IDictionary _dictValues; public FacetResult(IEnumerable values) { @@ -18,9 +19,24 @@ public IEnumerator GetEnumerator() return _values.GetEnumerator(); } + private void SetValuesDictionary() + { + if(_dictValues == null) + { + _dictValues = _values.ToDictionary(src => src.Label, src => src); + } + } + public IFacetValue Facet(string label) { - return _values.FirstOrDefault(field => field.Label.Equals(label)); + SetValuesDictionary(); + return _dictValues[label]; + } + + public bool TryGetFacet(string label, out IFacetValue facetValue) + { + SetValuesDictionary(); + return _dictValues.TryGetValue(label, out facetValue); } IEnumerator IEnumerable.GetEnumerator() From 9a546ec6286f80da12326ca7dc77afedcca7b024 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 28 Dec 2022 22:58:39 +0100 Subject: [PATCH 32/42] refactor: Combine facetable types with the standard types --- src/Examine.Lucene/Indexing/DateTimeType.cs | 14 ++++++++-- src/Examine.Lucene/Indexing/DoubleType.cs | 12 ++++++++- .../Indexing/FacetDateTimeType.cs | 27 ------------------- .../Indexing/FacetDoubleType.cs | 24 ----------------- .../Indexing/FacetFullTextType.cs | 26 ------------------ src/Examine.Lucene/Indexing/FacetInt32Type.cs | 24 ----------------- src/Examine.Lucene/Indexing/FacetInt64Type.cs | 24 ----------------- .../Indexing/FacetSingleType.cs | 24 ----------------- src/Examine.Lucene/Indexing/FullTextType.cs | 10 ++++++- src/Examine.Lucene/Indexing/Int32Type.cs | 14 ++++++++-- src/Examine.Lucene/Indexing/Int64Type.cs | 14 ++++++++-- src/Examine.Lucene/Indexing/SingleType.cs | 12 ++++++++- .../ValueTypeFactoryCollection.cs | 24 ++++++++--------- 13 files changed, 79 insertions(+), 170 deletions(-) delete mode 100644 src/Examine.Lucene/Indexing/FacetDateTimeType.cs delete mode 100644 src/Examine.Lucene/Indexing/FacetDoubleType.cs delete mode 100644 src/Examine.Lucene/Indexing/FacetFullTextType.cs delete mode 100644 src/Examine.Lucene/Indexing/FacetInt32Type.cs delete mode 100644 src/Examine.Lucene/Indexing/FacetInt64Type.cs delete mode 100644 src/Examine.Lucene/Indexing/FacetSingleType.cs diff --git a/src/Examine.Lucene/Indexing/DateTimeType.cs b/src/Examine.Lucene/Indexing/DateTimeType.cs index 685f4c181..e0416854b 100644 --- a/src/Examine.Lucene/Indexing/DateTimeType.cs +++ b/src/Examine.Lucene/Indexing/DateTimeType.cs @@ -1,6 +1,7 @@ using System; using Examine.Lucene.Providers; using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; using Lucene.Net.Search; using Microsoft.Extensions.Logging; @@ -11,15 +12,18 @@ public class DateTimeType : IndexFieldRangeValueType { public DateResolution Resolution { get; } + private readonly bool _isFacetable; + /// /// Can be sorted by the normal field name /// public override string SortableFieldName => FieldName; - public DateTimeType(string fieldName, ILoggerFactory logger, DateResolution resolution, bool store = true) + public DateTimeType(string fieldName, ILoggerFactory logger, DateResolution resolution, bool store = true, bool isFacetable = false) : base(fieldName, logger, store) { Resolution = resolution; + _isFacetable = isFacetable; } protected override void AddSingleValue(Document doc, object value) @@ -29,7 +33,13 @@ protected override void AddSingleValue(Document doc, object value) var val = DateToLong(parsedVal); - doc.Add(new Int64Field(FieldName,val, Store ? Field.Store.YES : Field.Store.NO));; + doc.Add(new Int64Field(FieldName,val, Store ? Field.Store.YES : Field.Store.NO)); + + if (_isFacetable) + { + doc.Add(new SortedSetDocValuesFacetField(FieldName, val.ToString())); + doc.Add(new NumericDocValuesField(FieldName, val)); + } } /// diff --git a/src/Examine.Lucene/Indexing/DoubleType.cs b/src/Examine.Lucene/Indexing/DoubleType.cs index 39b26d24a..77b7a428c 100644 --- a/src/Examine.Lucene/Indexing/DoubleType.cs +++ b/src/Examine.Lucene/Indexing/DoubleType.cs @@ -1,5 +1,6 @@ using Examine.Lucene.Providers; using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; using Lucene.Net.Search; using Microsoft.Extensions.Logging; @@ -7,9 +8,12 @@ namespace Examine.Lucene.Indexing { public class DoubleType : IndexFieldRangeValueType { - public DoubleType(string fieldName, ILoggerFactory logger, bool store= true) + private readonly bool _isFacetable; + + public DoubleType(string fieldName, ILoggerFactory logger, bool store = true, bool isFacetable = false) : base(fieldName, logger, store) { + _isFacetable = isFacetable; } /// @@ -23,6 +27,12 @@ protected override void AddSingleValue(Document doc, object value) return; doc.Add(new DoubleField(FieldName,parsedVal, Store ? Field.Store.YES : Field.Store.NO)); + + if (_isFacetable) + { + doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); + doc.Add(new DoubleDocValuesField(FieldName, parsedVal)); + } } public override Query GetQuery(string query) diff --git a/src/Examine.Lucene/Indexing/FacetDateTimeType.cs b/src/Examine.Lucene/Indexing/FacetDateTimeType.cs deleted file mode 100644 index 43558c41a..000000000 --- a/src/Examine.Lucene/Indexing/FacetDateTimeType.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Lucene.Net.Documents; -using Lucene.Net.Facet.SortedSet; -using Microsoft.Extensions.Logging; - -namespace Examine.Lucene.Indexing -{ - public class FacetDateTimeType : DateTimeType - { - public FacetDateTimeType(string fieldName, ILoggerFactory logger, DateResolution resolution, bool store = true) : base(fieldName, logger, resolution, store) - { - } - - protected override void AddSingleValue(Document doc, object value) - { - base.AddSingleValue(doc, value); - - if (!TryConvert(value, out DateTime parsedVal)) - return; - - var val = DateToLong(parsedVal); - - doc.Add(new SortedSetDocValuesFacetField(FieldName, val.ToString())); - doc.Add(new NumericDocValuesField(FieldName, val)); - } - } -} diff --git a/src/Examine.Lucene/Indexing/FacetDoubleType.cs b/src/Examine.Lucene/Indexing/FacetDoubleType.cs deleted file mode 100644 index ffc112ee4..000000000 --- a/src/Examine.Lucene/Indexing/FacetDoubleType.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Lucene.Net.Documents; -using Lucene.Net.Facet.SortedSet; -using Microsoft.Extensions.Logging; - -namespace Examine.Lucene.Indexing -{ - public class FacetDoubleType : DoubleType - { - public FacetDoubleType(string fieldName, ILoggerFactory logger, bool store = true) : base(fieldName, logger, store) - { - } - - protected override void AddSingleValue(Document doc, object value) - { - base.AddSingleValue(doc, value); - - if (!TryConvert(value, out double parsedVal)) - return; - - doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); - doc.Add(new DoubleDocValuesField(FieldName, parsedVal)); - } - } -} diff --git a/src/Examine.Lucene/Indexing/FacetFullTextType.cs b/src/Examine.Lucene/Indexing/FacetFullTextType.cs deleted file mode 100644 index aa14eee87..000000000 --- a/src/Examine.Lucene/Indexing/FacetFullTextType.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Lucene.Net.Analysis; -using Lucene.Net.Documents; -using Lucene.Net.Facet.SortedSet; -using Microsoft.Extensions.Logging; - -namespace Examine.Lucene.Indexing -{ - public class FacetFullTextType : FullTextType - { - public FacetFullTextType(string fieldName, ILoggerFactory logger, Analyzer analyzer = null, bool sortable = false) : base(fieldName, logger, analyzer, sortable) - { - } - - protected override void AddSingleValue(Document doc, object value) - { - base.AddSingleValue(doc, value); - - if (!TryConvert(value, out var str)) - { - return; - } - - doc.Add(new SortedSetDocValuesFacetField(FieldName, str)); - } - } -} diff --git a/src/Examine.Lucene/Indexing/FacetInt32Type.cs b/src/Examine.Lucene/Indexing/FacetInt32Type.cs deleted file mode 100644 index 15f6a6fd6..000000000 --- a/src/Examine.Lucene/Indexing/FacetInt32Type.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Lucene.Net.Documents; -using Lucene.Net.Facet.SortedSet; -using Microsoft.Extensions.Logging; - -namespace Examine.Lucene.Indexing -{ - public class FacetInt32Type : Int32Type - { - public FacetInt32Type(string fieldName, ILoggerFactory logger, bool store = true) : base(fieldName, logger, store) - { - } - - protected override void AddSingleValue(Document doc, object value) - { - base.AddSingleValue(doc, value); - - if (!TryConvert(value, out int parsedVal)) - return; - - doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); - doc.Add(new NumericDocValuesField(FieldName, parsedVal)); - } - } -} diff --git a/src/Examine.Lucene/Indexing/FacetInt64Type.cs b/src/Examine.Lucene/Indexing/FacetInt64Type.cs deleted file mode 100644 index 037a6120f..000000000 --- a/src/Examine.Lucene/Indexing/FacetInt64Type.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Lucene.Net.Documents; -using Lucene.Net.Facet.SortedSet; -using Microsoft.Extensions.Logging; - -namespace Examine.Lucene.Indexing -{ - public class FacetInt64Type : Int64Type - { - public FacetInt64Type(string fieldName, ILoggerFactory logger, bool store = true) : base(fieldName, logger, store) - { - } - - protected override void AddSingleValue(Document doc, object value) - { - base.AddSingleValue(doc, value); - - if (!TryConvert(value, out long parsedVal)) - return; - - doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); - doc.Add(new NumericDocValuesField(FieldName, parsedVal)); - } - } -} diff --git a/src/Examine.Lucene/Indexing/FacetSingleType.cs b/src/Examine.Lucene/Indexing/FacetSingleType.cs deleted file mode 100644 index e6969c160..000000000 --- a/src/Examine.Lucene/Indexing/FacetSingleType.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Lucene.Net.Documents; -using Lucene.Net.Facet.SortedSet; -using Microsoft.Extensions.Logging; - -namespace Examine.Lucene.Indexing -{ - public class FacetSingleType : SingleType - { - public FacetSingleType(string fieldName, ILoggerFactory logger, bool store = true) : base(fieldName, logger, store) - { - } - - protected override void AddSingleValue(Document doc, object value) - { - base.AddSingleValue(doc, value); - - if (!TryConvert(value, out float parsedVal)) - return; - - doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); - doc.Add(new SingleDocValuesField(FieldName, parsedVal)); - } - } -} diff --git a/src/Examine.Lucene/Indexing/FullTextType.cs b/src/Examine.Lucene/Indexing/FullTextType.cs index 67d274587..7fbae8aa7 100644 --- a/src/Examine.Lucene/Indexing/FullTextType.cs +++ b/src/Examine.Lucene/Indexing/FullTextType.cs @@ -6,6 +6,7 @@ using Lucene.Net.Analysis.Miscellaneous; using Lucene.Net.Analysis.TokenAttributes; using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; using Lucene.Net.Index; using Lucene.Net.Search; using Microsoft.Extensions.Logging; @@ -24,6 +25,7 @@ public class FullTextType : IndexFieldValueTypeBase { private readonly bool _sortable; private readonly Analyzer _analyzer; + private readonly bool _isFacetable; /// /// Constructor @@ -33,11 +35,12 @@ public class FullTextType : IndexFieldValueTypeBase /// Defaults to /// /// - public FullTextType(string fieldName, ILoggerFactory logger, Analyzer analyzer = null, bool sortable = false) + public FullTextType(string fieldName, ILoggerFactory logger, Analyzer analyzer = null, bool sortable = false, bool isFacetable = false) : base(fieldName, logger, true) { _sortable = sortable; _analyzer = analyzer ?? new CultureInvariantStandardAnalyzer(); + _isFacetable = isFacetable; } /// @@ -61,6 +64,11 @@ protected override void AddSingleValue(Document doc, object value) str, Field.Store.YES)); } + + if (_isFacetable) + { + doc.Add(new SortedSetDocValuesFacetField(FieldName, str)); + } } } diff --git a/src/Examine.Lucene/Indexing/Int32Type.cs b/src/Examine.Lucene/Indexing/Int32Type.cs index f3c42ae74..664293ed2 100644 --- a/src/Examine.Lucene/Indexing/Int32Type.cs +++ b/src/Examine.Lucene/Indexing/Int32Type.cs @@ -1,5 +1,6 @@ using Examine.Lucene.Providers; using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; using Lucene.Net.Search; using Microsoft.Extensions.Logging; @@ -7,9 +8,12 @@ namespace Examine.Lucene.Indexing { public class Int32Type : IndexFieldRangeValueType { - public Int32Type(string fieldName, ILoggerFactory logger, bool store = true) + private readonly bool _isFacetable; + + public Int32Type(string fieldName, ILoggerFactory logger, bool store = true, bool isFacetable = false) : base(fieldName, logger, store) { + _isFacetable = isFacetable; } /// @@ -22,7 +26,13 @@ protected override void AddSingleValue(Document doc, object value) if (!TryConvert(value, out int parsedVal)) return; - doc.Add(new Int32Field(FieldName,parsedVal, Store ? Field.Store.YES : Field.Store.NO));; + doc.Add(new Int32Field(FieldName,parsedVal, Store ? Field.Store.YES : Field.Store.NO)); + + if (_isFacetable) + { + doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); + doc.Add(new NumericDocValuesField(FieldName, parsedVal)); + } } public override Query GetQuery(string query) diff --git a/src/Examine.Lucene/Indexing/Int64Type.cs b/src/Examine.Lucene/Indexing/Int64Type.cs index 45412ab2a..f17b34241 100644 --- a/src/Examine.Lucene/Indexing/Int64Type.cs +++ b/src/Examine.Lucene/Indexing/Int64Type.cs @@ -1,5 +1,6 @@ using Examine.Lucene.Providers; using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; using Lucene.Net.Search; using Microsoft.Extensions.Logging; @@ -7,9 +8,12 @@ namespace Examine.Lucene.Indexing { public class Int64Type : IndexFieldRangeValueType { - public Int64Type(string fieldName, ILoggerFactory logger, bool store = true) + private readonly bool _isFacetable; + + public Int64Type(string fieldName, ILoggerFactory logger, bool store = true, bool isFacetable = false) : base(fieldName, logger, store) { + _isFacetable = isFacetable; } /// @@ -22,7 +26,13 @@ protected override void AddSingleValue(Document doc, object value) if (!TryConvert(value, out long parsedVal)) return; - doc.Add(new Int64Field(FieldName,parsedVal, Store ? Field.Store.YES : Field.Store.NO));; + doc.Add(new Int64Field(FieldName,parsedVal, Store ? Field.Store.YES : Field.Store.NO)); + + if (_isFacetable) + { + doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); + doc.Add(new NumericDocValuesField(FieldName, parsedVal)); + } } public override Query GetQuery(string query) diff --git a/src/Examine.Lucene/Indexing/SingleType.cs b/src/Examine.Lucene/Indexing/SingleType.cs index 90c9e842f..ceb3f6888 100644 --- a/src/Examine.Lucene/Indexing/SingleType.cs +++ b/src/Examine.Lucene/Indexing/SingleType.cs @@ -1,5 +1,6 @@ using Examine.Lucene.Providers; using Lucene.Net.Documents; +using Lucene.Net.Facet.SortedSet; using Lucene.Net.Search; using Microsoft.Extensions.Logging; @@ -7,9 +8,12 @@ namespace Examine.Lucene.Indexing { public class SingleType : IndexFieldRangeValueType { - public SingleType(string fieldName, ILoggerFactory logger, bool store = true) + private readonly bool _isFacetable; + + public SingleType(string fieldName, ILoggerFactory logger, bool store = true, bool isFacetable = false) : base(fieldName, logger, store) { + _isFacetable = isFacetable; } /// @@ -23,6 +27,12 @@ protected override void AddSingleValue(Document doc, object value) return; doc.Add(new DoubleField(FieldName,parsedVal, Store ? Field.Store.YES : Field.Store.NO)); + + if (_isFacetable) + { + doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); + doc.Add(new SingleDocValuesField(FieldName, parsedVal)); + } } public override Query GetQuery(string query) diff --git a/src/Examine.Lucene/ValueTypeFactoryCollection.cs b/src/Examine.Lucene/ValueTypeFactoryCollection.cs index 92a9646a3..8bfe34529 100644 --- a/src/Examine.Lucene/ValueTypeFactoryCollection.cs +++ b/src/Examine.Lucene/ValueTypeFactoryCollection.cs @@ -72,18 +72,18 @@ private static IReadOnlyDictionary> G {FieldDefinitionTypes.FullTextSortable, name => new FullTextType(name, loggerFactory, defaultAnalyzer, true)}, {FieldDefinitionTypes.InvariantCultureIgnoreCase, name => new GenericAnalyzerFieldValueType(name, loggerFactory, new CultureInvariantWhitespaceAnalyzer())}, {FieldDefinitionTypes.EmailAddress, name => new GenericAnalyzerFieldValueType(name, loggerFactory, new EmailAddressAnalyzer())}, - {FieldDefinitionTypes.FacetInteger, name => new FacetInt32Type(name, loggerFactory)}, - {FieldDefinitionTypes.FacetFloat, name => new FacetSingleType(name, loggerFactory)}, - {FieldDefinitionTypes.FacetDouble, name => new FacetDoubleType(name, loggerFactory)}, - {FieldDefinitionTypes.FacetLong, name => new FacetInt64Type(name, loggerFactory)}, - {FieldDefinitionTypes.FacetDateTime, name => new FacetDateTimeType(name, loggerFactory, DateResolution.MILLISECOND)}, - {FieldDefinitionTypes.FacetDateYear, name => new FacetDateTimeType(name, loggerFactory, DateResolution.YEAR)}, - {FieldDefinitionTypes.FacetDateMonth, name => new FacetDateTimeType(name, loggerFactory, DateResolution.MONTH)}, - {FieldDefinitionTypes.FacetDateDay, name => new FacetDateTimeType(name, loggerFactory, DateResolution.DAY)}, - {FieldDefinitionTypes.FacetDateHour, name => new FacetDateTimeType(name, loggerFactory, DateResolution.HOUR)}, - {FieldDefinitionTypes.FacetDateMinute, name => new FacetDateTimeType(name, loggerFactory, DateResolution.MINUTE)}, - {FieldDefinitionTypes.FacetFullText, name => new FacetFullTextType(name, loggerFactory, defaultAnalyzer)}, - {FieldDefinitionTypes.FacetFullTextSortable, name => new FacetFullTextType(name, loggerFactory, defaultAnalyzer, true)}, + {FieldDefinitionTypes.FacetInteger, name => new Int32Type(name, loggerFactory, isFacetable: true)}, + {FieldDefinitionTypes.FacetFloat, name => new SingleType(name, loggerFactory, isFacetable: true)}, + {FieldDefinitionTypes.FacetDouble, name => new DoubleType(name, loggerFactory, isFacetable: true)}, + {FieldDefinitionTypes.FacetLong, name => new Int64Type(name, loggerFactory, isFacetable: true)}, + {FieldDefinitionTypes.FacetDateTime, name => new DateTimeType(name, loggerFactory, DateResolution.MILLISECOND, isFacetable: true)}, + {FieldDefinitionTypes.FacetDateYear, name => new DateTimeType(name, loggerFactory, DateResolution.YEAR, isFacetable: true)}, + {FieldDefinitionTypes.FacetDateMonth, name => new DateTimeType(name, loggerFactory, DateResolution.MONTH, isFacetable: true)}, + {FieldDefinitionTypes.FacetDateDay, name => new DateTimeType(name, loggerFactory, DateResolution.DAY, isFacetable: true)}, + {FieldDefinitionTypes.FacetDateHour, name => new DateTimeType(name, loggerFactory, DateResolution.HOUR, isFacetable: true)}, + {FieldDefinitionTypes.FacetDateMinute, name => new DateTimeType(name, loggerFactory, DateResolution.MINUTE, isFacetable: true)}, + {FieldDefinitionTypes.FacetFullText, name => new FullTextType(name, loggerFactory, defaultAnalyzer, isFacetable: true)}, + {FieldDefinitionTypes.FacetFullTextSortable, name => new FullTextType(name, loggerFactory, defaultAnalyzer, true, isFacetable: true)}, }; From 8ac423f2e3086e335aedc9e4fee7452476ef7ea5 Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Mon, 2 Jan 2023 15:48:56 +0100 Subject: [PATCH 33/42] refactor: Move facet extraction --- src/Examine.Core/Search/FacetDoubleField.cs | 18 ----- src/Examine.Core/Search/FacetFloatField.cs | 18 ----- src/Examine.Core/Search/FacetFullTextField.cs | 21 ----- src/Examine.Core/Search/FacetLongField.cs | 18 ----- src/Examine.Core/Search/IFacetField.cs | 15 ---- src/Examine.Lucene/Indexing/DateTimeType.cs | 8 +- src/Examine.Lucene/Indexing/DoubleType.cs | 7 +- src/Examine.Lucene/Indexing/FullTextType.cs | 6 +- .../Indexing/IIndexFacetValueType.cs | 20 +++++ src/Examine.Lucene/Indexing/Int32Type.cs | 8 +- src/Examine.Lucene/Indexing/Int64Type.cs | 7 +- src/Examine.Lucene/Indexing/SingleType.cs | 8 +- src/Examine.Lucene/Search/FacetDoubleField.cs | 39 ++++++++++ src/Examine.Lucene/Search/FacetFloatField.cs | 40 ++++++++++ .../Search/FacetFullTextField.cs | 54 +++++++++++++ src/Examine.Lucene/Search/FacetLongField.cs | 39 ++++++++++ .../Search/FacetQueryField.cs | 4 +- src/Examine.Lucene/Search/IFacetField.cs | 28 +++++++ .../Search/LuceneSearchExecutor.cs | 76 ++----------------- 19 files changed, 267 insertions(+), 167 deletions(-) delete mode 100644 src/Examine.Core/Search/FacetDoubleField.cs delete mode 100644 src/Examine.Core/Search/FacetFloatField.cs delete mode 100644 src/Examine.Core/Search/FacetFullTextField.cs delete mode 100644 src/Examine.Core/Search/FacetLongField.cs delete mode 100644 src/Examine.Core/Search/IFacetField.cs create mode 100644 src/Examine.Lucene/Indexing/IIndexFacetValueType.cs create mode 100644 src/Examine.Lucene/Search/FacetDoubleField.cs create mode 100644 src/Examine.Lucene/Search/FacetFloatField.cs create mode 100644 src/Examine.Lucene/Search/FacetFullTextField.cs create mode 100644 src/Examine.Lucene/Search/FacetLongField.cs rename src/{Examine.Core => Examine.Lucene}/Search/FacetQueryField.cs (86%) create mode 100644 src/Examine.Lucene/Search/IFacetField.cs diff --git a/src/Examine.Core/Search/FacetDoubleField.cs b/src/Examine.Core/Search/FacetDoubleField.cs deleted file mode 100644 index 063932c96..000000000 --- a/src/Examine.Core/Search/FacetDoubleField.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Examine.Search -{ - public readonly struct FacetDoubleField : IFacetField - { - public DoubleRange[] DoubleRanges { get; } - - public string Field { get; } - - public string FacetField { get; } - - public FacetDoubleField(string field, DoubleRange[] doubleRanges, string facetField) - { - Field = field; - DoubleRanges = doubleRanges; - FacetField = facetField; - } - } -} diff --git a/src/Examine.Core/Search/FacetFloatField.cs b/src/Examine.Core/Search/FacetFloatField.cs deleted file mode 100644 index 0945138d0..000000000 --- a/src/Examine.Core/Search/FacetFloatField.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Examine.Search -{ - public readonly struct FacetFloatField : IFacetField - { - public FloatRange[] FloatRanges { get; } - - public string Field { get; } - - public string FacetField { get; } - - public FacetFloatField(string field, FloatRange[] floatRanges, string facetField) - { - Field = field; - FloatRanges = floatRanges; - FacetField = facetField; - } - } -} diff --git a/src/Examine.Core/Search/FacetFullTextField.cs b/src/Examine.Core/Search/FacetFullTextField.cs deleted file mode 100644 index 15b7b82c7..000000000 --- a/src/Examine.Core/Search/FacetFullTextField.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Examine.Search -{ - public class FacetFullTextField : IFacetField - { - public int MaxCount { get; internal set; } - - public string[] Values { get; } - - public string Field { get; } - - public string FacetField { get; } - - public FacetFullTextField(string field, string[] values, string facetField, int maxCount = 10) - { - Field = field; - Values = values; - FacetField = facetField; - MaxCount = maxCount; - } - } -} diff --git a/src/Examine.Core/Search/FacetLongField.cs b/src/Examine.Core/Search/FacetLongField.cs deleted file mode 100644 index dc4862da4..000000000 --- a/src/Examine.Core/Search/FacetLongField.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Examine.Search -{ - public readonly struct FacetLongField : IFacetField - { - public string Field { get; } - - public Int64Range[] LongRanges { get; } - - public string FacetField { get; } - - public FacetLongField(string field, Int64Range[] longRanges, string facetField) - { - Field = field; - LongRanges = longRanges; - FacetField = facetField; - } - } -} diff --git a/src/Examine.Core/Search/IFacetField.cs b/src/Examine.Core/Search/IFacetField.cs deleted file mode 100644 index 25d73e70f..000000000 --- a/src/Examine.Core/Search/IFacetField.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Examine.Search -{ - public interface IFacetField - { - /// - /// The field name - /// - string Field { get; } - - /// - /// The field to get the facet field from - /// - string FacetField { get; } - } -} diff --git a/src/Examine.Lucene/Indexing/DateTimeType.cs b/src/Examine.Lucene/Indexing/DateTimeType.cs index e0416854b..45eafd40e 100644 --- a/src/Examine.Lucene/Indexing/DateTimeType.cs +++ b/src/Examine.Lucene/Indexing/DateTimeType.cs @@ -1,6 +1,10 @@ using System; +using System.Collections.Generic; using Examine.Lucene.Providers; +using Examine.Lucene.Search; +using Examine.Search; using Lucene.Net.Documents; +using Lucene.Net.Facet; using Lucene.Net.Facet.SortedSet; using Lucene.Net.Search; using Microsoft.Extensions.Logging; @@ -8,7 +12,7 @@ namespace Examine.Lucene.Indexing { - public class DateTimeType : IndexFieldRangeValueType + public class DateTimeType : IndexFieldRangeValueType, IIndexFacetValueType { public DateResolution Resolution { get; } @@ -66,5 +70,7 @@ public override Query GetQuery(DateTime? lower, DateTime? upper, bool lowerInclu lower != null ? DateToLong(lower.Value) : (long?)null, upper != null ? DateToLong(upper.Value) : (long?)null, lowerInclusive, upperInclusive); } + + public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); } } diff --git a/src/Examine.Lucene/Indexing/DoubleType.cs b/src/Examine.Lucene/Indexing/DoubleType.cs index 77b7a428c..80084cf5f 100644 --- a/src/Examine.Lucene/Indexing/DoubleType.cs +++ b/src/Examine.Lucene/Indexing/DoubleType.cs @@ -1,12 +1,16 @@ +using System.Collections.Generic; using Examine.Lucene.Providers; +using Examine.Lucene.Search; +using Examine.Search; using Lucene.Net.Documents; +using Lucene.Net.Facet; using Lucene.Net.Facet.SortedSet; using Lucene.Net.Search; using Microsoft.Extensions.Logging; namespace Examine.Lucene.Indexing { - public class DoubleType : IndexFieldRangeValueType + public class DoubleType : IndexFieldRangeValueType, IIndexFacetValueType { private readonly bool _isFacetable; @@ -46,5 +50,6 @@ public override Query GetQuery(double? lower, double? upper, bool lowerInclusive lower ?? double.MinValue, upper ?? double.MaxValue, lowerInclusive, upperInclusive); } + public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); } } diff --git a/src/Examine.Lucene/Indexing/FullTextType.cs b/src/Examine.Lucene/Indexing/FullTextType.cs index 7fbae8aa7..5b32e4ecb 100644 --- a/src/Examine.Lucene/Indexing/FullTextType.cs +++ b/src/Examine.Lucene/Indexing/FullTextType.cs @@ -2,10 +2,13 @@ using System.IO; using Examine.Lucene.Analyzers; using Examine.Lucene.Providers; +using Examine.Lucene.Search; +using Examine.Search; using Lucene.Net.Analysis; using Lucene.Net.Analysis.Miscellaneous; using Lucene.Net.Analysis.TokenAttributes; using Lucene.Net.Documents; +using Lucene.Net.Facet; using Lucene.Net.Facet.SortedSet; using Lucene.Net.Index; using Lucene.Net.Search; @@ -21,7 +24,7 @@ namespace Examine.Lucene.Indexing /// do an exact match search if the term is less than 4 chars, else it will do a full text search on the phrase /// with a higher boost, then /// - public class FullTextType : IndexFieldValueTypeBase + public class FullTextType : IndexFieldValueTypeBase, IIndexFacetValueType { private readonly bool _sortable; private readonly Analyzer _analyzer; @@ -150,5 +153,6 @@ public override Query GetQuery(string query) return GenerateQuery(FieldName, query, _analyzer); } + public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); } } diff --git a/src/Examine.Lucene/Indexing/IIndexFacetValueType.cs b/src/Examine.Lucene/Indexing/IIndexFacetValueType.cs new file mode 100644 index 000000000..58436d58a --- /dev/null +++ b/src/Examine.Lucene/Indexing/IIndexFacetValueType.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Examine.Lucene.Search; +using Examine.Search; +using Lucene.Net.Facet; +using Lucene.Net.Facet.SortedSet; + +namespace Examine.Lucene.Indexing +{ + public interface IIndexFacetValueType + { + /// + /// Extracts the facets from the field + /// + /// + /// + /// + /// + void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field); + } +} diff --git a/src/Examine.Lucene/Indexing/Int32Type.cs b/src/Examine.Lucene/Indexing/Int32Type.cs index 664293ed2..64184a7e8 100644 --- a/src/Examine.Lucene/Indexing/Int32Type.cs +++ b/src/Examine.Lucene/Indexing/Int32Type.cs @@ -1,12 +1,16 @@ +using System.Collections.Generic; using Examine.Lucene.Providers; +using Examine.Lucene.Search; +using Examine.Search; using Lucene.Net.Documents; +using Lucene.Net.Facet; using Lucene.Net.Facet.SortedSet; using Lucene.Net.Search; using Microsoft.Extensions.Logging; namespace Examine.Lucene.Indexing { - public class Int32Type : IndexFieldRangeValueType + public class Int32Type : IndexFieldRangeValueType, IIndexFacetValueType { private readonly bool _isFacetable; @@ -46,5 +50,7 @@ public override Query GetQuery(int? lower, int? upper, bool lowerInclusive = tru lower, upper, lowerInclusive, upperInclusive); } + + public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); } } diff --git a/src/Examine.Lucene/Indexing/Int64Type.cs b/src/Examine.Lucene/Indexing/Int64Type.cs index f17b34241..46d185592 100644 --- a/src/Examine.Lucene/Indexing/Int64Type.cs +++ b/src/Examine.Lucene/Indexing/Int64Type.cs @@ -1,12 +1,16 @@ +using System.Collections.Generic; using Examine.Lucene.Providers; +using Examine.Lucene.Search; +using Examine.Search; using Lucene.Net.Documents; +using Lucene.Net.Facet; using Lucene.Net.Facet.SortedSet; using Lucene.Net.Search; using Microsoft.Extensions.Logging; namespace Examine.Lucene.Indexing { - public class Int64Type : IndexFieldRangeValueType + public class Int64Type : IndexFieldRangeValueType, IIndexFacetValueType { private readonly bool _isFacetable; @@ -46,5 +50,6 @@ public override Query GetQuery(long? lower, long? upper, bool lowerInclusive = t lower, upper, lowerInclusive, upperInclusive); } + public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); } } diff --git a/src/Examine.Lucene/Indexing/SingleType.cs b/src/Examine.Lucene/Indexing/SingleType.cs index ceb3f6888..39d61ce84 100644 --- a/src/Examine.Lucene/Indexing/SingleType.cs +++ b/src/Examine.Lucene/Indexing/SingleType.cs @@ -1,12 +1,16 @@ +using System.Collections.Generic; using Examine.Lucene.Providers; +using Examine.Lucene.Search; +using Examine.Search; using Lucene.Net.Documents; +using Lucene.Net.Facet; using Lucene.Net.Facet.SortedSet; using Lucene.Net.Search; using Microsoft.Extensions.Logging; namespace Examine.Lucene.Indexing { - public class SingleType : IndexFieldRangeValueType + public class SingleType : IndexFieldRangeValueType, IIndexFacetValueType { private readonly bool _isFacetable; @@ -46,5 +50,7 @@ public override Query GetQuery(float? lower, float? upper, bool lowerInclusive = lower ?? float.MinValue, upper ?? float.MaxValue, lowerInclusive, upperInclusive); } + + public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); } } diff --git a/src/Examine.Lucene/Search/FacetDoubleField.cs b/src/Examine.Lucene/Search/FacetDoubleField.cs new file mode 100644 index 000000000..eed47fd62 --- /dev/null +++ b/src/Examine.Lucene/Search/FacetDoubleField.cs @@ -0,0 +1,39 @@ +using Lucene.Net.Facet.Range; +using Lucene.Net.Facet; +using System.Collections.Generic; +using Examine.Search; +using System.Linq; +using Lucene.Net.Facet.SortedSet; + +namespace Examine.Lucene.Search +{ + public readonly struct FacetDoubleField : IFacetField + { + public Examine.Search.DoubleRange[] DoubleRanges { get; } + + public string Field { get; } + + public string FacetField { get; } + + public FacetDoubleField(string field, Examine.Search.DoubleRange[] doubleRanges, string facetField) + { + Field = field; + DoubleRanges = doubleRanges; + FacetField = facetField; + } + + public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets) + { + var doubleFacetCounts = new DoubleRangeFacetCounts(Field, facetsCollector, DoubleRanges.AsLuceneRange().ToArray()); + + var doubleFacets = doubleFacetCounts.GetTopChildren(0, Field); + + if (doubleFacets == null) + { + return; + } + + facets.Add(Field, new Examine.Search.FacetResult(doubleFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); + } + } +} diff --git a/src/Examine.Lucene/Search/FacetFloatField.cs b/src/Examine.Lucene/Search/FacetFloatField.cs new file mode 100644 index 000000000..ce26ea617 --- /dev/null +++ b/src/Examine.Lucene/Search/FacetFloatField.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Linq; +using Examine.Search; +using Lucene.Net.Facet; +using Lucene.Net.Facet.Range; +using Lucene.Net.Facet.SortedSet; +using Lucene.Net.Queries.Function.ValueSources; + +namespace Examine.Lucene.Search +{ + public readonly struct FacetFloatField : IFacetField + { + public FloatRange[] FloatRanges { get; } + + public string Field { get; } + + public string FacetField { get; } + + public FacetFloatField(string field, FloatRange[] floatRanges, string facetField) + { + Field = field; + FloatRanges = floatRanges; + FacetField = facetField; + } + + public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets) + { + var floatFacetCounts = new DoubleRangeFacetCounts(Field, new SingleFieldSource(Field), facetsCollector, FloatRanges.AsLuceneRange().ToArray()); + + var floatFacets = floatFacetCounts.GetTopChildren(0, Field); + + if (floatFacets == null) + { + return; + } + + facets.Add(Field, new Examine.Search.FacetResult(floatFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); + } + } +} diff --git a/src/Examine.Lucene/Search/FacetFullTextField.cs b/src/Examine.Lucene/Search/FacetFullTextField.cs new file mode 100644 index 000000000..411b7dc8a --- /dev/null +++ b/src/Examine.Lucene/Search/FacetFullTextField.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Linq; +using Examine.Search; +using Lucene.Net.Facet; +using Lucene.Net.Facet.SortedSet; + +namespace Examine.Lucene.Search +{ + public class FacetFullTextField : IFacetField + { + public int MaxCount { get; internal set; } + + public string[] Values { get; } + + public string Field { get; } + + public string FacetField { get; } + + public FacetFullTextField(string field, string[] values, string facetField, int maxCount = 10) + { + Field = field; + Values = values; + FacetField = facetField; + MaxCount = maxCount; + } + + public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets) + { + var sortedFacetsCounts = new SortedSetDocValuesFacetCounts(sortedSetReaderState, facetsCollector); + + if (Values != null && Values.Length > 0) + { + var facetValues = new List(); + foreach (var label in Values) + { + var value = sortedFacetsCounts.GetSpecificValue(Field, label); + facetValues.Add(new FacetValue(label, value)); + } + facets.Add(Field, new Examine.Search.FacetResult(facetValues.OrderBy(value => value.Value).Take(MaxCount).OfType())); + } + else + { + var sortedFacets = sortedFacetsCounts.GetTopChildren(MaxCount, Field); + + if (sortedFacets == null) + { + return; + } + + facets.Add(Field, new Examine.Search.FacetResult(sortedFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); + } + } + } +} diff --git a/src/Examine.Lucene/Search/FacetLongField.cs b/src/Examine.Lucene/Search/FacetLongField.cs new file mode 100644 index 000000000..73f2a0dbc --- /dev/null +++ b/src/Examine.Lucene/Search/FacetLongField.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Linq; +using Examine.Search; +using Lucene.Net.Facet; +using Lucene.Net.Facet.Range; +using Lucene.Net.Facet.SortedSet; + +namespace Examine.Lucene.Search +{ + public readonly struct FacetLongField : IFacetField + { + public string Field { get; } + + public Examine.Search.Int64Range[] LongRanges { get; } + + public string FacetField { get; } + + public FacetLongField(string field, Examine.Search.Int64Range[] longRanges, string facetField) + { + Field = field; + LongRanges = longRanges; + FacetField = facetField; + } + + public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets) + { + var longFacetCounts = new Int64RangeFacetCounts(Field, facetsCollector, LongRanges.AsLuceneRange().ToArray()); + + var longFacets = longFacetCounts.GetTopChildren(0, Field); + + if (longFacets == null) + { + return; + } + + facets.Add(Field, new Examine.Search.FacetResult(longFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); + } + } +} diff --git a/src/Examine.Core/Search/FacetQueryField.cs b/src/Examine.Lucene/Search/FacetQueryField.cs similarity index 86% rename from src/Examine.Core/Search/FacetQueryField.cs rename to src/Examine.Lucene/Search/FacetQueryField.cs index 706268d5e..22e461a89 100644 --- a/src/Examine.Core/Search/FacetQueryField.cs +++ b/src/Examine.Lucene/Search/FacetQueryField.cs @@ -1,4 +1,6 @@ -namespace Examine.Search +using Examine.Search; + +namespace Examine.Lucene.Search { public class FacetQueryField : IFacetQueryField { diff --git a/src/Examine.Lucene/Search/IFacetField.cs b/src/Examine.Lucene/Search/IFacetField.cs new file mode 100644 index 000000000..4bfa26211 --- /dev/null +++ b/src/Examine.Lucene/Search/IFacetField.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Examine.Search; +using Lucene.Net.Facet; +using Lucene.Net.Facet.SortedSet; + +namespace Examine.Lucene.Search +{ + public interface IFacetField + { + /// + /// The field name + /// + string Field { get; } + + /// + /// The field to get the facet field from + /// + string FacetField { get; } + + /// + /// Extracts the facets from the field + /// + /// + /// + /// + void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets); + } +} diff --git a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs index d342ef4e8..a3b4dbb25 100644 --- a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs +++ b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Examine.Lucene.Indexing; using Examine.Search; using Lucene.Net.Documents; using Lucene.Net.Facet; @@ -150,86 +151,21 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector foreach(var field in facetFields) { - if (field is FacetFullTextField facetFullTextField) + var valueType = _searchContext.GetFieldValueType(field.Field); + if(valueType is IIndexFacetValueType facetValueType) { - ExtractFullTextFacets(facetsCollector, searcher, facets, sortedSetReaderState, field, facetFullTextField); - } - else if (field is FacetLongField facetLongField) - { - var longFacetCounts = new Int64RangeFacetCounts(facetLongField.Field, facetsCollector, facetLongField.LongRanges.AsLuceneRange().ToArray()); - - var longFacets = longFacetCounts.GetTopChildren(0, facetLongField.Field); - - if(longFacets == null) + if (field is FacetFullTextField && (sortedSetReaderState == null || !sortedSetReaderState.Field.Equals(field.FacetField))) { - continue; + sortedSetReaderState = new DefaultSortedSetDocValuesReaderState(searcher.IndexSearcher.IndexReader, field.FacetField); } - facets.Add(facetLongField.Field, new Examine.Search.FacetResult(longFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); - } - else if (field is FacetDoubleField facetDoubleField) - { - var doubleFacetCounts = new DoubleRangeFacetCounts(facetDoubleField.Field, facetsCollector, facetDoubleField.DoubleRanges.AsLuceneRange().ToArray()); - - var doubleFacets = doubleFacetCounts.GetTopChildren(0, facetDoubleField.Field); - - if(doubleFacets == null) - { - continue; - } - - facets.Add(facetDoubleField.Field, new Examine.Search.FacetResult(doubleFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); - } - else if(field is FacetFloatField facetFloatField) - { - var floatFacetCounts = new DoubleRangeFacetCounts(facetFloatField.Field, new SingleFieldSource(facetFloatField.Field), facetsCollector, facetFloatField.FloatRanges.AsLuceneRange().ToArray()); - - var floatFacets = floatFacetCounts.GetTopChildren(0, facetFloatField.Field); - - if (floatFacets == null) - { - continue; - } - - facets.Add(facetFloatField.Field, new Examine.Search.FacetResult(floatFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); + facetValueType.ExtractFacets(facetsCollector, sortedSetReaderState, facets, field); } } return facets; } - private static void ExtractFullTextFacets(FacetsCollector facetsCollector, ISearcherReference searcher, Dictionary facets, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field, FacetFullTextField facetFullTextField) - { - if (sortedSetReaderState == null || !sortedSetReaderState.Field.Equals(field.FacetField)) - { - sortedSetReaderState = new DefaultSortedSetDocValuesReaderState(searcher.IndexSearcher.IndexReader, field.FacetField); - } - - var sortedFacetsCounts = new SortedSetDocValuesFacetCounts(sortedSetReaderState, facetsCollector); - - if (facetFullTextField.Values != null && facetFullTextField.Values.Length > 0) - { - var facetValues = new List(); - foreach (var label in facetFullTextField.Values) - { - var value = sortedFacetsCounts.GetSpecificValue(facetFullTextField.Field, label); - facetValues.Add(new FacetValue(label, value)); - } - facets.Add(facetFullTextField.Field, new Examine.Search.FacetResult(facetValues.OrderBy(value => value.Value).Take(facetFullTextField.MaxCount).OfType())); - } - else - { - var sortedFacets = sortedFacetsCounts.GetTopChildren(facetFullTextField.MaxCount, facetFullTextField.Field); - - if(sortedFacets == null) - { - return; - } - - facets.Add(facetFullTextField.Field, new Examine.Search.FacetResult(sortedFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); - } - } - private ISearchResult GetSearchResult(int index, TopDocs topDocs, IndexSearcher luceneSearcher) { // I have seen IndexOutOfRangeException here which is strange as this is only called in one place From da5171dcd933b5693ddd285a7265eb7443dbf4b7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Feb 2023 12:58:29 -0700 Subject: [PATCH 34/42] Fix ctor overloads for backward compat --- src/Examine.Lucene/Indexing/DateTimeType.cs | 9 ++++++- src/Examine.Lucene/Indexing/DoubleType.cs | 8 ++++++- src/Examine.Lucene/Indexing/FullTextType.cs | 18 +++++++++++++- src/Examine.Lucene/Indexing/Int32Type.cs | 8 ++++++- src/Examine.Lucene/Indexing/Int64Type.cs | 8 ++++++- src/Examine.Lucene/Indexing/SingleType.cs | 8 ++++++- .../ValueTypeFactoryCollection.cs | 24 +++++++++---------- 7 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/Examine.Lucene/Indexing/DateTimeType.cs b/src/Examine.Lucene/Indexing/DateTimeType.cs index e0416854b..32dca4d83 100644 --- a/src/Examine.Lucene/Indexing/DateTimeType.cs +++ b/src/Examine.Lucene/Indexing/DateTimeType.cs @@ -19,13 +19,20 @@ public class DateTimeType : IndexFieldRangeValueType /// public override string SortableFieldName => FieldName; - public DateTimeType(string fieldName, ILoggerFactory logger, DateResolution resolution, bool store = true, bool isFacetable = false) + public DateTimeType(string fieldName, ILoggerFactory logger, DateResolution resolution, bool store, bool isFacetable) : base(fieldName, logger, store) { Resolution = resolution; _isFacetable = isFacetable; } + public DateTimeType(string fieldName, ILoggerFactory logger, DateResolution resolution, bool store = true) + : base(fieldName, logger, store) + { + Resolution = resolution; + _isFacetable = false; + } + protected override void AddSingleValue(Document doc, object value) { if (!TryConvert(value, out DateTime parsedVal)) diff --git a/src/Examine.Lucene/Indexing/DoubleType.cs b/src/Examine.Lucene/Indexing/DoubleType.cs index 77b7a428c..71c2bd73d 100644 --- a/src/Examine.Lucene/Indexing/DoubleType.cs +++ b/src/Examine.Lucene/Indexing/DoubleType.cs @@ -10,12 +10,18 @@ public class DoubleType : IndexFieldRangeValueType { private readonly bool _isFacetable; - public DoubleType(string fieldName, ILoggerFactory logger, bool store = true, bool isFacetable = false) + public DoubleType(string fieldName, ILoggerFactory logger, bool store, bool isFacetable) : base(fieldName, logger, store) { _isFacetable = isFacetable; } + public DoubleType(string fieldName, ILoggerFactory logger, bool store = true) + : base(fieldName, logger, store) + { + _isFacetable = false; + } + /// /// Can be sorted by the normal field name /// diff --git a/src/Examine.Lucene/Indexing/FullTextType.cs b/src/Examine.Lucene/Indexing/FullTextType.cs index 7fbae8aa7..58ff20946 100644 --- a/src/Examine.Lucene/Indexing/FullTextType.cs +++ b/src/Examine.Lucene/Indexing/FullTextType.cs @@ -35,7 +35,7 @@ public class FullTextType : IndexFieldValueTypeBase /// Defaults to /// /// - public FullTextType(string fieldName, ILoggerFactory logger, Analyzer analyzer = null, bool sortable = false, bool isFacetable = false) + public FullTextType(string fieldName, ILoggerFactory logger, bool sortable = false, bool isFacetable = false, Analyzer analyzer = null) : base(fieldName, logger, true) { _sortable = sortable; @@ -43,6 +43,22 @@ public FullTextType(string fieldName, ILoggerFactory logger, Analyzer analyzer = _isFacetable = isFacetable; } + /// + /// Constructor + /// + /// + /// + /// Defaults to + /// + /// + public FullTextType(string fieldName, ILoggerFactory logger, Analyzer analyzer = null, bool sortable = false) + : base(fieldName, logger, true) + { + _sortable = sortable; + _analyzer = analyzer ?? new CultureInvariantStandardAnalyzer(); + _isFacetable = false; + } + /// /// Can be sorted by a concatenated field name since to be sortable it cannot be analyzed /// diff --git a/src/Examine.Lucene/Indexing/Int32Type.cs b/src/Examine.Lucene/Indexing/Int32Type.cs index 664293ed2..bddb1034c 100644 --- a/src/Examine.Lucene/Indexing/Int32Type.cs +++ b/src/Examine.Lucene/Indexing/Int32Type.cs @@ -10,12 +10,18 @@ public class Int32Type : IndexFieldRangeValueType { private readonly bool _isFacetable; - public Int32Type(string fieldName, ILoggerFactory logger, bool store = true, bool isFacetable = false) + public Int32Type(string fieldName, ILoggerFactory logger, bool store, bool isFacetable) : base(fieldName, logger, store) { _isFacetable = isFacetable; } + public Int32Type(string fieldName, ILoggerFactory logger, bool store = true) + : base(fieldName, logger, store) + { + _isFacetable = false; + } + /// /// Can be sorted by the normal field name /// diff --git a/src/Examine.Lucene/Indexing/Int64Type.cs b/src/Examine.Lucene/Indexing/Int64Type.cs index f17b34241..b2a7c6dda 100644 --- a/src/Examine.Lucene/Indexing/Int64Type.cs +++ b/src/Examine.Lucene/Indexing/Int64Type.cs @@ -10,12 +10,18 @@ public class Int64Type : IndexFieldRangeValueType { private readonly bool _isFacetable; - public Int64Type(string fieldName, ILoggerFactory logger, bool store = true, bool isFacetable = false) + public Int64Type(string fieldName, ILoggerFactory logger, bool store, bool isFacetable) : base(fieldName, logger, store) { _isFacetable = isFacetable; } + public Int64Type(string fieldName, ILoggerFactory logger, bool store = true) + : base(fieldName, logger, store) + { + _isFacetable = false; + } + /// /// Can be sorted by the normal field name /// diff --git a/src/Examine.Lucene/Indexing/SingleType.cs b/src/Examine.Lucene/Indexing/SingleType.cs index ceb3f6888..0a55a5dfb 100644 --- a/src/Examine.Lucene/Indexing/SingleType.cs +++ b/src/Examine.Lucene/Indexing/SingleType.cs @@ -10,12 +10,18 @@ public class SingleType : IndexFieldRangeValueType { private readonly bool _isFacetable; - public SingleType(string fieldName, ILoggerFactory logger, bool store = true, bool isFacetable = false) + public SingleType(string fieldName, ILoggerFactory logger, bool store, bool isFacetable) : base(fieldName, logger, store) { _isFacetable = isFacetable; } + public SingleType(string fieldName, ILoggerFactory logger, bool store = true) + : base(fieldName, logger, store) + { + _isFacetable = false; + } + /// /// Can be sorted by the normal field name /// diff --git a/src/Examine.Lucene/ValueTypeFactoryCollection.cs b/src/Examine.Lucene/ValueTypeFactoryCollection.cs index 8bfe34529..db7bbad27 100644 --- a/src/Examine.Lucene/ValueTypeFactoryCollection.cs +++ b/src/Examine.Lucene/ValueTypeFactoryCollection.cs @@ -72,18 +72,18 @@ private static IReadOnlyDictionary> G {FieldDefinitionTypes.FullTextSortable, name => new FullTextType(name, loggerFactory, defaultAnalyzer, true)}, {FieldDefinitionTypes.InvariantCultureIgnoreCase, name => new GenericAnalyzerFieldValueType(name, loggerFactory, new CultureInvariantWhitespaceAnalyzer())}, {FieldDefinitionTypes.EmailAddress, name => new GenericAnalyzerFieldValueType(name, loggerFactory, new EmailAddressAnalyzer())}, - {FieldDefinitionTypes.FacetInteger, name => new Int32Type(name, loggerFactory, isFacetable: true)}, - {FieldDefinitionTypes.FacetFloat, name => new SingleType(name, loggerFactory, isFacetable: true)}, - {FieldDefinitionTypes.FacetDouble, name => new DoubleType(name, loggerFactory, isFacetable: true)}, - {FieldDefinitionTypes.FacetLong, name => new Int64Type(name, loggerFactory, isFacetable: true)}, - {FieldDefinitionTypes.FacetDateTime, name => new DateTimeType(name, loggerFactory, DateResolution.MILLISECOND, isFacetable: true)}, - {FieldDefinitionTypes.FacetDateYear, name => new DateTimeType(name, loggerFactory, DateResolution.YEAR, isFacetable: true)}, - {FieldDefinitionTypes.FacetDateMonth, name => new DateTimeType(name, loggerFactory, DateResolution.MONTH, isFacetable: true)}, - {FieldDefinitionTypes.FacetDateDay, name => new DateTimeType(name, loggerFactory, DateResolution.DAY, isFacetable: true)}, - {FieldDefinitionTypes.FacetDateHour, name => new DateTimeType(name, loggerFactory, DateResolution.HOUR, isFacetable: true)}, - {FieldDefinitionTypes.FacetDateMinute, name => new DateTimeType(name, loggerFactory, DateResolution.MINUTE, isFacetable: true)}, - {FieldDefinitionTypes.FacetFullText, name => new FullTextType(name, loggerFactory, defaultAnalyzer, isFacetable: true)}, - {FieldDefinitionTypes.FacetFullTextSortable, name => new FullTextType(name, loggerFactory, defaultAnalyzer, true, isFacetable: true)}, + {FieldDefinitionTypes.FacetInteger, name => new Int32Type(name, loggerFactory, true, true)}, + {FieldDefinitionTypes.FacetFloat, name => new SingleType(name, loggerFactory, true, true)}, + {FieldDefinitionTypes.FacetDouble, name => new DoubleType(name, loggerFactory, true, true)}, + {FieldDefinitionTypes.FacetLong, name => new Int64Type(name, loggerFactory, true, true)}, + {FieldDefinitionTypes.FacetDateTime, name => new DateTimeType(name, loggerFactory, DateResolution.MILLISECOND, true, true)}, + {FieldDefinitionTypes.FacetDateYear, name => new DateTimeType(name, loggerFactory, DateResolution.YEAR, true, true)}, + {FieldDefinitionTypes.FacetDateMonth, name => new DateTimeType(name, loggerFactory, DateResolution.MONTH, true, true)}, + {FieldDefinitionTypes.FacetDateDay, name => new DateTimeType(name, loggerFactory, DateResolution.DAY, true, true)}, + {FieldDefinitionTypes.FacetDateHour, name => new DateTimeType(name, loggerFactory, DateResolution.HOUR, true, true)}, + {FieldDefinitionTypes.FacetDateMinute, name => new DateTimeType(name, loggerFactory, DateResolution.MINUTE, true, true)}, + {FieldDefinitionTypes.FacetFullText, name => new FullTextType(name, loggerFactory, false, true, defaultAnalyzer)}, + {FieldDefinitionTypes.FacetFullTextSortable, name => new FullTextType(name, loggerFactory, false, true, defaultAnalyzer)}, }; From ec470636f3d06b05f3877abba20fa193d144c354 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Feb 2023 14:02:54 -0700 Subject: [PATCH 35/42] return a collection instead of modifying existing. --- src/Examine.Lucene/Indexing/DateTimeType.cs | 3 ++- src/Examine.Lucene/Indexing/DoubleType.cs | 3 ++- src/Examine.Lucene/Indexing/FullTextType.cs | 3 ++- src/Examine.Lucene/Indexing/IIndexFacetValueType.cs | 4 ++-- src/Examine.Lucene/Indexing/Int32Type.cs | 3 ++- src/Examine.Lucene/Indexing/Int64Type.cs | 5 +++-- src/Examine.Lucene/Indexing/SingleType.cs | 3 ++- src/Examine.Lucene/Search/FacetDoubleField.cs | 6 +++--- src/Examine.Lucene/Search/FacetFloatField.cs | 6 +++--- src/Examine.Lucene/Search/FacetFullTextField.cs | 8 ++++---- src/Examine.Lucene/Search/FacetLongField.cs | 6 +++--- src/Examine.Lucene/Search/IFacetField.cs | 4 ++-- src/Examine.Lucene/Search/LuceneSearchExecutor.cs | 7 ++++++- 13 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/Examine.Lucene/Indexing/DateTimeType.cs b/src/Examine.Lucene/Indexing/DateTimeType.cs index 6207bda97..42fc93388 100644 --- a/src/Examine.Lucene/Indexing/DateTimeType.cs +++ b/src/Examine.Lucene/Indexing/DateTimeType.cs @@ -78,6 +78,7 @@ public override Query GetQuery(DateTime? lower, DateTime? upper, bool lowerInclu upper != null ? DateToLong(upper.Value) : (long?)null, lowerInclusive, upperInclusive); } - public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); + public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) + => field.ExtractFacets(facetsCollector, sortedSetReaderState); } } diff --git a/src/Examine.Lucene/Indexing/DoubleType.cs b/src/Examine.Lucene/Indexing/DoubleType.cs index b51459f41..6818ea9c2 100644 --- a/src/Examine.Lucene/Indexing/DoubleType.cs +++ b/src/Examine.Lucene/Indexing/DoubleType.cs @@ -56,6 +56,7 @@ public override Query GetQuery(double? lower, double? upper, bool lowerInclusive lower ?? double.MinValue, upper ?? double.MaxValue, lowerInclusive, upperInclusive); } - public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); + public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) + => field.ExtractFacets(facetsCollector, sortedSetReaderState); } } diff --git a/src/Examine.Lucene/Indexing/FullTextType.cs b/src/Examine.Lucene/Indexing/FullTextType.cs index 3a788d44b..2134667f3 100644 --- a/src/Examine.Lucene/Indexing/FullTextType.cs +++ b/src/Examine.Lucene/Indexing/FullTextType.cs @@ -169,6 +169,7 @@ public override Query GetQuery(string query) return GenerateQuery(FieldName, query, _analyzer); } - public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); + public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) + => field.ExtractFacets(facetsCollector, sortedSetReaderState); } } diff --git a/src/Examine.Lucene/Indexing/IIndexFacetValueType.cs b/src/Examine.Lucene/Indexing/IIndexFacetValueType.cs index 58436d58a..3bf1d2596 100644 --- a/src/Examine.Lucene/Indexing/IIndexFacetValueType.cs +++ b/src/Examine.Lucene/Indexing/IIndexFacetValueType.cs @@ -13,8 +13,8 @@ public interface IIndexFacetValueType /// /// /// - /// /// - void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field); + /// A dictionary of facets for this field + IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field); } } diff --git a/src/Examine.Lucene/Indexing/Int32Type.cs b/src/Examine.Lucene/Indexing/Int32Type.cs index 71912b74a..0af21825d 100644 --- a/src/Examine.Lucene/Indexing/Int32Type.cs +++ b/src/Examine.Lucene/Indexing/Int32Type.cs @@ -57,6 +57,7 @@ public override Query GetQuery(int? lower, int? upper, bool lowerInclusive = tru upper, lowerInclusive, upperInclusive); } - public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); + public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) + => field.ExtractFacets(facetsCollector, sortedSetReaderState); } } diff --git a/src/Examine.Lucene/Indexing/Int64Type.cs b/src/Examine.Lucene/Indexing/Int64Type.cs index 2f2c119a1..80cc6d08a 100644 --- a/src/Examine.Lucene/Indexing/Int64Type.cs +++ b/src/Examine.Lucene/Indexing/Int64Type.cs @@ -36,7 +36,7 @@ protected override void AddSingleValue(Document doc, object value) if (!TryConvert(value, out long parsedVal)) return; - doc.Add(new Int64Field(FieldName,parsedVal, Store ? Field.Store.YES : Field.Store.NO)); + doc.Add(new Int64Field(FieldName, parsedVal, Store ? Field.Store.YES : Field.Store.NO)); if (_isFacetable) { @@ -56,6 +56,7 @@ public override Query GetQuery(long? lower, long? upper, bool lowerInclusive = t lower, upper, lowerInclusive, upperInclusive); } - public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); + public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) + => field.ExtractFacets(facetsCollector, sortedSetReaderState); } } diff --git a/src/Examine.Lucene/Indexing/SingleType.cs b/src/Examine.Lucene/Indexing/SingleType.cs index 0f0d10c08..04c6e51d8 100644 --- a/src/Examine.Lucene/Indexing/SingleType.cs +++ b/src/Examine.Lucene/Indexing/SingleType.cs @@ -57,6 +57,7 @@ public override Query GetQuery(float? lower, float? upper, bool lowerInclusive = upper ?? float.MaxValue, lowerInclusive, upperInclusive); } - public virtual void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets, IFacetField field) => field.ExtractFacets(facetsCollector, sortedSetReaderState, facets); + public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) + => field.ExtractFacets(facetsCollector, sortedSetReaderState); } } diff --git a/src/Examine.Lucene/Search/FacetDoubleField.cs b/src/Examine.Lucene/Search/FacetDoubleField.cs index eed47fd62..a5146d03d 100644 --- a/src/Examine.Lucene/Search/FacetDoubleField.cs +++ b/src/Examine.Lucene/Search/FacetDoubleField.cs @@ -22,7 +22,7 @@ public FacetDoubleField(string field, Examine.Search.DoubleRange[] doubleRanges, FacetField = facetField; } - public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets) + public IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState) { var doubleFacetCounts = new DoubleRangeFacetCounts(Field, facetsCollector, DoubleRanges.AsLuceneRange().ToArray()); @@ -30,10 +30,10 @@ public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesRea if (doubleFacets == null) { - return; + yield break; } - facets.Add(Field, new Examine.Search.FacetResult(doubleFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); + yield return new KeyValuePair(Field, new Examine.Search.FacetResult(doubleFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } } } diff --git a/src/Examine.Lucene/Search/FacetFloatField.cs b/src/Examine.Lucene/Search/FacetFloatField.cs index ce26ea617..8e1cafe5a 100644 --- a/src/Examine.Lucene/Search/FacetFloatField.cs +++ b/src/Examine.Lucene/Search/FacetFloatField.cs @@ -23,7 +23,7 @@ public FacetFloatField(string field, FloatRange[] floatRanges, string facetField FacetField = facetField; } - public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets) + public IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState) { var floatFacetCounts = new DoubleRangeFacetCounts(Field, new SingleFieldSource(Field), facetsCollector, FloatRanges.AsLuceneRange().ToArray()); @@ -31,10 +31,10 @@ public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesRea if (floatFacets == null) { - return; + yield break; } - facets.Add(Field, new Examine.Search.FacetResult(floatFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); + yield return new KeyValuePair(Field, new Examine.Search.FacetResult(floatFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } } } diff --git a/src/Examine.Lucene/Search/FacetFullTextField.cs b/src/Examine.Lucene/Search/FacetFullTextField.cs index 411b7dc8a..957aa634a 100644 --- a/src/Examine.Lucene/Search/FacetFullTextField.cs +++ b/src/Examine.Lucene/Search/FacetFullTextField.cs @@ -24,7 +24,7 @@ public FacetFullTextField(string field, string[] values, string facetField, int MaxCount = maxCount; } - public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets) + public IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState) { var sortedFacetsCounts = new SortedSetDocValuesFacetCounts(sortedSetReaderState, facetsCollector); @@ -36,7 +36,7 @@ public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesRea var value = sortedFacetsCounts.GetSpecificValue(Field, label); facetValues.Add(new FacetValue(label, value)); } - facets.Add(Field, new Examine.Search.FacetResult(facetValues.OrderBy(value => value.Value).Take(MaxCount).OfType())); + yield return new KeyValuePair(Field, new Examine.Search.FacetResult(facetValues.OrderBy(value => value.Value).Take(MaxCount).OfType())); } else { @@ -44,10 +44,10 @@ public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesRea if (sortedFacets == null) { - return; + yield break; } - facets.Add(Field, new Examine.Search.FacetResult(sortedFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); + yield return new KeyValuePair(Field, new Examine.Search.FacetResult(sortedFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } } } diff --git a/src/Examine.Lucene/Search/FacetLongField.cs b/src/Examine.Lucene/Search/FacetLongField.cs index 73f2a0dbc..e4e8c2db0 100644 --- a/src/Examine.Lucene/Search/FacetLongField.cs +++ b/src/Examine.Lucene/Search/FacetLongField.cs @@ -22,7 +22,7 @@ public FacetLongField(string field, Examine.Search.Int64Range[] longRanges, stri FacetField = facetField; } - public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets) + public IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState) { var longFacetCounts = new Int64RangeFacetCounts(Field, facetsCollector, LongRanges.AsLuceneRange().ToArray()); @@ -30,10 +30,10 @@ public void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesRea if (longFacets == null) { - return; + yield break; } - facets.Add(Field, new Examine.Search.FacetResult(longFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); + yield return new KeyValuePair(Field, new Examine.Search.FacetResult(longFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } } } diff --git a/src/Examine.Lucene/Search/IFacetField.cs b/src/Examine.Lucene/Search/IFacetField.cs index 4bfa26211..a9ee39054 100644 --- a/src/Examine.Lucene/Search/IFacetField.cs +++ b/src/Examine.Lucene/Search/IFacetField.cs @@ -22,7 +22,7 @@ public interface IFacetField /// /// /// - /// - void ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, Dictionary facets); + /// Returns the facets for this field + IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState); } } diff --git a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs index a3b4dbb25..0b4639dfd 100644 --- a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs +++ b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs @@ -159,7 +159,12 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector sortedSetReaderState = new DefaultSortedSetDocValuesReaderState(searcher.IndexSearcher.IndexReader, field.FacetField); } - facetValueType.ExtractFacets(facetsCollector, sortedSetReaderState, facets, field); + var fieldFacets = facetValueType.ExtractFacets(facetsCollector, sortedSetReaderState, field); + foreach(var fieldFacet in fieldFacets) + { + // overwrite if necessary (no exceptions thrown in case of collision) + facets[fieldFacet.Key] = fieldFacet.Value; + } } } From ae085e6b597d8bfacb5571619495b6de5ddae02c Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 15 Feb 2023 14:10:12 -0700 Subject: [PATCH 36/42] Removes breaking change and moves to ext method --- src/Examine.Core/Search/IFacetOperations.cs | 43 +++++++++++++++++++ src/Examine.Core/Search/IFaceting.cs | 35 +++------------ src/Examine.Core/Search/IOrdering.cs | 9 +--- src/Examine.Core/Search/OrderingExtensions.cs | 22 ++++++++++ .../Search/LuceneBooleanOperation.cs | 2 +- .../Search/LuceneBooleanOperationBase.cs | 2 +- .../Search/LuceneFacetOperation.cs | 12 +++--- .../Search/LuceneSearchQuery.cs | 8 ++-- 8 files changed, 83 insertions(+), 50 deletions(-) create mode 100644 src/Examine.Core/Search/IFacetOperations.cs create mode 100644 src/Examine.Core/Search/OrderingExtensions.cs diff --git a/src/Examine.Core/Search/IFacetOperations.cs b/src/Examine.Core/Search/IFacetOperations.cs new file mode 100644 index 000000000..c4bb8465c --- /dev/null +++ b/src/Examine.Core/Search/IFacetOperations.cs @@ -0,0 +1,43 @@ +using System; + +namespace Examine.Search +{ + + /// + /// Faceting operations + /// + public interface IFacetOperations : IQueryExecutor + { + /// + /// Add a facet string to the current query + /// + /// + /// + /// + IFacetOperations Facet(string field, Action facetConfiguration = null); + + /// + /// Add a facet string to the current query, filtered by a single value or multiple values + /// + /// + /// + /// + /// + IFacetOperations Facet(string field, Action facetConfiguration = null, params string[] values); + + /// + /// Add a range facet to the current query + /// + IFacetOperations Facet(string field, params DoubleRange[] doubleRanges); + + /// + /// Add a range facet to the current query + /// + IFacetOperations Facet(string field, params FloatRange[] floatRanges); + + /// + /// Add a range facet to the current query + /// + IFacetOperations Facet(string field, params Int64Range[] longRanges); + } +} diff --git a/src/Examine.Core/Search/IFaceting.cs b/src/Examine.Core/Search/IFaceting.cs index c7d6315a5..530e5a590 100644 --- a/src/Examine.Core/Search/IFaceting.cs +++ b/src/Examine.Core/Search/IFaceting.cs @@ -3,40 +3,15 @@ namespace Examine.Search { /// - /// Faceting operations + /// Allows for selecting facets to return in your query /// - public interface IFaceting : IQueryExecutor + public interface IFaceting { /// - /// Add a facet string to the current query + /// Allows for selecting facets to return in your query /// - /// - /// + /// /// - IFaceting Facet(string field, Action facetConfiguration = null); - - /// - /// Add a facet string to the current query, filtered by a single value or multiple values - /// - /// - /// - /// - /// - IFaceting Facet(string field, Action facetConfiguration = null, params string[] values); - - /// - /// Add a range facet to the current query - /// - IFaceting Facet(string field, params DoubleRange[] doubleRanges); - - /// - /// Add a range facet to the current query - /// - IFaceting Facet(string field, params FloatRange[] floatRanges); - - /// - /// Add a range facet to the current query - /// - IFaceting Facet(string field, params Int64Range[] longRanges); + IQueryExecutor WithFacets(Action facets); } } diff --git a/src/Examine.Core/Search/IOrdering.cs b/src/Examine.Core/Search/IOrdering.cs index ffedeba4b..41b304fe2 100644 --- a/src/Examine.Core/Search/IOrdering.cs +++ b/src/Examine.Core/Search/IOrdering.cs @@ -37,13 +37,6 @@ public interface IOrdering : IQueryExecutor /// Return all fields in the index /// /// - IOrdering SelectAllFields(); - - /// - /// Allows for selecting facets to return in your query - /// - /// - /// - IQueryExecutor WithFacets(Action facets); + IOrdering SelectAllFields(); } } diff --git a/src/Examine.Core/Search/OrderingExtensions.cs b/src/Examine.Core/Search/OrderingExtensions.cs new file mode 100644 index 000000000..d0c5f5cbf --- /dev/null +++ b/src/Examine.Core/Search/OrderingExtensions.cs @@ -0,0 +1,22 @@ +using System; + +namespace Examine.Search +{ + public static class OrderingExtensions + { + /// + /// Allows for selecting facets to return in your query + /// + /// + /// + public static IQueryExecutor WithFacets(this IOrdering ordering, Action facets) + { + if (ordering is IFaceting faceting) + { + return faceting.WithFacets(facets); + } + + throw new NotSupportedException("The current implementation of Examine does not support faceting"); + } + } +} diff --git a/src/Examine.Lucene/Search/LuceneBooleanOperation.cs b/src/Examine.Lucene/Search/LuceneBooleanOperation.cs index ac03df096..8d104b9d5 100644 --- a/src/Examine.Lucene/Search/LuceneBooleanOperation.cs +++ b/src/Examine.Lucene/Search/LuceneBooleanOperation.cs @@ -67,7 +67,7 @@ public LuceneBooleanOperation(LuceneSearchQuery search) public override string ToString() => _search.ToString(); - public override IQueryExecutor WithFacets(Action facets) + public override IQueryExecutor WithFacets(Action facets) { var luceneFacetOperation = new LuceneFacetOperation(_search); facets.Invoke(luceneFacetOperation); diff --git a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs index 5440ae01f..95c3bf559 100644 --- a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs +++ b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs @@ -78,6 +78,6 @@ protected internal LuceneBooleanOperationBase Op( public abstract IOrdering SelectField(string fieldName); public abstract IOrdering SelectAllFields(); - public abstract IQueryExecutor WithFacets(Action facets); + public abstract IQueryExecutor WithFacets(Action facets); } } diff --git a/src/Examine.Lucene/Search/LuceneFacetOperation.cs b/src/Examine.Lucene/Search/LuceneFacetOperation.cs index 38d8233d7..1ff67b613 100644 --- a/src/Examine.Lucene/Search/LuceneFacetOperation.cs +++ b/src/Examine.Lucene/Search/LuceneFacetOperation.cs @@ -12,7 +12,7 @@ namespace Examine.Lucene.Search /// An implementation of the fluent API boolean operations /// [DebuggerDisplay("{_search}")] - public class LuceneFacetOperation : IFaceting + public class LuceneFacetOperation : IFacetOperations { private readonly LuceneSearchQuery _search; @@ -23,15 +23,15 @@ public LuceneFacetOperation(LuceneSearchQuery search) public ISearchResults Execute(QueryOptions options = null) => _search.Execute(options); - public IFaceting Facet(string field, Action facetConfiguration = null) => _search.FacetInternal(field, facetConfiguration, Array.Empty()); + public IFacetOperations Facet(string field, Action facetConfiguration = null) => _search.FacetInternal(field, facetConfiguration, Array.Empty()); - public IFaceting Facet(string field, Action facetConfiguration = null, params string[] values) => _search.FacetInternal(field, facetConfiguration, values); + public IFacetOperations Facet(string field, Action facetConfiguration = null, params string[] values) => _search.FacetInternal(field, facetConfiguration, values); - public IFaceting Facet(string field, params DoubleRange[] doubleRanges) => _search.FacetInternal(field, doubleRanges); + public IFacetOperations Facet(string field, params DoubleRange[] doubleRanges) => _search.FacetInternal(field, doubleRanges); - public IFaceting Facet(string field, params FloatRange[] floatRanges) => _search.FacetInternal(field, floatRanges); + public IFacetOperations Facet(string field, params FloatRange[] floatRanges) => _search.FacetInternal(field, floatRanges); - public IFaceting Facet(string field, params Int64Range[] longRanges) => _search.FacetInternal(field, longRanges); + public IFacetOperations Facet(string field, params Int64Range[] longRanges) => _search.FacetInternal(field, longRanges); public override string ToString() => _search.ToString(); } diff --git a/src/Examine.Lucene/Search/LuceneSearchQuery.cs b/src/Examine.Lucene/Search/LuceneSearchQuery.cs index 10860b949..12466e368 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQuery.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQuery.cs @@ -313,7 +313,7 @@ public IBooleanOperation SelectAllFieldsInternal() protected override LuceneBooleanOperationBase CreateOp() => new LuceneBooleanOperation(this); - internal IFaceting FacetInternal(string field, Action facetConfiguration, params string[] values) + internal IFacetOperations FacetInternal(string field, Action facetConfiguration, params string[] values) { if(values == null) { @@ -332,7 +332,7 @@ internal IFaceting FacetInternal(string field, Action facetCon return new LuceneFacetOperation(this); } - internal IFaceting FacetInternal(string field, params DoubleRange[] doubleRanges) + internal IFacetOperations FacetInternal(string field, params DoubleRange[] doubleRanges) { if(doubleRanges == null) { @@ -346,7 +346,7 @@ internal IFaceting FacetInternal(string field, params DoubleRange[] doubleRanges return new LuceneFacetOperation(this); } - internal IFaceting FacetInternal(string field, params FloatRange[] floatRanges) + internal IFacetOperations FacetInternal(string field, params FloatRange[] floatRanges) { if (floatRanges == null) { @@ -360,7 +360,7 @@ internal IFaceting FacetInternal(string field, params FloatRange[] floatRanges) return new LuceneFacetOperation(this); } - internal IFaceting FacetInternal(string field, params Int64Range[] longRanges) + internal IFacetOperations FacetInternal(string field, params Int64Range[] longRanges) { if(longRanges == null) { From fdf99ba08f27a0d88e0050b2d643c6d6dc55eeff Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Mon, 20 Mar 2023 19:28:09 +1300 Subject: [PATCH 37/42] Add missing interface --- src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs index 95c3bf559..781ce3256 100644 --- a/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs +++ b/src/Examine.Lucene/Search/LuceneBooleanOperationBase.cs @@ -5,7 +5,7 @@ namespace Examine.Lucene.Search { - public abstract class LuceneBooleanOperationBase : IBooleanOperation, INestedBooleanOperation, IOrdering + public abstract class LuceneBooleanOperationBase : IBooleanOperation, INestedBooleanOperation, IOrdering, IFaceting { private readonly LuceneSearchQueryBase _search; From 1b08a1714b4345be1b25362874562a87de61eb49 Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Mon, 20 Mar 2023 19:40:16 +1300 Subject: [PATCH 38/42] fix test --- src/Examine.Lucene/ValueTypeFactoryCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Examine.Lucene/ValueTypeFactoryCollection.cs b/src/Examine.Lucene/ValueTypeFactoryCollection.cs index db7bbad27..7917e051f 100644 --- a/src/Examine.Lucene/ValueTypeFactoryCollection.cs +++ b/src/Examine.Lucene/ValueTypeFactoryCollection.cs @@ -83,7 +83,7 @@ private static IReadOnlyDictionary> G {FieldDefinitionTypes.FacetDateHour, name => new DateTimeType(name, loggerFactory, DateResolution.HOUR, true, true)}, {FieldDefinitionTypes.FacetDateMinute, name => new DateTimeType(name, loggerFactory, DateResolution.MINUTE, true, true)}, {FieldDefinitionTypes.FacetFullText, name => new FullTextType(name, loggerFactory, false, true, defaultAnalyzer)}, - {FieldDefinitionTypes.FacetFullTextSortable, name => new FullTextType(name, loggerFactory, false, true, defaultAnalyzer)}, + {FieldDefinitionTypes.FacetFullTextSortable, name => new FullTextType(name, loggerFactory, true, true, defaultAnalyzer)}, }; From 226245ed7789bb592128f0b34dab8658365360ad Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Mon, 20 Mar 2023 21:30:46 +1300 Subject: [PATCH 39/42] Introduce IFacetExtractionContext --- src/Examine.Lucene/Indexing/DateTimeType.cs | 4 +-- src/Examine.Lucene/Indexing/DoubleType.cs | 4 +-- src/Examine.Lucene/Indexing/FullTextType.cs | 4 +-- .../Indexing/IIndexFacetValueType.cs | 4 +-- src/Examine.Lucene/Indexing/Int32Type.cs | 4 +-- src/Examine.Lucene/Indexing/Int64Type.cs | 4 +-- src/Examine.Lucene/Indexing/SingleType.cs | 4 +-- src/Examine.Lucene/Search/FacetDoubleField.cs | 6 ++-- src/Examine.Lucene/Search/FacetFloatField.cs | 7 ++--- .../Search/FacetFullTextField.cs | 5 ++- src/Examine.Lucene/Search/FacetLongField.cs | 4 +-- .../Search/IFacetExtractionContext.cs | 12 +++++++ src/Examine.Lucene/Search/IFacetField.cs | 4 +-- .../Search/LuceneFacetExtractionContext.cs | 31 +++++++++++++++++++ .../Search/LuceneSearchExecutor.cs | 7 ++--- .../Search/LuceneSearchQuery.cs | 2 +- 16 files changed, 69 insertions(+), 37 deletions(-) create mode 100644 src/Examine.Lucene/Search/IFacetExtractionContext.cs create mode 100644 src/Examine.Lucene/Search/LuceneFacetExtractionContext.cs diff --git a/src/Examine.Lucene/Indexing/DateTimeType.cs b/src/Examine.Lucene/Indexing/DateTimeType.cs index 42fc93388..67b01820b 100644 --- a/src/Examine.Lucene/Indexing/DateTimeType.cs +++ b/src/Examine.Lucene/Indexing/DateTimeType.cs @@ -78,7 +78,7 @@ public override Query GetQuery(DateTime? lower, DateTime? upper, bool lowerInclu upper != null ? DateToLong(upper.Value) : (long?)null, lowerInclusive, upperInclusive); } - public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) - => field.ExtractFacets(facetsCollector, sortedSetReaderState); + public virtual IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext, IFacetField field) + => field.ExtractFacets(facetExtractionContext); } } diff --git a/src/Examine.Lucene/Indexing/DoubleType.cs b/src/Examine.Lucene/Indexing/DoubleType.cs index 6818ea9c2..f036afdc9 100644 --- a/src/Examine.Lucene/Indexing/DoubleType.cs +++ b/src/Examine.Lucene/Indexing/DoubleType.cs @@ -56,7 +56,7 @@ public override Query GetQuery(double? lower, double? upper, bool lowerInclusive lower ?? double.MinValue, upper ?? double.MaxValue, lowerInclusive, upperInclusive); } - public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) - => field.ExtractFacets(facetsCollector, sortedSetReaderState); + public virtual IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext, IFacetField field) + => field.ExtractFacets(facetExtractionContext); } } diff --git a/src/Examine.Lucene/Indexing/FullTextType.cs b/src/Examine.Lucene/Indexing/FullTextType.cs index 2134667f3..0c347b3cf 100644 --- a/src/Examine.Lucene/Indexing/FullTextType.cs +++ b/src/Examine.Lucene/Indexing/FullTextType.cs @@ -169,7 +169,7 @@ public override Query GetQuery(string query) return GenerateQuery(FieldName, query, _analyzer); } - public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) - => field.ExtractFacets(facetsCollector, sortedSetReaderState); + public virtual IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext, IFacetField field) + => field.ExtractFacets(facetExtractionContext); } } diff --git a/src/Examine.Lucene/Indexing/IIndexFacetValueType.cs b/src/Examine.Lucene/Indexing/IIndexFacetValueType.cs index 3bf1d2596..55740e208 100644 --- a/src/Examine.Lucene/Indexing/IIndexFacetValueType.cs +++ b/src/Examine.Lucene/Indexing/IIndexFacetValueType.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using Examine.Lucene.Search; using Examine.Search; -using Lucene.Net.Facet; -using Lucene.Net.Facet.SortedSet; namespace Examine.Lucene.Indexing { @@ -15,6 +13,6 @@ public interface IIndexFacetValueType /// /// /// A dictionary of facets for this field - IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field); + IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext, IFacetField field); } } diff --git a/src/Examine.Lucene/Indexing/Int32Type.cs b/src/Examine.Lucene/Indexing/Int32Type.cs index 0af21825d..5ac9207ec 100644 --- a/src/Examine.Lucene/Indexing/Int32Type.cs +++ b/src/Examine.Lucene/Indexing/Int32Type.cs @@ -57,7 +57,7 @@ public override Query GetQuery(int? lower, int? upper, bool lowerInclusive = tru upper, lowerInclusive, upperInclusive); } - public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) - => field.ExtractFacets(facetsCollector, sortedSetReaderState); + public virtual IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext, IFacetField field) + => field.ExtractFacets(facetExtractionContext); } } diff --git a/src/Examine.Lucene/Indexing/Int64Type.cs b/src/Examine.Lucene/Indexing/Int64Type.cs index 80cc6d08a..1b55aae55 100644 --- a/src/Examine.Lucene/Indexing/Int64Type.cs +++ b/src/Examine.Lucene/Indexing/Int64Type.cs @@ -56,7 +56,7 @@ public override Query GetQuery(long? lower, long? upper, bool lowerInclusive = t lower, upper, lowerInclusive, upperInclusive); } - public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) - => field.ExtractFacets(facetsCollector, sortedSetReaderState); + public virtual IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext, IFacetField field) + => field.ExtractFacets(facetExtractionContext); } } diff --git a/src/Examine.Lucene/Indexing/SingleType.cs b/src/Examine.Lucene/Indexing/SingleType.cs index 04c6e51d8..6db66b326 100644 --- a/src/Examine.Lucene/Indexing/SingleType.cs +++ b/src/Examine.Lucene/Indexing/SingleType.cs @@ -57,7 +57,7 @@ public override Query GetQuery(float? lower, float? upper, bool lowerInclusive = upper ?? float.MaxValue, lowerInclusive, upperInclusive); } - public virtual IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState, IFacetField field) - => field.ExtractFacets(facetsCollector, sortedSetReaderState); + public virtual IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext, IFacetField field) + => field.ExtractFacets(facetExtractionContext); } } diff --git a/src/Examine.Lucene/Search/FacetDoubleField.cs b/src/Examine.Lucene/Search/FacetDoubleField.cs index a5146d03d..1f7e4d63c 100644 --- a/src/Examine.Lucene/Search/FacetDoubleField.cs +++ b/src/Examine.Lucene/Search/FacetDoubleField.cs @@ -1,9 +1,7 @@ using Lucene.Net.Facet.Range; -using Lucene.Net.Facet; using System.Collections.Generic; using Examine.Search; using System.Linq; -using Lucene.Net.Facet.SortedSet; namespace Examine.Lucene.Search { @@ -22,9 +20,9 @@ public FacetDoubleField(string field, Examine.Search.DoubleRange[] doubleRanges, FacetField = facetField; } - public IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState) + public IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext) { - var doubleFacetCounts = new DoubleRangeFacetCounts(Field, facetsCollector, DoubleRanges.AsLuceneRange().ToArray()); + var doubleFacetCounts = new DoubleRangeFacetCounts(Field, facetExtractionContext.FacetsCollector, DoubleRanges.AsLuceneRange().ToArray()); var doubleFacets = doubleFacetCounts.GetTopChildren(0, Field); diff --git a/src/Examine.Lucene/Search/FacetFloatField.cs b/src/Examine.Lucene/Search/FacetFloatField.cs index 8e1cafe5a..1ba31ada9 100644 --- a/src/Examine.Lucene/Search/FacetFloatField.cs +++ b/src/Examine.Lucene/Search/FacetFloatField.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using System.Linq; using Examine.Search; -using Lucene.Net.Facet; using Lucene.Net.Facet.Range; -using Lucene.Net.Facet.SortedSet; using Lucene.Net.Queries.Function.ValueSources; namespace Examine.Lucene.Search @@ -23,9 +21,9 @@ public FacetFloatField(string field, FloatRange[] floatRanges, string facetField FacetField = facetField; } - public IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState) + public IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext) { - var floatFacetCounts = new DoubleRangeFacetCounts(Field, new SingleFieldSource(Field), facetsCollector, FloatRanges.AsLuceneRange().ToArray()); + var floatFacetCounts = new DoubleRangeFacetCounts(Field, new SingleFieldSource(Field), facetExtractionContext.FacetsCollector, FloatRanges.AsLuceneRange().ToArray()); var floatFacets = floatFacetCounts.GetTopChildren(0, Field); @@ -36,5 +34,6 @@ public IEnumerable> ExtractFacets(FacetsColle yield return new KeyValuePair(Field, new Examine.Search.FacetResult(floatFacets.LabelValues.Select(labelValue => new FacetValue(labelValue.Label, labelValue.Value) as IFacetValue))); } + } } diff --git a/src/Examine.Lucene/Search/FacetFullTextField.cs b/src/Examine.Lucene/Search/FacetFullTextField.cs index 957aa634a..74bfe2b57 100644 --- a/src/Examine.Lucene/Search/FacetFullTextField.cs +++ b/src/Examine.Lucene/Search/FacetFullTextField.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using Examine.Search; -using Lucene.Net.Facet; using Lucene.Net.Facet.SortedSet; namespace Examine.Lucene.Search @@ -24,9 +23,9 @@ public FacetFullTextField(string field, string[] values, string facetField, int MaxCount = maxCount; } - public IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState) + public IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext) { - var sortedFacetsCounts = new SortedSetDocValuesFacetCounts(sortedSetReaderState, facetsCollector); + var sortedFacetsCounts = new SortedSetDocValuesFacetCounts(facetExtractionContext.GetSortedSetReaderState(FacetField), facetExtractionContext.FacetsCollector); if (Values != null && Values.Length > 0) { diff --git a/src/Examine.Lucene/Search/FacetLongField.cs b/src/Examine.Lucene/Search/FacetLongField.cs index e4e8c2db0..1d2683cc5 100644 --- a/src/Examine.Lucene/Search/FacetLongField.cs +++ b/src/Examine.Lucene/Search/FacetLongField.cs @@ -22,9 +22,9 @@ public FacetLongField(string field, Examine.Search.Int64Range[] longRanges, stri FacetField = facetField; } - public IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState) + public IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext) { - var longFacetCounts = new Int64RangeFacetCounts(Field, facetsCollector, LongRanges.AsLuceneRange().ToArray()); + var longFacetCounts = new Int64RangeFacetCounts(Field, facetExtractionContext.FacetsCollector, LongRanges.AsLuceneRange().ToArray()); var longFacets = longFacetCounts.GetTopChildren(0, Field); diff --git a/src/Examine.Lucene/Search/IFacetExtractionContext.cs b/src/Examine.Lucene/Search/IFacetExtractionContext.cs new file mode 100644 index 000000000..fb71759f1 --- /dev/null +++ b/src/Examine.Lucene/Search/IFacetExtractionContext.cs @@ -0,0 +1,12 @@ +using Lucene.Net.Facet; +using Lucene.Net.Facet.SortedSet; + +namespace Examine.Lucene.Search +{ + public interface IFacetExtractionContext + { + FacetsCollector FacetsCollector { get; } + + SortedSetDocValuesReaderState GetSortedSetReaderState(string facetFieldName); + } +} diff --git a/src/Examine.Lucene/Search/IFacetField.cs b/src/Examine.Lucene/Search/IFacetField.cs index a9ee39054..32646a1e5 100644 --- a/src/Examine.Lucene/Search/IFacetField.cs +++ b/src/Examine.Lucene/Search/IFacetField.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; using Examine.Search; -using Lucene.Net.Facet; -using Lucene.Net.Facet.SortedSet; namespace Examine.Lucene.Search { @@ -23,6 +21,6 @@ public interface IFacetField /// /// /// Returns the facets for this field - IEnumerable> ExtractFacets(FacetsCollector facetsCollector, SortedSetDocValuesReaderState sortedSetReaderState); + IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext); } } diff --git a/src/Examine.Lucene/Search/LuceneFacetExtractionContext.cs b/src/Examine.Lucene/Search/LuceneFacetExtractionContext.cs new file mode 100644 index 000000000..01a2d1b97 --- /dev/null +++ b/src/Examine.Lucene/Search/LuceneFacetExtractionContext.cs @@ -0,0 +1,31 @@ +using System; +using Lucene.Net.Facet; +using Lucene.Net.Facet.SortedSet; +using Lucene.Net.Index; + +namespace Examine.Lucene.Search +{ + public class LuceneFacetExtractionContext : IFacetExtractionContext + { + private readonly IndexReader _indexReader; + + private SortedSetDocValuesReaderState _sortedSetReaderState = null; + + public LuceneFacetExtractionContext(FacetsCollector facetsCollector, IndexReader indexReader) + { + FacetsCollector = facetsCollector; + _indexReader = indexReader; + } + + public FacetsCollector FacetsCollector { get; } + + public virtual SortedSetDocValuesReaderState GetSortedSetReaderState(string facetFieldName) + { + if (_sortedSetReaderState == null || !_sortedSetReaderState.Field.Equals(facetFieldName)) + { + _sortedSetReaderState = new DefaultSortedSetDocValuesReaderState(_indexReader, facetFieldName); + } + return _sortedSetReaderState; + } + } +} diff --git a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs index 0b4639dfd..f7e0ce033 100644 --- a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs +++ b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs @@ -154,12 +154,9 @@ private IReadOnlyDictionary ExtractFacets(FacetsCollector var valueType = _searchContext.GetFieldValueType(field.Field); if(valueType is IIndexFacetValueType facetValueType) { - if (field is FacetFullTextField && (sortedSetReaderState == null || !sortedSetReaderState.Field.Equals(field.FacetField))) - { - sortedSetReaderState = new DefaultSortedSetDocValuesReaderState(searcher.IndexSearcher.IndexReader, field.FacetField); - } + var facetExtractionContext = new LuceneFacetExtractionContext(facetsCollector, searcher.IndexSearcher.IndexReader); - var fieldFacets = facetValueType.ExtractFacets(facetsCollector, sortedSetReaderState, field); + var fieldFacets = facetValueType.ExtractFacets(facetExtractionContext, field); foreach(var fieldFacet in fieldFacets) { // overwrite if necessary (no exceptions thrown in case of collision) diff --git a/src/Examine.Lucene/Search/LuceneSearchQuery.cs b/src/Examine.Lucene/Search/LuceneSearchQuery.cs index 12466e368..8b66bf628 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQuery.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQuery.cs @@ -319,7 +319,7 @@ internal IFacetOperations FacetInternal(string field, Action f { values = Array.Empty(); } - + var fieldValueType = _searchContext.GetFieldValueType(field); var facet = new FacetFullTextField(field, values, GetFacetField(field)); if(facetConfiguration != null) From f58f672c88708e2bdff2b1ed8e0cb96c32baf9a0 Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Tue, 21 Mar 2023 00:20:29 +1300 Subject: [PATCH 40/42] Support facet extraction --- src/Examine.Lucene/Search/FacetDoubleField.cs | 4 +- src/Examine.Lucene/Search/FacetFloatField.cs | 4 +- .../Search/FacetFullTextField.cs | 12 +++-- src/Examine.Lucene/Search/FacetLongField.cs | 4 +- .../Search/IFacetExtractionContext.cs | 25 +++++++++- src/Examine.Lucene/Search/IFacetField.cs | 5 ++ .../Search/LuceneFacetExtractionContext.cs | 46 ++++++++++++------- .../Search/LuceneSearchExecutor.cs | 8 ++-- .../Search/LuceneSearchQuery.cs | 2 +- 9 files changed, 78 insertions(+), 32 deletions(-) diff --git a/src/Examine.Lucene/Search/FacetDoubleField.cs b/src/Examine.Lucene/Search/FacetDoubleField.cs index 1f7e4d63c..e63a76b37 100644 --- a/src/Examine.Lucene/Search/FacetDoubleField.cs +++ b/src/Examine.Lucene/Search/FacetDoubleField.cs @@ -12,12 +12,14 @@ namespace Examine.Lucene.Search public string Field { get; } public string FacetField { get; } + public bool IsTaxonomyIndexed { get; } - public FacetDoubleField(string field, Examine.Search.DoubleRange[] doubleRanges, string facetField) + public FacetDoubleField(string field, Examine.Search.DoubleRange[] doubleRanges, string facetField, bool isTaxonomyIndexed = false) { Field = field; DoubleRanges = doubleRanges; FacetField = facetField; + IsTaxonomyIndexed = isTaxonomyIndexed; } public IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext) diff --git a/src/Examine.Lucene/Search/FacetFloatField.cs b/src/Examine.Lucene/Search/FacetFloatField.cs index 1ba31ada9..5cdabfdd4 100644 --- a/src/Examine.Lucene/Search/FacetFloatField.cs +++ b/src/Examine.Lucene/Search/FacetFloatField.cs @@ -13,12 +13,14 @@ namespace Examine.Lucene.Search public string Field { get; } public string FacetField { get; } + public bool IsTaxonomyIndexed { get; } - public FacetFloatField(string field, FloatRange[] floatRanges, string facetField) + public FacetFloatField(string field, FloatRange[] floatRanges, string facetField, bool isTaxonomyIndexed = false) { Field = field; FloatRanges = floatRanges; FacetField = facetField; + IsTaxonomyIndexed = isTaxonomyIndexed; } public IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext) diff --git a/src/Examine.Lucene/Search/FacetFullTextField.cs b/src/Examine.Lucene/Search/FacetFullTextField.cs index 74bfe2b57..c92641d0b 100644 --- a/src/Examine.Lucene/Search/FacetFullTextField.cs +++ b/src/Examine.Lucene/Search/FacetFullTextField.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using Examine.Search; +using Lucene.Net.Facet; using Lucene.Net.Facet.SortedSet; namespace Examine.Lucene.Search @@ -8,38 +9,39 @@ namespace Examine.Lucene.Search public class FacetFullTextField : IFacetField { public int MaxCount { get; internal set; } - + public bool IsTaxonomyIndexed { get; } public string[] Values { get; } public string Field { get; } public string FacetField { get; } - public FacetFullTextField(string field, string[] values, string facetField, int maxCount = 10) + public FacetFullTextField(string field, string[] values, string facetField, int maxCount = 10, bool isTaxonomyIndexed = false) { Field = field; Values = values; FacetField = facetField; MaxCount = maxCount; + IsTaxonomyIndexed = isTaxonomyIndexed; } public IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext) { - var sortedFacetsCounts = new SortedSetDocValuesFacetCounts(facetExtractionContext.GetSortedSetReaderState(FacetField), facetExtractionContext.FacetsCollector); + Facets facetCounts = facetExtractionContext.GetFacetCounts(FacetField, false); if (Values != null && Values.Length > 0) { var facetValues = new List(); foreach (var label in Values) { - var value = sortedFacetsCounts.GetSpecificValue(Field, label); + var value = facetCounts.GetSpecificValue(Field, label); facetValues.Add(new FacetValue(label, value)); } yield return new KeyValuePair(Field, new Examine.Search.FacetResult(facetValues.OrderBy(value => value.Value).Take(MaxCount).OfType())); } else { - var sortedFacets = sortedFacetsCounts.GetTopChildren(MaxCount, Field); + var sortedFacets = facetCounts.GetTopChildren(MaxCount, Field); if (sortedFacets == null) { diff --git a/src/Examine.Lucene/Search/FacetLongField.cs b/src/Examine.Lucene/Search/FacetLongField.cs index 1d2683cc5..cee6a2214 100644 --- a/src/Examine.Lucene/Search/FacetLongField.cs +++ b/src/Examine.Lucene/Search/FacetLongField.cs @@ -14,12 +14,14 @@ namespace Examine.Lucene.Search public Examine.Search.Int64Range[] LongRanges { get; } public string FacetField { get; } + public bool IsTaxonomyIndexed { get; } - public FacetLongField(string field, Examine.Search.Int64Range[] longRanges, string facetField) + public FacetLongField(string field, Examine.Search.Int64Range[] longRanges, string facetField, bool isTaxonomyIndexed = false) { Field = field; LongRanges = longRanges; FacetField = facetField; + IsTaxonomyIndexed = isTaxonomyIndexed; } public IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext) diff --git a/src/Examine.Lucene/Search/IFacetExtractionContext.cs b/src/Examine.Lucene/Search/IFacetExtractionContext.cs index fb71759f1..f9b17ef6e 100644 --- a/src/Examine.Lucene/Search/IFacetExtractionContext.cs +++ b/src/Examine.Lucene/Search/IFacetExtractionContext.cs @@ -1,12 +1,33 @@ using Lucene.Net.Facet; -using Lucene.Net.Facet.SortedSet; namespace Examine.Lucene.Search { + /// + /// Context for Extracting the Facets for a field + /// public interface IFacetExtractionContext { + /// + /// Facet Collector + /// FacetsCollector FacetsCollector { get; } - SortedSetDocValuesReaderState GetSortedSetReaderState(string facetFieldName); + /// + /// Facet Configuration + /// + FacetsConfig FacetConfig { get; } + + /// + /// Index Searcher Reference + /// + ISearcherReference SearcherReference { get; } + + /// + /// Get the facet counts for the faceted field + /// + /// The name of the field the facet data is stored in + /// Whether the facet is stored in the Taxonomy index + /// + Facets GetFacetCounts(string facetIndexFieldName, bool isTaxonomyIndexed); } } diff --git a/src/Examine.Lucene/Search/IFacetField.cs b/src/Examine.Lucene/Search/IFacetField.cs index 32646a1e5..bd4f66326 100644 --- a/src/Examine.Lucene/Search/IFacetField.cs +++ b/src/Examine.Lucene/Search/IFacetField.cs @@ -15,6 +15,11 @@ public interface IFacetField /// string FacetField { get; } + /// + /// Whether this field is indexed in the Taxonomy index + /// + bool IsTaxonomyIndexed { get; } + /// /// Extracts the facets from the field /// diff --git a/src/Examine.Lucene/Search/LuceneFacetExtractionContext.cs b/src/Examine.Lucene/Search/LuceneFacetExtractionContext.cs index 01a2d1b97..9d0f50ef3 100644 --- a/src/Examine.Lucene/Search/LuceneFacetExtractionContext.cs +++ b/src/Examine.Lucene/Search/LuceneFacetExtractionContext.cs @@ -1,31 +1,43 @@ -using System; -using Lucene.Net.Facet; using Lucene.Net.Facet.SortedSet; -using Lucene.Net.Index; +using Lucene.Net.Facet; +using System; +using Examine.Lucene.Search; -namespace Examine.Lucene.Search +public class LuceneFacetExtractionContext : IFacetExtractionContext { - public class LuceneFacetExtractionContext : IFacetExtractionContext + + private SortedSetDocValuesReaderState _sortedSetReaderState = null; + + public LuceneFacetExtractionContext(FacetsCollector facetsCollector, ISearcherReference searcherReference, FacetsConfig facetConfig) { - private readonly IndexReader _indexReader; + FacetsCollector = facetsCollector; + FacetConfig = facetConfig; + SearcherReference = searcherReference; + } - private SortedSetDocValuesReaderState _sortedSetReaderState = null; + /// + public FacetsCollector FacetsCollector { get; } - public LuceneFacetExtractionContext(FacetsCollector facetsCollector, IndexReader indexReader) - { - FacetsCollector = facetsCollector; - _indexReader = indexReader; - } + /// + public FacetsConfig FacetConfig { get; } - public FacetsCollector FacetsCollector { get; } + /// + public ISearcherReference SearcherReference { get; } - public virtual SortedSetDocValuesReaderState GetSortedSetReaderState(string facetFieldName) + /// + public virtual Facets GetFacetCounts(string facetIndexFieldName, bool isTaxonomyIndexed) + { + if (isTaxonomyIndexed) + { + throw new NotSupportedException("Taxonomy Index not supported"); + } + else { - if (_sortedSetReaderState == null || !_sortedSetReaderState.Field.Equals(facetFieldName)) + if (_sortedSetReaderState == null || !_sortedSetReaderState.Field.Equals(facetIndexFieldName)) { - _sortedSetReaderState = new DefaultSortedSetDocValuesReaderState(_indexReader, facetFieldName); + _sortedSetReaderState = new DefaultSortedSetDocValuesReaderState(SearcherReference.IndexSearcher.IndexReader, facetIndexFieldName); } - return _sortedSetReaderState; + return new SortedSetDocValuesFacetCounts(_sortedSetReaderState, FacetsCollector); } } } diff --git a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs index f7e0ce033..109d63baf 100644 --- a/src/Examine.Lucene/Search/LuceneSearchExecutor.cs +++ b/src/Examine.Lucene/Search/LuceneSearchExecutor.cs @@ -5,10 +5,8 @@ using Examine.Search; using Lucene.Net.Documents; using Lucene.Net.Facet; -using Lucene.Net.Facet.Range; using Lucene.Net.Facet.SortedSet; using Lucene.Net.Index; -using Lucene.Net.Queries.Function.ValueSources; using Lucene.Net.Search; namespace Examine.Lucene.Search @@ -26,8 +24,9 @@ public class LuceneSearchExecutor private readonly ISet _fieldsToLoad; private readonly IEnumerable _facetFields; private int? _maxDoc; + private readonly FacetsConfig _facetsConfig; - internal LuceneSearchExecutor(QueryOptions options, Query query, IEnumerable sortField, ISearchContext searchContext, ISet fieldsToLoad, IEnumerable facetFields) + internal LuceneSearchExecutor(QueryOptions options, Query query, IEnumerable sortField, ISearchContext searchContext, ISet fieldsToLoad, IEnumerable facetFields, FacetsConfig facetsConfig) { _options = options ?? QueryOptions.Default; _luceneQuery = query ?? throw new ArgumentNullException(nameof(query)); @@ -35,6 +34,7 @@ internal LuceneSearchExecutor(QueryOptions options, Query query, IEnumerable ExtractFacets(FacetsCollector var valueType = _searchContext.GetFieldValueType(field.Field); if(valueType is IIndexFacetValueType facetValueType) { - var facetExtractionContext = new LuceneFacetExtractionContext(facetsCollector, searcher.IndexSearcher.IndexReader); + var facetExtractionContext = new LuceneFacetExtractionContext(facetsCollector, searcher, _facetsConfig); var fieldFacets = facetValueType.ExtractFacets(facetExtractionContext, field); foreach(var fieldFacet in fieldFacets) diff --git a/src/Examine.Lucene/Search/LuceneSearchQuery.cs b/src/Examine.Lucene/Search/LuceneSearchQuery.cs index 8b66bf628..fad9e8965 100644 --- a/src/Examine.Lucene/Search/LuceneSearchQuery.cs +++ b/src/Examine.Lucene/Search/LuceneSearchQuery.cs @@ -231,7 +231,7 @@ private ISearchResults Search(QueryOptions options) } } - var executor = new LuceneSearchExecutor(options, query, SortFields, _searchContext, _fieldsToLoad, _facetFields); + var executor = new LuceneSearchExecutor(options, query, SortFields, _searchContext, _fieldsToLoad, _facetFields, _facetsConfig); var pagesResults = executor.Execute(); From 838ee06a402e2b8787917e97131818011fcabe54 Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Tue, 21 Mar 2023 00:27:36 +1300 Subject: [PATCH 41/42] path support --- src/Examine.Core/Search/IFacetQueryField.cs | 5 +++++ src/Examine.Lucene/Search/FacetFullTextField.cs | 10 +++++++--- src/Examine.Lucene/Search/FacetQueryField.cs | 8 ++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Examine.Core/Search/IFacetQueryField.cs b/src/Examine.Core/Search/IFacetQueryField.cs index b9bb1e739..3838b780b 100644 --- a/src/Examine.Core/Search/IFacetQueryField.cs +++ b/src/Examine.Core/Search/IFacetQueryField.cs @@ -6,5 +6,10 @@ public interface IFacetQueryField /// Maximum number of terms to return /// IFacetQueryField MaxCount(int count); + + /// + /// Path Hierarchy + /// + IFacetQueryField SetPath(string[] path); } } diff --git a/src/Examine.Lucene/Search/FacetFullTextField.cs b/src/Examine.Lucene/Search/FacetFullTextField.cs index c92641d0b..be0fe2617 100644 --- a/src/Examine.Lucene/Search/FacetFullTextField.cs +++ b/src/Examine.Lucene/Search/FacetFullTextField.cs @@ -2,26 +2,30 @@ using System.Linq; using Examine.Search; using Lucene.Net.Facet; -using Lucene.Net.Facet.SortedSet; namespace Examine.Lucene.Search { public class FacetFullTextField : IFacetField { public int MaxCount { get; internal set; } - public bool IsTaxonomyIndexed { get; } + public string[] Values { get; } public string Field { get; } public string FacetField { get; } - public FacetFullTextField(string field, string[] values, string facetField, int maxCount = 10, bool isTaxonomyIndexed = false) + public string[] Path { get; internal set; } + + public bool IsTaxonomyIndexed { get; } + + public FacetFullTextField(string field, string[] values, string facetField, int maxCount = 10, string[] path = null, bool isTaxonomyIndexed = false) { Field = field; Values = values; FacetField = facetField; MaxCount = maxCount; + Path = path; IsTaxonomyIndexed = isTaxonomyIndexed; } diff --git a/src/Examine.Lucene/Search/FacetQueryField.cs b/src/Examine.Lucene/Search/FacetQueryField.cs index 22e461a89..ada636135 100644 --- a/src/Examine.Lucene/Search/FacetQueryField.cs +++ b/src/Examine.Lucene/Search/FacetQueryField.cs @@ -11,11 +11,19 @@ public FacetQueryField(FacetFullTextField field) _field = field; } + /// public IFacetQueryField MaxCount(int count) { _field.MaxCount = count; return this; } + + public IFacetQueryField SetPath(string[] path) + { + _field.Path = path; + + return this; + } } } From e23a8cbf42fccbb8db5d7976bfdd63839ec43bc9 Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Tue, 21 Mar 2023 00:29:00 +1300 Subject: [PATCH 42/42] params --- src/Examine.Lucene/Search/FacetQueryField.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Examine.Lucene/Search/FacetQueryField.cs b/src/Examine.Lucene/Search/FacetQueryField.cs index ada636135..d6a50513e 100644 --- a/src/Examine.Lucene/Search/FacetQueryField.cs +++ b/src/Examine.Lucene/Search/FacetQueryField.cs @@ -19,7 +19,7 @@ public IFacetQueryField MaxCount(int count) return this; } - public IFacetQueryField SetPath(string[] path) + public IFacetQueryField SetPath(params string[] path) { _field.Path = path;