lucenenet-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nightowl...@apache.org
Subject [11/72] [abbrv] [partial] lucenenet git commit: Lucene.Net.Tests: Removed \core directory and put its contents in root directory
Date Sun, 26 Feb 2017 23:36:59 GMT
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/Spans/TestFieldMaskingSpanQuery.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/Spans/TestFieldMaskingSpanQuery.cs b/src/Lucene.Net.Tests/Search/Spans/TestFieldMaskingSpanQuery.cs
new file mode 100644
index 0000000..7e78239
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/Spans/TestFieldMaskingSpanQuery.cs
@@ -0,0 +1,326 @@
+using System.Collections.Generic;
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search.Spans
+{
+    using NUnit.Framework;
+    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 RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+    using Term = Lucene.Net.Index.Term;
+    using TFIDFSimilarity = Lucene.Net.Search.Similarities.TFIDFSimilarity;
+
+    [TestFixture]
+    public class TestFieldMaskingSpanQuery : LuceneTestCase
+    {
+        protected internal static Document Doc(Field[] fields)
+        {
+            Document doc = new Document();
+            for (int i = 0; i < fields.Length; i++)
+            {
+                doc.Add(fields[i]);
+            }
+            return doc;
+        }
+
+        protected internal Field GetField(string name, string value)
+        {
+            return NewTextField(name, value, Field.Store.NO);
+        }
+
+        protected internal static IndexSearcher Searcher;
+        protected internal static Directory Directory;
+        protected internal static IndexReader Reader;
+
+        /// <summary>
+        /// LUCENENET specific
+        /// Is non-static because NewIndexWriterConfig is no longer static.
+        /// </summary>
+        [OneTimeSetUp]
+        public void BeforeClass()
+        {
+            Directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), Directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy()));
+
+            writer.AddDocument(Doc(new Field[] { GetField("id", "0"), GetField("gender", "male"), GetField("first", "james"), GetField("last", "jones") }));
+
+            writer.AddDocument(Doc(new Field[] { GetField("id", "1"), GetField("gender", "male"), GetField("first", "james"), GetField("last", "smith"), GetField("gender", "female"), GetField("first", "sally"), GetField("last", "jones") }));
+
+            writer.AddDocument(Doc(new Field[] { GetField("id", "2"), GetField("gender", "female"), GetField("first", "greta"), GetField("last", "jones"), GetField("gender", "female"), GetField("first", "sally"), GetField("last", "smith"), GetField("gender", "male"), GetField("first", "james"), GetField("last", "jones") }));
+
+            writer.AddDocument(Doc(new Field[] { GetField("id", "3"), GetField("gender", "female"), GetField("first", "lisa"), GetField("last", "jones"), GetField("gender", "male"), GetField("first", "bob"), GetField("last", "costas") }));
+
+            writer.AddDocument(Doc(new Field[] { GetField("id", "4"), GetField("gender", "female"), GetField("first", "sally"), GetField("last", "smith"), GetField("gender", "female"), GetField("first", "linda"), GetField("last", "dixit"), GetField("gender", "male"), GetField("first", "bubba"), GetField("last", "jones") }));
+            Reader = writer.Reader;
+            writer.Dispose();
+            Searcher = NewSearcher(Reader);
+        }
+
+        [OneTimeTearDown]
+        public static void AfterClass()
+        {
+            Searcher = null;
+            Reader.Dispose();
+            Reader = null;
+            Directory.Dispose();
+            Directory = null;
+        }
+
+        protected internal virtual void Check(SpanQuery q, int[] docs)
+        {
+            CheckHits.CheckHitCollector(Random(), q, null, Searcher, docs, Similarity);
+        }
+
+        [Test]
+        public virtual void TestRewrite0()
+        {
+            SpanQuery q = new FieldMaskingSpanQuery(new SpanTermQuery(new Term("last", "sally")), "first");
+            q.Boost = 8.7654321f;
+            SpanQuery qr = (SpanQuery)Searcher.Rewrite(q);
+
+            QueryUtils.CheckEqual(q, qr);
+
+            HashSet<Term> terms = new HashSet<Term>();
+            qr.ExtractTerms(terms);
+            Assert.AreEqual(1, terms.Count);
+        }
+
+        [Test]
+        public virtual void TestRewrite1()
+        {
+            // mask an anon SpanQuery class that rewrites to something else.
+            SpanQuery q = new FieldMaskingSpanQuery(new SpanTermQueryAnonymousInnerClassHelper(this, new Term("last", "sally")), "first");
+
+            SpanQuery qr = (SpanQuery)Searcher.Rewrite(q);
+
+            QueryUtils.CheckUnequal(q, qr);
+
+            HashSet<Term> terms = new HashSet<Term>();
+            qr.ExtractTerms(terms);
+            Assert.AreEqual(2, terms.Count);
+        }
+
+        private class SpanTermQueryAnonymousInnerClassHelper : SpanTermQuery
+        {
+            private readonly TestFieldMaskingSpanQuery OuterInstance;
+
+            public SpanTermQueryAnonymousInnerClassHelper(TestFieldMaskingSpanQuery outerInstance, Term term)
+                : base(term)
+            {
+                this.OuterInstance = outerInstance;
+            }
+
+            public override Query Rewrite(IndexReader reader)
+            {
+                return new SpanOrQuery(new SpanTermQuery(new Term("first", "sally")), new SpanTermQuery(new Term("first", "james")));
+            }
+        }
+
+        [Test]
+        public virtual void TestRewrite2()
+        {
+            SpanQuery q1 = new SpanTermQuery(new Term("last", "smith"));
+            SpanQuery q2 = new SpanTermQuery(new Term("last", "jones"));
+            SpanQuery q = new SpanNearQuery(new SpanQuery[] { q1, new FieldMaskingSpanQuery(q2, "last") }, 1, true);
+            Query qr = Searcher.Rewrite(q);
+
+            QueryUtils.CheckEqual(q, qr);
+
+            HashSet<Term> set = new HashSet<Term>();
+            qr.ExtractTerms(set);
+            Assert.AreEqual(2, set.Count);
+        }
+
+        [Test]
+        public virtual void TestEquality1()
+        {
+            SpanQuery q1 = new FieldMaskingSpanQuery(new SpanTermQuery(new Term("last", "sally")), "first");
+            SpanQuery q2 = new FieldMaskingSpanQuery(new SpanTermQuery(new Term("last", "sally")), "first");
+            SpanQuery q3 = new FieldMaskingSpanQuery(new SpanTermQuery(new Term("last", "sally")), "XXXXX");
+            SpanQuery q4 = new FieldMaskingSpanQuery(new SpanTermQuery(new Term("last", "XXXXX")), "first");
+            SpanQuery q5 = new FieldMaskingSpanQuery(new SpanTermQuery(new Term("xXXX", "sally")), "first");
+            QueryUtils.CheckEqual(q1, q2);
+            QueryUtils.CheckUnequal(q1, q3);
+            QueryUtils.CheckUnequal(q1, q4);
+            QueryUtils.CheckUnequal(q1, q5);
+
+            SpanQuery qA = new FieldMaskingSpanQuery(new SpanTermQuery(new Term("last", "sally")), "first");
+            qA.Boost = 9f;
+            SpanQuery qB = new FieldMaskingSpanQuery(new SpanTermQuery(new Term("last", "sally")), "first");
+            QueryUtils.CheckUnequal(qA, qB);
+            qB.Boost = 9f;
+            QueryUtils.CheckEqual(qA, qB);
+        }
+
+        [Test]
+        public virtual void TestNoop0()
+        {
+            SpanQuery q1 = new SpanTermQuery(new Term("last", "sally"));
+            SpanQuery q = new FieldMaskingSpanQuery(q1, "first");
+            Check(q, new int[] { }); // :EMPTY:
+        }
+
+        [Test]
+        public virtual void TestNoop1()
+        {
+            SpanQuery q1 = new SpanTermQuery(new Term("last", "smith"));
+            SpanQuery q2 = new SpanTermQuery(new Term("last", "jones"));
+            SpanQuery q = new SpanNearQuery(new SpanQuery[] { q1, new FieldMaskingSpanQuery(q2, "last") }, 0, true);
+            Check(q, new int[] { 1, 2 });
+            q = new SpanNearQuery(new SpanQuery[] { new FieldMaskingSpanQuery(q1, "last"), new FieldMaskingSpanQuery(q2, "last") }, 0, true);
+            Check(q, new int[] { 1, 2 });
+        }
+
+        [Test]
+        public virtual void TestSimple1()
+        {
+            SpanQuery q1 = new SpanTermQuery(new Term("first", "james"));
+            SpanQuery q2 = new SpanTermQuery(new Term("last", "jones"));
+            SpanQuery q = new SpanNearQuery(new SpanQuery[] { q1, new FieldMaskingSpanQuery(q2, "first") }, -1, false);
+            Check(q, new int[] { 0, 2 });
+            q = new SpanNearQuery(new SpanQuery[] { new FieldMaskingSpanQuery(q2, "first"), q1 }, -1, false);
+            Check(q, new int[] { 0, 2 });
+            q = new SpanNearQuery(new SpanQuery[] { q2, new FieldMaskingSpanQuery(q1, "last") }, -1, false);
+            Check(q, new int[] { 0, 2 });
+            q = new SpanNearQuery(new SpanQuery[] { new FieldMaskingSpanQuery(q1, "last"), q2 }, -1, false);
+            Check(q, new int[] { 0, 2 });
+        }
+
+        [Test]
+        public virtual void TestSimple2()
+        {
+            AssumeTrue("Broken scoring: LUCENE-3723", Searcher.Similarity is TFIDFSimilarity);
+            SpanQuery q1 = new SpanTermQuery(new Term("gender", "female"));
+            SpanQuery q2 = new SpanTermQuery(new Term("last", "smith"));
+            SpanQuery q = new SpanNearQuery(new SpanQuery[] { q1, new FieldMaskingSpanQuery(q2, "gender") }, -1, false);
+            Check(q, new int[] { 2, 4 });
+            q = new SpanNearQuery(new SpanQuery[] { new FieldMaskingSpanQuery(q1, "id"), new FieldMaskingSpanQuery(q2, "id") }, -1, false);
+            Check(q, new int[] { 2, 4 });
+        }
+
+        [Test]
+        public virtual void TestSpans0()
+        {
+            SpanQuery q1 = new SpanTermQuery(new Term("gender", "female"));
+            SpanQuery q2 = new SpanTermQuery(new Term("first", "james"));
+            SpanQuery q = new SpanOrQuery(q1, new FieldMaskingSpanQuery(q2, "gender"));
+            Check(q, new int[] { 0, 1, 2, 3, 4 });
+
+            Spans span = MultiSpansWrapper.Wrap(Searcher.TopReaderContext, q);
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(0, 0, 1), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(1, 0, 1), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(1, 1, 2), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(2, 0, 1), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(2, 1, 2), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(2, 2, 3), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(3, 0, 1), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(4, 0, 1), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(4, 1, 2), s(span));
+
+            Assert.AreEqual(false, span.Next());
+        }
+
+        [Test]
+        public virtual void TestSpans1()
+        {
+            SpanQuery q1 = new SpanTermQuery(new Term("first", "sally"));
+            SpanQuery q2 = new SpanTermQuery(new Term("first", "james"));
+            SpanQuery qA = new SpanOrQuery(q1, q2);
+            SpanQuery qB = new FieldMaskingSpanQuery(qA, "id");
+
+            Check(qA, new int[] { 0, 1, 2, 4 });
+            Check(qB, new int[] { 0, 1, 2, 4 });
+
+            Spans spanA = MultiSpansWrapper.Wrap(Searcher.TopReaderContext, qA);
+            Spans spanB = MultiSpansWrapper.Wrap(Searcher.TopReaderContext, qB);
+
+            while (spanA.Next())
+            {
+                Assert.IsTrue(spanB.Next(), "spanB not still going");
+                Assert.AreEqual(s(spanA), s(spanB), "spanA not equal spanB");
+            }
+            Assert.IsTrue(!(spanB.Next()), "spanB still going even tough spanA is done");
+        }
+
+        [Test]
+        public virtual void TestSpans2()
+        {
+            AssumeTrue("Broken scoring: LUCENE-3723", Searcher.Similarity is TFIDFSimilarity);
+            SpanQuery qA1 = new SpanTermQuery(new Term("gender", "female"));
+            SpanQuery qA2 = new SpanTermQuery(new Term("first", "james"));
+            SpanQuery qA = new SpanOrQuery(qA1, new FieldMaskingSpanQuery(qA2, "gender"));
+            SpanQuery qB = new SpanTermQuery(new Term("last", "jones"));
+            SpanQuery q = new SpanNearQuery(new SpanQuery[] { new FieldMaskingSpanQuery(qA, "id"), new FieldMaskingSpanQuery(qB, "id") }, -1, false);
+            Check(q, new int[] { 0, 1, 2, 3 });
+
+            Spans span = MultiSpansWrapper.Wrap(Searcher.TopReaderContext, q);
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(0, 0, 1), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(1, 1, 2), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(2, 0, 1), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(2, 2, 3), s(span));
+
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(3, 0, 1), s(span));
+
+            Assert.AreEqual(false, span.Next());
+        }
+
+        public virtual string s(Spans span)
+        {
+            return s(span.Doc, span.Start, span.End);
+        }
+
+        public virtual string s(int doc, int start, int end)
+        {
+            return "s(" + doc + "," + start + "," + end + ")";
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/Spans/TestNearSpansOrdered.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/Spans/TestNearSpansOrdered.cs b/src/Lucene.Net.Tests/Search/Spans/TestNearSpansOrdered.cs
new file mode 100644
index 0000000..0bbcafb
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/Spans/TestNearSpansOrdered.cs
@@ -0,0 +1,203 @@
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search.Spans
+{
+    using Lucene.Net.Index;
+    using NUnit.Framework;
+    using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
+    using Directory = Lucene.Net.Store.Directory;
+    using Document = Documents.Document;
+    using Field = Field;
+    using IndexReader = Lucene.Net.Index.IndexReader;
+    using IndexReaderContext = Lucene.Net.Index.IndexReaderContext;
+    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 TestNearSpansOrdered : LuceneTestCase
+    {
+        protected internal IndexSearcher Searcher;
+        protected internal Directory Directory;
+        protected internal IndexReader Reader;
+
+        public const string FIELD = "field";
+
+        [TearDown]
+        public override void TearDown()
+        {
+            Reader.Dispose();
+            Directory.Dispose();
+            base.TearDown();
+        }
+
+        [SetUp]
+        public override void SetUp()
+        {
+            base.SetUp();
+            Directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), Directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy()));
+            for (int i = 0; i < DocFields.Length; i++)
+            {
+                Document doc = new Document();
+                doc.Add(NewTextField(FIELD, DocFields[i], Field.Store.NO));
+                writer.AddDocument(doc);
+            }
+            Reader = writer.Reader;
+            writer.Dispose();
+            Searcher = NewSearcher(Reader);
+        }
+
+        protected internal string[] DocFields = new string[] { "w1 w2 w3 w4 w5", "w1 w3 w2 w3 zz", "w1 xx w2 yy w3", "w1 w3 xx w2 yy w3 zz" };
+
+        protected internal virtual SpanNearQuery MakeQuery(string s1, string s2, string s3, int slop, bool inOrder)
+        {
+            return new SpanNearQuery(new SpanQuery[] { new SpanTermQuery(new Term(FIELD, s1)), new SpanTermQuery(new Term(FIELD, s2)), new SpanTermQuery(new Term(FIELD, s3)) }, slop, inOrder);
+        }
+
+        protected internal virtual SpanNearQuery MakeQuery()
+        {
+            return MakeQuery("w1", "w2", "w3", 1, true);
+        }
+
+        [Test]
+        public virtual void TestSpanNearQuery()
+        {
+            SpanNearQuery q = MakeQuery();
+            CheckHits.DoCheckHits(Random(), q, FIELD, Searcher, new int[] { 0, 1 }, Similarity);
+        }
+
+        public virtual string s(Spans span)
+        {
+            return s(span.Doc, span.Start, span.End);
+        }
+
+        public virtual string s(int doc, int start, int end)
+        {
+            return "s(" + doc + "," + start + "," + end + ")";
+        }
+
+        [Test]
+        public virtual void TestNearSpansNext()
+        {
+            SpanNearQuery q = MakeQuery();
+            Spans span = MultiSpansWrapper.Wrap(Searcher.TopReaderContext, q);
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(0, 0, 3), s(span));
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(1, 0, 4), s(span));
+            Assert.AreEqual(false, span.Next());
+        }
+
+        /// <summary>
+        /// test does not imply that skipTo(doc+1) should work exactly the
+        /// same as next -- it's only applicable in this case since we know doc
+        /// does not contain more than one span
+        /// </summary>
+        [Test]
+        public virtual void TestNearSpansSkipToLikeNext()
+        {
+            SpanNearQuery q = MakeQuery();
+            Spans span = MultiSpansWrapper.Wrap(Searcher.TopReaderContext, q);
+            Assert.AreEqual(true, span.SkipTo(0));
+            Assert.AreEqual(s(0, 0, 3), s(span));
+            Assert.AreEqual(true, span.SkipTo(1));
+            Assert.AreEqual(s(1, 0, 4), s(span));
+            Assert.AreEqual(false, span.SkipTo(2));
+        }
+
+        [Test]
+        public virtual void TestNearSpansNextThenSkipTo()
+        {
+            SpanNearQuery q = MakeQuery();
+            Spans span = MultiSpansWrapper.Wrap(Searcher.TopReaderContext, q);
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(0, 0, 3), s(span));
+            Assert.AreEqual(true, span.SkipTo(1));
+            Assert.AreEqual(s(1, 0, 4), s(span));
+            Assert.AreEqual(false, span.Next());
+        }
+
+        [Test]
+        public virtual void TestNearSpansNextThenSkipPast()
+        {
+            SpanNearQuery q = MakeQuery();
+            Spans span = MultiSpansWrapper.Wrap(Searcher.TopReaderContext, q);
+            Assert.AreEqual(true, span.Next());
+            Assert.AreEqual(s(0, 0, 3), s(span));
+            Assert.AreEqual(false, span.SkipTo(2));
+        }
+
+        [Test]
+        public virtual void TestNearSpansSkipPast()
+        {
+            SpanNearQuery q = MakeQuery();
+            Spans span = MultiSpansWrapper.Wrap(Searcher.TopReaderContext, q);
+            Assert.AreEqual(false, span.SkipTo(2));
+        }
+
+        [Test]
+        public virtual void TestNearSpansSkipTo0()
+        {
+            SpanNearQuery q = MakeQuery();
+            Spans span = MultiSpansWrapper.Wrap(Searcher.TopReaderContext, q);
+            Assert.AreEqual(true, span.SkipTo(0));
+            Assert.AreEqual(s(0, 0, 3), s(span));
+        }
+
+        [Test]
+        public virtual void TestNearSpansSkipTo1()
+        {
+            SpanNearQuery q = MakeQuery();
+            Spans span = MultiSpansWrapper.Wrap(Searcher.TopReaderContext, q);
+            Assert.AreEqual(true, span.SkipTo(1));
+            Assert.AreEqual(s(1, 0, 4), s(span));
+        }
+
+        /// <summary>
+        /// not a direct test of NearSpans, but a demonstration of how/when
+        /// this causes problems
+        /// </summary>
+        [Test]
+        public virtual void TestSpanNearScorerSkipTo1()
+        {
+            SpanNearQuery q = MakeQuery();
+            Weight w = Searcher.CreateNormalizedWeight(q);
+            IndexReaderContext topReaderContext = Searcher.TopReaderContext;
+            AtomicReaderContext leave = topReaderContext.Leaves[0];
+            Scorer s = w.GetScorer(leave, ((AtomicReader)leave.Reader).LiveDocs);
+            Assert.AreEqual(1, s.Advance(1));
+        }
+
+        /// <summary>
+        /// not a direct test of NearSpans, but a demonstration of how/when
+        /// this causes problems
+        /// </summary>
+        [Test]
+        public virtual void TestSpanNearScorerExplain()
+        {
+            SpanNearQuery q = MakeQuery();
+            Explanation e = Searcher.Explain(q, 1);
+            Assert.IsTrue(0.0f < e.Value, "Scorer explanation value for doc#1 isn't positive: " + e.ToString());
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/Spans/TestPayloadSpans.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/Spans/TestPayloadSpans.cs b/src/Lucene.Net.Tests/Search/Spans/TestPayloadSpans.cs
new file mode 100644
index 0000000..a9393b4
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/Spans/TestPayloadSpans.cs
@@ -0,0 +1,589 @@
+using Lucene.Net.Analysis.TokenAttributes;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search.Spans
+{
+    using NUnit.Framework;
+    using System.IO;
+
+    /*
+        /// Copyright 2004 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.
+        */
+
+    using Analyzer = Lucene.Net.Analysis.Analyzer;
+    using BytesRef = Lucene.Net.Util.BytesRef;
+    using DefaultSimilarity = Lucene.Net.Search.Similarities.DefaultSimilarity;
+    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 MockTokenizer = Lucene.Net.Analysis.MockTokenizer;
+    using PayloadHelper = Lucene.Net.Search.Payloads.PayloadHelper;
+    using PayloadSpanUtil = Lucene.Net.Search.Payloads.PayloadSpanUtil;
+    using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
+    using Similarity = Lucene.Net.Search.Similarities.Similarity;
+    using Term = Lucene.Net.Index.Term;
+    using TextField = TextField;
+    using TokenFilter = Lucene.Net.Analysis.TokenFilter;
+    using Tokenizer = Lucene.Net.Analysis.Tokenizer;
+    using TokenStream = Lucene.Net.Analysis.TokenStream;
+
+    [TestFixture]
+    public class TestPayloadSpans : LuceneTestCase
+    {
+        private IndexSearcher Searcher_Renamed;
+        private Similarity similarity = new DefaultSimilarity();
+        protected internal IndexReader IndexReader;
+        private IndexReader CloseIndexReader;
+        private Directory Directory;
+
+        [SetUp]
+        public override void SetUp()
+        {
+            base.SetUp();
+            PayloadHelper helper = new PayloadHelper();
+            Searcher_Renamed = helper.SetUp(Random(), similarity, 1000);
+            IndexReader = Searcher_Renamed.IndexReader;
+        }
+
+        [Test]
+        public virtual void TestSpanTermQuery()
+        {
+            SpanTermQuery stq;
+            Spans spans;
+            stq = new SpanTermQuery(new Term(PayloadHelper.FIELD, "seventy"));
+            spans = MultiSpansWrapper.Wrap(IndexReader.Context, stq);
+            Assert.IsTrue(spans != null, "spans is null and it shouldn't be");
+            CheckSpans(spans, 100, 1, 1, 1);
+
+            stq = new SpanTermQuery(new Term(PayloadHelper.NO_PAYLOAD_FIELD, "seventy"));
+            spans = MultiSpansWrapper.Wrap(IndexReader.Context, stq);
+            Assert.IsTrue(spans != null, "spans is null and it shouldn't be");
+            CheckSpans(spans, 100, 0, 0, 0);
+        }
+
+        [Test]
+        public virtual void TestSpanFirst()
+        {
+            SpanQuery match;
+            SpanFirstQuery sfq;
+            match = new SpanTermQuery(new Term(PayloadHelper.FIELD, "one"));
+            sfq = new SpanFirstQuery(match, 2);
+            Spans spans = MultiSpansWrapper.Wrap(IndexReader.Context, sfq);
+            CheckSpans(spans, 109, 1, 1, 1);
+            //Test more complicated subclause
+            SpanQuery[] clauses = new SpanQuery[2];
+            clauses[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "one"));
+            clauses[1] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "hundred"));
+            match = new SpanNearQuery(clauses, 0, true);
+            sfq = new SpanFirstQuery(match, 2);
+            CheckSpans(MultiSpansWrapper.Wrap(IndexReader.Context, sfq), 100, 2, 1, 1);
+
+            match = new SpanNearQuery(clauses, 0, false);
+            sfq = new SpanFirstQuery(match, 2);
+            CheckSpans(MultiSpansWrapper.Wrap(IndexReader.Context, sfq), 100, 2, 1, 1);
+        }
+
+        [Test]
+        public virtual void TestSpanNot()
+        {
+            SpanQuery[] clauses = new SpanQuery[2];
+            clauses[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "one"));
+            clauses[1] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "three"));
+            SpanQuery spq = new SpanNearQuery(clauses, 5, true);
+            SpanNotQuery snq = new SpanNotQuery(spq, new SpanTermQuery(new Term(PayloadHelper.FIELD, "two")));
+
+            Directory directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new PayloadAnalyzer(this)).SetSimilarity(similarity));
+
+            Document doc = new Document();
+            doc.Add(NewTextField(PayloadHelper.FIELD, "one two three one four three", Field.Store.YES));
+            writer.AddDocument(doc);
+            IndexReader reader = writer.Reader;
+            writer.Dispose();
+
+            CheckSpans(MultiSpansWrapper.Wrap(reader.Context, snq), 1, new int[] { 2 });
+            reader.Dispose();
+            directory.Dispose();
+        }
+
+        [Test]
+        public virtual void TestNestedSpans()
+        {
+            SpanTermQuery stq;
+            Spans spans;
+            IndexSearcher searcher = Searcher;
+            stq = new SpanTermQuery(new Term(PayloadHelper.FIELD, "mark"));
+            spans = MultiSpansWrapper.Wrap(searcher.TopReaderContext, stq);
+            Assert.IsTrue(spans != null, "spans is null and it shouldn't be");
+            CheckSpans(spans, 0, null);
+
+            SpanQuery[] clauses = new SpanQuery[3];
+            clauses[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "rr"));
+            clauses[1] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "yy"));
+            clauses[2] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "xx"));
+            SpanNearQuery spanNearQuery = new SpanNearQuery(clauses, 12, false);
+
+            spans = MultiSpansWrapper.Wrap(searcher.TopReaderContext, spanNearQuery);
+            Assert.IsTrue(spans != null, "spans is null and it shouldn't be");
+            CheckSpans(spans, 2, new int[] { 3, 3 });
+
+            clauses[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "xx"));
+            clauses[1] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "rr"));
+            clauses[2] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "yy"));
+
+            spanNearQuery = new SpanNearQuery(clauses, 6, true);
+
+            spans = MultiSpansWrapper.Wrap(searcher.TopReaderContext, spanNearQuery);
+
+            Assert.IsTrue(spans != null, "spans is null and it shouldn't be");
+            CheckSpans(spans, 1, new int[] { 3 });
+
+            clauses = new SpanQuery[2];
+
+            clauses[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "xx"));
+            clauses[1] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "rr"));
+
+            spanNearQuery = new SpanNearQuery(clauses, 6, true);
+
+            // xx within 6 of rr
+
+            SpanQuery[] clauses2 = new SpanQuery[2];
+
+            clauses2[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "yy"));
+            clauses2[1] = spanNearQuery;
+
+            SpanNearQuery nestedSpanNearQuery = new SpanNearQuery(clauses2, 6, false);
+
+            // yy within 6 of xx within 6 of rr
+
+            spans = MultiSpansWrapper.Wrap(searcher.TopReaderContext, nestedSpanNearQuery);
+            Assert.IsTrue(spans != null, "spans is null and it shouldn't be");
+            CheckSpans(spans, 2, new int[] { 3, 3 });
+            CloseIndexReader.Dispose();
+            Directory.Dispose();
+        }
+
+        [Test]
+        public virtual void TestFirstClauseWithoutPayload()
+        {
+            Spans spans;
+            IndexSearcher searcher = Searcher;
+
+            SpanQuery[] clauses = new SpanQuery[3];
+            clauses[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "nopayload"));
+            clauses[1] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "qq"));
+            clauses[2] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "ss"));
+
+            SpanNearQuery spanNearQuery = new SpanNearQuery(clauses, 6, true);
+
+            SpanQuery[] clauses2 = new SpanQuery[2];
+
+            clauses2[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "pp"));
+            clauses2[1] = spanNearQuery;
+
+            SpanNearQuery snq = new SpanNearQuery(clauses2, 6, false);
+
+            SpanQuery[] clauses3 = new SpanQuery[2];
+
+            clauses3[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "np"));
+            clauses3[1] = snq;
+
+            SpanNearQuery nestedSpanNearQuery = new SpanNearQuery(clauses3, 6, false);
+            spans = MultiSpansWrapper.Wrap(searcher.TopReaderContext, nestedSpanNearQuery);
+
+            Assert.IsTrue(spans != null, "spans is null and it shouldn't be");
+            CheckSpans(spans, 1, new int[] { 3 });
+            CloseIndexReader.Dispose();
+            Directory.Dispose();
+        }
+
+        [Test]
+        public virtual void TestHeavilyNestedSpanQuery()
+        {
+            Spans spans;
+            IndexSearcher searcher = Searcher;
+
+            SpanQuery[] clauses = new SpanQuery[3];
+            clauses[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "one"));
+            clauses[1] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "two"));
+            clauses[2] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "three"));
+
+            SpanNearQuery spanNearQuery = new SpanNearQuery(clauses, 5, true);
+
+            clauses = new SpanQuery[3];
+            clauses[0] = spanNearQuery;
+            clauses[1] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "five"));
+            clauses[2] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "six"));
+
+            SpanNearQuery spanNearQuery2 = new SpanNearQuery(clauses, 6, true);
+
+            SpanQuery[] clauses2 = new SpanQuery[2];
+            clauses2[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "eleven"));
+            clauses2[1] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "ten"));
+            SpanNearQuery spanNearQuery3 = new SpanNearQuery(clauses2, 2, false);
+
+            SpanQuery[] clauses3 = new SpanQuery[3];
+            clauses3[0] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "nine"));
+            clauses3[1] = spanNearQuery2;
+            clauses3[2] = spanNearQuery3;
+
+            SpanNearQuery nestedSpanNearQuery = new SpanNearQuery(clauses3, 6, false);
+
+            spans = MultiSpansWrapper.Wrap(searcher.TopReaderContext, nestedSpanNearQuery);
+            Assert.IsTrue(spans != null, "spans is null and it shouldn't be");
+            CheckSpans(spans, 2, new int[] { 8, 8 });
+            CloseIndexReader.Dispose();
+            Directory.Dispose();
+        }
+
+        [Test]
+        public virtual void TestShrinkToAfterShortestMatch()
+        {
+            Directory directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new TestPayloadAnalyzer(this)));
+
+            Document doc = new Document();
+            doc.Add(new TextField("content", new StringReader("a b c d e f g h i j a k")));
+            writer.AddDocument(doc);
+
+            IndexReader reader = writer.Reader;
+            IndexSearcher @is = NewSearcher(reader);
+            writer.Dispose();
+
+            SpanTermQuery stq1 = new SpanTermQuery(new Term("content", "a"));
+            SpanTermQuery stq2 = new SpanTermQuery(new Term("content", "k"));
+            SpanQuery[] sqs = new SpanQuery[] { stq1, stq2 };
+            SpanNearQuery snq = new SpanNearQuery(sqs, 1, true);
+            Spans spans = MultiSpansWrapper.Wrap(@is.TopReaderContext, snq);
+
+            TopDocs topDocs = @is.Search(snq, 1);
+            HashSet<string> payloadSet = new HashSet<string>();
+            for (int i = 0; i < topDocs.ScoreDocs.Length; i++)
+            {
+                while (spans.Next())
+                {
+                    var payloads = spans.GetPayload();
+                    foreach (var payload in payloads)
+                    {
+                        payloadSet.Add(Encoding.UTF8.GetString(payload));
+                    }
+                }
+            }
+            Assert.AreEqual(2, payloadSet.Count);
+            Assert.IsTrue(payloadSet.Contains("a:Noise:10"));
+            Assert.IsTrue(payloadSet.Contains("k:Noise:11"));
+            reader.Dispose();
+            directory.Dispose();
+        }
+
+        [Test]
+        public virtual void TestShrinkToAfterShortestMatch2()
+        {
+            Directory directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new TestPayloadAnalyzer(this)));
+
+            Document doc = new Document();
+            doc.Add(new TextField("content", new StringReader("a b a d k f a h i k a k")));
+            writer.AddDocument(doc);
+            IndexReader reader = writer.Reader;
+            IndexSearcher @is = NewSearcher(reader);
+            writer.Dispose();
+
+            SpanTermQuery stq1 = new SpanTermQuery(new Term("content", "a"));
+            SpanTermQuery stq2 = new SpanTermQuery(new Term("content", "k"));
+            SpanQuery[] sqs = { stq1, stq2 };
+            SpanNearQuery snq = new SpanNearQuery(sqs, 0, true);
+            Spans spans = MultiSpansWrapper.Wrap(@is.TopReaderContext, snq);
+
+            TopDocs topDocs = @is.Search(snq, 1);
+            HashSet<string> payloadSet = new HashSet<string>();
+            for (int i = 0; i < topDocs.ScoreDocs.Length; i++)
+            {
+                while (spans.Next())
+                {
+                    var payloads = spans.GetPayload();
+                    foreach (var payload in payloads)
+                    {
+                        payloadSet.Add(Encoding.UTF8.GetString((byte[])(Array)payload));
+                    }
+                }
+            }
+            Assert.AreEqual(2, payloadSet.Count);
+            Assert.IsTrue(payloadSet.Contains("a:Noise:10"));
+            Assert.IsTrue(payloadSet.Contains("k:Noise:11"));
+            reader.Dispose();
+            directory.Dispose();
+        }
+
+        [Test]
+        public virtual void TestShrinkToAfterShortestMatch3()
+        {
+            Directory directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new TestPayloadAnalyzer(this)));
+
+            Document doc = new Document();
+            doc.Add(new TextField("content", new StringReader("j k a l f k k p a t a k l k t a")));
+            writer.AddDocument(doc);
+            IndexReader reader = writer.Reader;
+            IndexSearcher @is = NewSearcher(reader);
+            writer.Dispose();
+
+            SpanTermQuery stq1 = new SpanTermQuery(new Term("content", "a"));
+            SpanTermQuery stq2 = new SpanTermQuery(new Term("content", "k"));
+            SpanQuery[] sqs = new SpanQuery[] { stq1, stq2 };
+            SpanNearQuery snq = new SpanNearQuery(sqs, 0, true);
+            Spans spans = MultiSpansWrapper.Wrap(@is.TopReaderContext, snq);
+
+            TopDocs topDocs = @is.Search(snq, 1);
+            HashSet<string> payloadSet = new HashSet<string>();
+            for (int i = 0; i < topDocs.ScoreDocs.Length; i++)
+            {
+                while (spans.Next())
+                {
+                    var payloads = spans.GetPayload();
+                    foreach (var payload in payloads)
+                    {
+                        payloadSet.Add(Encoding.UTF8.GetString(payload));
+                    }
+                }
+            }
+            Assert.AreEqual(2, payloadSet.Count);
+            if (VERBOSE)
+            {
+                foreach (String payload in payloadSet)
+                {
+                    Console.WriteLine("match:" + payload);
+                }
+            }
+            Assert.IsTrue(payloadSet.Contains("a:Noise:10"));
+            Assert.IsTrue(payloadSet.Contains("k:Noise:11"));
+            reader.Dispose();
+            directory.Dispose();
+        }
+
+        [Test]
+        public virtual void TestPayloadSpanUtil()
+        {
+            Directory directory = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new PayloadAnalyzer(this)).SetSimilarity(similarity));
+
+            Document doc = new Document();
+            doc.Add(NewTextField(PayloadHelper.FIELD, "xx rr yy mm  pp", Field.Store.YES));
+            writer.AddDocument(doc);
+
+            IndexReader reader = writer.Reader;
+            writer.Dispose();
+            IndexSearcher searcher = NewSearcher(reader);
+
+            PayloadSpanUtil psu = new PayloadSpanUtil(searcher.TopReaderContext);
+
+            var payloads = psu.GetPayloadsForQuery(new TermQuery(new Term(PayloadHelper.FIELD, "rr")));
+            if (VERBOSE)
+            {
+                Console.WriteLine("Num payloads:" + payloads.Count);
+                foreach (var bytes in payloads)
+                {
+                    Console.WriteLine(Encoding.UTF8.GetString((byte[])(Array)bytes));
+                }
+            }
+            reader.Dispose();
+            directory.Dispose();
+        }
+
+        private void CheckSpans(Spans spans, int expectedNumSpans, int expectedNumPayloads, int expectedPayloadLength, int expectedFirstByte)
+        {
+            Assert.IsTrue(spans != null, "spans is null and it shouldn't be");
+            //each position match should have a span associated with it, since there is just one underlying term query, there should
+            //only be one entry in the span
+            int seen = 0;
+            while (spans.Next() == true)
+            {
+                //if we expect payloads, then isPayloadAvailable should be true
+                if (expectedNumPayloads > 0)
+                {
+                    Assert.IsTrue(spans.IsPayloadAvailable == true, "isPayloadAvailable is not returning the correct value: " + spans.IsPayloadAvailable + " and it should be: " + (expectedNumPayloads > 0));
+                }
+                else
+                {
+                    Assert.IsTrue(spans.IsPayloadAvailable == false, "isPayloadAvailable should be false");
+                }
+                //See payload helper, for the PayloadHelper.FIELD field, there is a single byte payload at every token
+                if (spans.IsPayloadAvailable)
+                {
+                    var payload = spans.GetPayload();
+                    Assert.IsTrue(payload.Count == expectedNumPayloads, "payload Size: " + payload.Count + " is not: " + expectedNumPayloads);
+                    foreach (var thePayload in payload)
+                    {
+                        Assert.IsTrue(thePayload.Length == expectedPayloadLength, "payload[0] Size: " + thePayload.Length + " is not: " + expectedPayloadLength);
+                        Assert.IsTrue(thePayload[0] == expectedFirstByte, thePayload[0] + " does not equal: " + expectedFirstByte);
+                    }
+                }
+                seen++;
+            }
+            Assert.IsTrue(seen == expectedNumSpans, seen + " does not equal: " + expectedNumSpans);
+        }
+
+        private IndexSearcher Searcher
+        {
+            get
+            {
+                Directory = NewDirectory();
+                string[] docs = new string[] { "xx rr yy mm  pp", "xx yy mm rr pp", "nopayload qq ss pp np", "one two three four five six seven eight nine ten eleven", "nine one two three four five six seven eight eleven ten" };
+                RandomIndexWriter writer = new RandomIndexWriter(Random(), Directory, NewIndexWriterConfig(TEST_VERSION_CURRENT, new PayloadAnalyzer(this)).SetSimilarity(similarity));
+
+                Document doc = null;
+                for (int i = 0; i < docs.Length; i++)
+                {
+                    doc = new Document();
+                    string docText = docs[i];
+                    doc.Add(NewTextField(PayloadHelper.FIELD, docText, Field.Store.YES));
+                    writer.AddDocument(doc);
+                }
+
+                CloseIndexReader = writer.Reader;
+                writer.Dispose();
+
+                IndexSearcher searcher = NewSearcher(CloseIndexReader);
+                return searcher;
+            }
+        }
+
+        private void CheckSpans(Spans spans, int numSpans, int[] numPayloads)
+        {
+            int cnt = 0;
+
+            while (spans.Next() == true)
+            {
+                if (VERBOSE)
+                {
+                    Console.WriteLine("\nSpans Dump --");
+                }
+                if (spans.IsPayloadAvailable)
+                {
+                    var payload = spans.GetPayload();
+                    if (VERBOSE)
+                    {
+                        Console.WriteLine("payloads for span:" + payload.Count);
+                        foreach (var bytes in payload)
+                        {
+                            Console.WriteLine("doc:" + spans.Doc + " s:" + spans.Start + " e:" + spans.End + " " + Encoding.UTF8.GetString((byte[])(Array)bytes));
+                        }
+                    }
+
+                    Assert.AreEqual(numPayloads[cnt], payload.Count);
+                }
+                else
+                {
+                    Assert.IsFalse(numPayloads.Length > 0 && numPayloads[cnt] > 0, "Expected spans:" + numPayloads[cnt] + " found: 0");
+                }
+                cnt++;
+            }
+
+            Assert.AreEqual(numSpans, cnt);
+        }
+
+        internal sealed class PayloadAnalyzer : Analyzer
+        {
+            private readonly TestPayloadSpans OuterInstance;
+
+            public PayloadAnalyzer(TestPayloadSpans outerInstance)
+            {
+                this.OuterInstance = outerInstance;
+            }
+
+            protected internal override TokenStreamComponents CreateComponents(string fieldName, TextReader reader)
+            {
+                Tokenizer result = new MockTokenizer(reader, MockTokenizer.SIMPLE, true);
+                return new TokenStreamComponents(result, new PayloadFilter(OuterInstance, result));
+            }
+        }
+
+        internal sealed class PayloadFilter : TokenFilter
+        {
+            private readonly TestPayloadSpans OuterInstance;
+
+            internal HashSet<string> Entities = new HashSet<string>();
+            internal HashSet<string> Nopayload = new HashSet<string>();
+            internal int Pos;
+            internal IPayloadAttribute PayloadAtt;
+            internal ICharTermAttribute TermAtt;
+            internal IPositionIncrementAttribute PosIncrAtt;
+
+            public PayloadFilter(TestPayloadSpans outerInstance, TokenStream input)
+                : base(input)
+            {
+                this.OuterInstance = outerInstance;
+                Pos = 0;
+                Entities.Add("xx");
+                Entities.Add("one");
+                Nopayload.Add("nopayload");
+                Nopayload.Add("np");
+                TermAtt = AddAttribute<ICharTermAttribute>();
+                PosIncrAtt = AddAttribute<IPositionIncrementAttribute>();
+                PayloadAtt = AddAttribute<IPayloadAttribute>();
+            }
+
+            public override bool IncrementToken()
+            {
+                if (m_input.IncrementToken())
+                {
+                    string token = TermAtt.ToString();
+
+                    if (!Nopayload.Contains(token))
+                    {
+                        if (Entities.Contains(token))
+                        {
+                            PayloadAtt.Payload = new BytesRef(token + ":Entity:" + Pos);
+                        }
+                        else
+                        {
+                            PayloadAtt.Payload = new BytesRef(token + ":Noise:" + Pos);
+                        }
+                    }
+                    Pos += PosIncrAtt.PositionIncrement;
+                    return true;
+                }
+                return false;
+            }
+
+            public override void Reset()
+            {
+                base.Reset();
+                this.Pos = 0;
+            }
+        }
+
+        public sealed class TestPayloadAnalyzer : Analyzer
+        {
+            private readonly TestPayloadSpans OuterInstance;
+
+            public TestPayloadAnalyzer(TestPayloadSpans outerInstance)
+            {
+                this.OuterInstance = outerInstance;
+            }
+
+            protected internal override TokenStreamComponents CreateComponents(string fieldName, TextReader reader)
+            {
+                Tokenizer result = new MockTokenizer(reader, MockTokenizer.SIMPLE, true);
+                return new TokenStreamComponents(result, new PayloadFilter(OuterInstance, result));
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/Spans/TestSpanExplanations.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/Spans/TestSpanExplanations.cs b/src/Lucene.Net.Tests/Search/Spans/TestSpanExplanations.cs
new file mode 100644
index 0000000..a5b92ec
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/Spans/TestSpanExplanations.cs
@@ -0,0 +1,260 @@
+using NUnit.Framework;
+
+namespace Lucene.Net.Search.Spans
+{
+    /*
+     * 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.Search;
+
+    /// <summary>
+    /// TestExplanations subclass focusing on span queries
+    /// </summary>
+    [TestFixture]
+    public class TestSpanExplanations : TestExplanations
+    {
+        /* simple SpanTermQueries */
+
+        [Test]
+        public virtual void TestST1()
+        {
+            SpanQuery q = St("w1");
+            Qtest(q, new int[] { 0, 1, 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestST2()
+        {
+            SpanQuery q = St("w1");
+            q.Boost = 1000;
+            Qtest(q, new int[] { 0, 1, 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestST4()
+        {
+            SpanQuery q = St("xx");
+            Qtest(q, new int[] { 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestST5()
+        {
+            SpanQuery q = St("xx");
+            q.Boost = 1000;
+            Qtest(q, new int[] { 2, 3 });
+        }
+
+        /* some SpanFirstQueries */
+
+        [Test]
+        public virtual void TestSF1()
+        {
+            SpanQuery q = Sf(("w1"), 1);
+            Qtest(q, new int[] { 0, 1, 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSF2()
+        {
+            SpanQuery q = Sf(("w1"), 1);
+            q.Boost = 1000;
+            Qtest(q, new int[] { 0, 1, 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSF4()
+        {
+            SpanQuery q = Sf(("xx"), 2);
+            Qtest(q, new int[] { 2 });
+        }
+
+        [Test]
+        public virtual void TestSF5()
+        {
+            SpanQuery q = Sf(("yy"), 2);
+            Qtest(q, new int[] { });
+        }
+
+        [Test]
+        public virtual void TestSF6()
+        {
+            SpanQuery q = Sf(("yy"), 4);
+            q.Boost = 1000;
+            Qtest(q, new int[] { 2 });
+        }
+
+        /* some SpanOrQueries */
+
+        [Test]
+        public virtual void TestSO1()
+        {
+            SpanQuery q = Sor("w1", "QQ");
+            Qtest(q, new int[] { 0, 1, 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSO2()
+        {
+            SpanQuery q = Sor("w1", "w3", "zz");
+            Qtest(q, new int[] { 0, 1, 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSO3()
+        {
+            SpanQuery q = Sor("w5", "QQ", "yy");
+            Qtest(q, new int[] { 0, 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSO4()
+        {
+            SpanQuery q = Sor("w5", "QQ", "yy");
+            Qtest(q, new int[] { 0, 2, 3 });
+        }
+
+        /* some SpanNearQueries */
+
+        [Test]
+        public virtual void TestSNear1()
+        {
+            SpanQuery q = Snear("w1", "QQ", 100, true);
+            Qtest(q, new int[] { });
+        }
+
+        [Test]
+        public virtual void TestSNear2()
+        {
+            SpanQuery q = Snear("w1", "xx", 100, true);
+            Qtest(q, new int[] { 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSNear3()
+        {
+            SpanQuery q = Snear("w1", "xx", 0, true);
+            Qtest(q, new int[] { 2 });
+        }
+
+        [Test]
+        public virtual void TestSNear4()
+        {
+            SpanQuery q = Snear("w1", "xx", 1, true);
+            Qtest(q, new int[] { 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSNear5()
+        {
+            SpanQuery q = Snear("xx", "w1", 0, false);
+            Qtest(q, new int[] { 2 });
+        }
+
+        [Test]
+        public virtual void TestSNear6()
+        {
+            SpanQuery q = Snear("w1", "w2", "QQ", 100, true);
+            Qtest(q, new int[] { });
+        }
+
+        [Test]
+        public virtual void TestSNear7()
+        {
+            SpanQuery q = Snear("w1", "xx", "w2", 100, true);
+            Qtest(q, new int[] { 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSNear8()
+        {
+            SpanQuery q = Snear("w1", "xx", "w2", 0, true);
+            Qtest(q, new int[] { 2 });
+        }
+
+        [Test]
+        public virtual void TestSNear9()
+        {
+            SpanQuery q = Snear("w1", "xx", "w2", 1, true);
+            Qtest(q, new int[] { 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSNear10()
+        {
+            SpanQuery q = Snear("xx", "w1", "w2", 0, false);
+            Qtest(q, new int[] { 2 });
+        }
+
+        [Test]
+        public virtual void TestSNear11()
+        {
+            SpanQuery q = Snear("w1", "w2", "w3", 1, true);
+            Qtest(q, new int[] { 0, 1 });
+        }
+
+        /* some SpanNotQueries */
+
+        [Test]
+        public virtual void TestSNot1()
+        {
+            SpanQuery q = Snot(Sf("w1", 10), St("QQ"));
+            Qtest(q, new int[] { 0, 1, 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSNot2()
+        {
+            SpanQuery q = Snot(Sf("w1", 10), St("QQ"));
+            q.Boost = 1000;
+            Qtest(q, new int[] { 0, 1, 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSNot4()
+        {
+            SpanQuery q = Snot(Sf("w1", 10), St("xx"));
+            Qtest(q, new int[] { 0, 1, 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSNot5()
+        {
+            SpanQuery q = Snot(Sf("w1", 10), St("xx"));
+            q.Boost = 1000;
+            Qtest(q, new int[] { 0, 1, 2, 3 });
+        }
+
+        [Test]
+        public virtual void TestSNot7()
+        {
+            SpanQuery f = Snear("w1", "w3", 10, true);
+            f.Boost = 1000;
+            SpanQuery q = Snot(f, St("xx"));
+            Qtest(q, new int[] { 0, 1, 3 });
+        }
+
+        [Test]
+        public virtual void TestSNot10()
+        {
+            SpanQuery t = St("xx");
+            t.Boost = 10000;
+            SpanQuery q = Snot(Snear("w1", "w3", 10, true), t);
+            Qtest(q, new int[] { 0, 1, 3 });
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/Spans/TestSpanExplanationsOfNonMatches.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/Spans/TestSpanExplanationsOfNonMatches.cs b/src/Lucene.Net.Tests/Search/Spans/TestSpanExplanationsOfNonMatches.cs
new file mode 100644
index 0000000..307c51f
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/Spans/TestSpanExplanationsOfNonMatches.cs
@@ -0,0 +1,251 @@
+using NUnit.Framework;
+
+namespace Lucene.Net.Search.Spans
+{
+    /*
+     * 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.
+     */
+
+    /// <summary>
+    /// subclass of TestSimpleExplanations that verifies non matches.
+    /// </summary>
+    [TestFixture]
+    public class TestSpanExplanationsOfNonMatches : TestSpanExplanations
+    {
+        /// <summary>
+        /// Overrides superclass to ignore matches and focus on non-matches
+        /// </summary>
+        /// <seealso> cref= CheckHits#checkNoMatchExplanations </seealso>
+        public override void Qtest(Query q, int[] expDocNrs)
+        {
+            CheckHits.CheckNoMatchExplanations(q, FIELD, Searcher, expDocNrs);
+        }
+
+
+        #region TestSpanExplanations
+        // 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 TestST1()
+        {
+            base.TestST1();
+        }
+
+        [Test]
+        public override void TestST2()
+        {
+            base.TestST2();
+        }
+
+        [Test]
+        public override void TestST4()
+        {
+            base.TestST4();
+        }
+
+        [Test]
+        public override void TestST5()
+        {
+            base.TestST5();
+        }
+
+        /* some SpanFirstQueries */
+
+        [Test]
+        public override void TestSF1()
+        {
+            base.TestSF1();
+        }
+
+        [Test]
+        public override void TestSF2()
+        {
+            base.TestSF2();
+        }
+
+        [Test]
+        public override void TestSF4()
+        {
+            base.TestSF4();
+        }
+
+        [Test]
+        public override void TestSF5()
+        {
+            base.TestSF5();
+        }
+
+        [Test]
+        public override void TestSF6()
+        {
+            base.TestSF6();
+        }
+
+        /* some SpanOrQueries */
+
+        [Test]
+        public override void TestSO1()
+        {
+            base.TestSO1();
+        }
+
+        [Test]
+        public override void TestSO2()
+        {
+            base.TestSO2();
+        }
+
+        [Test]
+        public override void TestSO3()
+        {
+            base.TestSO3();
+        }
+
+        [Test]
+        public override void TestSO4()
+        {
+            base.TestSO4();
+        }
+
+        /* some SpanNearQueries */
+
+        [Test]
+        public override void TestSNear1()
+        {
+            base.TestSNear1();
+        }
+
+        [Test]
+        public override void TestSNear2()
+        {
+            base.TestSNear2();
+        }
+
+        [Test]
+        public override void TestSNear3()
+        {
+            base.TestSNear3();
+        }
+
+        [Test]
+        public override void TestSNear4()
+        {
+            base.TestSNear4();
+        }
+
+        [Test]
+        public override void TestSNear5()
+        {
+            base.TestSNear5();
+        }
+
+        [Test]
+        public override void TestSNear6()
+        {
+            base.TestSNear6();
+        }
+
+        [Test]
+        public override void TestSNear7()
+        {
+            base.TestSNear7();
+        }
+
+        [Test]
+        public override void TestSNear8()
+        {
+            base.TestSNear8();
+        }
+
+        [Test]
+        public override void TestSNear9()
+        {
+            base.TestSNear9();
+        }
+
+        [Test]
+        public override void TestSNear10()
+        {
+            base.TestSNear10();
+        }
+
+        [Test]
+        public override void TestSNear11()
+        {
+            base.TestSNear11();
+        }
+
+        /* some SpanNotQueries */
+
+        [Test]
+        public override void TestSNot1()
+        {
+            base.TestSNot1();
+        }
+
+        [Test]
+        public override void TestSNot2()
+        {
+            base.TestSNot2();
+        }
+
+        [Test]
+        public override void TestSNot4()
+        {
+            base.TestSNot4();
+        }
+
+        [Test]
+        public override void TestSNot5()
+        {
+            base.TestSNot5();
+        }
+
+        [Test]
+        public override void TestSNot7()
+        {
+            base.TestSNot7();
+        }
+
+        [Test]
+        public override void TestSNot10()
+        {
+            base.TestSNot10();
+        }
+
+        #endregion
+
+        #region TestExplanations
+        // LUCENENET NOTE: Tests in an abstract 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>
+        /// Placeholder: JUnit freaks if you don't have one test ... making
+        /// class abstract doesn't help
+        /// </summary>
+        [Test]
+        public override void TestNoop()
+        {
+            base.TestNoop();
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/Spans/TestSpanFirstQuery.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/Spans/TestSpanFirstQuery.cs b/src/Lucene.Net.Tests/Search/Spans/TestSpanFirstQuery.cs
new file mode 100644
index 0000000..2266bf7
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/Spans/TestSpanFirstQuery.cs
@@ -0,0 +1,74 @@
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search.Spans
+{
+    using NUnit.Framework;
+
+    /*
+         * 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 Analyzer = Lucene.Net.Analysis.Analyzer;
+    using CharacterRunAutomaton = Lucene.Net.Util.Automaton.CharacterRunAutomaton;
+    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 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 Term = Lucene.Net.Index.Term;
+
+    [TestFixture]
+    public class TestSpanFirstQuery : LuceneTestCase
+    {
+        [Test]
+        public virtual void TestStartPositions()
+        {
+            Directory dir = NewDirectory();
+
+            // mimic StopAnalyzer
+            CharacterRunAutomaton stopSet = new CharacterRunAutomaton((new RegExp("the|a|of")).ToAutomaton());
+            Analyzer analyzer = new MockAnalyzer(Random(), MockTokenizer.SIMPLE, true, stopSet);
+
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), dir, analyzer, Similarity, TimeZone);
+            Document doc = new Document();
+            doc.Add(NewTextField("field", "the quick brown fox", Field.Store.NO));
+            writer.AddDocument(doc);
+            Document doc2 = new Document();
+            doc2.Add(NewTextField("field", "quick brown fox", Field.Store.NO));
+            writer.AddDocument(doc2);
+
+            IndexReader reader = writer.Reader;
+            IndexSearcher searcher = NewSearcher(reader);
+
+            // user queries on "starts-with quick"
+            SpanQuery sfq = new SpanFirstQuery(new SpanTermQuery(new Term("field", "quick")), 1);
+            Assert.AreEqual(1, searcher.Search(sfq, 10).TotalHits);
+
+            // user queries on "starts-with the quick"
+            SpanQuery include = new SpanFirstQuery(new SpanTermQuery(new Term("field", "quick")), 2);
+            sfq = new SpanNotQuery(include, sfq);
+            Assert.AreEqual(1, searcher.Search(sfq, 10).TotalHits);
+
+            writer.Dispose();
+            reader.Dispose();
+            dir.Dispose();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/Spans/TestSpanMultiTermQueryWrapper.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/Spans/TestSpanMultiTermQueryWrapper.cs b/src/Lucene.Net.Tests/Search/Spans/TestSpanMultiTermQueryWrapper.cs
new file mode 100644
index 0000000..4d27ecc
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/Spans/TestSpanMultiTermQueryWrapper.cs
@@ -0,0 +1,245 @@
+using Lucene.Net.Documents;
+
+namespace Lucene.Net.Search.Spans
+{
+    using NUnit.Framework;
+    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;
+    using Term = Lucene.Net.Index.Term;
+
+    /// <summary>
+    /// Tests for <seealso cref="SpanMultiTermQueryWrapper"/>, wrapping a few MultiTermQueries.
+    /// </summary>
+    [TestFixture]
+    public class TestSpanMultiTermQueryWrapper : LuceneTestCase
+    {
+        private Directory Directory;
+        private IndexReader Reader;
+        private IndexSearcher Searcher;
+
+        [SetUp]
+        public override void SetUp()
+        {
+            base.SetUp();
+            Directory = NewDirectory();
+            RandomIndexWriter iw = new RandomIndexWriter(Random(), Directory, Similarity, TimeZone);
+            Document doc = new Document();
+            Field field = NewTextField("field", "", Field.Store.NO);
+            doc.Add(field);
+
+            field.SetStringValue("quick brown fox");
+            iw.AddDocument(doc);
+            field.SetStringValue("jumps over lazy broun dog");
+            iw.AddDocument(doc);
+            field.SetStringValue("jumps over extremely very lazy broxn dog");
+            iw.AddDocument(doc);
+            Reader = iw.Reader;
+            iw.Dispose();
+            Searcher = NewSearcher(Reader);
+        }
+
+        [TearDown]
+        public override void TearDown()
+        {
+            Reader.Dispose();
+            Directory.Dispose();
+            base.TearDown();
+        }
+
+        [Test]
+        public virtual void TestWildcard()
+        {
+            WildcardQuery wq = new WildcardQuery(new Term("field", "bro?n"));
+            SpanQuery swq = new SpanMultiTermQueryWrapper<MultiTermQuery>(wq);
+            // will only match quick brown fox
+            SpanFirstQuery sfq = new SpanFirstQuery(swq, 2);
+            Assert.AreEqual(1, Searcher.Search(sfq, 10).TotalHits);
+        }
+
+        [Test]
+        public virtual void TestPrefix()
+        {
+            WildcardQuery wq = new WildcardQuery(new Term("field", "extrem*"));
+            SpanQuery swq = new SpanMultiTermQueryWrapper<MultiTermQuery>(wq);
+            // will only match "jumps over extremely very lazy broxn dog"
+            SpanFirstQuery sfq = new SpanFirstQuery(swq, 3);
+            Assert.AreEqual(1, Searcher.Search(sfq, 10).TotalHits);
+        }
+
+        [Test]
+        public virtual void TestFuzzy()
+        {
+            FuzzyQuery fq = new FuzzyQuery(new Term("field", "broan"));
+            SpanQuery sfq = new SpanMultiTermQueryWrapper<MultiTermQuery>(fq);
+            // will not match quick brown fox
+            SpanPositionRangeQuery sprq = new SpanPositionRangeQuery(sfq, 3, 6);
+            Assert.AreEqual(2, Searcher.Search(sprq, 10).TotalHits);
+        }
+
+        [Test]
+        public virtual void TestFuzzy2()
+        {
+            // maximum of 1 term expansion
+            FuzzyQuery fq = new FuzzyQuery(new Term("field", "broan"), 1, 0, 1, false);
+            SpanQuery sfq = new SpanMultiTermQueryWrapper<MultiTermQuery>(fq);
+            // will only match jumps over lazy broun dog
+            SpanPositionRangeQuery sprq = new SpanPositionRangeQuery(sfq, 0, 100);
+            Assert.AreEqual(1, Searcher.Search(sprq, 10).TotalHits);
+        }
+
+        [Test]
+        public virtual void TestNoSuchMultiTermsInNear()
+        {
+            //test to make sure non existent multiterms aren't throwing null pointer exceptions
+            FuzzyQuery fuzzyNoSuch = new FuzzyQuery(new Term("field", "noSuch"), 1, 0, 1, false);
+            SpanQuery spanNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(fuzzyNoSuch);
+            SpanQuery term = new SpanTermQuery(new Term("field", "brown"));
+            SpanQuery near = new SpanNearQuery(new SpanQuery[] { term, spanNoSuch }, 1, true);
+            Assert.AreEqual(0, Searcher.Search(near, 10).TotalHits);
+            //flip order
+            near = new SpanNearQuery(new SpanQuery[] { spanNoSuch, term }, 1, true);
+            Assert.AreEqual(0, Searcher.Search(near, 10).TotalHits);
+
+            WildcardQuery wcNoSuch = new WildcardQuery(new Term("field", "noSuch*"));
+            SpanQuery spanWCNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(wcNoSuch);
+            near = new SpanNearQuery(new SpanQuery[] { term, spanWCNoSuch }, 1, true);
+            Assert.AreEqual(0, Searcher.Search(near, 10).TotalHits);
+
+            RegexpQuery rgxNoSuch = new RegexpQuery(new Term("field", "noSuch"));
+            SpanQuery spanRgxNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(rgxNoSuch);
+            near = new SpanNearQuery(new SpanQuery[] { term, spanRgxNoSuch }, 1, true);
+            Assert.AreEqual(0, Searcher.Search(near, 10).TotalHits);
+
+            PrefixQuery prfxNoSuch = new PrefixQuery(new Term("field", "noSuch"));
+            SpanQuery spanPrfxNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(prfxNoSuch);
+            near = new SpanNearQuery(new SpanQuery[] { term, spanPrfxNoSuch }, 1, true);
+            Assert.AreEqual(0, Searcher.Search(near, 10).TotalHits);
+
+            //test single noSuch
+            near = new SpanNearQuery(new SpanQuery[] { spanPrfxNoSuch }, 1, true);
+            Assert.AreEqual(0, Searcher.Search(near, 10).TotalHits);
+
+            //test double noSuch
+            near = new SpanNearQuery(new SpanQuery[] { spanPrfxNoSuch, spanPrfxNoSuch }, 1, true);
+            Assert.AreEqual(0, Searcher.Search(near, 10).TotalHits);
+        }
+
+        [Test]
+        public virtual void TestNoSuchMultiTermsInNotNear()
+        {
+            //test to make sure non existent multiterms aren't throwing non-matching field exceptions
+            FuzzyQuery fuzzyNoSuch = new FuzzyQuery(new Term("field", "noSuch"), 1, 0, 1, false);
+            SpanQuery spanNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(fuzzyNoSuch);
+            SpanQuery term = new SpanTermQuery(new Term("field", "brown"));
+            SpanNotQuery notNear = new SpanNotQuery(term, spanNoSuch, 0, 0);
+            Assert.AreEqual(1, Searcher.Search(notNear, 10).TotalHits);
+
+            //flip
+            notNear = new SpanNotQuery(spanNoSuch, term, 0, 0);
+            Assert.AreEqual(0, Searcher.Search(notNear, 10).TotalHits);
+
+            //both noSuch
+            notNear = new SpanNotQuery(spanNoSuch, spanNoSuch, 0, 0);
+            Assert.AreEqual(0, Searcher.Search(notNear, 10).TotalHits);
+
+            WildcardQuery wcNoSuch = new WildcardQuery(new Term("field", "noSuch*"));
+            SpanQuery spanWCNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(wcNoSuch);
+            notNear = new SpanNotQuery(term, spanWCNoSuch, 0, 0);
+            Assert.AreEqual(1, Searcher.Search(notNear, 10).TotalHits);
+
+            RegexpQuery rgxNoSuch = new RegexpQuery(new Term("field", "noSuch"));
+            SpanQuery spanRgxNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(rgxNoSuch);
+            notNear = new SpanNotQuery(term, spanRgxNoSuch, 1, 1);
+            Assert.AreEqual(1, Searcher.Search(notNear, 10).TotalHits);
+
+            PrefixQuery prfxNoSuch = new PrefixQuery(new Term("field", "noSuch"));
+            SpanQuery spanPrfxNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(prfxNoSuch);
+            notNear = new SpanNotQuery(term, spanPrfxNoSuch, 1, 1);
+            Assert.AreEqual(1, Searcher.Search(notNear, 10).TotalHits);
+        }
+
+        [Test]
+        public virtual void TestNoSuchMultiTermsInOr()
+        {
+            //test to make sure non existent multiterms aren't throwing null pointer exceptions
+            FuzzyQuery fuzzyNoSuch = new FuzzyQuery(new Term("field", "noSuch"), 1, 0, 1, false);
+            SpanQuery spanNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(fuzzyNoSuch);
+            SpanQuery term = new SpanTermQuery(new Term("field", "brown"));
+            SpanOrQuery near = new SpanOrQuery(new SpanQuery[] { term, spanNoSuch });
+            Assert.AreEqual(1, Searcher.Search(near, 10).TotalHits);
+
+            //flip
+            near = new SpanOrQuery(new SpanQuery[] { spanNoSuch, term });
+            Assert.AreEqual(1, Searcher.Search(near, 10).TotalHits);
+
+            WildcardQuery wcNoSuch = new WildcardQuery(new Term("field", "noSuch*"));
+            SpanQuery spanWCNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(wcNoSuch);
+            near = new SpanOrQuery(new SpanQuery[] { term, spanWCNoSuch });
+            Assert.AreEqual(1, Searcher.Search(near, 10).TotalHits);
+
+            RegexpQuery rgxNoSuch = new RegexpQuery(new Term("field", "noSuch"));
+            SpanQuery spanRgxNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(rgxNoSuch);
+            near = new SpanOrQuery(new SpanQuery[] { term, spanRgxNoSuch });
+            Assert.AreEqual(1, Searcher.Search(near, 10).TotalHits);
+
+            PrefixQuery prfxNoSuch = new PrefixQuery(new Term("field", "noSuch"));
+            SpanQuery spanPrfxNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(prfxNoSuch);
+            near = new SpanOrQuery(new SpanQuery[] { term, spanPrfxNoSuch });
+            Assert.AreEqual(1, Searcher.Search(near, 10).TotalHits);
+
+            near = new SpanOrQuery(new SpanQuery[] { spanPrfxNoSuch });
+            Assert.AreEqual(0, Searcher.Search(near, 10).TotalHits);
+
+            near = new SpanOrQuery(new SpanQuery[] { spanPrfxNoSuch, spanPrfxNoSuch });
+            Assert.AreEqual(0, Searcher.Search(near, 10).TotalHits);
+        }
+
+        [Test]
+        public virtual void TestNoSuchMultiTermsInSpanFirst()
+        {
+            //this hasn't been a problem
+            FuzzyQuery fuzzyNoSuch = new FuzzyQuery(new Term("field", "noSuch"), 1, 0, 1, false);
+            SpanQuery spanNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(fuzzyNoSuch);
+            SpanQuery spanFirst = new SpanFirstQuery(spanNoSuch, 10);
+
+            Assert.AreEqual(0, Searcher.Search(spanFirst, 10).TotalHits);
+
+            WildcardQuery wcNoSuch = new WildcardQuery(new Term("field", "noSuch*"));
+            SpanQuery spanWCNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(wcNoSuch);
+            spanFirst = new SpanFirstQuery(spanWCNoSuch, 10);
+            Assert.AreEqual(0, Searcher.Search(spanFirst, 10).TotalHits);
+
+            RegexpQuery rgxNoSuch = new RegexpQuery(new Term("field", "noSuch"));
+            SpanQuery spanRgxNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(rgxNoSuch);
+            spanFirst = new SpanFirstQuery(spanRgxNoSuch, 10);
+            Assert.AreEqual(0, Searcher.Search(spanFirst, 10).TotalHits);
+
+            PrefixQuery prfxNoSuch = new PrefixQuery(new Term("field", "noSuch"));
+            SpanQuery spanPrfxNoSuch = new SpanMultiTermQueryWrapper<MultiTermQuery>(prfxNoSuch);
+            spanFirst = new SpanFirstQuery(spanPrfxNoSuch, 10);
+            Assert.AreEqual(0, Searcher.Search(spanFirst, 10).TotalHits);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Search/Spans/TestSpanSearchEquivalence.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Search/Spans/TestSpanSearchEquivalence.cs b/src/Lucene.Net.Tests/Search/Spans/TestSpanSearchEquivalence.cs
new file mode 100644
index 0000000..569a03e
--- /dev/null
+++ b/src/Lucene.Net.Tests/Search/Spans/TestSpanSearchEquivalence.cs
@@ -0,0 +1,134 @@
+using NUnit.Framework;
+
+namespace Lucene.Net.Search.Spans
+{
+    using Occur = Lucene.Net.Search.Occur;
+
+    /*
+         * 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>
+    /// Basic equivalence tests for span queries
+    /// </summary>
+    [TestFixture]
+    public class TestSpanSearchEquivalence : SearchEquivalenceTestBase
+    {
+        // TODO: we could go a little crazy for a lot of these,
+        // but these are just simple minimal cases in case something
+        // goes horribly wrong. Put more intense tests elsewhere.
+
+        /// <summary>
+        /// SpanTermQuery(A) = TermQuery(A) </summary>
+        [Test]
+        public virtual void TestSpanTermVersusTerm()
+        {
+            Term t1 = RandomTerm();
+            AssertSameSet(new TermQuery(t1), new SpanTermQuery(t1));
+        }
+
+        /// <summary>
+        /// SpanOrQuery(A, B) = (A B) </summary>
+        [Test]
+        public virtual void TestSpanOrVersusBoolean()
+        {
+            Term t1 = RandomTerm();
+            Term t2 = RandomTerm();
+            BooleanQuery q1 = new BooleanQuery();
+            q1.Add(new TermQuery(t1), Occur.SHOULD);
+            q1.Add(new TermQuery(t2), Occur.SHOULD);
+            SpanOrQuery q2 = new SpanOrQuery(new SpanTermQuery(t1), new SpanTermQuery(t2));
+            AssertSameSet(q1, q2);
+        }
+
+        /// <summary>
+        /// SpanNotQuery(A, B) ⊆ SpanTermQuery(A) </summary>
+        [Test]
+        public virtual void TestSpanNotVersusSpanTerm()
+        {
+            Term t1 = RandomTerm();
+            Term t2 = RandomTerm();
+            AssertSubsetOf(new SpanNotQuery(new SpanTermQuery(t1), new SpanTermQuery(t2)), new SpanTermQuery(t1));
+        }
+
+        /// <summary>
+        /// SpanFirstQuery(A, 10) ⊆ SpanTermQuery(A) </summary>
+        [Test]
+        public virtual void TestSpanFirstVersusSpanTerm()
+        {
+            Term t1 = RandomTerm();
+            AssertSubsetOf(new SpanFirstQuery(new SpanTermQuery(t1), 10), new SpanTermQuery(t1));
+        }
+
+        /// <summary>
+        /// SpanNearQuery([A, B], 0, true) = "A B" </summary>
+        [Test]
+        public virtual void TestSpanNearVersusPhrase()
+        {
+            Term t1 = RandomTerm();
+            Term t2 = RandomTerm();
+            SpanQuery[] subquery = new SpanQuery[] { new SpanTermQuery(t1), new SpanTermQuery(t2) };
+            SpanNearQuery q1 = new SpanNearQuery(subquery, 0, true);
+            PhraseQuery q2 = new PhraseQuery();
+            q2.Add(t1);
+            q2.Add(t2);
+            AssertSameSet(q1, q2);
+        }
+
+        /// <summary>
+        /// SpanNearQuery([A, B], ∞, false) = +A +B </summary>
+        [Test]
+        public virtual void TestSpanNearVersusBooleanAnd()
+        {
+            Term t1 = RandomTerm();
+            Term t2 = RandomTerm();
+            SpanQuery[] subquery = new SpanQuery[] { new SpanTermQuery(t1), new SpanTermQuery(t2) };
+            SpanNearQuery q1 = new SpanNearQuery(subquery, int.MaxValue, false);
+            BooleanQuery q2 = new BooleanQuery();
+            q2.Add(new TermQuery(t1), Occur.MUST);
+            q2.Add(new TermQuery(t2), Occur.MUST);
+            AssertSameSet(q1, q2);
+        }
+
+        /// <summary>
+        /// SpanNearQuery([A B], 0, false) ⊆ SpanNearQuery([A B], 1, false) </summary>
+        [Test]
+        public virtual void TestSpanNearVersusSloppySpanNear()
+        {
+            Term t1 = RandomTerm();
+            Term t2 = RandomTerm();
+            SpanQuery[] subquery = new SpanQuery[] { new SpanTermQuery(t1), new SpanTermQuery(t2) };
+            SpanNearQuery q1 = new SpanNearQuery(subquery, 0, false);
+            SpanNearQuery q2 = new SpanNearQuery(subquery, 1, false);
+            AssertSubsetOf(q1, q2);
+        }
+
+        /// <summary>
+        /// SpanNearQuery([A B], 3, true) ⊆ SpanNearQuery([A B], 3, false) </summary>
+        [Test]
+        public virtual void TestSpanNearInOrderVersusOutOfOrder()
+        {
+            Term t1 = RandomTerm();
+            Term t2 = RandomTerm();
+            SpanQuery[] subquery = new SpanQuery[] { new SpanTermQuery(t1), new SpanTermQuery(t2) };
+            SpanNearQuery q1 = new SpanNearQuery(subquery, 3, true);
+            SpanNearQuery q2 = new SpanNearQuery(subquery, 3, false);
+            AssertSubsetOf(q1, q2);
+        }
+    }
+}
\ No newline at end of file


Mime
View raw message