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:53 UTC

[05/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/TestFieldCacheRangeFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestFieldCacheRangeFilter.cs b/src/Lucene.Net.Tests/Search/TestFieldCacheRangeFilter.cs
new file mode 100644
index 0000000..63f77d9
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestFieldCacheRangeFilter.cs
@@ -0,0 +1,613 @@
+using Lucene.Net.Documents;
+using NUnit.Framework;
+using System;
+using System.IO;
+
+namespace Lucene.Net.Search
+{
+    /*
+    * 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 Directory = Lucene.Net.Store.Directory;
+    using DirectoryReader = Lucene.Net.Index.DirectoryReader;
+    using Document = Documents.Document;
+    using Field = Field;
+    using IndexReader = Lucene.Net.Index.IndexReader;
+    using IndexWriter = Lucene.Net.Index.IndexWriter;
+    using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
+    using Term = Lucene.Net.Index.Term;
+
+    /// <summary>
+    /// A basic 'positive' Unit test class for the FieldCacheRangeFilter class.
+    ///
+    /// <p>
+    /// NOTE: at the moment, this class only tests for 'positive' results,
+    /// it does not verify the results to ensure there are no 'false positives',
+    /// nor does it adequately test 'negative' results.  It also does not test
+    /// that garbage in results in an Exception.
+    /// </summary>
+    [TestFixture]
+    public class TestFieldCacheRangeFilter : BaseTestRangeFilter
+    {
+        /// <summary>
+        /// LUCENENET specific. Ensure we have an infostream attached to the default FieldCache
+        /// when running the tests. In Java, this was done in the Core.Search.TestFieldCache.TestInfoStream() 
+        /// method (which polluted the state of these tests), but we need to make the tests self-contained 
+        /// so they can be run correctly regardless of order. Not setting the InfoStream skips an execution
+        /// path within these tests, so we should do it to make sure we test all of the code.
+        /// </summary>
+        public override void SetUp()
+        {
+            base.SetUp();
+            FieldCache.DEFAULT.InfoStream = new StringWriter();
+        }
+
+        /// <summary>
+        /// LUCENENET specific. See <see cref="SetUp()"/>. Dispose our InfoStream and set it to null
+        /// to avoid polluting the state of other tests.
+        /// </summary>
+        public override void TearDown()
+        {
+            FieldCache.DEFAULT.InfoStream.Dispose();
+            FieldCache.DEFAULT.InfoStream = null;
+            base.TearDown();
+        }
+
+        [Test]
+        public virtual void TestRangeFilterId()
+        {
+            IndexReader reader = SignedIndexReader;
+            IndexSearcher search = NewSearcher(reader);
+
+            int medId = ((MaxId - MinId) / 2);
+
+            string minIP = Pad(MinId);
+            string maxIP = Pad(MaxId);
+            string medIP = Pad(medId);
+
+            int numDocs = reader.NumDocs;
+
+            Assert.AreEqual(numDocs, 1 + MaxId - MinId, "num of docs");
+
+            ScoreDoc[] result;
+            Query q = new TermQuery(new Term("body", "body"));
+
+            // test id, bounded on both ends
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", minIP, maxIP, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", minIP, maxIP, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "all but last");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", minIP, maxIP, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "all but first");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", minIP, maxIP, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 2, result.Length, "all but ends");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", medIP, maxIP, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1 + MaxId - medId, result.Length, "med and up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", minIP, medIP, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1 + medId - MinId, result.Length, "up to med");
+
+            // unbounded id
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", null, null, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", minIP, null, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "min and up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", null, maxIP, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "max and down");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", minIP, null, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "not min, but up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", null, maxIP, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "not max, but down");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", medIP, maxIP, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(MaxId - medId, result.Length, "med and up, not max");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", minIP, medIP, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(medId - MinId, result.Length, "not min, up to med");
+
+            // very small sets
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", minIP, minIP, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "min,min,F,F");
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", medIP, medIP, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "med,med,F,F");
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", maxIP, maxIP, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "max,max,F,F");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", minIP, minIP, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "min,min,T,T");
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", null, minIP, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "nul,min,F,T");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", maxIP, maxIP, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "max,max,T,T");
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", maxIP, null, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "max,nul,T,T");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("id", medIP, medIP, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "med,med,T,T");
+        }
+
+        [Test]
+        public virtual void TestFieldCacheRangeFilterRand()
+        {
+            IndexReader reader = SignedIndexReader;
+            IndexSearcher search = NewSearcher(reader);
+
+            string minRP = Pad(SignedIndexDir.MinR);
+            string maxRP = Pad(SignedIndexDir.MaxR);
+
+            int numDocs = reader.NumDocs;
+
+            Assert.AreEqual(numDocs, 1 + MaxId - MinId, "num of docs");
+
+            ScoreDoc[] result;
+            Query q = new TermQuery(new Term("body", "body"));
+
+            // test extremes, bounded on both ends
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", minRP, maxRP, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", minRP, maxRP, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "all but biggest");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", minRP, maxRP, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "all but smallest");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", minRP, maxRP, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 2, result.Length, "all but extremes");
+
+            // unbounded
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", minRP, null, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "smallest and up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", null, maxRP, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "biggest and down");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", minRP, null, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "not smallest, but up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", null, maxRP, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "not biggest, but down");
+
+            // very small sets
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", minRP, minRP, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "min,min,F,F");
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", maxRP, maxRP, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "max,max,F,F");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", minRP, minRP, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "min,min,T,T");
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", null, minRP, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "nul,min,F,T");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", maxRP, maxRP, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "max,max,T,T");
+            result = search.Search(q, FieldCacheRangeFilter.NewStringRange("rand", maxRP, null, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "max,nul,T,T");
+        }
+
+        // byte-ranges cannot be tested, because all ranges are too big for bytes, need an extra range for that
+
+        [Test]
+        public virtual void TestFieldCacheRangeFilterShorts()
+        {
+            IndexReader reader = SignedIndexReader;
+            IndexSearcher search = NewSearcher(reader);
+
+            int numDocs = reader.NumDocs;
+            int medId = ((MaxId - MinId) / 2);
+            short? minIdO = Convert.ToInt16((short)MinId);
+            short? maxIdO = Convert.ToInt16((short)MaxId);
+            short? medIdO = Convert.ToInt16((short)medId);
+
+            Assert.AreEqual(numDocs, 1 + MaxId - MinId, "num of docs");
+
+            ScoreDoc[] result;
+            Query q = new TermQuery(new Term("body", "body"));
+
+#pragma warning disable 612, 618
+            // test id, bounded on both ends
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", minIdO, maxIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", minIdO, maxIdO, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "all but last");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", minIdO, maxIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "all but first");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", minIdO, maxIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 2, result.Length, "all but ends");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", medIdO, maxIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1 + MaxId - medId, result.Length, "med and up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", minIdO, medIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1 + medId - MinId, result.Length, "up to med");
+
+            // unbounded id
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", null, null, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", minIdO, null, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "min and up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", null, maxIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "max and down");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", minIdO, null, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "not min, but up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", null, maxIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "not max, but down");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", medIdO, maxIdO, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(MaxId - medId, result.Length, "med and up, not max");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", minIdO, medIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(medId - MinId, result.Length, "not min, up to med");
+
+            // very small sets
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", minIdO, minIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "min,min,F,F");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", medIdO, medIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "med,med,F,F");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", maxIdO, maxIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "max,max,F,F");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", minIdO, minIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "min,min,T,T");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", null, minIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "nul,min,F,T");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", maxIdO, maxIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "max,max,T,T");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", maxIdO, null, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "max,nul,T,T");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", medIdO, medIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "med,med,T,T");
+
+            // special cases
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", Convert.ToInt16(short.MaxValue), null, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "overflow special case");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", null, Convert.ToInt16(short.MinValue), F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "overflow special case");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt16Range("id", maxIdO, minIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "inverse range");
+#pragma warning restore 612, 618
+        }
+
+        [Test]
+        public virtual void TestFieldCacheRangeFilterInts()
+        {
+            IndexReader reader = SignedIndexReader;
+            IndexSearcher search = NewSearcher(reader);
+
+            int numDocs = reader.NumDocs;
+            int medId = ((MaxId - MinId) / 2);
+            int? minIdO = Convert.ToInt32(MinId);
+            int? maxIdO = Convert.ToInt32(MaxId);
+            int? medIdO = Convert.ToInt32(medId);
+
+            Assert.AreEqual(numDocs, 1 + MaxId - MinId, "num of docs");
+
+            ScoreDoc[] result;
+            Query q = new TermQuery(new Term("body", "body"));
+
+            // test id, bounded on both ends
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", minIdO, maxIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", minIdO, maxIdO, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "all but last");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", minIdO, maxIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "all but first");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", minIdO, maxIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 2, result.Length, "all but ends");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", medIdO, maxIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1 + MaxId - medId, result.Length, "med and up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", minIdO, medIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1 + medId - MinId, result.Length, "up to med");
+
+            // unbounded id
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", null, null, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", minIdO, null, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "min and up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", null, maxIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "max and down");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", minIdO, null, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "not min, but up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", null, maxIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "not max, but down");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", medIdO, maxIdO, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(MaxId - medId, result.Length, "med and up, not max");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", minIdO, medIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(medId - MinId, result.Length, "not min, up to med");
+
+            // very small sets
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", minIdO, minIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "min,min,F,F");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", medIdO, medIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "med,med,F,F");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", maxIdO, maxIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "max,max,F,F");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", minIdO, minIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "min,min,T,T");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", null, minIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "nul,min,F,T");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", maxIdO, maxIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "max,max,T,T");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", maxIdO, null, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "max,nul,T,T");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", medIdO, medIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "med,med,T,T");
+
+            // special cases
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", Convert.ToInt32(int.MaxValue), null, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "overflow special case");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", null, Convert.ToInt32(int.MinValue), F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "overflow special case");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt32Range("id", maxIdO, minIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "inverse range");
+        }
+
+        [Test]
+        public virtual void TestFieldCacheRangeFilterLongs()
+        {
+            IndexReader reader = SignedIndexReader;
+            IndexSearcher search = NewSearcher(reader);
+
+            int numDocs = reader.NumDocs;
+            int medId = ((MaxId - MinId) / 2);
+            long? minIdO = Convert.ToInt64(MinId);
+            long? maxIdO = Convert.ToInt64(MaxId);
+            long? medIdO = Convert.ToInt64(medId);
+
+            Assert.AreEqual(numDocs, 1 + MaxId - MinId, "num of docs");
+
+            ScoreDoc[] result;
+            Query q = new TermQuery(new Term("body", "body"));
+
+            // test id, bounded on both ends
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", minIdO, maxIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", minIdO, maxIdO, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "all but last");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", minIdO, maxIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "all but first");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", minIdO, maxIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 2, result.Length, "all but ends");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", medIdO, maxIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1 + MaxId - medId, result.Length, "med and up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", minIdO, medIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1 + medId - MinId, result.Length, "up to med");
+
+            // unbounded id
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", null, null, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", minIdO, null, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "min and up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", null, maxIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "max and down");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", minIdO, null, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "not min, but up");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", null, maxIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs - 1, result.Length, "not max, but down");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", medIdO, maxIdO, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(MaxId - medId, result.Length, "med and up, not max");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", minIdO, medIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(medId - MinId, result.Length, "not min, up to med");
+
+            // very small sets
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", minIdO, minIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "min,min,F,F");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", medIdO, medIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "med,med,F,F");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", maxIdO, maxIdO, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "max,max,F,F");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", minIdO, minIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "min,min,T,T");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", null, minIdO, F, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "nul,min,F,T");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", maxIdO, maxIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "max,max,T,T");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", maxIdO, null, T, F), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "max,nul,T,T");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", medIdO, medIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(1, result.Length, "med,med,T,T");
+
+            // special cases
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", Convert.ToInt64(long.MaxValue), null, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "overflow special case");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", null, Convert.ToInt64(long.MinValue), F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "overflow special case");
+            result = search.Search(q, FieldCacheRangeFilter.NewInt64Range("id", maxIdO, minIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "inverse range");
+        }
+
+        // float and double tests are a bit minimalistic, but its complicated, because missing precision
+
+        [Test]
+        public virtual void TestFieldCacheRangeFilterFloats()
+        {
+            IndexReader reader = SignedIndexReader;
+            IndexSearcher search = NewSearcher(reader);
+
+            int numDocs = reader.NumDocs;
+            float? minIdO = Convert.ToSingle(MinId + .5f);
+            float? medIdO = Convert.ToSingle((float)minIdO + ((MaxId - MinId)) / 2.0f);
+
+            ScoreDoc[] result;
+            Query q = new TermQuery(new Term("body", "body"));
+
+            result = search.Search(q, FieldCacheRangeFilter.NewSingleRange("id", minIdO, medIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs / 2, result.Length, "find all");
+            int count = 0;
+            result = search.Search(q, FieldCacheRangeFilter.NewSingleRange("id", null, medIdO, F, T), numDocs).ScoreDocs;
+            count += result.Length;
+            result = search.Search(q, FieldCacheRangeFilter.NewSingleRange("id", medIdO, null, F, F), numDocs).ScoreDocs;
+            count += result.Length;
+            Assert.AreEqual(numDocs, count, "sum of two concenatted ranges");
+            result = search.Search(q, FieldCacheRangeFilter.NewSingleRange("id", null, null, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "find all");
+            result = search.Search(q, FieldCacheRangeFilter.NewSingleRange("id", Convert.ToSingle(float.PositiveInfinity), null, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "infinity special case");
+            result = search.Search(q, FieldCacheRangeFilter.NewSingleRange("id", null, Convert.ToSingle(float.NegativeInfinity), F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "infinity special case");
+        }
+
+        [Test]
+        public virtual void TestFieldCacheRangeFilterDoubles()
+        {
+            IndexReader reader = SignedIndexReader;
+            IndexSearcher search = NewSearcher(reader);
+
+            int numDocs = reader.NumDocs;
+            double? minIdO = Convert.ToDouble(MinId + .5);
+            double? medIdO = Convert.ToDouble((float)minIdO + ((MaxId - MinId)) / 2.0);
+
+            ScoreDoc[] result;
+            Query q = new TermQuery(new Term("body", "body"));
+
+            result = search.Search(q, FieldCacheRangeFilter.NewDoubleRange("id", minIdO, medIdO, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs / 2, result.Length, "find all");
+            int count = 0;
+            result = search.Search(q, FieldCacheRangeFilter.NewDoubleRange("id", null, medIdO, F, T), numDocs).ScoreDocs;
+            count += result.Length;
+            result = search.Search(q, FieldCacheRangeFilter.NewDoubleRange("id", medIdO, null, F, F), numDocs).ScoreDocs;
+            count += result.Length;
+            Assert.AreEqual(numDocs, count, "sum of two concenatted ranges");
+            result = search.Search(q, FieldCacheRangeFilter.NewDoubleRange("id", null, null, T, T), numDocs).ScoreDocs;
+            Assert.AreEqual(numDocs, result.Length, "find all");
+            result = search.Search(q, FieldCacheRangeFilter.NewDoubleRange("id", Convert.ToDouble(double.PositiveInfinity), null, F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "infinity special case");
+            result = search.Search(q, FieldCacheRangeFilter.NewDoubleRange("id", null, Convert.ToDouble(double.NegativeInfinity), F, F), numDocs).ScoreDocs;
+            Assert.AreEqual(0, result.Length, "infinity special case");
+        }
+
+        // test using a sparse index (with deleted docs).
+        [Test]
+        public virtual void TestSparseIndex()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+
+            for (int d = -20; d <= 20; d++)
+            {
+                Document doc = new Document();
+                doc.Add(NewStringField("id", Convert.ToString(d), Field.Store.NO));
+                doc.Add(NewStringField("body", "body", Field.Store.NO));
+                writer.AddDocument(doc);
+            }
+
+            writer.ForceMerge(1);
+            writer.DeleteDocuments(new Term("id", "0"));
+            writer.Dispose();
+
+            IndexReader reader = DirectoryReader.Open(dir);
+            IndexSearcher search = NewSearcher(reader);
+            Assert.IsTrue(reader.HasDeletions);
+
+            ScoreDoc[] result;
+            Query q = new TermQuery(new Term("body", "body"));
+
+#pragma warning disable 612, 618
+            result = search.Search(q, FieldCacheRangeFilter.NewByteRange("id", (sbyte?)-20, (sbyte?)20, T, T), 100).ScoreDocs;
+            Assert.AreEqual(40, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewByteRange("id", (sbyte?)0, (sbyte?)20, T, T), 100).ScoreDocs;
+            Assert.AreEqual(20, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewByteRange("id", (sbyte?)-20, (sbyte?)0, T, T), 100).ScoreDocs;
+            Assert.AreEqual(20, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewByteRange("id", (sbyte?)10, (sbyte?)20, T, T), 100).ScoreDocs;
+            Assert.AreEqual(11, result.Length, "find all");
+
+            result = search.Search(q, FieldCacheRangeFilter.NewByteRange("id", (sbyte?)-20, (sbyte?)-10, T, T), 100).ScoreDocs;
+            Assert.AreEqual(11, result.Length, "find all");
+#pragma warning restore 612, 618
+
+            reader.Dispose();
+            dir.Dispose();
+        }
+
+
+        #region SorterTestBase
+        // LUCENENET NOTE: Tests in a base class are not pulled into the correct
+        // context in Visual Studio. This fixes that with the minimum amount of code necessary
+        // to run them in the correct context without duplicating all of the tests.
+
+        [Test]
+        public override void TestPad()
+        {
+            base.TestPad();
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestFieldCacheRewriteMethod.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestFieldCacheRewriteMethod.cs b/src/Lucene.Net.Tests/Search/TestFieldCacheRewriteMethod.cs
new file mode 100644
index 0000000..ee87c43
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestFieldCacheRewriteMethod.cs
@@ -0,0 +1,86 @@
+namespace Lucene.Net.Search
+{
+    using Lucene.Net.Attributes;
+    using NUnit.Framework;
+    using RegExp = Lucene.Net.Util.Automaton.RegExp;
+
+    /*
+         * 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>
+    /// Tests the FieldcacheRewriteMethod with random regular expressions
+    /// </summary>
+    [TestFixture]
+    public class TestFieldCacheRewriteMethod : TestRegexpRandom2
+    {
+        /// <summary>
+        /// Test fieldcache rewrite against filter rewrite </summary>
+        protected internal override void AssertSame(string regexp)
+        {
+            RegexpQuery fieldCache = new RegexpQuery(new Term(FieldName, regexp), RegExp.NONE);
+            fieldCache.MultiTermRewriteMethod = (new FieldCacheRewriteMethod());
+
+            RegexpQuery filter = new RegexpQuery(new Term(FieldName, regexp), RegExp.NONE);
+            filter.MultiTermRewriteMethod = (MultiTermQuery.CONSTANT_SCORE_FILTER_REWRITE);
+
+            TopDocs fieldCacheDocs = Searcher1.Search(fieldCache, 25);
+            TopDocs filterDocs = Searcher2.Search(filter, 25);
+
+            CheckHits.CheckEqual(fieldCache, fieldCacheDocs.ScoreDocs, filterDocs.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 FieldCacheRewriteMethod());
+            a2.MultiTermRewriteMethod = (new FieldCacheRewriteMethod());
+            b.MultiTermRewriteMethod = (new FieldCacheRewriteMethod());
+            Assert.AreEqual(a1, a2);
+            Assert.IsFalse(a1.Equals(b));
+            QueryUtils.Check(a1);
+        }
+
+
+
+        #region TestSnapshotDeletionPolicy
+        // LUCENENET NOTE: Tests in a base class are not pulled into the correct
+        // context in Visual Studio. This fixes that with the minimum amount of code necessary
+        // to run them in the correct context without duplicating all of the tests.
+
+        /// <summary>
+        /// test a bunch of random regular expressions </summary>
+#if !NETSTANDARD
+        // LUCENENET: There is no Timeout on NUnit for .NET Core.
+        [Timeout(60000)]
+#endif
+        [Test, HasTimeout]
+        public override void TestRegexps()
+        {
+            base.TestRegexps();
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestFieldCacheTermsFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestFieldCacheTermsFilter.cs b/src/Lucene.Net.Tests/Search/TestFieldCacheTermsFilter.cs
new file mode 100644
index 0000000..a7d231f
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestFieldCacheTermsFilter.cs
@@ -0,0 +1,80 @@
+using System.Collections.Generic;
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search
+{
+    using NUnit.Framework;
+    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;
+
+    /// <summary>
+    /// A basic unit test for FieldCacheTermsFilter
+    /// </summary>
+    /// <seealso cref= Lucene.Net.Search.FieldCacheTermsFilter </seealso>
+    [TestFixture]
+    public class TestFieldCacheTermsFilter : LuceneTestCase
+    {
+        [Test]
+        public virtual void TestMissingTerms()
+        {
+            string fieldName = "field1";
+            Directory rd = NewDirectory();
+            RandomIndexWriter w = new RandomIndexWriter(Random(), rd, Similarity, TimeZone);
+            for (int i = 0; i < 100; i++)
+            {
+                Document doc = new Document();
+                int term = i * 10; //terms are units of 10;
+                doc.Add(NewStringField(fieldName, "" + term, Field.Store.YES));
+                w.AddDocument(doc);
+            }
+            IndexReader reader = w.Reader;
+            w.Dispose();
+
+            IndexSearcher searcher = NewSearcher(reader);
+            int numDocs = reader.NumDocs;
+            ScoreDoc[] results;
+            MatchAllDocsQuery q = new MatchAllDocsQuery();
+
+            List<string> terms = new List<string>();
+            terms.Add("5");
+            results = searcher.Search(q, new FieldCacheTermsFilter(fieldName, terms.ToArray()), numDocs).ScoreDocs;
+            Assert.AreEqual(0, results.Length, "Must match nothing");
+
+            terms = new List<string>();
+            terms.Add("10");
+            results = searcher.Search(q, new FieldCacheTermsFilter(fieldName, terms.ToArray()), numDocs).ScoreDocs;
+            Assert.AreEqual(1, results.Length, "Must match 1");
+
+            terms = new List<string>();
+            terms.Add("10");
+            terms.Add("20");
+            results = searcher.Search(q, new FieldCacheTermsFilter(fieldName, terms.ToArray()), numDocs).ScoreDocs;
+            Assert.AreEqual(2, results.Length, "Must match 2");
+
+            reader.Dispose();
+            rd.Dispose();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestFieldValueFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestFieldValueFilter.cs b/src/Lucene.Net.Tests/Search/TestFieldValueFilter.cs
new file mode 100644
index 0000000..4930c24
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestFieldValueFilter.cs
@@ -0,0 +1,127 @@
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search
+{
+    using Lucene.Net.Randomized.Generators;
+    using NUnit.Framework;
+    using Directory = Lucene.Net.Store.Directory;
+    using DirectoryReader = Lucene.Net.Index.DirectoryReader;
+    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 RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+    using Term = Lucene.Net.Index.Term;
+
+    ///
+    [TestFixture]
+    public class TestFieldValueFilter : LuceneTestCase
+    {
+        [Test]
+        public virtual void TestFieldValueFilterNoValue()
+        {
+            Directory directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            int docs = AtLeast(10);
+            int[] docStates = BuildIndex(writer, docs);
+            int numDocsNoValue = 0;
+            for (int i = 0; i < docStates.Length; i++)
+            {
+                if (docStates[i] == 0)
+                {
+                    numDocsNoValue++;
+                }
+            }
+
+            IndexReader reader = DirectoryReader.Open(directory);
+            IndexSearcher searcher = NewSearcher(reader);
+            TopDocs search = searcher.Search(new TermQuery(new Term("all", "test")), new FieldValueFilter("some", true), docs);
+            Assert.AreEqual(search.TotalHits, numDocsNoValue);
+
+            ScoreDoc[] scoreDocs = search.ScoreDocs;
+            foreach (ScoreDoc scoreDoc in scoreDocs)
+            {
+                Assert.IsNull(reader.Document(scoreDoc.Doc).Get("some"));
+            }
+
+            reader.Dispose();
+            directory.Dispose();
+        }
+
+        [Test]
+        public virtual void TestFieldValueFilter_Mem()
+        {
+            Directory directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            int docs = AtLeast(10);
+            int[] docStates = BuildIndex(writer, docs);
+            int numDocsWithValue = 0;
+            for (int i = 0; i < docStates.Length; i++)
+            {
+                if (docStates[i] == 1)
+                {
+                    numDocsWithValue++;
+                }
+            }
+            IndexReader reader = DirectoryReader.Open(directory);
+            IndexSearcher searcher = NewSearcher(reader);
+            TopDocs search = searcher.Search(new TermQuery(new Term("all", "test")), new FieldValueFilter("some"), docs);
+            Assert.AreEqual(search.TotalHits, numDocsWithValue);
+
+            ScoreDoc[] scoreDocs = search.ScoreDocs;
+            foreach (ScoreDoc scoreDoc in scoreDocs)
+            {
+                Assert.AreEqual("value", reader.Document(scoreDoc.Doc).Get("some"));
+            }
+
+            reader.Dispose();
+            directory.Dispose();
+        }
+
+        private int[] BuildIndex(RandomIndexWriter writer, int docs)
+        {
+            int[] docStates = new int[docs];
+            for (int i = 0; i < docs; i++)
+            {
+                Document doc = new Document();
+                if (Random().NextBoolean())
+                {
+                    docStates[i] = 1;
+                    doc.Add(NewTextField("some", "value", Field.Store.YES));
+                }
+                doc.Add(NewTextField("all", "test", Field.Store.NO));
+                doc.Add(NewTextField("id", "" + i, Field.Store.YES));
+                writer.AddDocument(doc);
+            }
+            writer.Commit();
+            int numDeletes = Random().Next(docs);
+            for (int i = 0; i < numDeletes; i++)
+            {
+                int docID = Random().Next(docs);
+                writer.DeleteDocuments(new Term("id", "" + docID));
+                docStates[docID] = 2;
+            }
+            writer.Dispose();
+            return docStates;
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestFilteredQuery.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestFilteredQuery.cs b/src/Lucene.Net.Tests/Search/TestFilteredQuery.cs
new file mode 100644
index 0000000..9847b9a
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestFilteredQuery.cs
@@ -0,0 +1,719 @@
+using Lucene.Net.Documents;
+using Lucene.Net.Support;
+using Lucene.Net.Util;
+using System;
+using System.Collections;
+
+namespace Lucene.Net.Search
+{
+    using Lucene.Net.Randomized.Generators;
+    using NUnit.Framework;
+    using System.Reflection;
+    using AtomicReader = Lucene.Net.Index.AtomicReader;
+    using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
+    using IBits = Lucene.Net.Util.IBits;
+    using Directory = Lucene.Net.Store.Directory;
+    using DocIdBitSet = Lucene.Net.Util.DocIdBitSet;
+    using DocsEnum = Lucene.Net.Index.DocsEnum;
+    using Document = Documents.Document;
+    using Field = Field;
+    using FilterStrategy = Lucene.Net.Search.FilteredQuery.FilterStrategy;
+    using IndexReader = Lucene.Net.Index.IndexReader;
+    using IOUtils = Lucene.Net.Util.IOUtils;
+    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;
+    using TestUtil = Lucene.Net.Util.TestUtil;
+
+    /// <summary>
+    /// FilteredQuery JUnit tests.
+    ///
+    /// <p>Created: Apr 21, 2004 1:21:46 PM
+    ///
+    ///
+    /// @since   1.4
+    /// </summary>
+    [TestFixture]
+    public class TestFilteredQuery : LuceneTestCase
+    {
+        private IndexSearcher Searcher;
+        private IndexReader Reader;
+        private Directory Directory;
+        private Query Query;
+        private Filter Filter;
+
+        [SetUp]
+        public override void SetUp()
+        {
+            base.SetUp();
+            Directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), Directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy()));
+
+            Document doc = new Document();
+            doc.Add(NewTextField("field", "one two three four five", Field.Store.YES));
+            doc.Add(NewTextField("sorter", "b", Field.Store.YES));
+            writer.AddDocument(doc);
+
+            doc = new Document();
+            doc.Add(NewTextField("field", "one two three four", Field.Store.YES));
+            doc.Add(NewTextField("sorter", "d", Field.Store.YES));
+            writer.AddDocument(doc);
+
+            doc = new Document();
+            doc.Add(NewTextField("field", "one two three y", Field.Store.YES));
+            doc.Add(NewTextField("sorter", "a", Field.Store.YES));
+            writer.AddDocument(doc);
+
+            doc = new Document();
+            doc.Add(NewTextField("field", "one two x", Field.Store.YES));
+            doc.Add(NewTextField("sorter", "c", Field.Store.YES));
+            writer.AddDocument(doc);
+
+            // tests here require single segment (eg try seed
+            // 8239472272678419952L), because SingleDocTestFilter(x)
+            // blindly accepts that docID in any sub-segment
+            writer.ForceMerge(1);
+
+            Reader = writer.Reader;
+            writer.Dispose();
+
+            Searcher = NewSearcher(Reader);
+
+            Query = new TermQuery(new Term("field", "three"));
+            Filter = NewStaticFilterB();
+        }
+
+        // must be static for serialization tests
+        private static Filter NewStaticFilterB()
+        {
+            return new FilterAnonymousInnerClassHelper();
+        }
+
+        private class FilterAnonymousInnerClassHelper : Filter
+        {
+            public FilterAnonymousInnerClassHelper()
+            {
+            }
+
+            public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs)
+            {
+                if (acceptDocs == null)
+                {
+                    acceptDocs = new Bits.MatchAllBits(5);
+                }
+                BitArray bitset = new BitArray(5);
+                if (acceptDocs.Get(1))
+                {
+                    bitset.SafeSet(1, true);
+                }
+                if (acceptDocs.Get(3))
+                {
+                    bitset.SafeSet(3, true);
+                }
+                return new DocIdBitSet(bitset);
+            }
+        }
+
+        [TearDown]
+        public override void TearDown()
+        {
+            Reader.Dispose();
+            Directory.Dispose();
+            base.TearDown();
+        }
+
+        [Test]
+        public virtual void TestFilteredQuery_Mem()
+        {
+            // force the filter to be executed as bits
+            TFilteredQuery(true);
+            // force the filter to be executed as iterator
+            TFilteredQuery(false);
+        }
+
+        private void TFilteredQuery(bool useRandomAccess)
+        {
+            Query filteredquery = new FilteredQuery(Query, Filter, RandomFilterStrategy(Random(), useRandomAccess));
+            ScoreDoc[] hits = Searcher.Search(filteredquery, null, 1000).ScoreDocs;
+            Assert.AreEqual(1, hits.Length);
+            Assert.AreEqual(1, hits[0].Doc);
+            QueryUtils.Check(Random(), filteredquery, Searcher, Similarity);
+
+            hits = Searcher.Search(filteredquery, null, 1000, new Sort(new SortField("sorter", SortFieldType.STRING))).ScoreDocs;
+            Assert.AreEqual(1, hits.Length);
+            Assert.AreEqual(1, hits[0].Doc);
+
+            filteredquery = new FilteredQuery(new TermQuery(new Term("field", "one")), Filter, RandomFilterStrategy(Random(), useRandomAccess));
+            hits = Searcher.Search(filteredquery, null, 1000).ScoreDocs;
+            Assert.AreEqual(2, hits.Length);
+            QueryUtils.Check(Random(), filteredquery, Searcher, Similarity);
+
+            filteredquery = new FilteredQuery(new MatchAllDocsQuery(), Filter, RandomFilterStrategy(Random(), useRandomAccess));
+            hits = Searcher.Search(filteredquery, null, 1000).ScoreDocs;
+            Assert.AreEqual(2, hits.Length);
+            QueryUtils.Check(Random(), filteredquery, Searcher, Similarity);
+
+            filteredquery = new FilteredQuery(new TermQuery(new Term("field", "x")), Filter, RandomFilterStrategy(Random(), useRandomAccess));
+            hits = Searcher.Search(filteredquery, null, 1000).ScoreDocs;
+            Assert.AreEqual(1, hits.Length);
+            Assert.AreEqual(3, hits[0].Doc);
+            QueryUtils.Check(Random(), filteredquery, Searcher, Similarity);
+
+            filteredquery = new FilteredQuery(new TermQuery(new Term("field", "y")), Filter, RandomFilterStrategy(Random(), useRandomAccess));
+            hits = Searcher.Search(filteredquery, null, 1000).ScoreDocs;
+            Assert.AreEqual(0, hits.Length);
+            QueryUtils.Check(Random(), filteredquery, Searcher, Similarity);
+
+            // test boost
+            Filter f = NewStaticFilterA();
+
+            float boost = 2.5f;
+            BooleanQuery bq1 = new BooleanQuery();
+            TermQuery tq = new TermQuery(new Term("field", "one"));
+            tq.Boost = boost;
+            bq1.Add(tq, Occur.MUST);
+            bq1.Add(new TermQuery(new Term("field", "five")), Occur.MUST);
+
+            BooleanQuery bq2 = new BooleanQuery();
+            tq = new TermQuery(new Term("field", "one"));
+            filteredquery = new FilteredQuery(tq, f, RandomFilterStrategy(Random(), useRandomAccess));
+            filteredquery.Boost = boost;
+            bq2.Add(filteredquery, Occur.MUST);
+            bq2.Add(new TermQuery(new Term("field", "five")), Occur.MUST);
+            AssertScoreEquals(bq1, bq2);
+
+            Assert.AreEqual(boost, filteredquery.Boost, 0);
+            Assert.AreEqual(1.0f, tq.Boost, 0); // the boost value of the underlying query shouldn't have changed
+        }
+
+        // must be static for serialization tests
+        private static Filter NewStaticFilterA()
+        {
+            return new FilterAnonymousInnerClassHelper2();
+        }
+
+        private class FilterAnonymousInnerClassHelper2 : Filter
+        {
+            public FilterAnonymousInnerClassHelper2()
+            {
+            }
+
+            public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs)
+            {
+                Assert.IsNull(acceptDocs, "acceptDocs should be null, as we have an index without deletions");
+                BitArray bitset = new BitArray(5, true);
+                return new DocIdBitSet(bitset);
+            }
+        }
+
+        /// <summary>
+        /// Tests whether the scores of the two queries are the same.
+        /// </summary>
+        public virtual void AssertScoreEquals(Query q1, Query q2)
+        {
+            ScoreDoc[] hits1 = Searcher.Search(q1, null, 1000).ScoreDocs;
+            ScoreDoc[] hits2 = Searcher.Search(q2, null, 1000).ScoreDocs;
+
+            Assert.AreEqual(hits1.Length, hits2.Length);
+
+            for (int i = 0; i < hits1.Length; i++)
+            {
+                Assert.AreEqual(hits1[i].Score, hits2[i].Score, 0.000001f);
+            }
+        }
+
+        /// <summary>
+        /// this tests FilteredQuery's rewrite correctness
+        /// </summary>
+        [Test]
+        public virtual void TestRangeQuery()
+        {
+            // force the filter to be executed as bits
+            TRangeQuery(true);
+            TRangeQuery(false);
+        }
+
+        private void TRangeQuery(bool useRandomAccess)
+        {
+            TermRangeQuery rq = TermRangeQuery.NewStringRange("sorter", "b", "d", true, true);
+
+            Query filteredquery = new FilteredQuery(rq, Filter, RandomFilterStrategy(Random(), useRandomAccess));
+            ScoreDoc[] hits = Searcher.Search(filteredquery, null, 1000).ScoreDocs;
+            Assert.AreEqual(2, hits.Length);
+            QueryUtils.Check(Random(), filteredquery, Searcher, Similarity);
+        }
+
+        [Test]
+        public virtual void TestBooleanMUST()
+        {
+            // force the filter to be executed as bits
+            TBooleanMUST(true);
+            // force the filter to be executed as iterator
+            TBooleanMUST(false);
+        }
+
+        private void TBooleanMUST(bool useRandomAccess)
+        {
+            BooleanQuery bq = new BooleanQuery();
+            Query query = new FilteredQuery(new TermQuery(new Term("field", "one")), new SingleDocTestFilter(0), RandomFilterStrategy(Random(), useRandomAccess));
+            bq.Add(query, Occur.MUST);
+            query = new FilteredQuery(new TermQuery(new Term("field", "one")), new SingleDocTestFilter(1), RandomFilterStrategy(Random(), useRandomAccess));
+            bq.Add(query, Occur.MUST);
+            ScoreDoc[] hits = Searcher.Search(bq, null, 1000).ScoreDocs;
+            Assert.AreEqual(0, hits.Length);
+            QueryUtils.Check(Random(), query, Searcher, Similarity);
+        }
+
+        [Test]
+        public virtual void TestBooleanSHOULD()
+        {
+            // force the filter to be executed as bits
+            TBooleanSHOULD(true);
+            // force the filter to be executed as iterator
+            TBooleanSHOULD(false);
+        }
+
+        private void TBooleanSHOULD(bool useRandomAccess)
+        {
+            BooleanQuery bq = new BooleanQuery();
+            Query query = new FilteredQuery(new TermQuery(new Term("field", "one")), new SingleDocTestFilter(0), RandomFilterStrategy(Random(), useRandomAccess));
+            bq.Add(query, Occur.SHOULD);
+            query = new FilteredQuery(new TermQuery(new Term("field", "one")), new SingleDocTestFilter(1), RandomFilterStrategy(Random(), useRandomAccess));
+            bq.Add(query, Occur.SHOULD);
+            ScoreDoc[] hits = Searcher.Search(bq, null, 1000).ScoreDocs;
+            Assert.AreEqual(2, hits.Length);
+            QueryUtils.Check(Random(), query, Searcher, Similarity);
+        }
+
+        // Make sure BooleanQuery, which does out-of-order
+        // scoring, inside FilteredQuery, works
+        [Test]
+        public virtual void TestBoolean2()
+        {
+            // force the filter to be executed as bits
+            TBoolean2(true);
+            // force the filter to be executed as iterator
+            TBoolean2(false);
+        }
+
+        private void TBoolean2(bool useRandomAccess)
+        {
+            BooleanQuery bq = new BooleanQuery();
+            Query query = new FilteredQuery(bq, new SingleDocTestFilter(0), RandomFilterStrategy(Random(), useRandomAccess));
+            bq.Add(new TermQuery(new Term("field", "one")), Occur.SHOULD);
+            bq.Add(new TermQuery(new Term("field", "two")), Occur.SHOULD);
+            ScoreDoc[] hits = Searcher.Search(query, 1000).ScoreDocs;
+            Assert.AreEqual(1, hits.Length);
+            QueryUtils.Check(Random(), query, Searcher, Similarity);
+        }
+
+        [Test]
+        public virtual void TestChainedFilters()
+        {
+            // force the filter to be executed as bits
+            TChainedFilters(true);
+            // force the filter to be executed as iterator
+            TChainedFilters(false);
+        }
+
+        private void TChainedFilters(bool useRandomAccess)
+        {
+            Query query = new FilteredQuery(new FilteredQuery(new MatchAllDocsQuery(), new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("field", "three")))), RandomFilterStrategy(Random(), useRandomAccess)), new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("field", "four")))), RandomFilterStrategy(Random(), useRandomAccess));
+            ScoreDoc[] hits = Searcher.Search(query, 10).ScoreDocs;
+            Assert.AreEqual(2, hits.Length);
+            QueryUtils.Check(Random(), query, Searcher, Similarity);
+
+            // one more:
+            query = new FilteredQuery(query, new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("field", "five")))), RandomFilterStrategy(Random(), useRandomAccess));
+            hits = Searcher.Search(query, 10).ScoreDocs;
+            Assert.AreEqual(1, hits.Length);
+            QueryUtils.Check(Random(), query, Searcher, Similarity);
+        }
+
+        [Test]
+        public virtual void TestEqualsHashcode()
+        {
+            // some tests before, if the used queries and filters work:
+            Assert.AreEqual(new PrefixFilter(new Term("field", "o")), new PrefixFilter(new Term("field", "o")));
+            Assert.IsFalse((new PrefixFilter(new Term("field", "a"))).Equals(new PrefixFilter(new Term("field", "o"))));
+            QueryUtils.CheckHashEquals(new TermQuery(new Term("field", "one")));
+            QueryUtils.CheckUnequal(new TermQuery(new Term("field", "one")), new TermQuery(new Term("field", "two"))
+           );
+            // now test FilteredQuery equals/hashcode:
+            QueryUtils.CheckHashEquals(new FilteredQuery(new TermQuery(new Term("field", "one")), new PrefixFilter(new Term("field", "o"))));
+            QueryUtils.CheckUnequal(new FilteredQuery(new TermQuery(new Term("field", "one")), new PrefixFilter(new Term("field", "o"))), new FilteredQuery(new TermQuery(new Term("field", "two")), new PrefixFilter(new Term("field", "o")))
+           );
+            QueryUtils.CheckUnequal(new FilteredQuery(new TermQuery(new Term("field", "one")), new PrefixFilter(new Term("field", "a"))), new FilteredQuery(new TermQuery(new Term("field", "one")), new PrefixFilter(new Term("field", "o")))
+           );
+        }
+
+        [Test]
+        public virtual void TestInvalidArguments()
+        {
+            try
+            {
+                new FilteredQuery(null, null);
+                Assert.Fail("Should throw IllegalArgumentException");
+            }
+#pragma warning disable 168
+            catch (System.ArgumentException iae)
+#pragma warning restore 168
+            {
+                // pass
+            }
+            try
+            {
+                new FilteredQuery(new TermQuery(new Term("field", "one")), null);
+                Assert.Fail("Should throw IllegalArgumentException");
+            }
+#pragma warning disable 168
+            catch (System.ArgumentException iae)
+#pragma warning restore 168
+            {
+                // pass
+            }
+            try
+            {
+                new FilteredQuery(null, new PrefixFilter(new Term("field", "o")));
+                Assert.Fail("Should throw IllegalArgumentException");
+            }
+#pragma warning disable 168
+            catch (System.ArgumentException iae)
+#pragma warning restore 168
+            {
+                // pass
+            }
+        }
+
+        private FilterStrategy RandomFilterStrategy()
+        {
+            return RandomFilterStrategy(Random(), true);
+        }
+
+        private void AssertRewrite(FilteredQuery fq, Type clazz)
+        {
+            // assign crazy boost to FQ
+            float boost = (float)Random().NextDouble() * 100.0f;
+            fq.Boost = boost;
+
+            // assign crazy boost to inner
+            float innerBoost = (float)Random().NextDouble() * 100.0f;
+            fq.Query.Boost = innerBoost;
+
+            // check the class and boosts of rewritten query
+            Query rewritten = Searcher.Rewrite(fq);
+            Assert.IsTrue(clazz.IsInstanceOfType(rewritten), "is not instance of " + clazz.Name);
+            if (rewritten is FilteredQuery)
+            {
+                Assert.AreEqual(boost, rewritten.Boost, 1E-5f);
+                Assert.AreEqual(innerBoost, ((FilteredQuery)rewritten).Query.Boost, 1E-5f);
+                Assert.AreEqual(fq.Strategy, ((FilteredQuery)rewritten).Strategy);
+            }
+            else
+            {
+                Assert.AreEqual(boost * innerBoost, rewritten.Boost, 1E-5f);
+            }
+
+            // check that the original query was not modified
+            Assert.AreEqual(boost, fq.Boost, 1E-5f);
+            Assert.AreEqual(innerBoost, fq.Query.Boost, 1E-5f);
+        }
+
+        [Test]
+        public virtual void TestRewrite()
+        {
+            AssertRewrite(new FilteredQuery(new TermQuery(new Term("field", "one")), new PrefixFilter(new Term("field", "o")), RandomFilterStrategy()), typeof(FilteredQuery));
+            AssertRewrite(new FilteredQuery(new PrefixQuery(new Term("field", "one")), new PrefixFilter(new Term("field", "o")), RandomFilterStrategy()), typeof(FilteredQuery));
+        }
+
+        [Test]
+        public virtual void TestGetFilterStrategy()
+        {
+            FilterStrategy randomFilterStrategy = RandomFilterStrategy();
+            FilteredQuery filteredQuery = new FilteredQuery(new TermQuery(new Term("field", "one")), new PrefixFilter(new Term("field", "o")), randomFilterStrategy);
+            Assert.AreSame(randomFilterStrategy, filteredQuery.Strategy);
+        }
+
+        private static FilteredQuery.FilterStrategy RandomFilterStrategy(Random random, bool useRandomAccess)
+        {
+            if (useRandomAccess)
+            {
+                return new RandomAccessFilterStrategyAnonymousInnerClassHelper();
+            }
+            return TestUtil.RandomFilterStrategy(random);
+        }
+
+        private class RandomAccessFilterStrategyAnonymousInnerClassHelper : FilteredQuery.RandomAccessFilterStrategy
+        {
+            public RandomAccessFilterStrategyAnonymousInnerClassHelper()
+            {
+            }
+
+            protected override bool UseRandomAccess(IBits bits, int firstFilterDoc)
+            {
+                return true;
+            }
+        }
+
+        /*
+         * Test if the QueryFirst strategy calls the bits only if the document has
+         * been matched by the query and not otherwise
+         */
+
+        [Test]
+        public virtual void TestQueryFirstFilterStrategy()
+        {
+            Directory directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            int numDocs = AtLeast(50);
+            int totalDocsWithZero = 0;
+            for (int i = 0; i < numDocs; i++)
+            {
+                Document doc = new Document();
+                int num = Random().Next(5);
+                if (num == 0)
+                {
+                    totalDocsWithZero++;
+                }
+                doc.Add(NewTextField("field", "" + num, Field.Store.YES));
+                writer.AddDocument(doc);
+            }
+            IndexReader reader = writer.Reader;
+            writer.Dispose();
+
+            IndexSearcher searcher = NewSearcher(reader);
+            Query query = new FilteredQuery(new TermQuery(new Term("field", "0")), new FilterAnonymousInnerClassHelper3(this, reader), FilteredQuery.QUERY_FIRST_FILTER_STRATEGY);
+
+            TopDocs search = searcher.Search(query, 10);
+            Assert.AreEqual(totalDocsWithZero, search.TotalHits);
+            IOUtils.Close(reader, writer, directory);
+        }
+
+        private class FilterAnonymousInnerClassHelper3 : Filter
+        {
+            private readonly TestFilteredQuery OuterInstance;
+
+            private IndexReader Reader;
+
+            public FilterAnonymousInnerClassHelper3(TestFilteredQuery outerInstance, IndexReader reader)
+            {
+                this.OuterInstance = outerInstance;
+                this.Reader = reader;
+            }
+
+            public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs)
+            {
+                bool nullBitset = Random().Next(10) == 5;
+                AtomicReader reader = context.AtomicReader;
+                DocsEnum termDocsEnum = reader.TermDocsEnum(new Term("field", "0"));
+                if (termDocsEnum == null)
+                {
+                    return null; // no docs -- return null
+                }
+                BitArray bitSet = new BitArray(reader.MaxDoc);
+                int d;
+                while ((d = termDocsEnum.NextDoc()) != DocsEnum.NO_MORE_DOCS)
+                {
+                    bitSet.SafeSet(d, true);
+                }
+                return new DocIdSetAnonymousInnerClassHelper(this, nullBitset, reader, bitSet);
+            }
+
+            private class DocIdSetAnonymousInnerClassHelper : DocIdSet
+            {
+                private readonly FilterAnonymousInnerClassHelper3 OuterInstance;
+
+                private bool NullBitset;
+                private AtomicReader Reader;
+                private BitArray BitSet;
+
+                public DocIdSetAnonymousInnerClassHelper(FilterAnonymousInnerClassHelper3 outerInstance, bool nullBitset, AtomicReader reader, BitArray bitSet)
+                {
+                    this.OuterInstance = outerInstance;
+                    this.NullBitset = nullBitset;
+                    this.Reader = reader;
+                    this.BitSet = bitSet;
+                }
+
+                public override IBits Bits
+                {
+                    get
+                    {
+                        if (NullBitset)
+                        {
+                            return null;
+                        }
+                        return new BitsAnonymousInnerClassHelper(this);
+                    }
+                }
+
+                private class BitsAnonymousInnerClassHelper : IBits
+                {
+                    private readonly DocIdSetAnonymousInnerClassHelper OuterInstance;
+
+                    public BitsAnonymousInnerClassHelper(DocIdSetAnonymousInnerClassHelper outerInstance)
+                    {
+                        this.OuterInstance = outerInstance;
+                    }
+
+                    public bool Get(int index)
+                    {
+                        Assert.IsTrue(OuterInstance.BitSet.SafeGet(index), "filter was called for a non-matching doc");
+                        return OuterInstance.BitSet.SafeGet(index);
+                    }
+
+                    public int Length
+                    {
+                        get { return OuterInstance.BitSet.Length; }
+                    }
+                }
+
+                public override DocIdSetIterator GetIterator()
+                {
+                    Assert.IsTrue(NullBitset, "iterator should not be called if bitset is present");
+                    return Reader.TermDocsEnum(new Term("field", "0"));
+                }
+            }
+        }
+
+        /*
+         * Test if the leapfrog strategy works correctly in terms
+         * of advancing / next the right thing first
+         */
+
+        [Test]
+        public virtual void TestLeapFrogStrategy()
+        {
+            Directory directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            int numDocs = AtLeast(50);
+            int totalDocsWithZero = 0;
+            for (int i = 0; i < numDocs; i++)
+            {
+                Document doc = new Document();
+                int num = Random().Next(10);
+                if (num == 0)
+                {
+                    totalDocsWithZero++;
+                }
+                doc.Add(NewTextField("field", "" + num, Field.Store.YES));
+                writer.AddDocument(doc);
+            }
+            IndexReader reader = writer.Reader;
+            writer.Dispose();
+            bool queryFirst = Random().NextBoolean();
+            IndexSearcher searcher = NewSearcher(reader);
+            Query query = new FilteredQuery(new TermQuery(new Term("field", "0")), new FilterAnonymousInnerClassHelper4(this, queryFirst), queryFirst ? FilteredQuery.LEAP_FROG_QUERY_FIRST_STRATEGY : Random()
+                  .NextBoolean() ? FilteredQuery.RANDOM_ACCESS_FILTER_STRATEGY : FilteredQuery.LEAP_FROG_FILTER_FIRST_STRATEGY); // if filterFirst, we can use random here since bits are null
+
+            TopDocs search = searcher.Search(query, 10);
+            Assert.AreEqual(totalDocsWithZero, search.TotalHits);
+            IOUtils.Close(reader, writer, directory);
+        }
+
+        private class FilterAnonymousInnerClassHelper4 : Filter
+        {
+            private readonly TestFilteredQuery OuterInstance;
+
+            private bool QueryFirst;
+
+            public FilterAnonymousInnerClassHelper4(TestFilteredQuery outerInstance, bool queryFirst)
+            {
+                this.OuterInstance = outerInstance;
+                this.QueryFirst = queryFirst;
+            }
+
+            public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs)
+            {
+                return new DocIdSetAnonymousInnerClassHelper2(this, context);
+            }
+
+            private class DocIdSetAnonymousInnerClassHelper2 : DocIdSet
+            {
+                private readonly FilterAnonymousInnerClassHelper4 OuterInstance;
+
+                private AtomicReaderContext Context;
+
+                public DocIdSetAnonymousInnerClassHelper2(FilterAnonymousInnerClassHelper4 outerInstance, AtomicReaderContext context)
+                {
+                    this.OuterInstance = outerInstance;
+                    this.Context = context;
+                }
+
+                public override IBits Bits
+                {
+                    get { return null; }
+                }
+
+                public override DocIdSetIterator GetIterator()
+                {
+                    DocsEnum termDocsEnum = ((AtomicReader)Context.Reader).TermDocsEnum(new Term("field", "0"));
+                    if (termDocsEnum == null)
+                    {
+                        return null;
+                    }
+                    return new DocIdSetIteratorAnonymousInnerClassHelper(this, termDocsEnum);
+                }
+
+                private class DocIdSetIteratorAnonymousInnerClassHelper : DocIdSetIterator
+                {
+                    private readonly DocIdSetAnonymousInnerClassHelper2 OuterInstance;
+
+                    private DocsEnum TermDocsEnum;
+
+                    public DocIdSetIteratorAnonymousInnerClassHelper(DocIdSetAnonymousInnerClassHelper2 outerInstance, DocsEnum termDocsEnum)
+                    {
+                        this.OuterInstance = outerInstance;
+                        this.TermDocsEnum = termDocsEnum;
+                    }
+
+                    internal bool nextCalled;
+                    internal bool advanceCalled;
+
+                    public override int NextDoc()
+                    {
+                        Assert.IsTrue(nextCalled || advanceCalled ^ !OuterInstance.OuterInstance.QueryFirst, "queryFirst: " + OuterInstance.OuterInstance.QueryFirst + " advanced: " + advanceCalled + " next: " + nextCalled);
+                        nextCalled = true;
+                        return TermDocsEnum.NextDoc();
+                    }
+
+                    public override int DocID
+                    {
+                        get { return TermDocsEnum.DocID; }
+                    }
+
+                    public override int Advance(int target)
+                    {
+                        Assert.IsTrue(advanceCalled || nextCalled ^ OuterInstance.OuterInstance.QueryFirst, "queryFirst: " + OuterInstance.OuterInstance.QueryFirst + " advanced: " + advanceCalled + " next: " + nextCalled);
+                        advanceCalled = true;
+                        return TermDocsEnum.Advance(target);
+                    }
+
+                    public override long GetCost()
+                    {
+                        return TermDocsEnum.GetCost();
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/TestFilteredSearch.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/TestFilteredSearch.cs b/src/Lucene.Net.Tests/Search/TestFilteredSearch.cs
new file mode 100644
index 0000000..88dada2
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/TestFilteredSearch.cs
@@ -0,0 +1,112 @@
+using System;
+/*
+ * 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 Lucene.Net.Documents;
+
+namespace Lucene.Net.Search
+{
+    using NUnit.Framework;
+    using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
+    using IBits = Lucene.Net.Util.IBits;
+    using Directory = Lucene.Net.Store.Directory;
+    using DirectoryReader = Lucene.Net.Index.DirectoryReader;
+    using Document = Documents.Document;
+    using Field = Field;
+    using FixedBitSet = Lucene.Net.Util.FixedBitSet;
+    using IndexReader = Lucene.Net.Index.IndexReader;
+    using IndexWriter = Lucene.Net.Index.IndexWriter;
+    using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+    using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
+    using OpenMode = Lucene.Net.Index.OpenMode;
+    using Term = Lucene.Net.Index.Term;
+
+    [TestFixture]
+    public class TestFilteredSearch : LuceneTestCase
+    {
+        private const string FIELD = "category";
+
+        [Test]
+        public virtual void TestFilteredSearch_Mem()
+        {
+            bool enforceSingleSegment = true;
+            Directory directory = NewDirectory();
+            int[] filterBits = new int[] { 1, 36 };
+            SimpleDocIdSetFilter filter = new SimpleDocIdSetFilter(filterBits);
+            IndexWriter writer = new IndexWriter(directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy()));
+            SearchFiltered(writer, directory, filter, enforceSingleSegment);
+            // run the test on more than one segment
+            enforceSingleSegment = false;
+            writer.Dispose();
+            writer = new IndexWriter(directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetOpenMode(OpenMode.CREATE).SetMaxBufferedDocs(10).SetMergePolicy(NewLogMergePolicy()));
+            // we index 60 docs - this will create 6 segments
+            SearchFiltered(writer, directory, filter, enforceSingleSegment);
+            writer.Dispose();
+            directory.Dispose();
+        }
+
+        public virtual void SearchFiltered(IndexWriter writer, Directory directory, Filter filter, bool fullMerge)
+        {
+            for (int i = 0; i < 60; i++) //Simple docs
+            {
+                Document doc = new Document();
+                doc.Add(NewStringField(FIELD, Convert.ToString(i), Field.Store.YES));
+                writer.AddDocument(doc);
+            }
+            if (fullMerge)
+            {
+                writer.ForceMerge(1);
+            }
+            writer.Dispose();
+
+            BooleanQuery booleanQuery = new BooleanQuery();
+            booleanQuery.Add(new TermQuery(new Term(FIELD, "36")), Occur.SHOULD);
+
+            IndexReader reader = DirectoryReader.Open(directory);
+            IndexSearcher indexSearcher = NewSearcher(reader);
+            ScoreDoc[] hits = indexSearcher.Search(booleanQuery, filter, 1000).ScoreDocs;
+            Assert.AreEqual(1, hits.Length, "Number of matched documents");
+            reader.Dispose();
+        }
+
+        public sealed class SimpleDocIdSetFilter : Filter
+        {
+            internal readonly int[] Docs;
+
+            public SimpleDocIdSetFilter(int[] docs)
+            {
+                this.Docs = docs;
+            }
+
+            public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs)
+            {
+                Assert.IsNull(acceptDocs, "acceptDocs should be null, as we have an index without deletions");
+                FixedBitSet set = new FixedBitSet(context.Reader.MaxDoc);
+                int docBase = context.DocBase;
+                int limit = docBase + context.Reader.MaxDoc;
+                for (int index = 0; index < Docs.Length; index++)
+                {
+                    int docId = Docs[index];
+                    if (docId >= docBase && docId < limit)
+                    {
+                        set.Set(docId - docBase);
+                    }
+                }
+                return set.Cardinality() == 0 ? null : set;
+            }
+        }
+    }
+}
\ No newline at end of file