You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by ni...@apache.org on 2016/11/10 11:33:40 UTC
[29/58] [abbrv] lucenenet git commit: WIP on Grouping
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/9d72bcb3/src/Lucene.Net.Tests.Grouping/GroupFacetCollectorTest.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests.Grouping/GroupFacetCollectorTest.cs b/src/Lucene.Net.Tests.Grouping/GroupFacetCollectorTest.cs
new file mode 100644
index 0000000..8a01ee0
--- /dev/null
+++ b/src/Lucene.Net.Tests.Grouping/GroupFacetCollectorTest.cs
@@ -0,0 +1,943 @@
+\ufeffusing Lucene.Net.Analysis;
+using Lucene.Net.Documents;
+using Lucene.Net.Search.Grouping.Terms;
+using Lucene.Net.Index;
+using Lucene.Net.Store;
+using Lucene.Net.Support;
+using Lucene.Net.Util;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+
+namespace Lucene.Net.Search.Grouping
+{
+ public class GroupFacetCollectorTest : AbstractGroupingTestCase
+ {
+ [Test]
+ public void TestSimple()
+ {
+ string groupField = "hotel";
+ FieldType customType = new FieldType();
+ customType.Stored = true;
+
+ Directory dir = NewDirectory();
+ RandomIndexWriter w = new RandomIndexWriter(
+ Random(),
+ dir,
+ NewIndexWriterConfig(TEST_VERSION_CURRENT,
+ new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy()));
+ bool canUseDV = !"Lucene3x".equals(w.w.Config.Codec.Name);
+ bool useDv = canUseDV && Random().nextBoolean();
+
+ // 0
+ Document doc = new Document();
+ AddField(doc, groupField, "a", useDv);
+ AddField(doc, "airport", "ams", useDv);
+ AddField(doc, "duration", "5", useDv);
+ w.AddDocument(doc);
+
+ // 1
+ doc = new Document();
+ AddField(doc, groupField, "a", useDv);
+ AddField(doc, "airport", "dus", useDv);
+ AddField(doc, "duration", "10", useDv);
+ w.AddDocument(doc);
+
+ // 2
+ doc = new Document();
+ AddField(doc, groupField, "b", useDv);
+ AddField(doc, "airport", "ams", useDv);
+ AddField(doc, "duration", "10", useDv);
+ w.AddDocument(doc);
+ w.Commit(); // To ensure a second segment
+
+ // 3
+ doc = new Document();
+ AddField(doc, groupField, "b", useDv);
+ AddField(doc, "airport", "ams", useDv);
+ AddField(doc, "duration", "5", useDv);
+ w.AddDocument(doc);
+
+ // 4
+ doc = new Document();
+ AddField(doc, groupField, "b", useDv);
+ AddField(doc, "airport", "ams", useDv);
+ AddField(doc, "duration", "5", useDv);
+ w.AddDocument(doc);
+
+ IndexSearcher indexSearcher = NewSearcher(w.Reader);
+
+ List<TermGroupFacetCollector.FacetEntry> entries = null;
+ AbstractGroupFacetCollector groupedAirportFacetCollector = null;
+ TermGroupFacetCollector.GroupedFacetResult airportResult = null;
+
+ foreach (int limit in new int[] { 2, 10, 100, int.MaxValue
+ })
+ {
+ // any of these limits is plenty for the data we have
+
+ groupedAirportFacetCollector = CreateRandomCollector
+ (useDv ? "hotel_dv" : "hotel",
+ useDv ? "airport_dv" : "airport", null, false);
+ indexSearcher.Search(new MatchAllDocsQuery(), groupedAirportFacetCollector);
+ int maxOffset = 5;
+ airportResult = groupedAirportFacetCollector.MergeSegmentResults
+ (int.MaxValue == limit ? limit : maxOffset + limit, 0, false);
+
+
+ assertEquals(3, airportResult.TotalCount);
+ assertEquals(0, airportResult.TotalMissingCount);
+
+ entries = airportResult.GetFacetEntries(maxOffset, limit);
+ assertEquals(0, entries.size());
+
+ entries = airportResult.GetFacetEntries(0, limit);
+ assertEquals(2, entries.size());
+ assertEquals("ams", entries[0].Value.Utf8ToString());
+ assertEquals(2, entries[0].Count);
+ assertEquals("dus", entries[1].Value.Utf8ToString());
+ assertEquals(1, entries[1].Count);
+
+ entries = airportResult.GetFacetEntries(1, limit);
+ assertEquals(1, entries.size());
+ assertEquals("dus", entries[0].Value.Utf8ToString());
+ assertEquals(1, entries[0].Count);
+ }
+
+ AbstractGroupFacetCollector groupedDurationFacetCollector = CreateRandomCollector(useDv ? "hotel_dv" : "hotel", useDv ? "duration_dv" : "duration", null, false);
+ indexSearcher.Search(new MatchAllDocsQuery(), groupedDurationFacetCollector);
+ TermGroupFacetCollector.GroupedFacetResult durationResult = groupedDurationFacetCollector.MergeSegmentResults(10, 0, false);
+ assertEquals(4, durationResult.TotalCount);
+ assertEquals(0, durationResult.TotalMissingCount);
+
+ entries = durationResult.GetFacetEntries(0, 10);
+ assertEquals(2, entries.size());
+ assertEquals("10", entries[0].Value.Utf8ToString());
+ assertEquals(2, entries[0].Count);
+ assertEquals("5", entries[1].Value.Utf8ToString());
+ assertEquals(2, entries[1].Count);
+
+ // 5
+ doc = new Document();
+ AddField(doc, groupField, "b", useDv);
+ // missing airport
+ if (useDv)
+ {
+ AddField(doc, "airport", "", useDv);
+ }
+ AddField(doc, "duration", "5", useDv);
+ w.AddDocument(doc);
+
+ // 6
+ doc = new Document();
+ AddField(doc, groupField, "b", useDv);
+ AddField(doc, "airport", "bru", useDv);
+ AddField(doc, "duration", "10", useDv);
+ w.AddDocument(doc);
+
+ // 7
+ doc = new Document();
+ AddField(doc, groupField, "b", useDv);
+ AddField(doc, "airport", "bru", useDv);
+ AddField(doc, "duration", "15", useDv);
+ w.AddDocument(doc);
+
+ // 8
+ doc = new Document();
+ AddField(doc, groupField, "a", useDv);
+ AddField(doc, "airport", "bru", useDv);
+ AddField(doc, "duration", "10", useDv);
+ w.AddDocument(doc);
+
+ indexSearcher.IndexReader.Dispose();
+ indexSearcher = NewSearcher(w.Reader);
+ groupedAirportFacetCollector = CreateRandomCollector(useDv ? "hotel_dv" : "hotel", useDv ? "airport_dv" : "airport", null, !useDv);
+ indexSearcher.Search(new MatchAllDocsQuery(), groupedAirportFacetCollector);
+ airportResult = groupedAirportFacetCollector.MergeSegmentResults(3, 0, true);
+ entries = airportResult.GetFacetEntries(1, 2);
+ assertEquals(2, entries.size());
+ if (useDv)
+ {
+ assertEquals(6, airportResult.TotalCount);
+ assertEquals(0, airportResult.TotalMissingCount);
+ assertEquals("bru", entries[0].Value.Utf8ToString());
+ assertEquals(2, entries[0].Count);
+ assertEquals("", entries[1].Value.Utf8ToString());
+ assertEquals(1, entries[1].Count);
+ }
+ else
+ {
+ assertEquals(5, airportResult.TotalCount);
+ assertEquals(1, airportResult.TotalMissingCount);
+ assertEquals("bru", entries[0].Value.Utf8ToString());
+ assertEquals(2, entries[0].Count);
+ assertEquals("dus", entries[1].Value.Utf8ToString());
+ assertEquals(1, entries[1].Count);
+ }
+
+ groupedDurationFacetCollector = CreateRandomCollector(useDv ? "hotel_dv" : "hotel", useDv ? "duration_dv" : "duration", null, false);
+ indexSearcher.Search(new MatchAllDocsQuery(), groupedDurationFacetCollector);
+ durationResult = groupedDurationFacetCollector.MergeSegmentResults(10, 2, true);
+ assertEquals(5, durationResult.TotalCount);
+ assertEquals(0, durationResult.TotalMissingCount);
+
+ entries = durationResult.GetFacetEntries(1, 1);
+ assertEquals(1, entries.size());
+ assertEquals("5", entries[0].Value.Utf8ToString());
+ assertEquals(2, entries[0].Count);
+
+ // 9
+ doc = new Document();
+ AddField(doc, groupField, "c", useDv);
+ AddField(doc, "airport", "bru", useDv);
+ AddField(doc, "duration", "15", useDv);
+ w.AddDocument(doc);
+
+ // 10
+ doc = new Document();
+ AddField(doc, groupField, "c", useDv);
+ AddField(doc, "airport", "dus", useDv);
+ AddField(doc, "duration", "10", useDv);
+ w.AddDocument(doc);
+
+ indexSearcher.IndexReader.Dispose();
+ indexSearcher = NewSearcher(w.Reader);
+ groupedAirportFacetCollector = CreateRandomCollector(useDv ? "hotel_dv" : "hotel", useDv ? "airport_dv" : "airport", null, false);
+ indexSearcher.Search(new MatchAllDocsQuery(), groupedAirportFacetCollector);
+ airportResult = groupedAirportFacetCollector.MergeSegmentResults(10, 0, false);
+ entries = airportResult.GetFacetEntries(0, 10);
+ if (useDv)
+ {
+ assertEquals(8, airportResult.TotalCount);
+ assertEquals(0, airportResult.TotalMissingCount);
+ assertEquals(4, entries.size());
+ assertEquals("", entries[0].Value.Utf8ToString());
+ assertEquals(1, entries[0].Count);
+ assertEquals("ams", entries[1].Value.Utf8ToString());
+ assertEquals(2, entries[1].Count);
+ assertEquals("bru", entries[2].Value.Utf8ToString());
+ assertEquals(3, entries[2].Count);
+ assertEquals("dus", entries[3].Value.Utf8ToString());
+ assertEquals(2, entries[3].Count);
+ }
+ else
+ {
+ assertEquals(7, airportResult.TotalCount);
+ assertEquals(1, airportResult.TotalMissingCount);
+ assertEquals(3, entries.size());
+ assertEquals("ams", entries[0].Value.Utf8ToString());
+ assertEquals(2, entries[0].Count);
+ assertEquals("bru", entries[1].Value.Utf8ToString());
+ assertEquals(3, entries[1].Count);
+ assertEquals("dus", entries[2].Value.Utf8ToString());
+ assertEquals(2, entries[2].Count);
+ }
+
+ groupedDurationFacetCollector = CreateRandomCollector(useDv ? "hotel_dv" : "hotel", useDv ? "duration_dv" : "duration", "1", false);
+ indexSearcher.Search(new MatchAllDocsQuery(), groupedDurationFacetCollector);
+ durationResult = groupedDurationFacetCollector.MergeSegmentResults(10, 0, true);
+ assertEquals(5, durationResult.TotalCount);
+ assertEquals(0, durationResult.TotalMissingCount);
+
+ entries = durationResult.GetFacetEntries(0, 10);
+ assertEquals(2, entries.size());
+ assertEquals("10", entries[0].Value.Utf8ToString());
+ assertEquals(3, entries[0].Count);
+ assertEquals("15", entries[1].Value.Utf8ToString());
+ assertEquals(2, entries[1].Count);
+
+ w.Dispose();
+ indexSearcher.IndexReader.Dispose();
+ dir.Dispose();
+ }
+
+ [Test]
+ public void TestMVGroupedFacetingWithDeletes()
+ {
+ string groupField = "hotel";
+ FieldType customType = new FieldType();
+ customType.Stored = (true);
+
+ Directory dir = NewDirectory();
+ RandomIndexWriter w = new RandomIndexWriter(
+ Random(),
+ dir,
+ NewIndexWriterConfig(TEST_VERSION_CURRENT,
+ new MockAnalyzer(Random())).SetMergePolicy(NoMergePolicy.COMPOUND_FILES));
+ bool useDv = false;
+
+ // Cannot assert this since we use NoMergePolicy:
+ w.DoRandomForceMergeAssert = (false);
+
+ // 0
+ Document doc = new Document();
+ doc.Add(new StringField("x", "x", Field.Store.NO));
+ w.AddDocument(doc);
+
+ // 1
+ doc = new Document();
+ AddField(doc, groupField, "a", useDv);
+ doc.Add(new StringField("airport", "ams", Field.Store.NO));
+ w.AddDocument(doc);
+
+ w.Commit();
+ w.DeleteDocuments(new TermQuery(new Term("airport", "ams")));
+
+ // 2
+ doc = new Document();
+ AddField(doc, groupField, "a", useDv);
+ doc.Add(new StringField("airport", "ams", Field.Store.NO));
+ w.AddDocument(doc);
+
+ // 3
+ doc = new Document();
+ AddField(doc, groupField, "a", useDv);
+ doc.Add(new StringField("airport", "dus", Field.Store.NO));
+
+ w.AddDocument(doc);
+
+ // 4
+ doc = new Document();
+ AddField(doc, groupField, "b", useDv);
+ doc.Add(new StringField("airport", "ams", Field.Store.NO));
+ w.AddDocument(doc);
+
+ // 5
+ doc = new Document();
+ AddField(doc, groupField, "b", useDv);
+ doc.Add(new StringField("airport", "ams", Field.Store.NO));
+ w.AddDocument(doc);
+
+ // 6
+ doc = new Document();
+ AddField(doc, groupField, "b", useDv);
+ doc.Add(new StringField("airport", "ams", Field.Store.NO));
+ w.AddDocument(doc);
+ w.Commit();
+
+ // 7
+ doc = new Document();
+ doc.Add(new StringField("x", "x", Field.Store.NO));
+ w.AddDocument(doc);
+ w.Commit();
+
+ w.Dispose();
+ IndexSearcher indexSearcher = NewSearcher(DirectoryReader.Open(dir));
+ AbstractGroupFacetCollector groupedAirportFacetCollector = CreateRandomCollector(groupField, "airport", null, true);
+ indexSearcher.Search(new MatchAllDocsQuery(), groupedAirportFacetCollector);
+ TermGroupFacetCollector.GroupedFacetResult airportResult = groupedAirportFacetCollector.MergeSegmentResults(10, 0, false);
+ assertEquals(3, airportResult.TotalCount);
+ assertEquals(1, airportResult.TotalMissingCount);
+
+ List<TermGroupFacetCollector.FacetEntry> entries = airportResult.GetFacetEntries(0, 10);
+ assertEquals(2, entries.size());
+ assertEquals("ams", entries[0].Value.Utf8ToString());
+ assertEquals(2, entries[0].Count);
+ assertEquals("dus", entries[1].Value.Utf8ToString());
+ assertEquals(1, entries[1].Count);
+
+ indexSearcher.IndexReader.Dispose();
+ dir.Dispose();
+ }
+
+ private void AddField(Document doc, string field, string value, bool canUseIDV)
+ {
+ doc.Add(new StringField(field, value, Field.Store.NO));
+ if (canUseIDV)
+ {
+ doc.Add(new SortedDocValuesField(field + "_dv", new BytesRef(value)));
+ }
+ }
+
+ [Test]
+ public void TestRandom()
+ {
+ Random random = Random();
+ int numberOfRuns = TestUtil.NextInt(random, 3, 6);
+ for (int indexIter = 0; indexIter < numberOfRuns; indexIter++)
+ {
+ bool multipleFacetsPerDocument = random.nextBoolean();
+ IndexContext context = CreateIndexContext(multipleFacetsPerDocument);
+ IndexSearcher searcher = NewSearcher(context.indexReader);
+
+ if (VERBOSE)
+ {
+ Console.WriteLine("TEST: searcher=" + searcher);
+ }
+
+ for (int searchIter = 0; searchIter < 100; searchIter++)
+ {
+ if (VERBOSE)
+ {
+ Console.WriteLine("TEST: searchIter=" + searchIter);
+ }
+ bool useDv = !multipleFacetsPerDocument && context.useDV && random.nextBoolean();
+ string searchTerm = context.contentStrings[random.nextInt(context.contentStrings.Length)];
+ int limit = random.nextInt(context.facetValues.size());
+ int offset = random.nextInt(context.facetValues.size() - limit);
+ int size = offset + limit;
+ int minCount = random.nextBoolean() ? 0 : random.nextInt(1 + context.facetWithMostGroups / 10);
+ bool orderByCount = random.nextBoolean();
+ string randomStr = GetFromSet(context.facetValues, random.nextInt(context.facetValues.size()));
+ string facetPrefix;
+ if (randomStr == null)
+ {
+ facetPrefix = null;
+ }
+ else
+ {
+ int codePointLen = randomStr.CodePointCount(0, randomStr.Length);
+ int randomLen = random.nextInt(codePointLen);
+ if (codePointLen == randomLen - 1)
+ {
+ facetPrefix = null;
+ }
+ else
+ {
+ int end = randomStr.OffsetByCodePoints(0, randomLen);
+ facetPrefix = random.nextBoolean() ? null : randomStr.Substring(end);
+ }
+ }
+
+ GroupedFacetResult expectedFacetResult = CreateExpectedFacetResult(searchTerm, context, offset, limit, minCount, orderByCount, facetPrefix);
+ AbstractGroupFacetCollector groupFacetCollector = CreateRandomCollector(useDv ? "group_dv" : "group", useDv ? "facet_dv" : "facet", facetPrefix, multipleFacetsPerDocument);
+ searcher.Search(new TermQuery(new Term("content", searchTerm)), groupFacetCollector);
+ TermGroupFacetCollector.GroupedFacetResult actualFacetResult = groupFacetCollector.MergeSegmentResults(size, minCount, orderByCount);
+
+ List<TermGroupFacetCollector.FacetEntry> expectedFacetEntries = expectedFacetResult.getFacetEntries();
+ List<TermGroupFacetCollector.FacetEntry> actualFacetEntries = actualFacetResult.GetFacetEntries(offset, limit);
+
+ if (VERBOSE)
+ {
+ Console.WriteLine("Use DV: " + useDv);
+ Console.WriteLine("Collector: " + groupFacetCollector.GetType().Name);
+ Console.WriteLine("Num group: " + context.numGroups);
+ Console.WriteLine("Num doc: " + context.numDocs);
+ Console.WriteLine("Index iter: " + indexIter);
+ Console.WriteLine("multipleFacetsPerDocument: " + multipleFacetsPerDocument);
+ Console.WriteLine("Search iter: " + searchIter);
+
+ Console.WriteLine("Search term: " + searchTerm);
+ Console.WriteLine("Min count: " + minCount);
+ Console.WriteLine("Facet offset: " + offset);
+ Console.WriteLine("Facet limit: " + limit);
+ Console.WriteLine("Facet prefix: " + facetPrefix);
+ Console.WriteLine("Order by count: " + orderByCount);
+
+ Console.WriteLine("\n=== Expected: \n");
+ Console.WriteLine("Total count " + expectedFacetResult.getTotalCount());
+ Console.WriteLine("Total missing count " + expectedFacetResult.getTotalMissingCount());
+ int counter = 0;
+ foreach (TermGroupFacetCollector.FacetEntry expectedFacetEntry in expectedFacetEntries)
+ {
+ Console.WriteLine(
+ string.Format(CultureInfo.InvariantCulture,
+ "{0}. Expected facet value {1} with count {2}",
+ counter++, expectedFacetEntry.Value.Utf8ToString(), expectedFacetEntry.Count
+ )
+ );
+ }
+
+ Console.WriteLine("\n=== Actual: \n");
+ Console.WriteLine("Total count " + actualFacetResult.TotalCount);
+ Console.WriteLine("Total missing count " + actualFacetResult.TotalMissingCount);
+ counter = 0;
+ foreach (TermGroupFacetCollector.FacetEntry actualFacetEntry in actualFacetEntries)
+ {
+ Console.WriteLine(
+ string.Format(CultureInfo.InvariantCulture,
+ "{0}. Actual facet value {1} with count {2}",
+ counter++, actualFacetEntry.Value.Utf8ToString(), actualFacetEntry.Count
+ )
+ );
+ }
+ Console.WriteLine("\n===================================================================================");
+ }
+
+ assertEquals(expectedFacetResult.getTotalCount(), actualFacetResult.TotalCount);
+ assertEquals(expectedFacetResult.getTotalMissingCount(), actualFacetResult.TotalMissingCount);
+ assertEquals(expectedFacetEntries.size(), actualFacetEntries.size());
+ for (int i = 0; i < expectedFacetEntries.size(); i++)
+ {
+ TermGroupFacetCollector.FacetEntry expectedFacetEntry = expectedFacetEntries[i];
+ TermGroupFacetCollector.FacetEntry actualFacetEntry = actualFacetEntries[i];
+ assertEquals("i=" + i + ": " + expectedFacetEntry.Value.Utf8ToString() + " != " + actualFacetEntry.Value.Utf8ToString(), expectedFacetEntry.Value, actualFacetEntry.Value);
+ assertEquals("i=" + i + ": " + expectedFacetEntry.Count + " != " + actualFacetEntry.Count, expectedFacetEntry.Count, actualFacetEntry.Count);
+ }
+ }
+
+ context.indexReader.Dispose();
+ context.dir.Dispose();
+ }
+ }
+
+ internal class ComparerAnonymousHelper1 : IComparer<string>
+ {
+ public int Compare(string a, string b)
+ {
+ if (a == b)
+ {
+ return 0;
+ }
+ else if (a == null)
+ {
+ return -1;
+ }
+ else if (b == null)
+ {
+ return 1;
+ }
+ else
+ {
+ return a.CompareToOrdinal(b);
+ }
+ }
+ }
+
+ private IndexContext CreateIndexContext(bool multipleFacetValuesPerDocument)
+ {
+ Random random = Random();
+ int numDocs = TestUtil.NextInt(random, 138, 1145) * RANDOM_MULTIPLIER;
+ int numGroups = TestUtil.NextInt(random, 1, numDocs / 4);
+ int numFacets = TestUtil.NextInt(random, 1, numDocs / 6);
+
+ if (VERBOSE)
+ {
+ Console.WriteLine("TEST: numDocs=" + numDocs + " numGroups=" + numGroups);
+ }
+
+ List<string> groups = new List<string>();
+ for (int i = 0; i < numGroups; i++)
+ {
+ groups.Add(GenerateRandomNonEmptyString());
+ }
+ List<string> facetValues = new List<string>();
+ for (int i = 0; i < numFacets; i++)
+ {
+ facetValues.Add(GenerateRandomNonEmptyString());
+ }
+ string[] contentBrs = new string[TestUtil.NextInt(random, 2, 20)];
+ if (VERBOSE)
+ {
+ Console.WriteLine("TEST: create fake content");
+ }
+ for (int contentIDX = 0; contentIDX < contentBrs.Length; contentIDX++)
+ {
+ contentBrs[contentIDX] = GenerateRandomNonEmptyString();
+ if (VERBOSE)
+ {
+ Console.WriteLine(" content=" + contentBrs[contentIDX]);
+ }
+ }
+
+ Directory dir = NewDirectory();
+ RandomIndexWriter writer = new RandomIndexWriter(
+ random,
+ dir,
+ NewIndexWriterConfig(
+ TEST_VERSION_CURRENT,
+ new MockAnalyzer(random)
+ )
+ );
+ bool canUseDV = !"Lucene3x".equals(writer.w.Config.Codec.Name);
+ bool useDv = canUseDV && !multipleFacetValuesPerDocument && random.nextBoolean();
+
+ Document doc = new Document();
+ Document docNoGroup = new Document();
+ Document docNoFacet = new Document();
+ Document docNoGroupNoFacet = new Document();
+ Field group = NewStringField("group", "", Field.Store.NO);
+ Field groupDc = new SortedDocValuesField("group_dv", new BytesRef());
+ if (useDv)
+ {
+ doc.Add(groupDc);
+ docNoFacet.Add(groupDc);
+ }
+ doc.Add(group);
+ docNoFacet.Add(group);
+ Field[] facetFields;
+ if (useDv)
+ {
+ Debug.Assert(!multipleFacetValuesPerDocument);
+ facetFields = new Field[2];
+ facetFields[0] = NewStringField("facet", "", Field.Store.NO);
+ doc.Add(facetFields[0]);
+ docNoGroup.Add(facetFields[0]);
+ facetFields[1] = new SortedDocValuesField("facet_dv", new BytesRef());
+ doc.Add(facetFields[1]);
+ docNoGroup.Add(facetFields[1]);
+ }
+ else
+ {
+ facetFields = multipleFacetValuesPerDocument ? new Field[2 + random.nextInt(6)] : new Field[1];
+ for (int i = 0; i < facetFields.Length; i++)
+ {
+ facetFields[i] = NewStringField("facet", "", Field.Store.NO);
+ doc.Add(facetFields[i]);
+ docNoGroup.Add(facetFields[i]);
+ }
+ }
+ Field content = NewStringField("content", "", Field.Store.NO);
+ doc.Add(content);
+ docNoGroup.Add(content);
+ docNoFacet.Add(content);
+ docNoGroupNoFacet.Add(content);
+
+ ISet<string> uniqueFacetValues = new SortedSet<string>(new ComparerAnonymousHelper1());
+ // ISet<string> uniqueFacetValues = new SortedSet<string>(new Comparator<String>() {
+
+ // @Override
+ // public int compare(String a, String b)
+ //{
+ // if (a == b)
+ // {
+ // return 0;
+ // }
+ // else if (a == null)
+ // {
+ // return -1;
+ // }
+ // else if (b == null)
+ // {
+ // return 1;
+ // }
+ // else
+ // {
+ // return a.compareTo(b);
+ // }
+ //}
+
+ // });
+ // LUCENENET NOTE: Need HashMap here because of null keys
+ IDictionary<string, HashMap<string, ISet<string>>> searchTermToFacetToGroups = new Dictionary<string, HashMap<string, ISet<string>>>();
+ int facetWithMostGroups = 0;
+ for (int i = 0; i < numDocs; i++)
+ {
+ string groupValue;
+ if (random.nextInt(24) == 17)
+ {
+ // So we test the "doc doesn't have the group'd
+ // field" case:
+ if (useDv)
+ {
+ groupValue = "";
+ }
+ else
+ {
+ groupValue = null;
+ }
+ }
+ else
+ {
+ groupValue = groups[random.nextInt(groups.size())];
+ }
+
+ string contentStr = contentBrs[random.nextInt(contentBrs.Length)];
+ if (!searchTermToFacetToGroups.ContainsKey(contentStr))
+ {
+ searchTermToFacetToGroups[contentStr] = new HashMap<string, ISet<string>>();
+ }
+ IDictionary<string, ISet<string>> facetToGroups = searchTermToFacetToGroups[contentStr];
+
+ List<string> facetVals = new List<string>();
+ if (useDv || random.nextInt(24) != 18)
+ {
+ if (useDv)
+ {
+ String facetValue = facetValues[random.nextInt(facetValues.size())];
+ uniqueFacetValues.add(facetValue);
+ if (!facetToGroups.ContainsKey(facetValue))
+ {
+ facetToGroups[facetValue] = new HashSet<string>();
+ }
+ ISet<string> groupsInFacet = facetToGroups[facetValue];
+ groupsInFacet.add(groupValue);
+ if (groupsInFacet.size() > facetWithMostGroups)
+ {
+ facetWithMostGroups = groupsInFacet.size();
+ }
+ facetFields[0].StringValue = (facetValue);
+ facetFields[1].BytesValue = (new BytesRef(facetValue));
+ facetVals.Add(facetValue);
+ }
+ else
+ {
+ foreach (Field facetField in facetFields)
+ {
+ string facetValue = facetValues[random.nextInt(facetValues.size())];
+ uniqueFacetValues.add(facetValue);
+ if (!facetToGroups.ContainsKey(facetValue))
+ {
+ facetToGroups[facetValue] = new HashSet<string>();
+ }
+ ISet<string> groupsInFacet = facetToGroups[facetValue];
+ groupsInFacet.add(groupValue);
+ if (groupsInFacet.size() > facetWithMostGroups)
+ {
+ facetWithMostGroups = groupsInFacet.size();
+ }
+ facetField.StringValue = (facetValue);
+ facetVals.Add(facetValue);
+ }
+ }
+ }
+ else
+ {
+ uniqueFacetValues.add(null);
+ if (!facetToGroups.ContainsKey(null))
+ {
+ facetToGroups.Put(null, new HashSet<string>());
+ }
+ ISet<string> groupsInFacet = facetToGroups[null];
+ groupsInFacet.add(groupValue);
+ if (groupsInFacet.size() > facetWithMostGroups)
+ {
+ facetWithMostGroups = groupsInFacet.size();
+ }
+ }
+
+ if (VERBOSE)
+ {
+ Console.WriteLine(" doc content=" + contentStr + " group=" + (groupValue == null ? "null" : groupValue) + " facetVals=" + facetVals);
+ }
+
+ if (groupValue != null)
+ {
+ if (useDv)
+ {
+ groupDc.BytesValue = (new BytesRef(groupValue));
+ }
+ group.StringValue = (groupValue);
+ }
+ else if (useDv)
+ {
+ // DV cannot have missing values:
+ groupDc.BytesValue = (new BytesRef());
+ }
+ content.StringValue = (contentStr);
+ if (groupValue == null && !facetVals.Any())
+ {
+ writer.AddDocument(docNoGroupNoFacet);
+ }
+ else if (!facetVals.Any())
+ {
+ writer.AddDocument(docNoFacet);
+ }
+ else if (groupValue == null)
+ {
+ writer.AddDocument(docNoGroup);
+ }
+ else
+ {
+ writer.AddDocument(doc);
+ }
+ }
+
+ DirectoryReader reader = writer.Reader;
+ writer.Dispose();
+
+ return new IndexContext(searchTermToFacetToGroups, reader, numDocs, dir, facetWithMostGroups, numGroups, contentBrs, uniqueFacetValues, useDv);
+ }
+
+ internal class ComparerAnonymousHelper2 : IComparer<TermGroupFacetCollector.FacetEntry>
+ {
+ private readonly bool orderByCount;
+
+ public ComparerAnonymousHelper2(bool orderByCount)
+ {
+ this.orderByCount = orderByCount;
+ }
+
+ public int Compare(AbstractGroupFacetCollector.FacetEntry a, AbstractGroupFacetCollector.FacetEntry b)
+ {
+ if (orderByCount)
+ {
+ int cmp = b.Count - a.Count;
+ if (cmp != 0)
+ {
+ return cmp;
+ }
+ }
+ return a.Value.CompareTo(b.Value);
+ }
+ }
+
+ private GroupedFacetResult CreateExpectedFacetResult(string searchTerm, IndexContext context, int offset, int limit, int minCount, bool orderByCount, string facetPrefix)
+ {
+ //IDictionary<string, ISet<string>> facetGroups = context.searchTermToFacetGroups.get(searchTerm);
+ //if (facetGroups == null)
+ HashMap<string, ISet<string>> facetGroups;
+ if (!context.searchTermToFacetGroups.TryGetValue(searchTerm, out facetGroups))
+ {
+ facetGroups = new HashMap<string, ISet<string>>();
+ }
+
+ int totalCount = 0;
+ int totalMissCount = 0;
+ ISet<string> facetValues;
+ if (facetPrefix != null)
+ {
+ facetValues = new HashSet<string>();
+ foreach (string facetValue in context.facetValues)
+ {
+ if (facetValue != null && facetValue.StartsWith(facetPrefix))
+ {
+ facetValues.add(facetValue);
+ }
+ }
+ }
+ else
+ {
+ facetValues = context.facetValues;
+ }
+
+ List<TermGroupFacetCollector.FacetEntry> entries = new List<TermGroupFacetCollector.FacetEntry>(facetGroups.size());
+ // also includes facets with count 0
+ foreach (string facetValue in facetValues)
+ {
+ if (facetValue == null)
+ {
+ continue;
+ }
+
+ ISet<string> groups = facetGroups.ContainsKey(facetValue) ? facetGroups[facetValue] : null;
+ int count = groups != null ? groups.size() : 0;
+ if (count >= minCount)
+ {
+ entries.Add(new TermGroupFacetCollector.FacetEntry(new BytesRef(facetValue), count));
+ }
+ totalCount += count;
+ }
+
+ // Only include null count when no facet prefix is specified
+ if (facetPrefix == null)
+ {
+ ISet<string> groups = facetGroups[null];
+ if (groups != null)
+ {
+ totalMissCount = groups.size();
+ }
+ }
+
+ entries.Sort(new ComparerAnonymousHelper2(orderByCount));
+
+ // Collections.sort(entries, new Comparator<TermGroupFacetCollector.FacetEntry>() {
+
+ // @Override
+ // public int compare(TermGroupFacetCollector.FacetEntry a, TermGroupFacetCollector.FacetEntry b)
+ //{
+ // if (orderByCount)
+ // {
+ // int cmp = b.getCount() - a.getCount();
+ // if (cmp != 0)
+ // {
+ // return cmp;
+ // }
+ // }
+ // return a.getValue().compareTo(b.getValue());
+ //}
+
+ // });
+
+ int endOffset = offset + limit;
+ List<TermGroupFacetCollector.FacetEntry> entriesResult;
+ if (offset >= entries.size())
+ {
+ entriesResult = new List<TermGroupFacetCollector.FacetEntry>();
+ }
+ else if (endOffset >= entries.size())
+ {
+ //entriesResult = (List<TermGroupFacetCollector.FacetEntry>)entries.SubList(offset, entries.size());
+ entriesResult = entries.GetRange(offset, entries.size() - offset);
+ }
+ else
+ {
+ //entriesResult = (List<TermGroupFacetCollector.FacetEntry>)entries.SubList(offset, endOffset);
+ entriesResult = entries.GetRange(offset, endOffset - offset);
+ }
+ return new GroupedFacetResult(totalCount, totalMissCount, entriesResult);
+ }
+
+ private AbstractGroupFacetCollector CreateRandomCollector(string groupField, string facetField, string facetPrefix, bool multipleFacetsPerDocument)
+ {
+ BytesRef facetPrefixBR = facetPrefix == null ? null : new BytesRef(facetPrefix);
+ // DocValues cannot be multi-valued:
+ Debug.Assert(!multipleFacetsPerDocument || !groupField.EndsWith("_dv"));
+ return TermGroupFacetCollector.CreateTermGroupFacetCollector(groupField, facetField, multipleFacetsPerDocument, facetPrefixBR, Random().nextInt(1024));
+ }
+
+ private string GetFromSet(ISet<string> set, int index)
+ {
+ int currentIndex = 0;
+ foreach (string bytesRef in set)
+ {
+ if (currentIndex++ == index)
+ {
+ return bytesRef;
+ }
+ }
+
+ return null;
+ }
+
+ internal class IndexContext
+ {
+ internal readonly int numDocs;
+ internal readonly DirectoryReader indexReader;
+ internal readonly IDictionary<string, HashMap<string, ISet<string>>> searchTermToFacetGroups;
+ internal readonly ISet<string> facetValues;
+ internal readonly Directory dir;
+ internal readonly int facetWithMostGroups;
+ internal readonly int numGroups;
+ internal readonly string[] contentStrings;
+ internal readonly bool useDV;
+
+ public IndexContext(IDictionary<string, HashMap<string, ISet<string>>> searchTermToFacetGroups, DirectoryReader r,
+ int numDocs, Directory dir, int facetWithMostGroups, int numGroups, string[] contentStrings, ISet<string> facetValues, bool useDV)
+ {
+ this.searchTermToFacetGroups = searchTermToFacetGroups;
+ this.indexReader = r;
+ this.numDocs = numDocs;
+ this.dir = dir;
+ this.facetWithMostGroups = facetWithMostGroups;
+ this.numGroups = numGroups;
+ this.contentStrings = contentStrings;
+ this.facetValues = facetValues;
+ this.useDV = useDV;
+ }
+ }
+
+ internal class GroupedFacetResult
+ {
+
+ internal readonly int totalCount;
+ internal readonly int totalMissingCount;
+ internal readonly List<TermGroupFacetCollector.FacetEntry> facetEntries;
+
+ internal GroupedFacetResult(int totalCount, int totalMissingCount, List<TermGroupFacetCollector.FacetEntry> facetEntries)
+ {
+ this.totalCount = totalCount;
+ this.totalMissingCount = totalMissingCount;
+ this.facetEntries = facetEntries;
+ }
+
+ public int getTotalCount()
+ {
+ return totalCount;
+ }
+
+ public int getTotalMissingCount()
+ {
+ return totalMissingCount;
+ }
+
+ public List<TermGroupFacetCollector.FacetEntry> getFacetEntries()
+ {
+ return facetEntries;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/9d72bcb3/src/Lucene.Net.Tests.Grouping/GroupingSearchTest.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests.Grouping/GroupingSearchTest.cs b/src/Lucene.Net.Tests.Grouping/GroupingSearchTest.cs
new file mode 100644
index 0000000..8f49614
--- /dev/null
+++ b/src/Lucene.Net.Tests.Grouping/GroupingSearchTest.cs
@@ -0,0 +1,245 @@
+\ufeffusing Lucene.Net.Analysis;
+using Lucene.Net.Documents;
+using Lucene.Net.Index;
+using Lucene.Net.Queries.Function;
+using Lucene.Net.Queries.Function.ValueSources;
+using Lucene.Net.Store;
+using Lucene.Net.Util;
+using Lucene.Net.Util.Mutable;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Lucene.Net.Search.Grouping
+{
+ public class GroupingSearchTest : LuceneTestCase
+ {
+ // Tests some very basic usages...
+ [Test]
+ public void testBasic()
+ {
+
+ string groupField = "author";
+
+ FieldType customType = new FieldType();
+ customType.Stored = (true);
+
+ Directory dir = NewDirectory();
+ RandomIndexWriter w = new RandomIndexWriter(
+ Random(),
+ dir,
+ NewIndexWriterConfig(TEST_VERSION_CURRENT,
+ new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy()));
+ bool canUseIDV = !"Lucene3x".equals(w.w.Config.Codec.Name);
+ List<Document> documents = new List<Document>();
+ // 0
+ Document doc = new Document();
+ addGroupField(doc, groupField, "author1", canUseIDV);
+ doc.Add(new TextField("content", "random text", Field.Store.YES));
+ doc.Add(new Field("id", "1", customType));
+ documents.Add(doc);
+
+ // 1
+ doc = new Document();
+ addGroupField(doc, groupField, "author1", canUseIDV);
+ doc.Add(new TextField("content", "some more random text", Field.Store.YES));
+ doc.Add(new Field("id", "2", customType));
+ documents.Add(doc);
+
+ // 2
+ doc = new Document();
+ addGroupField(doc, groupField, "author1", canUseIDV);
+ doc.Add(new TextField("content", "some more random textual data", Field.Store.YES));
+ doc.Add(new Field("id", "3", customType));
+ doc.Add(new StringField("groupend", "x", Field.Store.NO));
+ documents.Add(doc);
+ w.AddDocuments(documents);
+ documents.Clear();
+
+ // 3
+ doc = new Document();
+ addGroupField(doc, groupField, "author2", canUseIDV);
+ doc.Add(new TextField("content", "some random text", Field.Store.YES));
+ doc.Add(new Field("id", "4", customType));
+ doc.Add(new StringField("groupend", "x", Field.Store.NO));
+ w.AddDocument(doc);
+
+ // 4
+ doc = new Document();
+ addGroupField(doc, groupField, "author3", canUseIDV);
+ doc.Add(new TextField("content", "some more random text", Field.Store.YES));
+ doc.Add(new Field("id", "5", customType));
+ documents.Add(doc);
+
+ // 5
+ doc = new Document();
+ addGroupField(doc, groupField, "author3", canUseIDV);
+ doc.Add(new TextField("content", "random", Field.Store.YES));
+ doc.Add(new Field("id", "6", customType));
+ doc.Add(new StringField("groupend", "x", Field.Store.NO));
+ documents.Add(doc);
+ w.AddDocuments(documents);
+ documents.Clear();
+
+ // 6 -- no author field
+ doc = new Document();
+ doc.Add(new TextField("content", "random word stuck in alot of other text", Field.Store.YES));
+ doc.Add(new Field("id", "6", customType));
+ doc.Add(new StringField("groupend", "x", Field.Store.NO));
+
+ w.AddDocument(doc);
+
+ IndexSearcher indexSearcher = NewSearcher(w.Reader);
+ w.Dispose();
+
+ Sort groupSort = Sort.RELEVANCE;
+ GroupingSearch groupingSearch = createRandomGroupingSearch(groupField, groupSort, 5, canUseIDV);
+
+ var groups = groupingSearch.Search(indexSearcher, (Filter)null, new TermQuery(new Index.Term("content", "random")), 0, 10);
+
+ assertEquals(7, groups.totalHitCount);
+ assertEquals(7, groups.totalGroupedHitCount);
+ assertEquals(4, groups.groups.length);
+
+ // relevance order: 5, 0, 3, 4, 1, 2, 6
+
+ // the later a document is added the higher this docId
+ // value
+ GroupDocs <?> group = groups.groups[0];
+ compareGroupValue("author3", group);
+ assertEquals(2, group.scoreDocs.length);
+ assertEquals(5, group.scoreDocs[0].doc);
+ assertEquals(4, group.scoreDocs[1].doc);
+ assertTrue(group.scoreDocs[0].score > group.scoreDocs[1].score);
+
+ group = groups.groups[1];
+ compareGroupValue("author1", group);
+ assertEquals(3, group.scoreDocs.length);
+ assertEquals(0, group.scoreDocs[0].doc);
+ assertEquals(1, group.scoreDocs[1].doc);
+ assertEquals(2, group.scoreDocs[2].doc);
+ assertTrue(group.scoreDocs[0].score > group.scoreDocs[1].score);
+ assertTrue(group.scoreDocs[1].score > group.scoreDocs[2].score);
+
+ group = groups.groups[2];
+ compareGroupValue("author2", group);
+ assertEquals(1, group.scoreDocs.length);
+ assertEquals(3, group.scoreDocs[0].doc);
+
+ group = groups.groups[3];
+ compareGroupValue(null, group);
+ assertEquals(1, group.scoreDocs.length);
+ 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);
+
+ assertEquals(7, groups.totalHitCount);
+ assertEquals(7, groups.totalGroupedHitCount);
+ assertEquals(4, groups.totalGroupCount.longValue());
+ assertEquals(4, groups.groups.length);
+
+ indexSearcher.IndexReader.Dispose();
+ dir.Dispose();
+ }
+
+ private void addGroupField(Document doc, string groupField, string value, bool canUseIDV)
+ {
+ doc.Add(new TextField(groupField, value, Field.Store.YES));
+ if (canUseIDV)
+ {
+ doc.Add(new SortedDocValuesField(groupField, new BytesRef(value)));
+ }
+ }
+
+ private void compareGroupValue(string expected, GroupDocs<?> group)
+ {
+ if (expected == null)
+ {
+ if (group.groupValue == null)
+ {
+ return;
+ }
+ else if (group.groupValue.GetType().IsAssignableFrom(typeof(MutableValueStr)))
+ {
+ return;
+ }
+ else if (((BytesRef)group.groupValue).length == 0)
+ {
+ return;
+ }
+ fail();
+ }
+
+ if (group.groupValue.GetType().IsAssignableFrom(typeof(BytesRef)))
+ {
+ assertEquals(new BytesRef(expected), group.groupValue);
+ }
+ else if (group.groupValue.GetType().IsAssignableFrom(typeof(MutableValueStr)))
+ {
+ MutableValueStr v = new MutableValueStr();
+ v.value = new BytesRef(expected);
+ assertEquals(v, group.groupValue);
+ }
+ else
+ {
+ fail();
+ }
+ }
+
+ private GroupingSearch createRandomGroupingSearch(String groupField, Sort groupSort, int docsInGroup, bool canUseIDV)
+ {
+ GroupingSearch groupingSearch;
+ if (Random().nextBoolean())
+ {
+ ValueSource vs = new BytesRefFieldSource(groupField);
+ groupingSearch = new GroupingSearch(vs, new Dictionary());
+ }
+ else
+ {
+ groupingSearch = new GroupingSearch(groupField);
+ }
+
+ groupingSearch.SetGroupSort(groupSort);
+ groupingSearch.SetGroupDocsLimit(docsInGroup);
+
+ if (Random().nextBoolean())
+ {
+ groupingSearch.SetCachingInMB(4.0, true);
+ }
+
+ return groupingSearch;
+ }
+
+ [Test]
+ public void testSetAllGroups()
+ {
+ Directory dir = NewDirectory();
+ RandomIndexWriter w = new RandomIndexWriter(
+ Random(),
+ dir,
+ NewIndexWriterConfig(TEST_VERSION_CURRENT,
+ new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy()));
+ Document doc = new Document();
+ doc.Add(NewField("group", "foo", StringField.TYPE_NOT_STORED));
+ w.AddDocument(doc);
+
+ IndexSearcher indexSearcher = NewSearcher(w.Reader);
+ w.Dispose();
+
+ GroupingSearch gs = new GroupingSearch("group");
+ gs.SetAllGroups(true);
+ TopGroups <?> groups = gs.Search(indexSearcher, null, new TermQuery(new Index.Term("group", "foo")), 0, 10);
+ assertEquals(1, groups.totalHitCount);
+ //assertEquals(1, groups.totalGroupCount.intValue());
+ assertEquals(1, groups.totalGroupedHitCount);
+ assertEquals(1, gs.GetAllMatchingGroups().size());
+ indexSearcher.IndexReader.Dispose();
+ dir.Dispose();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/9d72bcb3/src/Lucene.Net.Tests.Grouping/Lucene.Net.Tests.Grouping.csproj
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests.Grouping/Lucene.Net.Tests.Grouping.csproj b/src/Lucene.Net.Tests.Grouping/Lucene.Net.Tests.Grouping.csproj
new file mode 100644
index 0000000..806839d
--- /dev/null
+++ b/src/Lucene.Net.Tests.Grouping/Lucene.Net.Tests.Grouping.csproj
@@ -0,0 +1,85 @@
+\ufeff<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{C2349F0D-FB66-4544-9C33-4D87F73C6004}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Lucene.Net.Tests.Grouping</RootNamespace>
+ <AssemblyName>Lucene.Net.Tests.Grouping</AssemblyName>
+ <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
+ <HintPath>..\..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AbstractGroupingTestCase.cs" />
+ <Compile Include="AllGroupHeadsCollectorTest.cs" />
+ <Compile Include="AllGroupsCollectorTest.cs" />
+ <Compile Include="GroupFacetCollectorTest.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Lucene.Net.Core\Lucene.Net.csproj">
+ <Project>{5d4ad9be-1ffb-41ab-9943-25737971bf57}</Project>
+ <Name>Lucene.Net</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Lucene.Net.Grouping\Lucene.Net.Grouping.csproj">
+ <Project>{02bab603-067d-48b1-aedd-316849652568}</Project>
+ <Name>Lucene.Net.Grouping</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Lucene.Net.Queries\Lucene.Net.Queries.csproj">
+ <Project>{69D7956C-C2CC-4708-B399-A188FEC384C4}</Project>
+ <Name>Lucene.Net.Queries</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Lucene.Net.TestFramework\Lucene.Net.TestFramework.csproj">
+ <Project>{B2C0D749-CE34-4F62-A15E-00CB2FF5DDB3}</Project>
+ <Name>Lucene.Net.TestFramework</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/9d72bcb3/src/Lucene.Net.Tests.Grouping/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests.Grouping/Properties/AssemblyInfo.cs b/src/Lucene.Net.Tests.Grouping/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..4944609
--- /dev/null
+++ b/src/Lucene.Net.Tests.Grouping/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+\ufeffusing System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Lucene.Net.Tests.Grouping")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Lucene.Net.Tests.Grouping")]
+[assembly: AssemblyCopyright("Copyright � 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c2349f0d-fb66-4544-9c33-4d87f73c6004")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]