diff --git a/src/Examine.Core/FieldDefinitionTypes.cs b/src/Examine.Core/FieldDefinitionTypes.cs index 5fe13cb3..a6f00b27 100644 --- a/src/Examine.Core/FieldDefinitionTypes.cs +++ b/src/Examine.Core/FieldDefinitionTypes.cs @@ -13,10 +13,16 @@ public static class FieldDefinitionTypes public const string Integer = "int"; /// - /// Will be indexed as a float + /// Will be indexed as a single /// public const string Float = "float"; + /// + /// Will be indexed as a single + /// + [Obsolete("To remove in Examine V5. Use Float")] + public const string Single = "single"; + /// /// Will be indexed as a double /// diff --git a/src/Examine.Lucene/Indexing/FloatType.cs b/src/Examine.Lucene/Indexing/FloatType.cs new file mode 100644 index 00000000..079f8e33 --- /dev/null +++ b/src/Examine.Lucene/Indexing/FloatType.cs @@ -0,0 +1,114 @@ +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; +using static Lucene.Net.Queries.Function.ValueSources.MultiFunction; + +namespace Examine.Lucene.Indexing +{ + /// + /// Represents a float/single + /// + public class FloatType : IndexFieldRangeValueType, IIndexFacetValueType + { + private readonly bool _isFacetable; +#pragma warning disable IDE0032 // Use auto property + private readonly bool _taxonomyIndex; +#pragma warning restore IDE0032 // Use auto property + + /// + public FloatType(string fieldName, bool isFacetable, bool taxonomyIndex, ILoggerFactory logger, bool store) + : base(fieldName, logger, store) + { + _isFacetable = isFacetable; + _taxonomyIndex = taxonomyIndex; + } + + /// + [Obsolete("To be removed in Examine V5")] +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public FloatType(string fieldName, ILoggerFactory logger, bool store = true) +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + : base(fieldName, logger, store) + { + _isFacetable = false; + } + + /// + /// Can be sorted by the normal field name + /// + public override string SortableFieldName => FieldName; + + /// + public bool IsTaxonomyFaceted => _taxonomyIndex; + + /// + public override void AddValue(Document doc, object? value) + { + // Support setting taxonomy path + if (_isFacetable && _taxonomyIndex && value is object[] objArr && objArr != null && objArr.Length == 2) + { + if (!TryConvert(objArr[0], out float parsedVal)) + { + return; + } + + if (!TryConvert(objArr[1], out string[]? parsedPathVal)) + { + return; + } + + doc.Add(new SingleField(FieldName, parsedVal, Store ? Field.Store.YES : Field.Store.NO)); + + doc.Add(new FacetField(FieldName, parsedPathVal)); + doc.Add(new SingleDocValuesField(FieldName, parsedVal)); + return; + } + base.AddValue(doc, value); + } + + /// + protected override void AddSingleValue(Document doc, object value) + { + if (!TryConvert(value, out float parsedVal)) + { + return; + } + + doc.Add(new SingleField(FieldName, parsedVal, Store ? Field.Store.YES : Field.Store.NO)); + + if (_isFacetable && _taxonomyIndex) + { + doc.Add(new FacetField(FieldName, parsedVal.ToString())); + doc.Add(new SingleDocValuesField(FieldName, parsedVal)); + } + else if (_isFacetable && !_taxonomyIndex) + { + doc.Add(new SortedSetDocValuesFacetField(FieldName, parsedVal.ToString())); + doc.Add(new SingleDocValuesField(FieldName, parsedVal)); + } + } + + /// + public override Query? GetQuery(string query) => !TryConvert(query, out float parsedVal) ? null : GetQuery(parsedVal, parsedVal); + + /// + public override Query GetQuery(float? lower, float? upper, bool lowerInclusive = true, bool upperInclusive = true) + { + return NumericRangeQuery.NewSingleRange(FieldName, + lower ?? float.MinValue, + upper ?? float.MaxValue, lowerInclusive, upperInclusive); + } + + /// + public virtual IEnumerable> ExtractFacets(IFacetExtractionContext facetExtractionContext, IFacetField field) + => field.ExtractFacets(facetExtractionContext); + } + +} diff --git a/src/Examine.Lucene/ValueTypeFactoryCollection.cs b/src/Examine.Lucene/ValueTypeFactoryCollection.cs index dc8f955b..9952f890 100644 --- a/src/Examine.Lucene/ValueTypeFactoryCollection.cs +++ b/src/Examine.Lucene/ValueTypeFactoryCollection.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.InteropServices; using Examine.Lucene.Analyzers; using Examine.Lucene.Indexing; using Lucene.Net.Analysis; @@ -75,7 +76,8 @@ private static IReadOnlyDictionary> G { {"number", name => new Int32Type(name, loggerFactory)}, {FieldDefinitionTypes.Integer, name => new Int32Type(name, loggerFactory)}, - {FieldDefinitionTypes.Float, name => new SingleType(name, loggerFactory)}, + {FieldDefinitionTypes.Float, name => new FloatType(name, loggerFactory)}, + {FieldDefinitionTypes.Single, name => new SingleType(name, loggerFactory)}, {FieldDefinitionTypes.Double, name => new DoubleType(name, loggerFactory)}, {FieldDefinitionTypes.Long, name => new Int64Type(name, loggerFactory)}, {"date", name => new DateTimeType(name, loggerFactory, DateResolution.MILLISECOND)}, @@ -91,7 +93,7 @@ private static IReadOnlyDictionary> G {FieldDefinitionTypes.InvariantCultureIgnoreCase, name => new GenericAnalyzerFieldValueType(name, loggerFactory, new CultureInvariantWhitespaceAnalyzer())}, {FieldDefinitionTypes.EmailAddress, name => new GenericAnalyzerFieldValueType(name, loggerFactory, new EmailAddressAnalyzer())}, {FieldDefinitionTypes.FacetInteger, name => new Int32Type(name, true,false,loggerFactory, true)}, - {FieldDefinitionTypes.FacetFloat, name => new SingleType(name, true, false, loggerFactory, true)}, + {FieldDefinitionTypes.FacetFloat, name => new FloatType(name, true, false, loggerFactory, true)}, {FieldDefinitionTypes.FacetDouble, name => new DoubleType(name,true, false, loggerFactory, true)}, {FieldDefinitionTypes.FacetLong, name => new Int64Type(name, true, false, loggerFactory, true)}, {FieldDefinitionTypes.FacetDateTime, name => new DateTimeType(name, true, true, false, loggerFactory, DateResolution.MILLISECOND)}, @@ -103,7 +105,7 @@ private static IReadOnlyDictionary> G {FieldDefinitionTypes.FacetFullText, name => new FullTextType(name, loggerFactory, true, false, false, defaultAnalyzer ?? new CultureInvariantStandardAnalyzer())}, {FieldDefinitionTypes.FacetFullTextSortable, name => new FullTextType(name, loggerFactory, true, false,true, defaultAnalyzer ?? new CultureInvariantStandardAnalyzer())}, {FieldDefinitionTypes.FacetTaxonomyInteger, name => new Int32Type(name,true,true, loggerFactory, true)}, - {FieldDefinitionTypes.FacetTaxonomyFloat, name => new SingleType(name,isFacetable: true, taxonomyIndex: true, loggerFactory, true)}, + {FieldDefinitionTypes.FacetTaxonomyFloat, name => new FloatType(name,isFacetable: true, taxonomyIndex: true, loggerFactory, true)}, {FieldDefinitionTypes.FacetTaxonomyDouble, name => new DoubleType(name, true, true, loggerFactory, true)}, {FieldDefinitionTypes.FacetTaxonomyLong, name => new Int64Type(name, isFacetable: true, taxonomyIndex: true, loggerFactory, true)}, {FieldDefinitionTypes.FacetTaxonomyDateTime, name => new DateTimeType(name,true, true, taxonomyIndex : true, loggerFactory, DateResolution.MILLISECOND)},