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 2017/02/26 23:36:55 UTC
[07/72] [abbrv] [partial] lucenenet git commit: Lucene.Net.Tests:
Removed \core directory and put its contents in root directory
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestCustomSearcherSort.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestCustomSearcherSort.cs b/src/Lucene.Net.Tests/Search/TestCustomSearcherSort.cs
new file mode 100644
index 0000000..0511e75
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestCustomSearcherSort.cs
@@ -0,0 +1,262 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search
+{
+ using NUnit.Framework;
+
+ /// <summary>
+ /// Copyright 2005 The Apache Software Foundation
+ ///
+ /// Licensed under the Apache License, Version 2.0 (the "License");
+ /// you may not use this file except in compliance with the License.
+ /// You may obtain a copy of the License at
+ ///
+ /// http://www.apache.org/licenses/LICENSE-2.0
+ ///
+ /// Unless required by applicable law or agreed to in writing, software
+ /// distributed under the License is distributed on an "AS IS" BASIS,
+ /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ /// See the License for the specific language governing permissions and
+ /// limitations under the License.
+ /// </summary>
+
+ using DateTools = DateTools;
+ using Directory = Lucene.Net.Store.Directory;
+ using Document = Documents.Document;
+ using Field = Field;
+ using IndexReader = Lucene.Net.Index.IndexReader;
+ using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+ using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+ using Term = Lucene.Net.Index.Term;
+
+ /// <summary>
+ /// Unit test for sorting code. </summary>
+ [TestFixture]
+ public class TestCustomSearcherSort : LuceneTestCase
+ {
+ private Directory Index = null;
+ private IndexReader Reader;
+ private Query Query = null;
+
+ // reduced from 20000 to 2000 to speed up test...
+ private int INDEX_SIZE;
+
+ /// <summary>
+ /// Create index and query for test cases.
+ /// </summary>
+ [SetUp]
+ public override void SetUp()
+ {
+ base.SetUp();
+ INDEX_SIZE = AtLeast(2000);
+ Index = NewDirectory();
+ RandomIndexWriter writer = new RandomIndexWriter(Random(), Index, Similarity, TimeZone);
+ RandomGen random = new RandomGen(this, Random());
+ for (int i = 0; i < INDEX_SIZE; ++i) // don't decrease; if to low the
+ {
+ // problem doesn't show up
+ Document doc = new Document();
+ if ((i % 5) != 0) // some documents must not have an entry in the first
+ {
+ // sort field
+ doc.Add(NewStringField("publicationDate_", random.LuceneDate, Field.Store.YES));
+ }
+ if ((i % 7) == 0) // some documents to match the query (see below)
+ {
+ doc.Add(NewTextField("content", "test", Field.Store.YES));
+ }
+ // every document has a defined 'mandant' field
+ doc.Add(NewStringField("mandant", Convert.ToString(i % 3), Field.Store.YES));
+ writer.AddDocument(doc);
+ }
+ Reader = writer.Reader;
+ writer.Dispose();
+ Query = new TermQuery(new Term("content", "test"));
+ }
+
+ [TearDown]
+ public override void TearDown()
+ {
+ Reader.Dispose();
+ Index.Dispose();
+ base.TearDown();
+ }
+
+ /// <summary>
+ /// Run the test using two CustomSearcher instances.
+ /// </summary>
+ [Test]
+ public virtual void TestFieldSortCustomSearcher()
+ {
+ // log("Run testFieldSortCustomSearcher");
+ // define the sort criteria
+ Sort custSort = new Sort(new SortField("publicationDate_", SortFieldType.STRING), SortField.FIELD_SCORE);
+ IndexSearcher searcher = new CustomSearcher(this, Reader, 2);
+ // search and check hits
+ MatchHits(searcher, custSort);
+ }
+
+ /// <summary>
+ /// Run the test using one CustomSearcher wrapped by a MultiSearcher.
+ /// </summary>
+ [Test]
+ public virtual void TestFieldSortSingleSearcher()
+ {
+ // log("Run testFieldSortSingleSearcher");
+ // define the sort criteria
+ Sort custSort = new Sort(new SortField("publicationDate_", SortFieldType.STRING), SortField.FIELD_SCORE);
+ IndexSearcher searcher = new CustomSearcher(this, Reader, 2);
+ // search and check hits
+ MatchHits(searcher, custSort);
+ }
+
+ // make sure the documents returned by the search match the expected list
+ private void MatchHits(IndexSearcher searcher, Sort sort)
+ {
+ // make a query without sorting first
+ ScoreDoc[] hitsByRank = searcher.Search(Query, null, int.MaxValue).ScoreDocs;
+ CheckHits(hitsByRank, "Sort by rank: "); // check for duplicates
+ IDictionary<int?, int?> resultMap = new SortedDictionary<int?, int?>();
+ // store hits in TreeMap - TreeMap does not allow duplicates; existing
+ // entries are silently overwritten
+ for (int hitid = 0; hitid < hitsByRank.Length; ++hitid)
+ {
+ resultMap[Convert.ToInt32(hitsByRank[hitid].Doc)] = Convert.ToInt32(hitid); // Value: Hits-Objekt Index - Key: Lucene
+ // Document ID
+ }
+
+ // now make a query using the sort criteria
+ ScoreDoc[] resultSort = searcher.Search(Query, null, int.MaxValue, sort).ScoreDocs;
+ CheckHits(resultSort, "Sort by custom criteria: "); // check for duplicates
+
+ // besides the sorting both sets of hits must be identical
+ for (int hitid = 0; hitid < resultSort.Length; ++hitid)
+ {
+ int? idHitDate = Convert.ToInt32(resultSort[hitid].Doc); // document ID
+ // from sorted
+ // search
+ if (!resultMap.ContainsKey(idHitDate))
+ {
+ Log("ID " + idHitDate + " not found. Possibliy a duplicate.");
+ }
+ Assert.IsTrue(resultMap.ContainsKey(idHitDate)); // same ID must be in the
+ // Map from the rank-sorted
+ // search
+ // every hit must appear once in both result sets --> remove it from the
+ // Map.
+ // At the end the Map must be empty!
+ resultMap.Remove(idHitDate);
+ }
+ if (resultMap.Count == 0)
+ {
+ // log("All hits matched");
+ }
+ else
+ {
+ Log("Couldn't match " + resultMap.Count + " hits.");
+ }
+ Assert.AreEqual(resultMap.Count, 0);
+ }
+
+ /// <summary>
+ /// Check the hits for duplicates.
+ /// </summary>
+ private void CheckHits(ScoreDoc[] hits, string prefix)
+ {
+ if (hits != null)
+ {
+ IDictionary<int?, int?> idMap = new SortedDictionary<int?, int?>();
+ for (int docnum = 0; docnum < hits.Length; ++docnum)
+ {
+ int? luceneId = null;
+
+ luceneId = Convert.ToInt32(hits[docnum].Doc);
+ if (idMap.ContainsKey(luceneId))
+ {
+ StringBuilder message = new StringBuilder(prefix);
+ message.Append("Duplicate key for hit index = ");
+ message.Append(docnum);
+ message.Append(", previous index = ");
+ message.Append((idMap[luceneId]).ToString());
+ message.Append(", Lucene ID = ");
+ message.Append(luceneId);
+ Log(message.ToString());
+ }
+ else
+ {
+ idMap[luceneId] = Convert.ToInt32(docnum);
+ }
+ }
+ }
+ }
+
+ // Simply write to console - choosen to be independant of log4j etc
+ private void Log(string message)
+ {
+ if (VERBOSE)
+ {
+ Console.WriteLine(message);
+ }
+ }
+
+ public class CustomSearcher : IndexSearcher
+ {
+ private readonly TestCustomSearcherSort OuterInstance;
+
+ internal int Switcher;
+
+ public CustomSearcher(TestCustomSearcherSort outerInstance, IndexReader r, int switcher)
+ : base(r)
+ {
+ this.OuterInstance = outerInstance;
+ this.Switcher = switcher;
+ }
+
+ public override TopFieldDocs Search(Query query, Filter filter, int nDocs, Sort sort)
+ {
+ BooleanQuery bq = new BooleanQuery();
+ bq.Add(query, Occur.MUST);
+ bq.Add(new TermQuery(new Term("mandant", Convert.ToString(Switcher))), Occur.MUST);
+ return base.Search(bq, filter, nDocs, sort);
+ }
+
+ public override TopDocs Search(Query query, Filter filter, int nDocs)
+ {
+ BooleanQuery bq = new BooleanQuery();
+ bq.Add(query, Occur.MUST);
+ bq.Add(new TermQuery(new Term("mandant", Convert.ToString(Switcher))), Occur.MUST);
+ return base.Search(bq, filter, nDocs);
+ }
+ }
+
+ private class RandomGen
+ {
+ private readonly TestCustomSearcherSort OuterInstance;
+
+ internal RandomGen(TestCustomSearcherSort outerInstance, Random random)
+ {
+ this.OuterInstance = outerInstance;
+ this.Random = random;
+ @base = new DateTime(1980, 1, 1);
+ }
+
+ internal Random Random;
+
+ // we use the default Locale/TZ since LuceneTestCase randomizes it
+ internal DateTime @base;
+
+ // Just to generate some different Lucene Date strings
+ internal virtual string LuceneDate
+ {
+ get
+ {
+ return DateTools.TimeToString((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) + Random.Next() - int.MinValue, DateTools.Resolution.DAY);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestDateFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestDateFilter.cs b/src/Lucene.Net.Tests/Search/TestDateFilter.cs
new file mode 100644
index 0000000..11e62e1
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestDateFilter.cs
@@ -0,0 +1,165 @@
+using System;
+using Lucene.Net.Attributes;
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search
+{
+ using NUnit.Framework;
+ using DateTools = DateTools;
+ using Directory = Lucene.Net.Store.Directory;
+ using Document = Documents.Document;
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ using Field = Field;
+ using IndexReader = Lucene.Net.Index.IndexReader;
+ using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+ using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+ using Term = Lucene.Net.Index.Term;
+
+ /// <summary>
+ /// DateFilter JUnit tests.
+ ///
+ ///
+ /// </summary>
+ [TestFixture]
+ public class TestDateFilter : LuceneTestCase
+ {
+ ///
+ [OneTimeSetUp]
+ public virtual void TestBefore()
+ {
+ // create an index
+ Directory indexStore = NewDirectory();
+ RandomIndexWriter writer = new RandomIndexWriter(Random(), indexStore, Similarity, TimeZone);
+
+ long now = DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond;
+
+ Document doc = new Document();
+ // add time that is in the past
+ doc.Add(NewStringField("datefield", DateTools.TimeToString(now - 1000, DateTools.Resolution.MILLISECOND), Field.Store.YES));
+ doc.Add(NewTextField("body", "Today is a very sunny day in New York City", Field.Store.YES));
+ writer.AddDocument(doc);
+
+ IndexReader reader = writer.Reader;
+ writer.Dispose();
+ IndexSearcher searcher = NewSearcher(reader);
+
+ // filter that should preserve matches
+ // DateFilter df1 = DateFilter.Before("datefield", now);
+ TermRangeFilter df1 = TermRangeFilter.NewStringRange("datefield", DateTools.TimeToString(now - 2000, DateTools.Resolution.MILLISECOND), DateTools.TimeToString(now, DateTools.Resolution.MILLISECOND), false, true);
+ // filter that should discard matches
+ // DateFilter df2 = DateFilter.Before("datefield", now - 999999);
+ TermRangeFilter df2 = TermRangeFilter.NewStringRange("datefield", DateTools.TimeToString(0, DateTools.Resolution.MILLISECOND), DateTools.TimeToString(now - 2000, DateTools.Resolution.MILLISECOND), true, false);
+
+ // search something that doesn't exist with DateFilter
+ Query query1 = new TermQuery(new Term("body", "NoMatchForthis"));
+
+ // search for something that does exists
+ Query query2 = new TermQuery(new Term("body", "sunny"));
+
+ ScoreDoc[] result;
+
+ // ensure that queries return expected results without DateFilter first
+ result = searcher.Search(query1, null, 1000).ScoreDocs;
+ Assert.AreEqual(0, result.Length);
+
+ result = searcher.Search(query2, null, 1000).ScoreDocs;
+ Assert.AreEqual(1, result.Length);
+
+ // run queries with DateFilter
+ result = searcher.Search(query1, df1, 1000).ScoreDocs;
+ Assert.AreEqual(0, result.Length);
+
+ result = searcher.Search(query1, df2, 1000).ScoreDocs;
+ Assert.AreEqual(0, result.Length);
+
+ result = searcher.Search(query2, df1, 1000).ScoreDocs;
+ Assert.AreEqual(1, result.Length);
+
+ result = searcher.Search(query2, df2, 1000).ScoreDocs;
+ Assert.AreEqual(0, result.Length);
+ reader.Dispose();
+ indexStore.Dispose();
+ }
+
+ [Test, LuceneNetSpecific]
+ public void Test()
+ {
+ // noop, required for the before and after tests to run
+ }
+
+ ///
+ [OneTimeTearDown]
+ public virtual void TestAfter()
+ {
+ // create an index
+ Directory indexStore = NewDirectory();
+ RandomIndexWriter writer = new RandomIndexWriter(Random(), indexStore, Similarity, TimeZone);
+
+ long now = DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond;
+
+ Document doc = new Document();
+ // add time that is in the future
+ doc.Add(NewStringField("datefield", DateTools.TimeToString(now + 888888, DateTools.Resolution.MILLISECOND), Field.Store.YES));
+ doc.Add(NewTextField("body", "Today is a very sunny day in New York City", Field.Store.YES));
+ writer.AddDocument(doc);
+
+ IndexReader reader = writer.Reader;
+ writer.Dispose();
+ IndexSearcher searcher = NewSearcher(reader);
+
+ // filter that should preserve matches
+ // DateFilter df1 = DateFilter.After("datefield", now);
+ TermRangeFilter df1 = TermRangeFilter.NewStringRange("datefield", DateTools.TimeToString(now, DateTools.Resolution.MILLISECOND), DateTools.TimeToString(now + 999999, DateTools.Resolution.MILLISECOND), true, false);
+ // filter that should discard matches
+ // DateFilter df2 = DateFilter.After("datefield", now + 999999);
+ TermRangeFilter df2 = TermRangeFilter.NewStringRange("datefield", DateTools.TimeToString(now + 999999, DateTools.Resolution.MILLISECOND), DateTools.TimeToString(now + 999999999, DateTools.Resolution.MILLISECOND), false, true);
+
+ // search something that doesn't exist with DateFilter
+ Query query1 = new TermQuery(new Term("body", "NoMatchForthis"));
+
+ // search for something that does exists
+ Query query2 = new TermQuery(new Term("body", "sunny"));
+
+ ScoreDoc[] result;
+
+ // ensure that queries return expected results without DateFilter first
+ result = searcher.Search(query1, null, 1000).ScoreDocs;
+ Assert.AreEqual(0, result.Length);
+
+ result = searcher.Search(query2, null, 1000).ScoreDocs;
+ Assert.AreEqual(1, result.Length);
+
+ // run queries with DateFilter
+ result = searcher.Search(query1, df1, 1000).ScoreDocs;
+ Assert.AreEqual(0, result.Length);
+
+ result = searcher.Search(query1, df2, 1000).ScoreDocs;
+ Assert.AreEqual(0, result.Length);
+
+ result = searcher.Search(query2, df1, 1000).ScoreDocs;
+ Assert.AreEqual(1, result.Length);
+
+ result = searcher.Search(query2, df2, 1000).ScoreDocs;
+ Assert.AreEqual(0, result.Length);
+ reader.Dispose();
+ indexStore.Dispose();
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestDateSort.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestDateSort.cs b/src/Lucene.Net.Tests/Search/TestDateSort.cs
new file mode 100644
index 0000000..3c45c9b
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestDateSort.cs
@@ -0,0 +1,125 @@
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search
+{
+ using Lucene.Net.Support;
+ using NUnit.Framework;
+ using DateTools = DateTools;
+ using Directory = Lucene.Net.Store.Directory;
+ using Document = Documents.Document;
+ using Field = Field;
+ using IndexReader = Lucene.Net.Index.IndexReader;
+ using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+ using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ using Term = Lucene.Net.Index.Term;
+
+ /// <summary>
+ /// Test date sorting, i.e. auto-sorting of fields with type "long".
+ /// See http://issues.apache.org/jira/browse/LUCENE-1045
+ /// </summary>
+ [TestFixture]
+ public class TestDateSort : LuceneTestCase
+ {
+ private const string TEXT_FIELD = "text";
+ private const string DATE_TIME_FIELD = "dateTime";
+
+ private Directory Directory;
+ private IndexReader Reader;
+
+ [SetUp]
+ public override void SetUp()
+ {
+ base.SetUp();
+ // Create an index writer.
+ Directory = NewDirectory();
+ RandomIndexWriter writer = new RandomIndexWriter(Random(), Directory, Similarity, TimeZone);
+
+ // oldest doc:
+ // Add the first document. text = "Document 1" dateTime = Oct 10 03:25:22 EDT 2007
+ writer.AddDocument(CreateDocument("Document 1", 1192001122000L));
+ // Add the second document. text = "Document 2" dateTime = Oct 10 03:25:26 EDT 2007
+ writer.AddDocument(CreateDocument("Document 2", 1192001126000L));
+ // Add the third document. text = "Document 3" dateTime = Oct 11 07:12:13 EDT 2007
+ writer.AddDocument(CreateDocument("Document 3", 1192101133000L));
+ // Add the fourth document. text = "Document 4" dateTime = Oct 11 08:02:09 EDT 2007
+ writer.AddDocument(CreateDocument("Document 4", 1192104129000L));
+ // latest doc:
+ // Add the fifth document. text = "Document 5" dateTime = Oct 12 13:25:43 EDT 2007
+ writer.AddDocument(CreateDocument("Document 5", 1192209943000L));
+
+ Reader = writer.Reader;
+ writer.Dispose();
+ }
+
+ [TearDown]
+ public override void TearDown()
+ {
+ Reader.Dispose();
+ Directory.Dispose();
+ base.TearDown();
+ }
+
+ [Test]
+ public virtual void TestReverseDateSort()
+ {
+ IndexSearcher searcher = NewSearcher(Reader);
+
+ Sort sort = new Sort(new SortField(DATE_TIME_FIELD, SortFieldType.STRING, true));
+ Query query = new TermQuery(new Term(TEXT_FIELD, "document"));
+
+ // Execute the search and process the search results.
+ string[] actualOrder = new string[5];
+ ScoreDoc[] hits = searcher.Search(query, null, 1000, sort).ScoreDocs;
+ for (int i = 0; i < hits.Length; i++)
+ {
+ Document document = searcher.Doc(hits[i].Doc);
+ string text = document.Get(TEXT_FIELD);
+ actualOrder[i] = text;
+ }
+
+ // Set up the expected order (i.e. Document 5, 4, 3, 2, 1).
+ string[] expectedOrder = new string[5];
+ expectedOrder[0] = "Document 5";
+ expectedOrder[1] = "Document 4";
+ expectedOrder[2] = "Document 3";
+ expectedOrder[3] = "Document 2";
+ expectedOrder[4] = "Document 1";
+
+ Assert.AreEqual(Arrays.AsList(expectedOrder), Arrays.AsList(actualOrder));
+ }
+
+ private Document CreateDocument(string text, long time)
+ {
+ Document document = new Document();
+
+ // Add the text field.
+ Field textField = NewTextField(TEXT_FIELD, text, Field.Store.YES);
+ document.Add(textField);
+
+ // Add the date/time field.
+ string dateTimeString = DateTools.TimeToString(time, DateTools.Resolution.SECOND);
+ Field dateTimeField = NewStringField(DATE_TIME_FIELD, dateTimeString, Field.Store.YES);
+ document.Add(dateTimeField);
+
+ return document;
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestDisjunctionMaxQuery.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestDisjunctionMaxQuery.cs b/src/Lucene.Net.Tests/Search/TestDisjunctionMaxQuery.cs
new file mode 100644
index 0000000..18db902
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestDisjunctionMaxQuery.cs
@@ -0,0 +1,570 @@
+using System;
+using System.Globalization;
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search
+{
+ using Lucene.Net.Index;
+ using NUnit.Framework;
+ using Analyzer = Lucene.Net.Analysis.Analyzer;
+ using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
+ using DefaultSimilarity = Lucene.Net.Search.Similarities.DefaultSimilarity;
+ using Directory = Lucene.Net.Store.Directory;
+ using DirectoryReader = Lucene.Net.Index.DirectoryReader;
+ using Document = Documents.Document;
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ using Field = Field;
+ using FieldInvertState = Lucene.Net.Index.FieldInvertState;
+ using FieldType = FieldType;
+ using IndexReader = Lucene.Net.Index.IndexReader;
+ using IndexWriter = Lucene.Net.Index.IndexWriter;
+ using IndexWriterConfig = Lucene.Net.Index.IndexWriterConfig;
+ using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+ using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
+ using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+ using Similarity = Lucene.Net.Search.Similarities.Similarity;
+ using SlowCompositeReaderWrapper = Lucene.Net.Index.SlowCompositeReaderWrapper;
+ using SpanQuery = Lucene.Net.Search.Spans.SpanQuery;
+ using SpanTermQuery = Lucene.Net.Search.Spans.SpanTermQuery;
+ using Term = Lucene.Net.Index.Term;
+ using TextField = TextField;
+
+ /// <summary>
+ /// Test of the DisjunctionMaxQuery.
+ ///
+ /// </summary>
+ [TestFixture]
+ public class TestDisjunctionMaxQuery : LuceneTestCase
+ {
+ /// <summary>
+ /// threshold for comparing floats </summary>
+ public static readonly float SCORE_COMP_THRESH = 0.0000f;
+
+ /// <summary>
+ /// Similarity to eliminate tf, idf and lengthNorm effects to isolate test
+ /// case.
+ ///
+ /// <p>
+ /// same as TestRankingSimilarity in TestRanking.zip from
+ /// http://issues.apache.org/jira/browse/LUCENE-323
+ /// </p>
+ /// </summary>
+ private class TestSimilarity : DefaultSimilarity
+ {
+ public TestSimilarity()
+ {
+ }
+
+ public override float Tf(float freq)
+ {
+ if (freq > 0.0f)
+ {
+ return 1.0f;
+ }
+ else
+ {
+ return 0.0f;
+ }
+ }
+
+ public override float LengthNorm(FieldInvertState state)
+ {
+ // Disable length norm
+ return state.Boost;
+ }
+
+ public override float Idf(long docFreq, long numDocs)
+ {
+ return 1.0f;
+ }
+ }
+
+ public Similarity Sim = new TestSimilarity();
+ public Directory Index;
+ public IndexReader r;
+ public IndexSearcher s;
+
+ private static readonly FieldType NonAnalyzedType = new FieldType(TextField.TYPE_STORED);
+
+ static TestDisjunctionMaxQuery()
+ {
+ NonAnalyzedType.IsTokenized = false;
+ }
+
+ [SetUp]
+ public override void SetUp()
+ {
+ base.SetUp();
+
+ Index = NewDirectory();
+ RandomIndexWriter writer = new RandomIndexWriter(Random(), Index, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetSimilarity(Sim).SetMergePolicy(NewLogMergePolicy()));
+
+ // hed is the most important field, dek is secondary
+
+ // d1 is an "ok" match for: albino elephant
+ {
+ Document d1 = new Document();
+ d1.Add(NewField("id", "d1", NonAnalyzedType)); // Field.Keyword("id",
+ // "d1"));
+ d1.Add(NewTextField("hed", "elephant", Field.Store.YES)); // Field.Text("hed", "elephant"));
+ d1.Add(NewTextField("dek", "elephant", Field.Store.YES)); // Field.Text("dek", "elephant"));
+ writer.AddDocument(d1);
+ }
+
+ // d2 is a "good" match for: albino elephant
+ {
+ Document d2 = new Document();
+ d2.Add(NewField("id", "d2", NonAnalyzedType)); // Field.Keyword("id",
+ // "d2"));
+ d2.Add(NewTextField("hed", "elephant", Field.Store.YES)); // Field.Text("hed", "elephant"));
+ d2.Add(NewTextField("dek", "albino", Field.Store.YES)); // Field.Text("dek",
+ // "albino"));
+ d2.Add(NewTextField("dek", "elephant", Field.Store.YES)); // Field.Text("dek", "elephant"));
+ writer.AddDocument(d2);
+ }
+
+ // d3 is a "better" match for: albino elephant
+ {
+ Document d3 = new Document();
+ d3.Add(NewField("id", "d3", NonAnalyzedType)); // Field.Keyword("id",
+ // "d3"));
+ d3.Add(NewTextField("hed", "albino", Field.Store.YES)); // Field.Text("hed",
+ // "albino"));
+ d3.Add(NewTextField("hed", "elephant", Field.Store.YES)); // Field.Text("hed", "elephant"));
+ writer.AddDocument(d3);
+ }
+
+ // d4 is the "best" match for: albino elephant
+ {
+ Document d4 = new Document();
+ d4.Add(NewField("id", "d4", NonAnalyzedType)); // Field.Keyword("id",
+ // "d4"));
+ d4.Add(NewTextField("hed", "albino", Field.Store.YES)); // Field.Text("hed",
+ // "albino"));
+ d4.Add(NewField("hed", "elephant", NonAnalyzedType)); // Field.Text("hed", "elephant"));
+ d4.Add(NewTextField("dek", "albino", Field.Store.YES)); // Field.Text("dek",
+ // "albino"));
+ writer.AddDocument(d4);
+ }
+
+ r = SlowCompositeReaderWrapper.Wrap(writer.Reader);
+ writer.Dispose();
+ s = NewSearcher(r);
+ s.Similarity = Sim;
+ }
+
+ [TearDown]
+ public override void TearDown()
+ {
+ r.Dispose();
+ Index.Dispose();
+ base.TearDown();
+ }
+
+ [Test]
+ public virtual void TestSkipToFirsttimeMiss()
+ {
+ DisjunctionMaxQuery dq = new DisjunctionMaxQuery(0.0f);
+ dq.Add(Tq("id", "d1"));
+ dq.Add(Tq("dek", "DOES_NOT_EXIST"));
+
+ QueryUtils.Check(Random(), dq, s, Similarity);
+ Assert.IsTrue(s.TopReaderContext is AtomicReaderContext);
+ Weight dw = s.CreateNormalizedWeight(dq);
+ AtomicReaderContext context = (AtomicReaderContext)s.TopReaderContext;
+ Scorer ds = dw.GetScorer(context, (context.AtomicReader).LiveDocs);
+ bool skipOk = ds.Advance(3) != DocIdSetIterator.NO_MORE_DOCS;
+ if (skipOk)
+ {
+ Assert.Fail("firsttime skipTo found a match? ... " + r.Document(ds.DocID).Get("id"));
+ }
+ }
+
+ [Test]
+ public virtual void TestSkipToFirsttimeHit()
+ {
+ DisjunctionMaxQuery dq = new DisjunctionMaxQuery(0.0f);
+ dq.Add(Tq("dek", "albino"));
+ dq.Add(Tq("dek", "DOES_NOT_EXIST"));
+ Assert.IsTrue(s.TopReaderContext is AtomicReaderContext);
+ QueryUtils.Check(Random(), dq, s, Similarity);
+ Weight dw = s.CreateNormalizedWeight(dq);
+ AtomicReaderContext context = (AtomicReaderContext)s.TopReaderContext;
+ Scorer ds = dw.GetScorer(context, (context.AtomicReader).LiveDocs);
+ Assert.IsTrue(ds.Advance(3) != DocIdSetIterator.NO_MORE_DOCS, "firsttime skipTo found no match");
+ Assert.AreEqual("d4", r.Document(ds.DocID).Get("id"), "found wrong docid");
+ }
+
+ [Test]
+ public virtual void TestSimpleEqualScores1()
+ {
+ DisjunctionMaxQuery q = new DisjunctionMaxQuery(0.0f);
+ q.Add(Tq("hed", "albino"));
+ q.Add(Tq("hed", "elephant"));
+ QueryUtils.Check(Random(), q, s, Similarity);
+
+ ScoreDoc[] h = s.Search(q, null, 1000).ScoreDocs;
+
+ try
+ {
+ Assert.AreEqual(4, h.Length, "all docs should match " + q.ToString());
+
+ float score = h[0].Score;
+ for (int i = 1; i < h.Length; i++)
+ {
+ Assert.AreEqual(score, h[i].Score, SCORE_COMP_THRESH, "score #" + i + " is not the same");
+ }
+ }
+ catch (Exception e)
+ {
+ PrintHits("testSimpleEqualScores1", h, s);
+ throw e;
+ }
+ }
+
+ [Test]
+ public virtual void TestSimpleEqualScores2()
+ {
+ DisjunctionMaxQuery q = new DisjunctionMaxQuery(0.0f);
+ q.Add(Tq("dek", "albino"));
+ q.Add(Tq("dek", "elephant"));
+ QueryUtils.Check(Random(), q, s, Similarity);
+
+ ScoreDoc[] h = s.Search(q, null, 1000).ScoreDocs;
+
+ try
+ {
+ Assert.AreEqual(3, h.Length, "3 docs should match " + q.ToString());
+ float score = h[0].Score;
+ for (int i = 1; i < h.Length; i++)
+ {
+ Assert.AreEqual(score, h[i].Score, SCORE_COMP_THRESH, "score #" + i + " is not the same");
+ }
+ }
+ catch (Exception e)
+ {
+ PrintHits("testSimpleEqualScores2", h, s);
+ throw e;
+ }
+ }
+
+ [Test]
+ public virtual void TestSimpleEqualScores3()
+ {
+ DisjunctionMaxQuery q = new DisjunctionMaxQuery(0.0f);
+ q.Add(Tq("hed", "albino"));
+ q.Add(Tq("hed", "elephant"));
+ q.Add(Tq("dek", "albino"));
+ q.Add(Tq("dek", "elephant"));
+ QueryUtils.Check(Random(), q, s, Similarity);
+
+ ScoreDoc[] h = s.Search(q, null, 1000).ScoreDocs;
+
+ try
+ {
+ Assert.AreEqual(4, h.Length, "all docs should match " + q.ToString());
+ float score = h[0].Score;
+ for (int i = 1; i < h.Length; i++)
+ {
+ Assert.AreEqual(score, h[i].Score, SCORE_COMP_THRESH, "score #" + i + " is not the same");
+ }
+ }
+ catch (Exception e)
+ {
+ PrintHits("testSimpleEqualScores3", h, s);
+ throw e;
+ }
+ }
+
+ [Test]
+ public virtual void TestSimpleTiebreaker()
+ {
+ DisjunctionMaxQuery q = new DisjunctionMaxQuery(0.01f);
+ q.Add(Tq("dek", "albino"));
+ q.Add(Tq("dek", "elephant"));
+ QueryUtils.Check(Random(), q, s, Similarity);
+
+ ScoreDoc[] h = s.Search(q, null, 1000).ScoreDocs;
+
+ try
+ {
+ Assert.AreEqual(3, h.Length, "3 docs should match " + q.ToString());
+ Assert.AreEqual("d2", s.Doc(h[0].Doc).Get("id"), "wrong first");
+ float score0 = h[0].Score;
+ float score1 = h[1].Score;
+ float score2 = h[2].Score;
+ Assert.IsTrue(score0 > score1, "d2 does not have better score then others: " + score0 + " >? " + score1);
+ Assert.AreEqual(score1, score2, SCORE_COMP_THRESH, "d4 and d1 don't have equal scores");
+ }
+ catch (Exception e)
+ {
+ PrintHits("testSimpleTiebreaker", h, s);
+ throw e;
+ }
+ }
+
+ [Test]
+ public virtual void TestBooleanRequiredEqualScores()
+ {
+ BooleanQuery q = new BooleanQuery();
+ {
+ DisjunctionMaxQuery q1 = new DisjunctionMaxQuery(0.0f);
+ q1.Add(Tq("hed", "albino"));
+ q1.Add(Tq("dek", "albino"));
+ q.Add(q1, Occur.MUST); // true,false);
+ QueryUtils.Check(Random(), q1, s, Similarity);
+ }
+ {
+ DisjunctionMaxQuery q2 = new DisjunctionMaxQuery(0.0f);
+ q2.Add(Tq("hed", "elephant"));
+ q2.Add(Tq("dek", "elephant"));
+ q.Add(q2, Occur.MUST); // true,false);
+ QueryUtils.Check(Random(), q2, s, Similarity);
+ }
+
+ QueryUtils.Check(Random(), q, s, Similarity);
+
+ ScoreDoc[] h = s.Search(q, null, 1000).ScoreDocs;
+
+ try
+ {
+ Assert.AreEqual(3, h.Length, "3 docs should match " + q.ToString());
+ float score = h[0].Score;
+ for (int i = 1; i < h.Length; i++)
+ {
+ Assert.AreEqual(score, h[i].Score, SCORE_COMP_THRESH, "score #" + i + " is not the same");
+ }
+ }
+ catch (Exception e)
+ {
+ PrintHits("testBooleanRequiredEqualScores1", h, s);
+ throw e;
+ }
+ }
+
+ [Test]
+ public virtual void TestBooleanOptionalNoTiebreaker()
+ {
+ BooleanQuery q = new BooleanQuery();
+ {
+ DisjunctionMaxQuery q1 = new DisjunctionMaxQuery(0.0f);
+ q1.Add(Tq("hed", "albino"));
+ q1.Add(Tq("dek", "albino"));
+ q.Add(q1, Occur.SHOULD); // false,false);
+ }
+ {
+ DisjunctionMaxQuery q2 = new DisjunctionMaxQuery(0.0f);
+ q2.Add(Tq("hed", "elephant"));
+ q2.Add(Tq("dek", "elephant"));
+ q.Add(q2, Occur.SHOULD); // false,false);
+ }
+ QueryUtils.Check(Random(), q, s, Similarity);
+
+ ScoreDoc[] h = s.Search(q, null, 1000).ScoreDocs;
+
+ try
+ {
+ Assert.AreEqual(4, h.Length, "4 docs should match " + q.ToString());
+ float score = h[0].Score;
+ for (int i = 1; i < h.Length - 1; i++) // note: -1
+ {
+ Assert.AreEqual(score, h[i].Score, SCORE_COMP_THRESH, "score #" + i + " is not the same");
+ }
+ Assert.AreEqual("d1", s.Doc(h[h.Length - 1].Doc).Get("id"), "wrong last");
+ float score1 = h[h.Length - 1].Score;
+ Assert.IsTrue(score > score1, "d1 does not have worse score then others: " + score + " >? " + score1);
+ }
+ catch (Exception e)
+ {
+ PrintHits("testBooleanOptionalNoTiebreaker", h, s);
+ throw e;
+ }
+ }
+
+ [Test]
+ public virtual void TestBooleanOptionalWithTiebreaker()
+ {
+ BooleanQuery q = new BooleanQuery();
+ {
+ DisjunctionMaxQuery q1 = new DisjunctionMaxQuery(0.01f);
+ q1.Add(Tq("hed", "albino"));
+ q1.Add(Tq("dek", "albino"));
+ q.Add(q1, Occur.SHOULD); // false,false);
+ }
+ {
+ DisjunctionMaxQuery q2 = new DisjunctionMaxQuery(0.01f);
+ q2.Add(Tq("hed", "elephant"));
+ q2.Add(Tq("dek", "elephant"));
+ q.Add(q2, Occur.SHOULD); // false,false);
+ }
+ QueryUtils.Check(Random(), q, s, Similarity);
+
+ ScoreDoc[] h = s.Search(q, null, 1000).ScoreDocs;
+
+ try
+ {
+ Assert.AreEqual(4, h.Length, "4 docs should match " + q.ToString());
+
+ float score0 = h[0].Score;
+ float score1 = h[1].Score;
+ float score2 = h[2].Score;
+ float score3 = h[3].Score;
+
+ string doc0 = s.Doc(h[0].Doc).Get("id");
+ string doc1 = s.Doc(h[1].Doc).Get("id");
+ string doc2 = s.Doc(h[2].Doc).Get("id");
+ string doc3 = s.Doc(h[3].Doc).Get("id");
+
+ Assert.IsTrue(doc0.Equals("d2") || doc0.Equals("d4"), "doc0 should be d2 or d4: " + doc0);
+ Assert.IsTrue(doc1.Equals("d2") || doc1.Equals("d4"), "doc1 should be d2 or d4: " + doc0);
+ Assert.AreEqual(score0, score1, SCORE_COMP_THRESH, "score0 and score1 should match");
+ Assert.AreEqual("d3", doc2, "wrong third");
+ Assert.IsTrue(score1 > score2, "d3 does not have worse score then d2 and d4: " + score1 + " >? " + score2);
+
+ Assert.AreEqual("d1", doc3, "wrong fourth");
+ Assert.IsTrue(score2 > score3, "d1 does not have worse score then d3: " + score2 + " >? " + score3);
+ }
+ catch (Exception e)
+ {
+ PrintHits("testBooleanOptionalWithTiebreaker", h, s);
+ throw e;
+ }
+ }
+
+ [Test]
+ public virtual void TestBooleanOptionalWithTiebreakerAndBoost()
+ {
+ BooleanQuery q = new BooleanQuery();
+ {
+ DisjunctionMaxQuery q1 = new DisjunctionMaxQuery(0.01f);
+ q1.Add(Tq("hed", "albino", 1.5f));
+ q1.Add(Tq("dek", "albino"));
+ q.Add(q1, Occur.SHOULD); // false,false);
+ }
+ {
+ DisjunctionMaxQuery q2 = new DisjunctionMaxQuery(0.01f);
+ q2.Add(Tq("hed", "elephant", 1.5f));
+ q2.Add(Tq("dek", "elephant"));
+ q.Add(q2, Occur.SHOULD); // false,false);
+ }
+ QueryUtils.Check(Random(), q, s, Similarity);
+
+ ScoreDoc[] h = s.Search(q, null, 1000).ScoreDocs;
+
+ try
+ {
+ Assert.AreEqual(4, h.Length, "4 docs should match " + q.ToString());
+
+ float score0 = h[0].Score;
+ float score1 = h[1].Score;
+ float score2 = h[2].Score;
+ float score3 = h[3].Score;
+
+ string doc0 = s.Doc(h[0].Doc).Get("id");
+ string doc1 = s.Doc(h[1].Doc).Get("id");
+ string doc2 = s.Doc(h[2].Doc).Get("id");
+ string doc3 = s.Doc(h[3].Doc).Get("id");
+
+ Assert.AreEqual("d4", doc0, "doc0 should be d4: ");
+ Assert.AreEqual("d3", doc1, "doc1 should be d3: ");
+ Assert.AreEqual("d2", doc2, "doc2 should be d2: ");
+ Assert.AreEqual("d1", doc3, "doc3 should be d1: ");
+
+ Assert.IsTrue(score0 > score1, "d4 does not have a better score then d3: " + score0 + " >? " + score1);
+ Assert.IsTrue(score1 > score2, "d3 does not have a better score then d2: " + score1 + " >? " + score2);
+ Assert.IsTrue(score2 > score3, "d3 does not have a better score then d1: " + score2 + " >? " + score3);
+ }
+ catch (Exception e)
+ {
+ PrintHits("TestBooleanOptionalWithTiebreakerAndBoost", h, s);
+ throw e;
+ }
+ }
+
+ // LUCENE-4477 / LUCENE-4401:
+ [Test]
+ public virtual void TestBooleanSpanQuery()
+ {
+ int hits = 0;
+ Directory directory = NewDirectory();
+ Analyzer indexerAnalyzer = new MockAnalyzer(Random());
+
+ IndexWriterConfig config = new IndexWriterConfig(TEST_VERSION_CURRENT, indexerAnalyzer);
+ IndexWriter writer = new IndexWriter(directory, config);
+ string FIELD = "content";
+ Document d = new Document();
+ d.Add(new TextField(FIELD, "clockwork orange", Field.Store.YES));
+ writer.AddDocument(d);
+ writer.Dispose();
+
+ IndexReader indexReader = DirectoryReader.Open(directory);
+ IndexSearcher searcher = NewSearcher(indexReader);
+
+ DisjunctionMaxQuery query = new DisjunctionMaxQuery(1.0f);
+ SpanQuery sq1 = new SpanTermQuery(new Term(FIELD, "clockwork"));
+ SpanQuery sq2 = new SpanTermQuery(new Term(FIELD, "clckwork"));
+ query.Add(sq1);
+ query.Add(sq2);
+ TopScoreDocCollector collector = TopScoreDocCollector.Create(1000, true);
+ searcher.Search(query, collector);
+ hits = collector.GetTopDocs().ScoreDocs.Length;
+ foreach (ScoreDoc scoreDoc in collector.GetTopDocs().ScoreDocs)
+ {
+ Console.WriteLine(scoreDoc.Doc);
+ }
+ indexReader.Dispose();
+ Assert.AreEqual(hits, 1);
+ directory.Dispose();
+ }
+
+ /// <summary>
+ /// macro </summary>
+ protected internal virtual Query Tq(string f, string t)
+ {
+ return new TermQuery(new Term(f, t));
+ }
+
+ /// <summary>
+ /// macro </summary>
+ protected internal virtual Query Tq(string f, string t, float b)
+ {
+ Query q = Tq(f, t);
+ q.Boost = b;
+ return q;
+ }
+
+ protected internal virtual void PrintHits(string test, ScoreDoc[] h, IndexSearcher searcher)
+ {
+ Console.Error.WriteLine("------- " + test + " -------");
+
+ //DecimalFormat f = new DecimalFormat("0.000000000", DecimalFormatSymbols.getInstance(Locale.ROOT));
+
+ NumberFormatInfo f = new NumberFormatInfo();
+ f.NumberDecimalSeparator = ".";
+
+ for (int i = 0; i < h.Length; i++)
+ {
+ Document d = searcher.Doc(h[i].Doc);
+ decimal score = (decimal)h[i].Score;
+ Console.Error.WriteLine("#" + i + ": " + score.ToString(f) + " - " + d.Get("id"));
+ }
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestDocBoost.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestDocBoost.cs b/src/Lucene.Net.Tests/Search/TestDocBoost.cs
new file mode 100644
index 0000000..29fe7d6
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestDocBoost.cs
@@ -0,0 +1,122 @@
+using System;
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search
+{
+
+ using NUnit.Framework;
+ using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
+ using Directory = Lucene.Net.Store.Directory;
+ using IndexReader = Lucene.Net.Index.IndexReader;
+ using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
+ using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+ using Term = Lucene.Net.Index.Term;
+
+ /// <summary>
+ /// Document boost unit test.
+ ///
+ ///
+ /// </summary>
+ [TestFixture]
+ public class TestDocBoost : LuceneTestCase
+ {
+ [Test]
+ public virtual void TestDocBoost_Mem()
+ {
+ Directory store = NewDirectory();
+ RandomIndexWriter writer = new RandomIndexWriter(Random(), store, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy()));
+
+ Field f1 = NewTextField("field", "word", Field.Store.YES);
+ Field f2 = NewTextField("field", "word", Field.Store.YES);
+ f2.Boost = 2.0f;
+
+ Documents.Document d1 = new Documents.Document();
+ Documents.Document d2 = new Documents.Document();
+
+ d1.Add(f1); // boost = 1
+ d2.Add(f2); // boost = 2
+
+ writer.AddDocument(d1);
+ writer.AddDocument(d2);
+
+ IndexReader reader = writer.Reader;
+ writer.Dispose();
+
+ float[] scores = new float[4];
+
+ IndexSearcher searcher = NewSearcher(reader);
+ searcher.Search(new TermQuery(new Term("field", "word")), new CollectorAnonymousInnerClassHelper(this, scores));
+
+ float lastScore = 0.0f;
+
+ for (int i = 0; i < 2; i++)
+ {
+ if (VERBOSE)
+ {
+ Console.WriteLine(searcher.Explain(new TermQuery(new Term("field", "word")), i));
+ }
+ Assert.IsTrue(scores[i] > lastScore, "score: " + scores[i] + " should be > lastScore: " + lastScore);
+ lastScore = scores[i];
+ }
+
+ reader.Dispose();
+ store.Dispose();
+ }
+
+ private class CollectorAnonymousInnerClassHelper : ICollector
+ {
+ private readonly TestDocBoost OuterInstance;
+
+ private float[] Scores;
+
+ public CollectorAnonymousInnerClassHelper(TestDocBoost outerInstance, float[] scores)
+ {
+ this.OuterInstance = outerInstance;
+ this.Scores = scores;
+ @base = 0;
+ }
+
+ private int @base;
+ private Scorer scorer;
+
+ public virtual void SetScorer(Scorer scorer)
+ {
+ this.scorer = scorer;
+ }
+
+ public virtual void Collect(int doc)
+ {
+ Scores[doc + @base] = scorer.GetScore();
+ }
+
+ public virtual void SetNextReader(AtomicReaderContext context)
+ {
+ @base = context.DocBase;
+ }
+
+ public virtual bool AcceptsDocsOutOfOrder
+ {
+ get { return true; }
+ }
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestDocIdSet.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestDocIdSet.cs b/src/Lucene.Net.Tests/Search/TestDocIdSet.cs
new file mode 100644
index 0000000..440fe7d
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestDocIdSet.cs
@@ -0,0 +1,254 @@
+using System;
+using System.Collections.Generic;
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search
+{
+ using Lucene.Net.Support;
+ using NUnit.Framework;
+ using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
+ using IBits = Lucene.Net.Util.IBits;
+ using Directory = Lucene.Net.Store.Directory;
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ using Document = Documents.Document;
+ using Field = Field;
+ using IndexReader = Lucene.Net.Index.IndexReader;
+ using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+ using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+
+ [TestFixture]
+ public class TestDocIdSet : LuceneTestCase
+ {
+ [Test]
+ public virtual void TestFilteredDocIdSet()
+ {
+ const int maxdoc = 10;
+ DocIdSet innerSet = new DocIdSetAnonymousInnerClassHelper(this, maxdoc);
+
+ DocIdSet filteredSet = new FilteredDocIdSetAnonymousInnerClassHelper(this, innerSet);
+
+ DocIdSetIterator iter = filteredSet.GetIterator();
+ List<int?> list = new List<int?>();
+ int doc = iter.Advance(3);
+ if (doc != DocIdSetIterator.NO_MORE_DOCS)
+ {
+ list.Add(Convert.ToInt32(doc));
+ while ((doc = iter.NextDoc()) != DocIdSetIterator.NO_MORE_DOCS)
+ {
+ list.Add(Convert.ToInt32(doc));
+ }
+ }
+
+ int[] docs = new int[list.Count];
+ int c = 0;
+ IEnumerator<int?> intIter = list.GetEnumerator();
+ while (intIter.MoveNext())
+ {
+ docs[c++] = (int)intIter.Current;
+ }
+ int[] answer = new int[] { 4, 6, 8 };
+ bool same = Arrays.Equals(answer, docs);
+ if (!same)
+ {
+ Console.WriteLine("answer: " + Arrays.ToString(answer));
+ Console.WriteLine("gotten: " + Arrays.ToString(docs));
+ Assert.Fail();
+ }
+ }
+
+ private class DocIdSetAnonymousInnerClassHelper : DocIdSet
+ {
+ private readonly TestDocIdSet OuterInstance;
+
+ private int Maxdoc;
+
+ public DocIdSetAnonymousInnerClassHelper(TestDocIdSet outerInstance, int maxdoc)
+ {
+ this.OuterInstance = outerInstance;
+ this.Maxdoc = maxdoc;
+ }
+
+ public override DocIdSetIterator GetIterator()
+ {
+ return new DocIdSetIteratorAnonymousInnerClassHelper(this);
+ }
+
+ private class DocIdSetIteratorAnonymousInnerClassHelper : DocIdSetIterator
+ {
+ private readonly DocIdSetAnonymousInnerClassHelper OuterInstance;
+
+ public DocIdSetIteratorAnonymousInnerClassHelper(DocIdSetAnonymousInnerClassHelper outerInstance)
+ {
+ this.OuterInstance = outerInstance;
+ docid = -1;
+ }
+
+ internal int docid;
+
+ public override int DocID
+ {
+ get { return docid; }
+ }
+
+ public override int NextDoc()
+ {
+ docid++;
+ return docid < OuterInstance.Maxdoc ? docid : (docid = NO_MORE_DOCS);
+ }
+
+ public override int Advance(int target)
+ {
+ return SlowAdvance(target);
+ }
+
+ public override long GetCost()
+ {
+ return 1;
+ }
+ }
+ }
+
+ private class FilteredDocIdSetAnonymousInnerClassHelper : FilteredDocIdSet
+ {
+ private readonly TestDocIdSet OuterInstance;
+
+ public FilteredDocIdSetAnonymousInnerClassHelper(TestDocIdSet outerInstance, DocIdSet innerSet)
+ : base(innerSet)
+ {
+ this.OuterInstance = outerInstance;
+ }
+
+ protected override bool Match(int docid)
+ {
+ return docid % 2 == 0; //validate only even docids
+ }
+ }
+
+ [Test]
+ public virtual void TestNullDocIdSet()
+ {
+ // Tests that if a Filter produces a null DocIdSet, which is given to
+ // IndexSearcher, everything works fine. this came up in LUCENE-1754.
+ Directory dir = NewDirectory();
+ RandomIndexWriter writer = new RandomIndexWriter(Random(), dir, Similarity, TimeZone);
+ Document doc = new Document();
+ doc.Add(NewStringField("c", "val", Field.Store.NO));
+ writer.AddDocument(doc);
+ IndexReader reader = writer.Reader;
+ writer.Dispose();
+
+ // First verify the document is searchable.
+ IndexSearcher searcher = NewSearcher(reader);
+ Assert.AreEqual(1, searcher.Search(new MatchAllDocsQuery(), 10).TotalHits);
+
+ // Now search w/ a Filter which returns a null DocIdSet
+ Filter f = new FilterAnonymousInnerClassHelper(this);
+
+ Assert.AreEqual(0, searcher.Search(new MatchAllDocsQuery(), f, 10).TotalHits);
+ reader.Dispose();
+ dir.Dispose();
+ }
+
+ private class FilterAnonymousInnerClassHelper : Filter
+ {
+ private readonly TestDocIdSet OuterInstance;
+
+ public FilterAnonymousInnerClassHelper(TestDocIdSet outerInstance)
+ {
+ this.OuterInstance = outerInstance;
+ }
+
+ public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs)
+ {
+ return null;
+ }
+ }
+
+ [Test]
+ public virtual void TestNullIteratorFilteredDocIdSet()
+ {
+ Directory dir = NewDirectory();
+ RandomIndexWriter writer = new RandomIndexWriter(Random(), dir, Similarity, TimeZone);
+ Document doc = new Document();
+ doc.Add(NewStringField("c", "val", Field.Store.NO));
+ writer.AddDocument(doc);
+ IndexReader reader = writer.Reader;
+ writer.Dispose();
+
+ // First verify the document is searchable.
+ IndexSearcher searcher = NewSearcher(reader);
+ Assert.AreEqual(1, searcher.Search(new MatchAllDocsQuery(), 10).TotalHits);
+
+ // Now search w/ a Filter which returns a null DocIdSet
+ Filter f = new FilterAnonymousInnerClassHelper2(this);
+
+ Assert.AreEqual(0, searcher.Search(new MatchAllDocsQuery(), f, 10).TotalHits);
+ reader.Dispose();
+ dir.Dispose();
+ }
+
+ private class FilterAnonymousInnerClassHelper2 : Filter
+ {
+ private readonly TestDocIdSet OuterInstance;
+
+ public FilterAnonymousInnerClassHelper2(TestDocIdSet outerInstance)
+ {
+ this.OuterInstance = outerInstance;
+ }
+
+ public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs)
+ {
+ DocIdSet innerNullIteratorSet = new DocIdSetAnonymousInnerClassHelper2(this);
+ return new FilteredDocIdSetAnonymousInnerClassHelper2(this, innerNullIteratorSet);
+ }
+
+ private class DocIdSetAnonymousInnerClassHelper2 : DocIdSet
+ {
+ private readonly FilterAnonymousInnerClassHelper2 OuterInstance;
+
+ public DocIdSetAnonymousInnerClassHelper2(FilterAnonymousInnerClassHelper2 outerInstance)
+ {
+ this.OuterInstance = outerInstance;
+ }
+
+ public override DocIdSetIterator GetIterator()
+ {
+ return null;
+ }
+ }
+
+ private class FilteredDocIdSetAnonymousInnerClassHelper2 : FilteredDocIdSet
+ {
+ private readonly FilterAnonymousInnerClassHelper2 OuterInstance;
+
+ public FilteredDocIdSetAnonymousInnerClassHelper2(FilterAnonymousInnerClassHelper2 outerInstance, DocIdSet innerNullIteratorSet)
+ : base(innerNullIteratorSet)
+ {
+ this.OuterInstance = outerInstance;
+ }
+
+ protected override bool Match(int docid)
+ {
+ return true;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestDocTermOrdsRangeFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestDocTermOrdsRangeFilter.cs b/src/Lucene.Net.Tests/Search/TestDocTermOrdsRangeFilter.cs
new file mode 100644
index 0000000..f6b2706
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestDocTermOrdsRangeFilter.cs
@@ -0,0 +1,149 @@
+using Lucene.Net.Documents;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+
+namespace Lucene.Net.Search
+{
+ using Lucene.Net.Randomized.Generators;
+ using BytesRef = Lucene.Net.Util.BytesRef;
+ using Directory = Lucene.Net.Store.Directory;
+ using Document = Documents.Document;
+ using Field = Field;
+ using IndexReader = Lucene.Net.Index.IndexReader;
+ using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
+ using MockTokenizer = Lucene.Net.Analysis.MockTokenizer;
+ using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+ using SortedSetDocValuesField = SortedSetDocValuesField;
+ using Term = Lucene.Net.Index.Term;
+ using TestUtil = Lucene.Net.Util.TestUtil;
+ using UnicodeUtil = Lucene.Net.Util.UnicodeUtil;
+
+ /// <summary>
+ /// Tests the DocTermOrdsRangeFilter
+ /// </summary>
+ [TestFixture]
+ public class TestDocTermOrdsRangeFilter : LuceneTestCase
+ {
+ protected internal IndexSearcher Searcher1;
+ protected internal IndexSearcher Searcher2;
+ private IndexReader Reader;
+ private Directory Dir;
+ protected internal string FieldName;
+
+ [SetUp]
+ public override void SetUp()
+ {
+ base.SetUp();
+ Dir = NewDirectory();
+ FieldName = Random().NextBoolean() ? "field" : ""; // sometimes use an empty string as field name
+ RandomIndexWriter writer = new RandomIndexWriter(Random(), Dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random(), MockTokenizer.KEYWORD, false)).SetMaxBufferedDocs(TestUtil.NextInt(Random(), 50, 1000)));
+ List<string> terms = new List<string>();
+ int num = AtLeast(200);
+ for (int i = 0; i < num; i++)
+ {
+ Document doc = new Document();
+ doc.Add(NewStringField("id", Convert.ToString(i), Field.Store.NO));
+ int numTerms = Random().Next(4);
+ for (int j = 0; j < numTerms; j++)
+ {
+ string s = TestUtil.RandomUnicodeString(Random());
+ doc.Add(NewStringField(FieldName, s, Field.Store.NO));
+ // if the default codec doesn't support sortedset, we will uninvert at search time
+ if (DefaultCodecSupportsSortedSet())
+ {
+ doc.Add(new SortedSetDocValuesField(FieldName, new BytesRef(s)));
+ }
+ terms.Add(s);
+ }
+ writer.AddDocument(doc);
+ }
+
+ if (VERBOSE)
+ {
+ // utf16 order
+ terms.Sort();
+ Console.WriteLine("UTF16 order:");
+ foreach (string s in terms)
+ {
+ Console.WriteLine(" " + UnicodeUtil.ToHexString(s));
+ }
+ }
+
+ int numDeletions = Random().Next(num / 10);
+ for (int i = 0; i < numDeletions; i++)
+ {
+ writer.DeleteDocuments(new Term("id", Convert.ToString(Random().Next(num))));
+ }
+
+ Reader = writer.Reader;
+ Searcher1 = NewSearcher(Reader);
+ Searcher2 = NewSearcher(Reader);
+ writer.Dispose();
+ }
+
+ [TearDown]
+ public override void TearDown()
+ {
+ Reader.Dispose();
+ Dir.Dispose();
+ base.TearDown();
+ }
+
+ /// <summary>
+ /// test a bunch of random ranges </summary>
+ [Test]
+ public virtual void TestRanges()
+ {
+ int num = AtLeast(1000);
+ for (int i = 0; i < num; i++)
+ {
+ BytesRef lowerVal = new BytesRef(TestUtil.RandomUnicodeString(Random()));
+ BytesRef upperVal = new BytesRef(TestUtil.RandomUnicodeString(Random()));
+ if (upperVal.CompareTo(lowerVal) < 0)
+ {
+ AssertSame(upperVal, lowerVal, Random().NextBoolean(), Random().NextBoolean());
+ }
+ else
+ {
+ AssertSame(lowerVal, upperVal, Random().NextBoolean(), Random().NextBoolean());
+ }
+ }
+ }
+
+ /// <summary>
+ /// check that the # of hits is the same as if the query
+ /// is run against the inverted index
+ /// </summary>
+ protected internal virtual void AssertSame(BytesRef lowerVal, BytesRef upperVal, bool includeLower, bool includeUpper)
+ {
+ Query docValues = new ConstantScoreQuery(DocTermOrdsRangeFilter.NewBytesRefRange(FieldName, lowerVal, upperVal, includeLower, includeUpper));
+ MultiTermQuery inverted = new TermRangeQuery(FieldName, lowerVal, upperVal, includeLower, includeUpper);
+ inverted.MultiTermRewriteMethod = (MultiTermQuery.CONSTANT_SCORE_FILTER_REWRITE);
+
+ TopDocs invertedDocs = Searcher1.Search(inverted, 25);
+ TopDocs docValuesDocs = Searcher2.Search(docValues, 25);
+
+ CheckHits.CheckEqual(inverted, invertedDocs.ScoreDocs, docValuesDocs.ScoreDocs);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestDocTermOrdsRewriteMethod.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestDocTermOrdsRewriteMethod.cs b/src/Lucene.Net.Tests/Search/TestDocTermOrdsRewriteMethod.cs
new file mode 100644
index 0000000..7389e9e
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestDocTermOrdsRewriteMethod.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Generic;
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search
+{
+ using Lucene.Net.Randomized.Generators;
+ using NUnit.Framework;
+ using AutomatonTestUtil = Lucene.Net.Util.Automaton.AutomatonTestUtil;
+ using BytesRef = Lucene.Net.Util.BytesRef;
+ using Directory = Lucene.Net.Store.Directory;
+ using Document = Documents.Document;
+ using Field = Field;
+ using IndexReader = Lucene.Net.Index.IndexReader;
+ using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
+ using MockTokenizer = Lucene.Net.Analysis.MockTokenizer;
+ using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+ using RegExp = Lucene.Net.Util.Automaton.RegExp;
+ using SortedSetDocValuesField = SortedSetDocValuesField;
+ using Term = Lucene.Net.Index.Term;
+ using TestUtil = Lucene.Net.Util.TestUtil;
+ using UnicodeUtil = Lucene.Net.Util.UnicodeUtil;
+
+ /// <summary>
+ /// Tests the DocTermOrdsRewriteMethod
+ /// </summary>
+ [TestFixture]
+ public class TestDocTermOrdsRewriteMethod : LuceneTestCase
+ {
+ protected internal IndexSearcher Searcher1;
+ protected internal IndexSearcher Searcher2;
+ private IndexReader Reader;
+ private Directory Dir;
+ protected internal string FieldName;
+
+ [SetUp]
+ public override void SetUp()
+ {
+ base.SetUp();
+ Dir = NewDirectory();
+ FieldName = Random().NextBoolean() ? "field" : ""; // sometimes use an empty string as field name
+ RandomIndexWriter writer = new RandomIndexWriter(Random(), Dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random(), MockTokenizer.KEYWORD, false)).SetMaxBufferedDocs(TestUtil.NextInt(Random(), 50, 1000)));
+ List<string> terms = new List<string>();
+ int num = AtLeast(200);
+ for (int i = 0; i < num; i++)
+ {
+ Document doc = new Document();
+ doc.Add(NewStringField("id", Convert.ToString(i), Field.Store.NO));
+ int numTerms = Random().Next(4);
+ for (int j = 0; j < numTerms; j++)
+ {
+ string s = TestUtil.RandomUnicodeString(Random());
+ doc.Add(NewStringField(FieldName, s, Field.Store.NO));
+ // if the default codec doesn't support sortedset, we will uninvert at search time
+ if (DefaultCodecSupportsSortedSet())
+ {
+ doc.Add(new SortedSetDocValuesField(FieldName, new BytesRef(s)));
+ }
+ terms.Add(s);
+ }
+ writer.AddDocument(doc);
+ }
+
+ if (VERBOSE)
+ {
+ // utf16 order
+ terms.Sort();
+ Console.WriteLine("UTF16 order:");
+ foreach (string s in terms)
+ {
+ Console.WriteLine(" " + UnicodeUtil.ToHexString(s));
+ }
+ }
+
+ int numDeletions = Random().Next(num / 10);
+ for (int i = 0; i < numDeletions; i++)
+ {
+ writer.DeleteDocuments(new Term("id", Convert.ToString(Random().Next(num))));
+ }
+
+ Reader = writer.Reader;
+ Searcher1 = NewSearcher(Reader);
+ Searcher2 = NewSearcher(Reader);
+ writer.Dispose();
+ }
+
+ [TearDown]
+ public override void TearDown()
+ {
+ Reader.Dispose();
+ Dir.Dispose();
+ base.TearDown();
+ }
+
+ /// <summary>
+ /// test a bunch of random regular expressions </summary>
+ [Test]
+ public virtual void TestRegexps()
+ {
+ int num = AtLeast(1000);
+ for (int i = 0; i < num; i++)
+ {
+ string reg = AutomatonTestUtil.RandomRegexp(Random());
+ if (VERBOSE)
+ {
+ Console.WriteLine("TEST: regexp=" + reg);
+ }
+ AssertSame(reg);
+ }
+ }
+
+ /// <summary>
+ /// check that the # of hits is the same as if the query
+ /// is run against the inverted index
+ /// </summary>
+ protected internal virtual void AssertSame(string regexp)
+ {
+ RegexpQuery docValues = new RegexpQuery(new Term(FieldName, regexp), RegExp.NONE);
+ docValues.MultiTermRewriteMethod = (new DocTermOrdsRewriteMethod());
+ RegexpQuery inverted = new RegexpQuery(new Term(FieldName, regexp), RegExp.NONE);
+
+ TopDocs invertedDocs = Searcher1.Search(inverted, 25);
+ TopDocs docValuesDocs = Searcher2.Search(docValues, 25);
+
+ CheckHits.CheckEqual(inverted, invertedDocs.ScoreDocs, docValuesDocs.ScoreDocs);
+ }
+
+ [Test]
+ public virtual void TestEquals()
+ {
+ RegexpQuery a1 = new RegexpQuery(new Term(FieldName, "[aA]"), RegExp.NONE);
+ RegexpQuery a2 = new RegexpQuery(new Term(FieldName, "[aA]"), RegExp.NONE);
+ RegexpQuery b = new RegexpQuery(new Term(FieldName, "[bB]"), RegExp.NONE);
+ Assert.AreEqual(a1, a2);
+ Assert.IsFalse(a1.Equals(b));
+
+ a1.MultiTermRewriteMethod = (new DocTermOrdsRewriteMethod());
+ a2.MultiTermRewriteMethod = (new DocTermOrdsRewriteMethod());
+ b.MultiTermRewriteMethod = (new DocTermOrdsRewriteMethod());
+ Assert.AreEqual(a1, a2);
+ Assert.IsFalse(a1.Equals(b));
+ QueryUtils.Check(a1);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestDocValuesScoring.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestDocValuesScoring.cs b/src/Lucene.Net.Tests/Search/TestDocValuesScoring.cs
new file mode 100644
index 0000000..ebd3e72
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestDocValuesScoring.cs
@@ -0,0 +1,233 @@
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search
+{
+ using Lucene.Net.Index;
+ using NUnit.Framework;
+ using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
+ using BytesRef = Lucene.Net.Util.BytesRef;
+ using Directory = Lucene.Net.Store.Directory;
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ using Document = Documents.Document;
+ using Field = Field;
+ using FieldInvertState = Lucene.Net.Index.FieldInvertState;
+ using SingleDocValuesField = SingleDocValuesField;
+ using IndexReader = Lucene.Net.Index.IndexReader;
+ using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+ using PerFieldSimilarityWrapper = Lucene.Net.Search.Similarities.PerFieldSimilarityWrapper;
+ using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+ using Similarity = Lucene.Net.Search.Similarities.Similarity;
+ using Term = Lucene.Net.Index.Term;
+
+ /// <summary>
+ /// Tests the use of indexdocvalues in scoring.
+ ///
+ /// In the example, a docvalues field is used as a per-document boost (separate from the norm)
+ /// @lucene.experimental
+ /// </summary>
+ [SuppressCodecs("Lucene3x")]
+ [TestFixture]
+ public class TestDocValuesScoring : LuceneTestCase
+ {
+ private const float SCORE_EPSILON = 0.001f; // for comparing floats
+
+ [Test]
+ public virtual void TestSimple()
+ {
+ Directory dir = NewDirectory();
+ RandomIndexWriter iw = new RandomIndexWriter(Random(), dir, Similarity, TimeZone);
+ Document doc = new Document();
+ Field field = NewTextField("foo", "", Field.Store.NO);
+ doc.Add(field);
+ Field dvField = new SingleDocValuesField("foo_boost", 0.0F);
+ doc.Add(dvField);
+ Field field2 = NewTextField("bar", "", Field.Store.NO);
+ doc.Add(field2);
+
+ field.SetStringValue("quick brown fox");
+ field2.SetStringValue("quick brown fox");
+ dvField.SetSingleValue(2f); // boost x2
+ iw.AddDocument(doc);
+ field.SetStringValue("jumps over lazy brown dog");
+ field2.SetStringValue("jumps over lazy brown dog");
+ dvField.SetSingleValue(4f); // boost x4
+ iw.AddDocument(doc);
+ IndexReader ir = iw.Reader;
+ iw.Dispose();
+
+ // no boosting
+ IndexSearcher searcher1 = NewSearcher(ir, false, Similarity);
+ Similarity @base = searcher1.Similarity;
+ // boosting
+ IndexSearcher searcher2 = NewSearcher(ir, false, Similarity);
+ searcher2.Similarity = new PerFieldSimilarityWrapperAnonymousInnerClassHelper(this, field, @base);
+
+ // in this case, we searched on field "foo". first document should have 2x the score.
+ TermQuery tq = new TermQuery(new Term("foo", "quick"));
+ QueryUtils.Check(Random(), tq, searcher1, Similarity);
+ QueryUtils.Check(Random(), tq, searcher2, Similarity);
+
+ TopDocs noboost = searcher1.Search(tq, 10);
+ TopDocs boost = searcher2.Search(tq, 10);
+ Assert.AreEqual(1, noboost.TotalHits);
+ Assert.AreEqual(1, boost.TotalHits);
+
+ //System.out.println(searcher2.Explain(tq, boost.ScoreDocs[0].Doc));
+ Assert.AreEqual(boost.ScoreDocs[0].Score, noboost.ScoreDocs[0].Score * 2f, SCORE_EPSILON);
+
+ // this query matches only the second document, which should have 4x the score.
+ tq = new TermQuery(new Term("foo", "jumps"));
+ QueryUtils.Check(Random(), tq, searcher1, Similarity);
+ QueryUtils.Check(Random(), tq, searcher2, Similarity);
+
+ noboost = searcher1.Search(tq, 10);
+ boost = searcher2.Search(tq, 10);
+ Assert.AreEqual(1, noboost.TotalHits);
+ Assert.AreEqual(1, boost.TotalHits);
+
+ Assert.AreEqual(boost.ScoreDocs[0].Score, noboost.ScoreDocs[0].Score * 4f, SCORE_EPSILON);
+
+ // search on on field bar just for kicks, nothing should happen, since we setup
+ // our sim provider to only use foo_boost for field foo.
+ tq = new TermQuery(new Term("bar", "quick"));
+ QueryUtils.Check(Random(), tq, searcher1, Similarity);
+ QueryUtils.Check(Random(), tq, searcher2, Similarity);
+
+ noboost = searcher1.Search(tq, 10);
+ boost = searcher2.Search(tq, 10);
+ Assert.AreEqual(1, noboost.TotalHits);
+ Assert.AreEqual(1, boost.TotalHits);
+
+ Assert.AreEqual(boost.ScoreDocs[0].Score, noboost.ScoreDocs[0].Score, SCORE_EPSILON);
+
+ ir.Dispose();
+ dir.Dispose();
+ }
+
+ private class PerFieldSimilarityWrapperAnonymousInnerClassHelper : PerFieldSimilarityWrapper
+ {
+ private readonly TestDocValuesScoring OuterInstance;
+
+ private Field Field;
+ private Similarity @base;
+
+ public PerFieldSimilarityWrapperAnonymousInnerClassHelper(TestDocValuesScoring outerInstance, Field field, Similarity @base)
+ {
+ this.OuterInstance = outerInstance;
+ this.Field = field;
+ this.@base = @base;
+ fooSim = new BoostingSimilarity(@base, "foo_boost");
+ }
+
+ internal readonly Similarity fooSim;
+
+ public override Similarity Get(string field)
+ {
+ return "foo".Equals(field) ? fooSim : @base;
+ }
+
+ public override float Coord(int overlap, int maxOverlap)
+ {
+ return @base.Coord(overlap, maxOverlap);
+ }
+
+ public override float QueryNorm(float sumOfSquaredWeights)
+ {
+ return @base.QueryNorm(sumOfSquaredWeights);
+ }
+ }
+
+ /// <summary>
+ /// Similarity that wraps another similarity and boosts the final score
+ /// according to whats in a docvalues field.
+ ///
+ /// @lucene.experimental
+ /// </summary>
+ internal class BoostingSimilarity : Similarity
+ {
+ internal readonly Similarity Sim;
+ internal readonly string BoostField;
+
+ public BoostingSimilarity(Similarity sim, string boostField)
+ {
+ this.Sim = sim;
+ this.BoostField = boostField;
+ }
+
+ public override long ComputeNorm(FieldInvertState state)
+ {
+ return Sim.ComputeNorm(state);
+ }
+
+ public override SimWeight ComputeWeight(float queryBoost, CollectionStatistics collectionStats, params TermStatistics[] termStats)
+ {
+ return Sim.ComputeWeight(queryBoost, collectionStats, termStats);
+ }
+
+ public override SimScorer GetSimScorer(SimWeight stats, AtomicReaderContext context)
+ {
+ SimScorer sub = Sim.GetSimScorer(stats, context);
+ FieldCache.Singles values = FieldCache.DEFAULT.GetSingles(context.AtomicReader, BoostField, false);
+
+ return new SimScorerAnonymousInnerClassHelper(this, sub, values);
+ }
+
+ private class SimScorerAnonymousInnerClassHelper : SimScorer
+ {
+ private readonly BoostingSimilarity OuterInstance;
+
+ private SimScorer Sub;
+ private FieldCache.Singles Values;
+
+ public SimScorerAnonymousInnerClassHelper(BoostingSimilarity outerInstance, SimScorer sub, FieldCache.Singles values)
+ {
+ this.OuterInstance = outerInstance;
+ this.Sub = sub;
+ this.Values = values;
+ }
+
+ public override float Score(int doc, float freq)
+ {
+ return Values.Get(doc) * Sub.Score(doc, freq);
+ }
+
+ public override float ComputeSlopFactor(int distance)
+ {
+ return Sub.ComputeSlopFactor(distance);
+ }
+
+ public override float ComputePayloadFactor(int doc, int start, int end, BytesRef payload)
+ {
+ return Sub.ComputePayloadFactor(doc, start, end, payload);
+ }
+
+ public override Explanation Explain(int doc, Explanation freq)
+ {
+ Explanation boostExplanation = new Explanation(Values.Get(doc), "indexDocValue(" + OuterInstance.BoostField + ")");
+ Explanation simExplanation = Sub.Explain(doc, freq);
+ Explanation expl = new Explanation(boostExplanation.Value * simExplanation.Value, "product of:");
+ expl.AddDetail(boostExplanation);
+ expl.AddDetail(simExplanation);
+ return expl;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file