diff --git a/src/Lucene.Net.Grouping/GroupingSearch.cs b/src/Lucene.Net.Grouping/GroupingSearch.cs index 96b23a068f..a8a2edd0dd 100644 --- a/src/Lucene.Net.Grouping/GroupingSearch.cs +++ b/src/Lucene.Net.Grouping/GroupingSearch.cs @@ -33,270 +33,39 @@ namespace Lucene.Net.Search.Grouping /// Convenience class to perform grouping in a non distributed environment. /// @lucene.experimental /// - public class GroupingSearch + public static class GroupingSearch { - private readonly string groupField; - private readonly ValueSource groupFunction; - private readonly IDictionary /* Map */ valueSourceContext; - private readonly Filter groupEndDocs; - - private Sort groupSort = Sort.RELEVANCE; - private Sort sortWithinGroup; - - private int groupDocsOffset; - private int groupDocsLimit = 1; - private bool fillSortFields; - private bool includeScores = true; - private bool includeMaxScore = true; - - private double? maxCacheRAMMB; - private int? maxDocsToCache; - private bool cacheScores; - private bool allGroups; - private bool allGroupHeads; - private int initialSize = 128; - - private ICollection /* Collection */ matchingGroups; - private IBits matchingGroupHeads; - - /// - /// Constructs a instance that groups documents by index terms using the . - /// The group field can only have one token per document. This means that the field must not be analysed. - /// - /// The name of the field to group by. - public GroupingSearch(string groupField) - : this(groupField, null, null, null) - { - } - - /// - /// Constructs a instance that groups documents by function using a - /// instance. - /// - /// The function to group by specified as - /// The context of the specified groupFunction - public GroupingSearch(ValueSource groupFunction, IDictionary /* Map */ valueSourceContext) - : this(null, groupFunction, valueSourceContext, null) - { - - } - - /// - /// Constructor for grouping documents by doc block. - /// This constructor can only be used when documents belonging in a group are indexed in one block. - /// - /// The filter that marks the last document in all doc blocks - public GroupingSearch(Filter groupEndDocs) - : this(null, null, null, groupEndDocs) - { - } - - private GroupingSearch(string groupField, ValueSource groupFunction, IDictionary /* Map */ valueSourceContext, Filter groupEndDocs) - { - this.groupField = groupField; - this.groupFunction = groupFunction; - this.valueSourceContext = valueSourceContext; - this.groupEndDocs = groupEndDocs; - } - - /// - /// Executes a grouped search. Both the first pass and second pass are executed on the specified searcher. - /// - /// The instance to execute the grouped search on. - /// The query to execute with the grouping - /// The group offset - /// The number of groups to return from the specified group offset - /// the grouped result as a instance - /// If any I/O related errors occur - // LUCENENET additional method signature. Makes discovering the return type easier. - public virtual ITopGroups Search(IndexSearcher searcher, Query query, int groupOffset, int groupLimit) + public static FieldGroupingSearch ByField(string groupField) { - return Search(searcher, null, query, groupOffset, groupLimit); + return new FieldGroupingSearch(groupField); } - /// - /// Executes a grouped search. Both the first pass and second pass are executed on the specified searcher. - /// - /// The instance to execute the grouped search on. - /// The filter to execute with the grouping - /// The query to execute with the grouping - /// The group offset - /// The number of groups to return from the specified group offset - /// the grouped result as a instance - /// If any I/O related errors occur - // LUCENENET additional method signature. Makes discovering the return type easier. - public virtual ITopGroups Search(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit) - { - if (groupFunction != null) - { - return GroupByFunction(searcher, filter, query, groupOffset, groupLimit); - } - else if (groupField != null) - { - return GroupByField(searcher, filter, query, groupOffset, groupLimit); - } - else if (groupEndDocs != null) - { - return GroupByDocBlock(searcher, filter, query, groupOffset, groupLimit); - } - else - { - throw IllegalStateException.Create("Either groupField, groupFunction or groupEndDocs must be set."); // This can't happen... - } - } - - /// - /// Executes a grouped search. Both the first pass and second pass are executed on the specified searcher. - /// - /// The expected return type of the search. - /// The instance to execute the grouped search on. - /// The query to execute with the grouping - /// The group offset - /// The number of groups to return from the specified group offset - /// the grouped result as a instance - /// If any I/O related errors occur - public virtual ITopGroups Search(IndexSearcher searcher, Query query, int groupOffset, int groupLimit) - { - return Search(searcher, null, query, groupOffset, groupLimit); - } - - /// - /// Executes a grouped search. Both the first pass and second pass are executed on the specified searcher. - /// - /// The expected return type of the search. - /// The instance to execute the grouped search on. - /// The filter to execute with the grouping - /// The query to execute with the grouping - /// The group offset - /// The number of groups to return from the specified group offset - /// the grouped result as a instance - /// If any I/O related errors occur - public virtual ITopGroups Search(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit) - { - if (groupFunction != null) - { - //LUCENENET Specific gaurd clause provides diagnostic info when TGroupValue is inappropriate type. - if (!typeof(TGroupValue).IsSubclassOf(typeof(MutableValue)) && typeof(TGroupValue) != typeof(MutableValue) && typeof(TGroupValue) != typeof(object)) - { - throw IllegalStateException.Create("Generic closing type specified must be MutableValue, inherit from MutableValue, or be of type object since grouping by function."); - } - return (ITopGroups)GroupByFunction(searcher, filter, query, groupOffset, groupLimit); - } - else if (groupField != null) - { - return GroupByField(searcher, filter, query, groupOffset, groupLimit); - } - else if (groupEndDocs != null) - { - return GroupByDocBlock(searcher, filter, query, groupOffset, groupLimit); - } - else - { - throw IllegalStateException.Create("Either groupField, groupFunction or groupEndDocs must be set."); // This can't happen... - } - } - - /// - /// Executes a grouped search base on the field specified via the constructor. Both the first pass - /// and second pass are executed on the specified searcher. - /// - /// The instance to execute the grouped search on. - /// The query to execute with the grouping - /// The group offset - /// The number of groups to return from the specified group offset - /// the grouped result as a instance - /// If any I/O related errors occur - // LUCENENET additional method signature. Makes searching by field easier due to concrete return type - public virtual ITopGroups SearchByField(IndexSearcher searcher, Query query, int groupOffset, int groupLimit) + public static FunctionGroupingSearch ByFunction(ValueSource groupFunction, IDictionary valueSourceContext) + where TMutableValue : MutableValue { - return GroupByField(searcher, null, query, groupOffset, groupLimit); + return new FunctionGroupingSearch(groupFunction, valueSourceContext); } - - /// - /// Executes a grouped search base on the field specified via the constructor. Both the first pass - /// and second pass are executed on the specified searcher. - /// - /// The instance to execute the grouped search on. - /// The filter to execute with the grouping - /// The query to execute with the grouping - /// The group offset - /// The number of groups to return from the specified group offset - /// the grouped result as a instance - /// If any I/O related errors occur - // LUCENENET additional method signature. Makes searching by field easier due to concrete return type - public virtual ITopGroups SearchByField(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit) + public static DocBlockGroupingSearch ByDocBlock(Filter groupEndDocs) { - if (groupField is null) - { - throw IllegalStateException.Create("Must use constructor to set pass a non null value for groupField."); - } - - if (groupFunction != null) - { - throw IllegalStateException.Create("The groupFunction must be null."); - } - return GroupByField(searcher, filter, query, groupOffset, groupLimit); + return new DocBlockGroupingSearch(groupEndDocs); } + } - /// - /// Executes a grouped search base on the function specified by a passed via the constructor. - /// Both the first pass and second pass are executed on the specified searcher. - /// - /// The instance to execute the grouped search on. - /// The query to execute with the grouping - /// The group offset - /// The number of groups to return from the specified group offset - /// the grouped result as a instance - /// If any I/O related errors occur - // LUCENENET additional method signature. Makes searching by function easier due to ability to specify type of MutableValue returned. - public virtual ITopGroups SearchByFunction(IndexSearcher searcher, Query query, int groupOffset, int groupLimit) - where TMutableValue : MutableValue - { - return GroupByFunction(searcher, null, query, groupOffset, groupLimit); - } + public class FieldGroupingSearch : AbstractGroupingSearch + { + private readonly string groupField; - /// - /// Executes a grouped search base on the function specified by a passed via the constructor. - /// Both the first pass and second pass are executed on the specified searcher. - /// - /// The instance to execute the grouped search on. - /// The filter to execute with the grouping - /// The query to execute with the grouping - /// The group offset - /// The number of groups to return from the specified group offset - /// the grouped result as a instance - /// If any I/O related errors occur - // LUCENENET additional method signature. Makes searching by function easier due to ability to specify type of MutableValue returned. - public virtual ITopGroups SearchByFunction(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit) - where TMutableValue : MutableValue + public FieldGroupingSearch(string groupField) { - if (groupFunction is null) - { - throw IllegalStateException.Create("Either groupField, groupFunction or groupEndDocs must be set."); // This can't happen... - } - - if (groupField != null) - { - throw new Exception("The valueSource must be null."); - } - return GroupByFunction(searcher, filter, query, groupOffset, groupLimit); + this.groupField = groupField; } - //LUCENENET: Method replaced by two methods GroupByField and GroupByFunction so that a generic type constraint - // can be added to the GroupByFunction case. - //protected virtual ITopGroups GroupByFieldOrFunction(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit) - //{ - //} - - - //LUCENENET Specific. One of two methods that replace GroupByFieldOrFunction. Used support SearchByField. - // This method is essentually a Field specific version of the GroupByFieldOrFunction. - protected virtual ITopGroups GroupByField(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit) + public override ITopGroups Search(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit) { int topN = groupOffset + groupLimit; - IAbstractFirstPassGroupingCollector firstPassCollector; - IAbstractAllGroupsCollector allGroupsCollector; + IAbstractFirstPassGroupingCollector firstPassCollector; + IAbstractAllGroupsCollector allGroupsCollector; AbstractAllGroupHeadsCollector allGroupHeadsCollector; if (groupField is null) @@ -304,10 +73,10 @@ protected virtual ITopGroups GroupByField(IndexSearche throw IllegalStateException.Create("groupField must be set via the constructor."); } - firstPassCollector = (IAbstractFirstPassGroupingCollector)new TermFirstPassGroupingCollector(groupField, groupSort, topN); + firstPassCollector = (IAbstractFirstPassGroupingCollector)new TermFirstPassGroupingCollector(groupField, groupSort, topN); if (allGroups) { - allGroupsCollector = (IAbstractAllGroupsCollector)new TermAllGroupsCollector(groupField, initialSize); + allGroupsCollector = (IAbstractAllGroupsCollector)new TermAllGroupsCollector(groupField, initialSize); } else { @@ -363,11 +132,11 @@ protected virtual ITopGroups GroupByField(IndexSearche if (allGroups) { - matchingGroups = (ICollection)allGroupsCollector.Groups; + matchingGroups = (ICollection)allGroupsCollector.Groups; } else { - matchingGroups = (ICollection)Collections.EmptyList(); + matchingGroups = Collections.EmptyList(); } if (allGroupHeads) { @@ -378,19 +147,19 @@ protected virtual ITopGroups GroupByField(IndexSearche matchingGroupHeads = new Bits.MatchNoBits(searcher.IndexReader.MaxDoc); } - IEnumerable> topSearchGroups = firstPassCollector.GetTopGroups(groupOffset, fillSortFields); + IEnumerable> topSearchGroups = firstPassCollector.GetTopGroups(groupOffset, fillSortFields); if (topSearchGroups is null) { // LUCENENET specific - optimized empty array creation - return new TopGroups(Array.Empty(), Array.Empty(), 0, 0, Array.Empty>(), float.NaN); + return new TopGroups(Array.Empty(), Array.Empty(), 0, 0, Array.Empty>(), float.NaN); } int topNInsideGroup = groupDocsOffset + groupDocsLimit; - IAbstractSecondPassGroupingCollector secondPassCollector; + IAbstractSecondPassGroupingCollector secondPassCollector; secondPassCollector = new TermSecondPassGroupingCollector(groupField, topSearchGroups as IEnumerable>, groupSort, sortWithinGroup, topNInsideGroup, includeScores, includeMaxScore, fillSortFields) - as IAbstractSecondPassGroupingCollector; + as IAbstractSecondPassGroupingCollector; if (cachedCollector != null && cachedCollector.IsCached) { @@ -403,23 +172,32 @@ protected virtual ITopGroups GroupByField(IndexSearche if (allGroups) { - return new TopGroups(secondPassCollector.GetTopGroups(groupDocsOffset), matchingGroups.Count); + return new TopGroups(secondPassCollector.GetTopGroups(groupDocsOffset), matchingGroups.Count); } else { return secondPassCollector.GetTopGroups(groupDocsOffset); } } + } - //LUCENENET Specific. One of two methods that replace GroupByFieldOrFunction. Used support - // SearchByFunction in a way that eliminates casting for the caller. - // This method is essentually a Function specific version of the GroupByFieldOrFunction. - protected virtual ITopGroups GroupByFunction(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit) - where TMutableValue: MutableValue + public class FunctionGroupingSearch : AbstractGroupingSearch + where T : MutableValue + { + private readonly ValueSource groupFunction; + private readonly IDictionary /* Map */ valueSourceContext; + + public FunctionGroupingSearch(ValueSource groupFunction, IDictionary /* Map */ valueSourceContext) + { + this.groupFunction = groupFunction; + this.valueSourceContext = valueSourceContext; + } + + public override ITopGroups Search(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit) { int topN = groupOffset + groupLimit; - FunctionFirstPassGroupingCollector firstPassCollector; - FunctionAllGroupsCollector allGroupsCollector; + FunctionFirstPassGroupingCollector firstPassCollector; + FunctionAllGroupsCollector allGroupsCollector; AbstractAllGroupHeadsCollector allGroupHeadsCollector; if (groupFunction is null) @@ -427,10 +205,10 @@ protected virtual ITopGroups GroupByFunction(Index throw IllegalStateException.Create("groupFunction must be set via the constructor by specifying a ValueSource."); } - firstPassCollector = new FunctionFirstPassGroupingCollector(groupFunction, valueSourceContext, groupSort, topN); + firstPassCollector = new FunctionFirstPassGroupingCollector(groupFunction, valueSourceContext, groupSort, topN); if (allGroups) { - allGroupsCollector = new FunctionAllGroupsCollector(groupFunction, valueSourceContext); + allGroupsCollector = new FunctionAllGroupsCollector(groupFunction, valueSourceContext); } else { @@ -487,11 +265,11 @@ protected virtual ITopGroups GroupByFunction(Index if (allGroups) { - matchingGroups = (ICollection)allGroupsCollector.Groups; + matchingGroups = (ICollection)allGroupsCollector.Groups; } else { - matchingGroups = (ICollection)Collections.EmptyList(); + matchingGroups = Collections.EmptyList(); } if (allGroupHeads) { @@ -502,19 +280,19 @@ protected virtual ITopGroups GroupByFunction(Index matchingGroupHeads = new Bits.MatchNoBits(searcher.IndexReader.MaxDoc); } - IEnumerable> topSearchGroups = firstPassCollector.GetTopGroups(groupOffset, fillSortFields); + IEnumerable> topSearchGroups = firstPassCollector.GetTopGroups(groupOffset, fillSortFields); if (topSearchGroups is null) { // LUCENENET specific - optimized empty array creation - return new TopGroups(Array.Empty(), Array.Empty(), 0, 0, Array.Empty>(), float.NaN); + return new TopGroups(Array.Empty(), Array.Empty(), 0, 0, Array.Empty>(), float.NaN); } int topNInsideGroup = groupDocsOffset + groupDocsLimit; - IAbstractSecondPassGroupingCollector secondPassCollector; + IAbstractSecondPassGroupingCollector secondPassCollector; - secondPassCollector = new FunctionSecondPassGroupingCollector(topSearchGroups as IEnumerable>, + secondPassCollector = new FunctionSecondPassGroupingCollector(topSearchGroups as IEnumerable>, groupSort, sortWithinGroup, topNInsideGroup, includeScores, includeMaxScore, fillSortFields, groupFunction, valueSourceContext) - as IAbstractSecondPassGroupingCollector; + as IAbstractSecondPassGroupingCollector; if (cachedCollector != null && cachedCollector.IsCached) @@ -528,23 +306,62 @@ protected virtual ITopGroups GroupByFunction(Index if (allGroups) { - return new TopGroups(secondPassCollector.GetTopGroups(groupDocsOffset), matchingGroups.Count); + return new TopGroups(secondPassCollector.GetTopGroups(groupDocsOffset), matchingGroups.Count); } else { return secondPassCollector.GetTopGroups(groupDocsOffset); } } + } + + public class DocBlockGroupingSearch : AbstractGroupingSearch + { + private readonly Filter groupEndDocs; - protected virtual ITopGroups GroupByDocBlock(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit) + public DocBlockGroupingSearch(Filter groupEndDocs) + { + this.groupEndDocs = groupEndDocs; + } + + public override ITopGroups Search(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit) { int topN = groupOffset + groupLimit; BlockGroupingCollector c = new BlockGroupingCollector(groupSort, topN, includeScores, groupEndDocs); searcher.Search(query, filter, c); int topNInsideGroup = groupDocsOffset + groupDocsLimit; - return c.GetTopGroups(sortWithinGroup, groupOffset, groupDocsOffset, topNInsideGroup, fillSortFields); + return c.GetTopGroups(sortWithinGroup, groupOffset, groupDocsOffset, topNInsideGroup, fillSortFields); + } + } + + public abstract class AbstractGroupingSearch + { + protected Sort groupSort = Sort.RELEVANCE; + protected Sort sortWithinGroup; + + protected int groupDocsOffset; + protected int groupDocsLimit = 1; + protected bool fillSortFields; + protected bool includeScores = true; + protected bool includeMaxScore = true; + + protected double? maxCacheRAMMB; + protected int? maxDocsToCache; + protected bool cacheScores; + protected bool allGroups; + protected bool allGroupHeads; + protected int initialSize = 128; + + protected ICollection matchingGroups; + protected IBits matchingGroupHeads; + + public ITopGroups Search(IndexSearcher searcher, Query query, int groupOffset, int groupLimit) + { + return Search(searcher, null, query, groupOffset, groupLimit); } + public abstract ITopGroups Search(IndexSearcher searcher, Filter filter, Query query, int groupOffset, int groupLimit); + /// /// Enables caching for the second pass search. The cache will not grow over a specified limit in MB. /// The cache is filled during the first pass searched and then replayed during the second pass searched. @@ -553,7 +370,7 @@ protected virtual ITopGroups GroupByDocBlock(IndexSear /// The maximum amount in MB the cache is allowed to hold /// Whether to cache the scores /// this - public virtual GroupingSearch SetCachingInMB(double maxCacheRAMMB, bool cacheScores) + public virtual AbstractGroupingSearch SetCachingInMB(double maxCacheRAMMB, bool cacheScores) { this.maxCacheRAMMB = maxCacheRAMMB; this.maxDocsToCache = null; @@ -569,7 +386,7 @@ public virtual GroupingSearch SetCachingInMB(double maxCacheRAMMB, bool cacheSco /// The maximum number of documents the cache is allowed to hold /// Whether to cache the scores /// this - public virtual GroupingSearch SetCaching(int maxDocsToCache, bool cacheScores) + public virtual AbstractGroupingSearch SetCaching(int maxDocsToCache, bool cacheScores) { this.maxDocsToCache = maxDocsToCache; this.maxCacheRAMMB = null; @@ -581,7 +398,7 @@ public virtual GroupingSearch SetCaching(int maxDocsToCache, bool cacheScores) /// Disables any enabled cache. /// /// this - public virtual GroupingSearch DisableCaching() + public virtual AbstractGroupingSearch DisableCaching() { this.maxCacheRAMMB = null; this.maxDocsToCache = null; @@ -594,7 +411,7 @@ public virtual GroupingSearch DisableCaching() /// /// The sort for the groups. /// this - public virtual GroupingSearch SetGroupSort(Sort groupSort) + public virtual AbstractGroupingSearch SetGroupSort(Sort groupSort) { this.groupSort = groupSort; return this; @@ -606,7 +423,7 @@ public virtual GroupingSearch SetGroupSort(Sort groupSort) /// /// The sort for documents inside a group /// this - public virtual GroupingSearch SetSortWithinGroup(Sort sortWithinGroup) + public virtual AbstractGroupingSearch SetSortWithinGroup(Sort sortWithinGroup) { this.sortWithinGroup = sortWithinGroup; return this; @@ -617,7 +434,7 @@ public virtual GroupingSearch SetSortWithinGroup(Sort sortWithinGroup) /// /// The offset for documents inside a /// this - public virtual GroupingSearch SetGroupDocsOffset(int groupDocsOffset) + public virtual AbstractGroupingSearch SetGroupDocsOffset(int groupDocsOffset) { this.groupDocsOffset = groupDocsOffset; return this; @@ -628,7 +445,7 @@ public virtual GroupingSearch SetGroupDocsOffset(int groupDocsOffset) /// /// The number of documents to return inside a group /// this - public virtual GroupingSearch SetGroupDocsLimit(int groupDocsLimit) + public virtual AbstractGroupingSearch SetGroupDocsLimit(int groupDocsLimit) { this.groupDocsLimit = groupDocsLimit; return this; @@ -639,7 +456,7 @@ public virtual GroupingSearch SetGroupDocsLimit(int groupDocsLimit) /// /// Whether to also fill the sort fields per returned group and groups docs /// this - public virtual GroupingSearch SetFillSortFields(bool fillSortFields) + public virtual AbstractGroupingSearch SetFillSortFields(bool fillSortFields) { this.fillSortFields = fillSortFields; return this; @@ -650,7 +467,7 @@ public virtual GroupingSearch SetFillSortFields(bool fillSortFields) /// /// Whether to include the scores per doc inside a group /// this - public virtual GroupingSearch SetIncludeScores(bool includeScores) + public virtual AbstractGroupingSearch SetIncludeScores(bool includeScores) { this.includeScores = includeScores; return this; @@ -661,7 +478,7 @@ public virtual GroupingSearch SetIncludeScores(bool includeScores) /// /// Whether to include the score of the most relevant document per group /// this - public virtual GroupingSearch SetIncludeMaxScore(bool includeMaxScore) + public virtual AbstractGroupingSearch SetIncludeMaxScore(bool includeMaxScore) { this.includeMaxScore = includeMaxScore; return this; @@ -677,35 +494,18 @@ public virtual GroupingSearch SetIncludeMaxScore(bool includeMaxScore) /// /// to also compute all groups matching the query /// this - public virtual GroupingSearch SetAllGroups(bool allGroups) + public virtual AbstractGroupingSearch SetAllGroups(bool allGroups) { this.allGroups = allGroups; return this; } - /// - /// If was set to true then all matching groups are returned, otherwise - /// an empty collection is returned. - /// - /// The group value type. This can be a or a instance. - /// If grouping by doc block this the group value is always null. - /// all matching groups are returned, or an empty collection - public virtual ICollection GetAllMatchingGroups() - { - return (ICollection)matchingGroups; - } - /// /// If was set to true then all matching groups are returned, otherwise /// an empty collection is returned. /// /// all matching groups are returned, or an empty collection - /// - /// LUCENENET specific used to get the groups if the type is unknown or if the code expects - /// any type, since - /// will throw an exception if the return type is incorrect. - /// - public virtual ICollection GetAllMatchingGroups() + public virtual ICollection GetAllMatchingGroups() { return matchingGroups; } @@ -718,7 +518,7 @@ public virtual ICollection GetAllMatchingGroups() /// /// Whether to compute all group heads (most relevant document per group) matching the query /// this - public virtual GroupingSearch SetAllGroupHeads(bool allGroupHeads) + public virtual AbstractGroupingSearch SetAllGroupHeads(bool allGroupHeads) { this.allGroupHeads = allGroupHeads; return this; @@ -744,7 +544,7 @@ public virtual IBits GetAllGroupHeads() /// /// The initial size of some internal used data structures /// this - public virtual GroupingSearch SetInitialSize(int initialSize) + public virtual AbstractGroupingSearch SetInitialSize(int initialSize) { this.initialSize = initialSize; return this; diff --git a/src/Lucene.Net.Tests.Grouping/GroupingSearchTest.cs b/src/Lucene.Net.Tests.Grouping/GroupingSearchTest.cs index d5b09c59a8..d05ec6f192 100644 --- a/src/Lucene.Net.Tests.Grouping/GroupingSearchTest.cs +++ b/src/Lucene.Net.Tests.Grouping/GroupingSearchTest.cs @@ -112,9 +112,9 @@ public virtual void TestBasic() w.Dispose(); Sort groupSort = Sort.RELEVANCE; - GroupingSearch groupingSearch = CreateRandomGroupingSearch(groupField, groupSort, 5, canUseIDV); + SearchDelegate groupingSearch = CreateRandomGroupingSearch(groupField, groupSort, 5, canUseIDV); - ITopGroups groups = groupingSearch.Search(indexSearcher, (Filter)null, new TermQuery(new Index.Term("content", "random")), 0, 10); + ITopGroups groups = groupingSearch(indexSearcher, (Filter)null, new TermQuery(new Index.Term("content", "random")), 0, 10); assertEquals(7, groups.TotalHitCount); assertEquals(7, groups.TotalGroupedHitCount); @@ -151,8 +151,8 @@ public virtual void TestBasic() assertEquals(6, group.ScoreDocs[0].Doc); Filter lastDocInBlock = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Index.Term("groupend", "x")))); - groupingSearch = new GroupingSearch(lastDocInBlock); - groups = groupingSearch.Search(indexSearcher, null, new TermQuery(new Index.Term("content", "random")), 0, 10); + groupingSearch = GroupingSearch.ByDocBlock(lastDocInBlock).Search; + groups = groupingSearch(indexSearcher, null, new TermQuery(new Index.Term("content", "random")), 0, 10); assertEquals(7, groups.TotalHitCount); assertEquals(7, groups.TotalGroupedHitCount); @@ -207,28 +207,37 @@ private void CompareGroupValue(string expected, IGroupDocs group) } } - private GroupingSearch CreateRandomGroupingSearch(string groupField, Sort groupSort, int docsInGroup, bool canUseIDV) + private delegate ITopGroups SearchDelegate(IndexSearcher indexSearcher, Filter filter, Query query, int groupOffset, int groupLimit); + + private SearchDelegate CreateRandomGroupingSearch(string groupField, Sort groupSort, int docsInGroup, bool canUseIDV) { - GroupingSearch groupingSearch; if (Random.nextBoolean()) { ValueSource vs = new BytesRefFieldSource(groupField); - groupingSearch = new GroupingSearch(vs, new Hashtable()); + var groupingSearch = GroupingSearch.ByFunction(vs, new Hashtable()); + groupingSearch.SetGroupSort(groupSort); + groupingSearch.SetGroupDocsLimit(docsInGroup); + + if (Random.nextBoolean()) + { + groupingSearch.SetCachingInMB(4.0, true); + } + + return groupingSearch.Search; } else { - groupingSearch = new GroupingSearch(groupField); - } + var groupingSearch = GroupingSearch.ByField(groupField); + groupingSearch.SetGroupSort(groupSort); + groupingSearch.SetGroupDocsLimit(docsInGroup); - groupingSearch.SetGroupSort(groupSort); - groupingSearch.SetGroupDocsLimit(docsInGroup); + if (Random.nextBoolean()) + { + groupingSearch.SetCachingInMB(4.0, true); + } - if (Random.nextBoolean()) - { - groupingSearch.SetCachingInMB(4.0, true); + return groupingSearch.Search; } - - return groupingSearch; } [Test] @@ -247,7 +256,7 @@ public virtual void TestSetAllGroups() IndexSearcher indexSearcher = NewSearcher(w.GetReader()); w.Dispose(); - GroupingSearch gs = new GroupingSearch("group"); + var gs = GroupingSearch.ByField("group"); gs.SetAllGroups(true); ITopGroups groups = gs.Search(indexSearcher, null, new TermQuery(new Index.Term("group", "foo")), 0, 10); assertEquals(1, groups.TotalHitCount); diff --git a/src/Lucene.Net.Tests.Grouping/TestGroupingExtra.cs b/src/Lucene.Net.Tests.Grouping/TestGroupingExtra.cs index 6bd0b9c802..c1126016f6 100644 --- a/src/Lucene.Net.Tests.Grouping/TestGroupingExtra.cs +++ b/src/Lucene.Net.Tests.Grouping/TestGroupingExtra.cs @@ -80,7 +80,7 @@ public void GroupingSearchByField_StringSorted_UsingDocValues_Top3Groups_Top4Doc } writer.Commit(); - GroupingSearch groupingSearch = new GroupingSearch("carMake"); + var groupingSearch = GroupingSearch.ByField("carMake"); groupingSearch.SetAllGroups(true); //true = compute all groups matching the query groupingSearch.SetGroupDocsLimit(4); //max docs returned in a group groupingSearch.SetGroupSort(new Sort(new SortField("carMake_dv", SortFieldType.STRING))); @@ -91,7 +91,7 @@ public void GroupingSearchByField_StringSorted_UsingDocValues_Top3Groups_Top4Doc IndexReader reader = writer.GetReader(applyAllDeletes: true); IndexSearcher searcher = new IndexSearcher(reader); Query matchAllQuery = new MatchAllDocsQuery(); - ITopGroups topGroups = groupingSearch.SearchByField(searcher, matchAllQuery, groupOffset: 0, groupLimit: 3); + ITopGroups topGroups = groupingSearch.Search(searcher, matchAllQuery, groupOffset: 0, groupLimit: 3); int? totalGroupCount = topGroups.TotalGroupCount; //null if not computed @@ -122,7 +122,7 @@ public void GroupingSearchByField_StringSorted_UsingDocValues_Top3Groups_Top4Doc assertEquals(expectdValue, output); /* Output: - + Group: Audi Audi A3 Orange Audi A3 Green @@ -165,7 +165,7 @@ public virtual void GroupingSearchByFunction_Int32Sorted_UsingFieldCache_Top10Gr //as a 8 term trie structure by default. But by specifying int.MaxValue as the NumericPrecisionStep //we force the inverted index to store the value as a single term. This allows us to use it for //grouping via FieldCache (although it's no longer good for range queries as they will be slow if - //the range is large). + //the range is large). var int32OneTerm = new FieldType { @@ -192,7 +192,7 @@ public virtual void GroupingSearchByFunction_Int32Sorted_UsingFieldCache_Top10Gr writer.Commit(); ValueSource vs = new BytesRefFieldSource("major"); - GroupingSearch groupingSearch = new GroupingSearch(vs, new Hashtable()); + var groupingSearch = GroupingSearch.ByFunction(vs, new Hashtable()); groupingSearch.SetAllGroups(true); //true = compute all groups matching the query groupingSearch.SetGroupDocsLimit(10); //max docs returned in a group groupingSearch.SetGroupSort(new Sort(new SortField("major", SortFieldType.INT32))); @@ -201,7 +201,7 @@ public virtual void GroupingSearchByFunction_Int32Sorted_UsingFieldCache_Top10Gr IndexReader reader = writer.GetReader(applyAllDeletes: true); IndexSearcher searcher = new IndexSearcher(reader); Query matchAllQuery = new MatchAllDocsQuery(); - ITopGroups topGroups = groupingSearch.SearchByFunction(searcher, matchAllQuery, groupOffset: 0, groupLimit: 10); + ITopGroups topGroups = groupingSearch.Search(searcher, matchAllQuery, groupOffset: 0, groupLimit: 10); StringBuilder sb = new StringBuilder(); foreach (GroupDocs groupDocs in topGroups.Groups) @@ -230,7 +230,7 @@ public virtual void GroupingSearchByFunction_Int32Sorted_UsingFieldCache_Top10Gr assertEquals(expectdValue, output); /* Output: - * + * Group: 1000 1000 1102 21 1000 1123 45 @@ -284,7 +284,7 @@ public void GroupingSearch_ViaField_StringSorted_UsingFieldCache_Top3Groups_Top4 } writer.Commit(); - GroupingSearch groupingSearch = new GroupingSearch("carMake"); + var groupingSearch = GroupingSearch.ByField("carMake"); groupingSearch.SetAllGroups(true); //true = compute all groups matching the query groupingSearch.SetGroupDocsLimit(4); //max docs returned in a group groupingSearch.SetGroupSort(new Sort(new SortField("carMake", SortFieldType.STRING))); @@ -326,7 +326,7 @@ public void GroupingSearch_ViaField_StringSorted_UsingFieldCache_Top3Groups_Top4 assertEquals(expectdValue, output); /* Output: - + Group: Audi Audi A3 Orange Audi A3 Green @@ -379,7 +379,7 @@ public void GroupingSearch_ViaField_StringSorted_UsingDocValues_Top3Groups_Top4D } writer.Commit(); - GroupingSearch groupingSearch = new GroupingSearch("carMake"); + var groupingSearch = GroupingSearch.ByField("carMake"); groupingSearch.SetAllGroups(true); //true = compute all groups matching the query groupingSearch.SetGroupDocsLimit(4); //max docs returned in a group groupingSearch.SetGroupSort(new Sort(new SortField("carMake_dv", SortFieldType.STRING))); @@ -421,7 +421,7 @@ public void GroupingSearch_ViaField_StringSorted_UsingDocValues_Top3Groups_Top4D assertEquals(expectdValue, output); /* Output: - + Group: Audi Audi A3 Orange Audi A3 Green @@ -464,7 +464,7 @@ public virtual void GroupingSearch_ViaField_Int32Sorted_UsingFieldCache_Top10Gro //as a 8 term trie structure by default. But by specifying int.MaxValue as the NumericPrecisionStep //we force the inverted index to store the value as a single term. This allows us to use it for //grouping via FieldCache (although it's no longer good for range queries as they will be slow if - //the range is large). + //the range is large). var int32OneTerm = new FieldType { @@ -490,7 +490,7 @@ public virtual void GroupingSearch_ViaField_Int32Sorted_UsingFieldCache_Top10Gro } writer.Commit(); - GroupingSearch groupingSearch = new GroupingSearch("major"); + var groupingSearch = GroupingSearch.ByField("major"); groupingSearch.SetAllGroups(true); //true = compute all groups matching the query groupingSearch.SetGroupDocsLimit(10); //max docs returned in a group groupingSearch.SetGroupSort(new Sort(new SortField("major", SortFieldType.INT32))); @@ -499,7 +499,7 @@ public virtual void GroupingSearch_ViaField_Int32Sorted_UsingFieldCache_Top10Gro IndexReader reader = writer.GetReader(applyAllDeletes: true); IndexSearcher searcher = new IndexSearcher(reader); Query matchAllQuery = new MatchAllDocsQuery(); - ITopGroups topGroups = groupingSearch.Search(searcher, matchAllQuery, groupOffset: 0, groupLimit: 10); + ITopGroups topGroups = groupingSearch.Search(searcher, matchAllQuery, groupOffset: 0, groupLimit: 10); StringBuilder sb = new StringBuilder(); foreach (GroupDocs groupDocs in topGroups.Groups) @@ -574,7 +574,7 @@ public virtual void GroupingSearch_ViaFunction_Int32Sorted_UsingFieldCache_Top10 //as a 8 term trie structure by default. But by specifying int.MaxValue as the NumericPrecisionStep //we force the inverted index to store the value as a single term. This allows us to use it for //grouping via FieldCache (although it's no longer good for range queries as they will be slow if - //the range is large). + //the range is large). var int32OneTerm = new FieldType { @@ -601,7 +601,7 @@ public virtual void GroupingSearch_ViaFunction_Int32Sorted_UsingFieldCache_Top10 writer.Commit(); ValueSource vs = new BytesRefFieldSource("major"); - GroupingSearch groupingSearch = new GroupingSearch(vs, new Hashtable()); + var groupingSearch = GroupingSearch.ByFunction(vs, new Hashtable()); groupingSearch.SetAllGroups(true); //true = compute all groups matching the query groupingSearch.SetGroupDocsLimit(10); //max docs returned in a group groupingSearch.SetGroupSort(new Sort(new SortField("major", SortFieldType.INT32))); @@ -610,7 +610,7 @@ public virtual void GroupingSearch_ViaFunction_Int32Sorted_UsingFieldCache_Top10 IndexReader reader = writer.GetReader(applyAllDeletes: true); IndexSearcher searcher = new IndexSearcher(reader); Query matchAllQuery = new MatchAllDocsQuery(); - ITopGroups topGroups = groupingSearch.Search(searcher, matchAllQuery, groupOffset: 0, groupLimit: 10); + ITopGroups topGroups = groupingSearch.Search(searcher, matchAllQuery, groupOffset: 0, groupLimit: 10); StringBuilder sb = new StringBuilder(); foreach (GroupDocs groupDocs in topGroups.Groups) @@ -639,7 +639,7 @@ public virtual void GroupingSearch_ViaFunction_Int32Sorted_UsingFieldCache_Top10 assertEquals(expectdValue, output); /* Output: - * + * Group: 1000 1000 1102 21 1000 1123 45 @@ -696,7 +696,7 @@ public virtual void GroupingSearchByFunction_Int32Sorted_UsingDocValues_Top10Gro writer.Commit(); ValueSource vs = new Int32FieldSource("major"); - GroupingSearch groupingSearch = new GroupingSearch(vs, new Hashtable()); + var groupingSearch = GroupingSearch.ByFunction(vs, new Hashtable()); groupingSearch.SetAllGroups(true); //true = compute all groups matching the query groupingSearch.SetGroupDocsLimit(10); //max docs returned in a group groupingSearch.SetGroupSort(new Sort(new SortField("major", SortFieldType.INT32))); @@ -706,7 +706,7 @@ public virtual void GroupingSearchByFunction_Int32Sorted_UsingDocValues_Top10Gro IndexSearcher searcher = new IndexSearcher(reader); Query matchAllQuery = new MatchAllDocsQuery(); - ITopGroups topGroups = groupingSearch.SearchByFunction(searcher, matchAllQuery, groupOffset: 0, groupLimit: 10); + ITopGroups topGroups = groupingSearch.Search(searcher, matchAllQuery, groupOffset: 0, groupLimit: 10); StringBuilder sb = new StringBuilder(); foreach (GroupDocs groupDocs in topGroups.Groups) @@ -734,7 +734,7 @@ public virtual void GroupingSearchByFunction_Int32Sorted_UsingDocValues_Top10Gro assertEquals(expectdValue, output); /* Output: - * + * Group: 1000 1000 1102 21 1000 1123 45 @@ -790,7 +790,7 @@ public virtual void GroupingSearch_ViaFunction_Int32Sorted_UsingDocValues_Top4Gr writer.Commit(); ValueSource vs = new Int32FieldSource("major"); - GroupingSearch groupingSearch = new GroupingSearch(vs, new Hashtable()); + var groupingSearch = GroupingSearch.ByFunction(vs, new Hashtable()); groupingSearch.SetAllGroups(true); //true = compute all groups matching the query groupingSearch.SetGroupDocsLimit(2); //max docs returned in a group groupingSearch.SetGroupSort(new Sort(new SortField("major", SortFieldType.INT32))); @@ -800,7 +800,7 @@ public virtual void GroupingSearch_ViaFunction_Int32Sorted_UsingDocValues_Top4Gr IndexSearcher searcher = new IndexSearcher(reader); Query matchAllQuery = new MatchAllDocsQuery(); - ITopGroups topGroups = groupingSearch.Search(searcher, matchAllQuery, groupOffset: 0, groupLimit: 4); + ITopGroups topGroups = groupingSearch.Search(searcher, matchAllQuery, groupOffset: 0, groupLimit: 4); StringBuilder sb = new StringBuilder(); foreach (GroupDocs groupDocs in topGroups.Groups) @@ -828,7 +828,7 @@ public virtual void GroupingSearch_ViaFunction_Int32Sorted_UsingDocValues_Top4Gr assertEquals(expectdValue, output); /* Output: - * + * Group: 1000 1000 1102 21 1000 1123 45 @@ -852,7 +852,7 @@ 4000 4011 10 /// LUCENENET: Additional Unit Test. Low Level test that does not use GroupingSearch /// Class. Tests grouping by an StringField via the /// 2 pass by field name approach. Uses DocValues, not FieldCache. - /// Demonstrates grouping returning ITopGroups + /// Demonstrates grouping returning ITopGroups /// [Test] [LuceneNetSpecific] @@ -939,7 +939,7 @@ public virtual void Group_LowLevelNoCaching_ViaField_StringStored_UsingDocValues assertEquals(expectdValue, output); /* Output: - + Group: Audi Audi A3 Orange Audi A3 Green @@ -982,7 +982,7 @@ public virtual void Group_LowLevelNoCaching_ViaField_Int32Sorted_UsingFieldCache //Normally we can not group on a Int32Field because it's stored as a 8 term trie structure //by default. But by specifying int.MaxValue as the NumericPrecisionStep we force the inverted //index to store the value as a single term. This allows us to use it for grouping (although - //it's no longer good for range queries as they will be slow if the range is large). + //it's no longer good for range queries as they will be slow if the range is large). var int32OneTerm = new FieldType { @@ -1065,7 +1065,7 @@ public virtual void Group_LowLevelNoCaching_ViaField_Int32Sorted_UsingFieldCache assertEquals(expectdValue, output); /* Output: - * + * Group: 1000 1000 1102 21 1000 1123 45 @@ -1105,7 +1105,7 @@ public virtual void Group_LowLevelNoCaching_ViaFunction_Int32Sorted_UsingFieldCa //Normally we can not group on a Int32Field because it's stored as a 8 term trie structure //by default. But by specifying int.MaxValue as the NumericPrecisionStep we force the inverted //index to store the value as a single term. This allows us to use it for grouping (although - //it's no longer good for range queries as they will be slow if the range is large). + //it's no longer good for range queries as they will be slow if the range is large). var int32OneTerm = new FieldType { @@ -1192,7 +1192,7 @@ public virtual void Group_LowLevelNoCaching_ViaFunction_Int32Sorted_UsingFieldCa assertEquals(expectdValue, output); /* Output: - * + * Group: 1000 1000 1102 21 1000 1123 45 @@ -1216,7 +1216,7 @@ 4000 4011 10 /// LUCENENET: Additional Unit Test. Low Level test that does not use GroupingSearch /// Class. Tests grouping by an Int32 via the /// 2 pass by fuction/valueSource approach. Uses DocValues, not FieldCache. - /// Demonstrates grouping returning ITopGroups + /// Demonstrates grouping returning ITopGroups /// [Test] [LuceneNetSpecific] @@ -1305,7 +1305,7 @@ public virtual void Group_LowLevelNoCaching_ViaFunction_Int32Sorted_UsingDocValu assertEquals(expectdValue, output); /* Output: - * + * Group: 1000 1000 1102 21 1000 1123 45