You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by sy...@apache.org on 2016/09/11 21:30:33 UTC

[02/50] [abbrv] lucenenet git commit: Ported tests for the QueryParser.Classic namespace and refactored QueryParserTestBase so the test runner will run all of the tests.

Ported tests for the QueryParser.Classic namespace and refactored QueryParserTestBase so the test runner will run all of the tests.


Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/6d711567
Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/6d711567
Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/6d711567

Branch: refs/heads/master
Commit: 6d711567c20492c89fc59be440be8a56d918235c
Parents: ca1a374
Author: Shad Storhaug <sh...@shadstorhaug.com>
Authored: Sun Jul 31 16:48:21 2016 +0700
Committer: Shad Storhaug <sh...@shadstorhaug.com>
Committed: Fri Sep 2 22:29:37 2016 +0700

----------------------------------------------------------------------
 .../Classic/TestMultiAnalyzer.cs                |  278 ++++
 .../Classic/TestMultiFieldQueryParser.cs        |  376 +++++
 .../Classic/TestMultiPhraseQueryParsing.cs      |  121 ++
 .../Classic/TestQueryParser.cs                  |  554 +++++++
 .../Lucene.Net.Tests.QueryParser.csproj         |   82 +
 .../Properties/AssemblyInfo.cs                  |   36 +
 .../Util/QueryParserTestBase.cs                 | 1499 ++++++++++++++++++
 Lucene.Net.Tests.QueryParser/packages.config    |    4 +
 Lucene.Net.sln                                  |   12 +
 9 files changed, 2962 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6d711567/Lucene.Net.Tests.QueryParser/Classic/TestMultiAnalyzer.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Classic/TestMultiAnalyzer.cs b/Lucene.Net.Tests.QueryParser/Classic/TestMultiAnalyzer.cs
new file mode 100644
index 0000000..fc1ce0c
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Classic/TestMultiAnalyzer.cs
@@ -0,0 +1,278 @@
+\ufeffusing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using Lucene.Net.Analysis;
+using Lucene.Net.Analysis.Tokenattributes;
+using Lucene.Net.Search;
+using Lucene.Net.Util;
+
+namespace Lucene.Net.QueryParser.Classic
+{
+    [TestFixture]
+    public class TestMultiAnalyzer_ : BaseTokenStreamTestCase
+    {
+
+        private static int multiToken = 0;
+
+        [Test]
+        public void TestMultiAnalyzer()
+        {
+
+            QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "", new MultiAnalyzer());
+
+            // trivial, no multiple tokens:
+            assertEquals("foo", qp.Parse("foo").toString());
+            assertEquals("foo", qp.Parse("\"foo\"").toString());
+            assertEquals("foo foobar", qp.Parse("foo foobar").toString());
+            assertEquals("\"foo foobar\"", qp.Parse("\"foo foobar\"").toString());
+            assertEquals("\"foo foobar blah\"", qp.Parse("\"foo foobar blah\"").toString());
+
+            // two tokens at the same position:
+            assertEquals("(multi multi2) foo", qp.Parse("multi foo").toString());
+            assertEquals("foo (multi multi2)", qp.Parse("foo multi").toString());
+            assertEquals("(multi multi2) (multi multi2)", qp.Parse("multi multi").toString());
+            assertEquals("+(foo (multi multi2)) +(bar (multi multi2))",
+                qp.Parse("+(foo multi) +(bar multi)").toString());
+            assertEquals("+(foo (multi multi2)) field:\"bar (multi multi2)\"",
+                qp.Parse("+(foo multi) field:\"bar multi\"").toString());
+
+            // phrases:
+            assertEquals("\"(multi multi2) foo\"", qp.Parse("\"multi foo\"").toString());
+            assertEquals("\"foo (multi multi2)\"", qp.Parse("\"foo multi\"").toString());
+            assertEquals("\"foo (multi multi2) foobar (multi multi2)\"",
+                qp.Parse("\"foo multi foobar multi\"").toString());
+
+            // fields:
+            assertEquals("(field:multi field:multi2) field:foo", qp.Parse("field:multi field:foo").toString());
+            assertEquals("field:\"(multi multi2) foo\"", qp.Parse("field:\"multi foo\"").toString());
+
+            // three tokens at one position:
+            assertEquals("triplemulti multi3 multi2", qp.Parse("triplemulti").toString());
+            assertEquals("foo (triplemulti multi3 multi2) foobar",
+                qp.Parse("foo triplemulti foobar").toString());
+
+            // phrase with non-default slop:
+            assertEquals("\"(multi multi2) foo\"~10", qp.Parse("\"multi foo\"~10").toString());
+
+            // phrase with non-default boost:
+            assertEquals("\"(multi multi2) foo\"^2.0", qp.Parse("\"multi foo\"^2").toString());
+
+            // phrase after changing default slop
+            qp.PhraseSlop=(99);
+            assertEquals("\"(multi multi2) foo\"~99 bar",
+                         qp.Parse("\"multi foo\" bar").toString());
+            assertEquals("\"(multi multi2) foo\"~99 \"foo bar\"~2",
+                         qp.Parse("\"multi foo\" \"foo bar\"~2").toString());
+            qp.PhraseSlop=(0);
+
+            // non-default operator:
+            qp.DefaultOperator=(QueryParserBase.AND_OPERATOR);
+            assertEquals("+(multi multi2) +foo", qp.Parse("multi foo").toString());
+
+        }
+
+        [Test]
+        public void TestMultiAnalyzerWithSubclassOfQueryParser()
+        {
+
+            DumbQueryParser qp = new DumbQueryParser("", new MultiAnalyzer());
+            qp.PhraseSlop = (99); // modified default slop
+
+            // direct call to (super's) getFieldQuery to demonstrate differnce
+            // between phrase and multiphrase with modified default slop
+            assertEquals("\"foo bar\"~99",
+                         qp.GetSuperFieldQuery("", "foo bar", true).toString());
+            assertEquals("\"(multi multi2) bar\"~99",
+                         qp.GetSuperFieldQuery("", "multi bar", true).toString());
+
+
+            // ask sublcass to parse phrase with modified default slop
+            assertEquals("\"(multi multi2) foo\"~99 bar",
+                         qp.Parse("\"multi foo\" bar").toString());
+
+        }
+
+        [Test]
+        public void TestPosIncrementAnalyzer()
+        {
+            QueryParser qp = new QueryParser(LuceneVersion.LUCENE_40, "", new PosIncrementAnalyzer());
+            assertEquals("quick brown", qp.Parse("the quick brown").toString());
+            assertEquals("quick brown fox", qp.Parse("the quick brown fox").toString());
+        }
+
+        /// <summary>
+        /// Expands "multi" to "multi" and "multi2", both at the same position,
+        /// and expands "triplemulti" to "triplemulti", "multi3", and "multi2".  
+        /// </summary>
+        private class MultiAnalyzer : Analyzer
+        {
+            public override TokenStreamComponents CreateComponents(string fieldName, System.IO.TextReader reader)
+            {
+                Tokenizer result = new MockTokenizer(reader, MockTokenizer.WHITESPACE, true);
+                return new TokenStreamComponents(result, new TestFilter(result));
+            }
+        }
+
+        private sealed class TestFilter : TokenFilter
+        {
+
+            private string prevType;
+            private int prevStartOffset;
+            private int prevEndOffset;
+
+            private readonly ICharTermAttribute termAtt;
+            private readonly IPositionIncrementAttribute posIncrAtt;
+            private readonly IOffsetAttribute offsetAtt;
+            private readonly ITypeAttribute typeAtt;
+
+            public TestFilter(TokenStream @in)
+                : base(@in)
+            {
+                termAtt = AddAttribute<ICharTermAttribute>();
+                posIncrAtt = AddAttribute<IPositionIncrementAttribute>();
+                offsetAtt = AddAttribute<IOffsetAttribute>();
+                typeAtt = AddAttribute<ITypeAttribute>();
+            }
+
+            public override bool IncrementToken()
+            {
+                if (multiToken > 0)
+                {
+                    termAtt.SetEmpty().Append("multi" + (multiToken + 1));
+                    offsetAtt.SetOffset(prevStartOffset, prevEndOffset);
+                    typeAtt.Type = (prevType);
+                    posIncrAtt.PositionIncrement = (0);
+                    multiToken--;
+                    return true;
+                }
+                else
+                {
+                    bool next = input.IncrementToken();
+                    if (!next)
+                    {
+                        return false;
+                    }
+                    prevType = typeAtt.Type;
+                    prevStartOffset = offsetAtt.StartOffset();
+                    prevEndOffset = offsetAtt.EndOffset();
+                    string text = termAtt.toString();
+                    if (text.equals("triplemulti"))
+                    {
+                        multiToken = 2;
+                        return true;
+                    }
+                    else if (text.equals("multi"))
+                    {
+                        multiToken = 1;
+                        return true;
+                    }
+                    else
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            public override void Reset()
+            {
+                base.Reset();
+                this.prevType = null;
+                this.prevStartOffset = 0;
+                this.prevEndOffset = 0;
+            }
+        }
+
+        /// <summary>
+        /// Analyzes "the quick brown" as: quick(incr=2) brown(incr=1).
+        /// Does not work correctly for input other than "the quick brown ...".
+        /// </summary>
+        private class PosIncrementAnalyzer : Analyzer
+        {
+            public override TokenStreamComponents CreateComponents(string fieldName, System.IO.TextReader reader)
+            {
+                Tokenizer result = new MockTokenizer(reader, MockTokenizer.WHITESPACE, true);
+                return new TokenStreamComponents(result, new TestPosIncrementFilter(result));
+            }
+        }
+
+        private sealed class TestPosIncrementFilter : TokenFilter
+        {
+            ICharTermAttribute termAtt;
+            IPositionIncrementAttribute posIncrAtt;
+
+            public TestPosIncrementFilter(TokenStream @in)
+                : base(@in)
+            {
+                termAtt = AddAttribute<ICharTermAttribute>();
+                posIncrAtt = AddAttribute<IPositionIncrementAttribute>();
+            }
+
+            public override bool IncrementToken()
+            {
+                while (input.IncrementToken())
+                {
+                    if (termAtt.toString().equals("the"))
+                    {
+                        // stopword, do nothing
+                    }
+                    else if (termAtt.toString().equals("quick"))
+                    {
+                        posIncrAtt.PositionIncrement = (2);
+                        return true;
+                    }
+                    else
+                    {
+                        posIncrAtt.PositionIncrement = (1);
+                        return true;
+                    }
+                }
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// a very simple subclass of QueryParser
+        /// </summary>
+        private sealed class DumbQueryParser : QueryParser
+        {
+            public DumbQueryParser(string f, Analyzer a)
+                : base(TEST_VERSION_CURRENT, f, a)
+            {
+            }
+
+            // expose super's version 
+            public Query GetSuperFieldQuery(string f, string t, bool quoted)
+            {
+                return base.GetFieldQuery(f, t, quoted);
+            }
+
+            // wrap super's version
+            protected override Query GetFieldQuery(string field, string queryText, bool quoted)
+            {
+                return new DumbQueryWrapper(GetSuperFieldQuery(field, queryText, quoted));
+            }
+        }
+
+        /// <summary>
+        /// A very simple wrapper to prevent instanceof checks but uses
+        /// the toString of the query it wraps.
+        /// </summary>
+        private sealed class DumbQueryWrapper : Query
+        {
+            private Query q;
+            public DumbQueryWrapper(Query q)
+            {
+                this.q = q;
+            }
+
+            public override string ToString(string field)
+            {
+                return q.ToString(field);
+            }
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6d711567/Lucene.Net.Tests.QueryParser/Classic/TestMultiFieldQueryParser.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Classic/TestMultiFieldQueryParser.cs b/Lucene.Net.Tests.QueryParser/Classic/TestMultiFieldQueryParser.cs
new file mode 100644
index 0000000..f233c02
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Classic/TestMultiFieldQueryParser.cs
@@ -0,0 +1,376 @@
+\ufeffusing Lucene.Net.Analysis;
+using Lucene.Net.Documents;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Util;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Lucene.Net.QueryParser.Classic
+{
+    /*
+     * 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.
+     */
+
+    [TestFixture]
+    public class TestMultiFieldQueryParser : LuceneTestCase
+    {
+        /// <summary>
+        /// test stop words parsing for both the non static form, and for the 
+        /// corresponding static form (qtxt, fields[]).
+        /// </summary>
+        [Test]
+        public void TestStopwordsParsing()
+        {
+            AssertStopQueryEquals("one", "b:one t:one");
+            AssertStopQueryEquals("one stop", "b:one t:one");
+            AssertStopQueryEquals("one (stop)", "b:one t:one");
+            AssertStopQueryEquals("one ((stop))", "b:one t:one");
+            AssertStopQueryEquals("stop", "");
+            AssertStopQueryEquals("(stop)", "");
+            AssertStopQueryEquals("((stop))", "");
+        }
+
+        /// <summary>
+        /// verify parsing of query using a stopping analyzer  
+        /// </summary>
+        /// <param name="qtxt"></param>
+        /// <param name="expectedRes"></param>
+        private void AssertStopQueryEquals(string qtxt, string expectedRes)
+        {
+            string[] fields = { "b", "t" };
+            BooleanClause.Occur[] occur = new BooleanClause.Occur[] { BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD };
+            TestQueryParser.QPTestAnalyzer a = new TestQueryParser.QPTestAnalyzer();
+            MultiFieldQueryParser mfqp = new MultiFieldQueryParser(TEST_VERSION_CURRENT, fields, a);
+
+            Query q = mfqp.Parse(qtxt);
+            assertEquals(expectedRes, q.toString());
+
+            q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, qtxt, fields, occur, a);
+            assertEquals(expectedRes, q.toString());
+        }
+
+        [Test]
+        public void TestSimple()
+        {
+            string[] fields = { "b", "t" };
+            MultiFieldQueryParser mfqp = new MultiFieldQueryParser(TEST_VERSION_CURRENT, fields, new MockAnalyzer(Random()));
+
+            Query q = mfqp.Parse("one");
+            assertEquals("b:one t:one", q.toString());
+
+            q = mfqp.Parse("one two");
+            assertEquals("(b:one t:one) (b:two t:two)", q.toString());
+
+            q = mfqp.Parse("+one +two");
+            assertEquals("+(b:one t:one) +(b:two t:two)", q.toString());
+
+            q = mfqp.Parse("+one -two -three");
+            assertEquals("+(b:one t:one) -(b:two t:two) -(b:three t:three)", q.toString());
+
+            q = mfqp.Parse("one^2 two");
+            assertEquals("((b:one t:one)^2.0) (b:two t:two)", q.toString());
+
+            q = mfqp.Parse("one~ two");
+            assertEquals("(b:one~2 t:one~2) (b:two t:two)", q.toString());
+
+            q = mfqp.Parse("one~0.8 two^2");
+            assertEquals("(b:one~0 t:one~0) ((b:two t:two)^2.0)", q.toString());
+
+            q = mfqp.Parse("one* two*");
+            assertEquals("(b:one* t:one*) (b:two* t:two*)", q.toString());
+
+            q = mfqp.Parse("[a TO c] two");
+            assertEquals("(b:[a TO c] t:[a TO c]) (b:two t:two)", q.toString());
+
+            q = mfqp.Parse("w?ldcard");
+            assertEquals("b:w?ldcard t:w?ldcard", q.toString());
+
+            q = mfqp.Parse("\"foo bar\"");
+            assertEquals("b:\"foo bar\" t:\"foo bar\"", q.toString());
+
+            q = mfqp.Parse("\"aa bb cc\" \"dd ee\"");
+            assertEquals("(b:\"aa bb cc\" t:\"aa bb cc\") (b:\"dd ee\" t:\"dd ee\")", q.toString());
+
+            q = mfqp.Parse("\"foo bar\"~4");
+            assertEquals("b:\"foo bar\"~4 t:\"foo bar\"~4", q.toString());
+
+            // LUCENE-1213: MultiFieldQueryParser was ignoring slop when phrase had a field.
+            q = mfqp.Parse("b:\"foo bar\"~4");
+            assertEquals("b:\"foo bar\"~4", q.toString());
+
+            // make sure that terms which have a field are not touched:
+            q = mfqp.Parse("one f:two");
+            assertEquals("(b:one t:one) f:two", q.toString());
+
+            // AND mode:
+            mfqp.DefaultOperator = QueryParserBase.AND_OPERATOR;
+            q = mfqp.Parse("one two");
+            assertEquals("+(b:one t:one) +(b:two t:two)", q.toString());
+            q = mfqp.Parse("\"aa bb cc\" \"dd ee\"");
+            assertEquals("+(b:\"aa bb cc\" t:\"aa bb cc\") +(b:\"dd ee\" t:\"dd ee\")", q.toString());
+        }
+
+        [Test]
+        public void TestBoostsSimple()
+        {
+            IDictionary<string, float> boosts = new Dictionary<string, float>();
+            boosts["b"] = (float)5;
+            boosts["t"] = (float)10;
+            string[] fields = { "b", "t" };
+            MultiFieldQueryParser mfqp = new MultiFieldQueryParser(TEST_VERSION_CURRENT, fields, new MockAnalyzer(Random()), boosts);
+
+
+            //Check for simple
+            Query q = mfqp.Parse("one");
+            assertEquals("b:one^5.0 t:one^10.0", q.toString());
+
+            //Check for AND
+            q = mfqp.Parse("one AND two");
+            assertEquals("+(b:one^5.0 t:one^10.0) +(b:two^5.0 t:two^10.0)", q.toString());
+
+            //Check for OR
+            q = mfqp.Parse("one OR two");
+            assertEquals("(b:one^5.0 t:one^10.0) (b:two^5.0 t:two^10.0)", q.toString());
+
+            //Check for AND and a field
+            q = mfqp.Parse("one AND two AND foo:test");
+            assertEquals("+(b:one^5.0 t:one^10.0) +(b:two^5.0 t:two^10.0) +foo:test", q.toString());
+
+            q = mfqp.Parse("one^3 AND two^4");
+            assertEquals("+((b:one^5.0 t:one^10.0)^3.0) +((b:two^5.0 t:two^10.0)^4.0)", q.toString());
+        }
+
+        [Test]
+        public void TestStaticMethod1()
+        {
+            string[] fields = { "b", "t" };
+            string[] queries = { "one", "two" };
+            Query q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, queries, fields, new MockAnalyzer(Random()));
+            assertEquals("b:one t:two", q.toString());
+
+            string[] queries2 = { "+one", "+two" };
+            q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, queries2, fields, new MockAnalyzer(Random()));
+            assertEquals("(+b:one) (+t:two)", q.toString());
+
+            string[] queries3 = { "one", "+two" };
+            q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, queries3, fields, new MockAnalyzer(Random()));
+            assertEquals("b:one (+t:two)", q.toString());
+
+            string[] queries4 = { "one +more", "+two" };
+            q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, queries4, fields, new MockAnalyzer(Random()));
+            assertEquals("(b:one +b:more) (+t:two)", q.toString());
+
+            string[] queries5 = { "blah" };
+            try
+            {
+                q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, queries5, fields, new MockAnalyzer(Random()));
+                fail();
+            }
+            catch (ArgumentException e)
+            {
+                // expected exception, array length differs
+            }
+
+            // check also with stop words for this static form (qtxts[], fields[]).
+            TestQueryParser.QPTestAnalyzer stopA = new TestQueryParser.QPTestAnalyzer();
+
+            string[] queries6 = { "((+stop))", "+((stop))" };
+            q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, queries6, fields, stopA);
+            assertEquals("", q.toString());
+
+            string[] queries7 = { "one ((+stop)) +more", "+((stop)) +two" };
+            q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, queries7, fields, stopA);
+            assertEquals("(b:one +b:more) (+t:two)", q.toString());
+        }
+
+        [Test]
+        public void TestStaticMethod2()
+        {
+            string[] fields = { "b", "t" };
+            BooleanClause.Occur[] flags = { BooleanClause.Occur.MUST, BooleanClause.Occur.MUST_NOT };
+            Query q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, "one", fields, flags, new MockAnalyzer(Random()));
+            assertEquals("+b:one -t:one", q.toString());
+
+            q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, "one two", fields, flags, new MockAnalyzer(Random()));
+            assertEquals("+(b:one b:two) -(t:one t:two)", q.toString());
+
+            try
+            {
+                BooleanClause.Occur[] flags2 = { BooleanClause.Occur.MUST };
+                q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, "blah", fields, flags2, new MockAnalyzer(Random()));
+                fail();
+            }
+            catch (ArgumentException e)
+            {
+                // expected exception, array length differs
+            }
+        }
+
+        [Test]
+        public void TestStaticMethod2Old()
+        {
+            string[] fields = { "b", "t" };
+            //int[] flags = {MultiFieldQueryParser.REQUIRED_FIELD, MultiFieldQueryParser.PROHIBITED_FIELD};
+            BooleanClause.Occur[] flags = { BooleanClause.Occur.MUST, BooleanClause.Occur.MUST_NOT };
+
+            Query q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, "one", fields, flags, new MockAnalyzer(Random()));//, fields, flags, new MockAnalyzer(random));
+            assertEquals("+b:one -t:one", q.toString());
+
+            q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, "one two", fields, flags, new MockAnalyzer(Random()));
+            assertEquals("+(b:one b:two) -(t:one t:two)", q.toString());
+
+            try
+            {
+                BooleanClause.Occur[] flags2 = { BooleanClause.Occur.MUST };
+                q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, "blah", fields, flags2, new MockAnalyzer(Random()));
+                fail();
+            }
+            catch (ArgumentException e)
+            {
+                // expected exception, array length differs
+            }
+        }
+
+        [Test]
+        public void TestStaticMethod3()
+        {
+            string[] queries = { "one", "two", "three" };
+            string[] fields = { "f1", "f2", "f3" };
+            BooleanClause.Occur[] flags = {BooleanClause.Occur.MUST,
+                BooleanClause.Occur.MUST_NOT, BooleanClause.Occur.SHOULD};
+            Query q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, queries, fields, flags, new MockAnalyzer(Random()));
+            assertEquals("+f1:one -f2:two f3:three", q.toString());
+
+            try
+            {
+                BooleanClause.Occur[] flags2 = { BooleanClause.Occur.MUST };
+                q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, queries, fields, flags2, new MockAnalyzer(Random()));
+                fail();
+            }
+            catch (ArgumentException e)
+            {
+                // expected exception, array length differs
+            }
+        }
+
+        [Test]
+        public void TestStaticMethod3Old()
+        {
+            string[] queries = { "one", "two" };
+            string[] fields = { "b", "t" };
+            BooleanClause.Occur[] flags = { BooleanClause.Occur.MUST, BooleanClause.Occur.MUST_NOT };
+            Query q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, queries, fields, flags, new MockAnalyzer(Random()));
+            assertEquals("+b:one -t:two", q.toString());
+
+            try
+            {
+                BooleanClause.Occur[] flags2 = { BooleanClause.Occur.MUST };
+                q = MultiFieldQueryParser.Parse(TEST_VERSION_CURRENT, queries, fields, flags2, new MockAnalyzer(Random()));
+                fail();
+            }
+            catch (ArgumentException e)
+            {
+                // expected exception, array length differs
+            }
+        }
+
+        [Test]
+        public void TestAnalyzerReturningNull()
+        {
+            string[] fields = new string[] { "f1", "f2", "f3" };
+            MultiFieldQueryParser parser = new MultiFieldQueryParser(TEST_VERSION_CURRENT, fields, new AnalyzerReturningNull());
+            Query q = parser.Parse("bla AND blo");
+            assertEquals("+(f2:bla f3:bla) +(f2:blo f3:blo)", q.toString());
+            // the following queries are not affected as their terms are not analyzed anyway:
+            q = parser.Parse("bla*");
+            assertEquals("f1:bla* f2:bla* f3:bla*", q.toString());
+            q = parser.Parse("bla~");
+            assertEquals("f1:bla~2 f2:bla~2 f3:bla~2", q.toString());
+            q = parser.Parse("[a TO c]");
+            assertEquals("f1:[a TO c] f2:[a TO c] f3:[a TO c]", q.toString());
+        }
+
+        [Test]
+        public void TestStopWordSearching()
+        {
+            Analyzer analyzer = new MockAnalyzer(Random());
+            using (var ramDir = NewDirectory())
+            {
+                using (IndexWriter iw = new IndexWriter(ramDir, NewIndexWriterConfig(TEST_VERSION_CURRENT, analyzer)))
+                {
+                    Document doc = new Document();
+                    doc.Add(NewTextField("body", "blah the footest blah", Field.Store.NO));
+                    iw.AddDocument(doc);
+                }
+
+                MultiFieldQueryParser mfqp =
+                  new MultiFieldQueryParser(TEST_VERSION_CURRENT, new string[] { "body" }, analyzer);
+                mfqp.DefaultOperator = QueryParser.Operator.AND;
+                Query q = mfqp.Parse("the footest");
+                using (IndexReader ir = DirectoryReader.Open(ramDir))
+                {
+                    IndexSearcher @is = NewSearcher(ir);
+                    ScoreDoc[] hits = @is.Search(q, null, 1000).ScoreDocs;
+                    assertEquals(1, hits.Length);
+                }
+            }
+        }
+
+        private class AnalyzerReturningNull : Analyzer
+        {
+            MockAnalyzer stdAnalyzer = new MockAnalyzer(Random());
+
+            public AnalyzerReturningNull()
+                : base(PER_FIELD_REUSE_STRATEGY)
+            { }
+
+            public override System.IO.TextReader InitReader(string fieldName, TextReader reader)
+            {
+                if ("f1".equals(fieldName))
+                {
+                    // we don't use the reader, so close it:
+                    IOUtils.CloseWhileHandlingException(reader);
+                    // return empty reader, so MockTokenizer returns no tokens:
+                    return new StringReader("");
+                }
+                else
+                {
+                    return base.InitReader(fieldName, reader);
+                }
+            }
+
+            public override TokenStreamComponents CreateComponents(string fieldName, TextReader reader)
+            {
+                return stdAnalyzer.CreateComponents(fieldName, reader);
+            }
+        }
+
+        [Test]
+        public void TestSimpleRegex()
+        {
+            string[] fields = new string[] { "a", "b" };
+            MultiFieldQueryParser mfqp = new MultiFieldQueryParser(TEST_VERSION_CURRENT, fields, new MockAnalyzer(Random()));
+
+            BooleanQuery bq = new BooleanQuery(true);
+            bq.Add(new RegexpQuery(new Term("a", "[a-z][123]")), BooleanClause.Occur.SHOULD);
+            bq.Add(new RegexpQuery(new Term("b", "[a-z][123]")), BooleanClause.Occur.SHOULD);
+            assertEquals(bq, mfqp.Parse("/[a-z][123]/"));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6d711567/Lucene.Net.Tests.QueryParser/Classic/TestMultiPhraseQueryParsing.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Classic/TestMultiPhraseQueryParsing.cs b/Lucene.Net.Tests.QueryParser/Classic/TestMultiPhraseQueryParsing.cs
new file mode 100644
index 0000000..3aaa9b2
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Classic/TestMultiPhraseQueryParsing.cs
@@ -0,0 +1,121 @@
+\ufeffusing Lucene.Net.Analysis;
+using Lucene.Net.Analysis.Tokenattributes;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Util;
+using NUnit.Framework;
+
+namespace Lucene.Net.QueryParser.Classic
+{
+    /*
+     * 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.
+     */
+
+    [TestFixture]
+    public class TestMultiPhraseQueryParsing_ : LuceneTestCase
+    {
+        private class TokenAndPos
+        {
+            public readonly string token;
+            public readonly int pos;
+            public TokenAndPos(string token, int pos)
+            {
+                this.token = token;
+                this.pos = pos;
+            }
+        }
+
+        private class CannedAnalyzer : Analyzer
+        {
+            private readonly TokenAndPos[] tokens;
+
+            public CannedAnalyzer(TokenAndPos[] tokens)
+            {
+                this.tokens = tokens;
+            }
+
+            public override TokenStreamComponents CreateComponents(string fieldName, System.IO.TextReader reader)
+            {
+                return new TokenStreamComponents(new CannedTokenizer(reader, tokens));
+            }
+        }
+
+        private class CannedTokenizer : Tokenizer
+        {
+            private readonly TokenAndPos[] tokens;
+            private int upto = 0;
+            private int lastPos = 0;
+            private readonly ICharTermAttribute termAtt;
+            private readonly IPositionIncrementAttribute posIncrAtt;
+
+            public CannedTokenizer(System.IO.TextReader reader, TokenAndPos[] tokens)
+                : base(reader)
+            {
+                this.tokens = tokens;
+                this.termAtt = AddAttribute<ICharTermAttribute>();
+                this.posIncrAtt = AddAttribute<IPositionIncrementAttribute>();
+            }
+
+            public override sealed bool IncrementToken()
+            {
+                ClearAttributes();
+                if (upto < tokens.Length)
+                {
+                    TokenAndPos token = tokens[upto++];
+                    termAtt.SetEmpty();
+                    termAtt.Append(token.token);
+                    posIncrAtt.PositionIncrement = (token.pos - lastPos);
+                    lastPos = token.pos;
+                    return true;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+            public override void Reset()
+            {
+                base.Reset();
+                this.upto = 0;
+                this.lastPos = 0;
+            }
+        }
+
+        [Test]
+        public void TestMultiPhraseQueryParsing()
+        {
+            TokenAndPos[] INCR_0_QUERY_TOKENS_AND = new TokenAndPos[]
+            {
+                new TokenAndPos("a", 0),
+                new TokenAndPos("1", 0),
+                new TokenAndPos("b", 1),
+                new TokenAndPos("1", 1),
+                new TokenAndPos("c", 2)
+            };
+
+            QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new CannedAnalyzer(INCR_0_QUERY_TOKENS_AND));
+            Query q = qp.Parse("\"this text is acually ignored\"");
+            assertTrue("wrong query type!", q is MultiPhraseQuery);
+
+            MultiPhraseQuery multiPhraseQuery = new MultiPhraseQuery();
+            multiPhraseQuery.Add(new Term[] { new Term("field", "a"), new Term("field", "1") }, -1);
+            multiPhraseQuery.Add(new Term[] { new Term("field", "b"), new Term("field", "1") }, 0);
+            multiPhraseQuery.Add(new Term[] { new Term("field", "c") }, 1);
+
+            assertEquals(multiPhraseQuery, q);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6d711567/Lucene.Net.Tests.QueryParser/Classic/TestQueryParser.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Classic/TestQueryParser.cs b/Lucene.Net.Tests.QueryParser/Classic/TestQueryParser.cs
new file mode 100644
index 0000000..0130bca
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Classic/TestQueryParser.cs
@@ -0,0 +1,554 @@
+\ufeffusing Lucene.Net.Analysis;
+using Lucene.Net.Analysis.Tokenattributes;
+using Lucene.Net.Documents;
+using Lucene.Net.QueryParser.Flexible.Standard;
+using Lucene.Net.QueryParser.Util;
+using Lucene.Net.Search;
+using Lucene.Net.Support;
+using NUnit.Framework;
+using System;
+using System.Diagnostics;
+
+namespace Lucene.Net.QueryParser.Classic
+{
+    /*
+     * 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.
+     */
+
+    [TestFixture]
+    public class TestQueryParser : QueryParserTestBase
+    {
+        public class QPTestParser : QueryParser
+        {
+            public QPTestParser(string f, Analyzer a)
+                : base(TEST_VERSION_CURRENT, f, a)
+            {
+            }
+
+            protected override Query GetFuzzyQuery(string field, string termStr, float minSimilarity)
+            {
+                throw new ParseException("Fuzzy queries not allowed");
+            }
+
+            protected override Query GetWildcardQuery(string field, string termStr)
+            {
+                throw new ParseException("Wildcard queries not allowed");
+            }
+
+        }
+
+        public QueryParser GetParser(Analyzer a)
+        {
+            if (a == null) a = new MockAnalyzer(Random(), MockTokenizer.SIMPLE, true);
+            QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, DefaultField, a);
+            qp.DefaultOperator = (QueryParserBase.OR_OPERATOR);
+            return qp;
+        }
+
+        public override ICommonQueryParserConfiguration GetParserConfig(Analyzer a)
+        {
+            return GetParser(a);
+        }
+
+        public override Query GetQuery(string query, ICommonQueryParserConfiguration cqpC)
+        {
+            Debug.Assert(cqpC != null, "Parameter must not be null");
+            Debug.Assert(cqpC is QueryParser, "Parameter must be instance of QueryParser");
+            QueryParser qp = (QueryParser)cqpC;
+            return qp.Parse(query);
+        }
+
+        public override Query GetQuery(string query, Analyzer a)
+        {
+            return GetParser(a).Parse(query);
+        }
+
+        public override bool IsQueryParserException(Exception exception)
+        {
+            return exception is ParseException;
+        }
+
+        public override void SetDefaultOperatorOR(ICommonQueryParserConfiguration cqpC)
+        {
+            Debug.Assert(cqpC is QueryParser);
+            QueryParser qp = (QueryParser)cqpC;
+            qp.DefaultOperator = QueryParserBase.Operator.OR;
+        }
+
+        public override void SetDefaultOperatorAND(ICommonQueryParserConfiguration cqpC)
+        {
+            Debug.Assert(cqpC is QueryParser);
+            QueryParser qp = (QueryParser)cqpC;
+            qp.DefaultOperator = QueryParserBase.Operator.AND;
+        }
+
+        public override void SetAnalyzeRangeTerms(ICommonQueryParserConfiguration cqpC, bool value)
+        {
+            Debug.Assert(cqpC is QueryParser);
+            QueryParser qp = (QueryParser)cqpC;
+            qp.AnalyzeRangeTerms = (value);
+        }
+
+        public override void SetAutoGeneratePhraseQueries(ICommonQueryParserConfiguration cqpC, bool value)
+        {
+            Debug.Assert(cqpC is QueryParser);
+            QueryParser qp = (QueryParser)cqpC;
+            qp.AutoGeneratePhraseQueries = value;
+        }
+
+        public override void SetDateResolution(ICommonQueryParserConfiguration cqpC, ICharSequence field, DateTools.Resolution value)
+        {
+            Debug.Assert(cqpC is QueryParser);
+            QueryParser qp = (QueryParser)cqpC;
+            qp.SetDateResolution(field.toString(), value);
+        }
+
+        [Test]
+        public override void TestDefaultOperator()
+        {
+            QueryParser qp = GetParser(new MockAnalyzer(Random()));
+            // make sure OR is the default:
+            assertEquals(QueryParserBase.OR_OPERATOR, qp.DefaultOperator);
+            SetDefaultOperatorAND(qp);
+            assertEquals(QueryParserBase.AND_OPERATOR, qp.DefaultOperator);
+            SetDefaultOperatorOR(qp);
+            assertEquals(QueryParserBase.OR_OPERATOR, qp.DefaultOperator);
+        }
+
+        // LUCENE-2002: when we run javacc to regen QueryParser,
+        // we also run a replaceregexp step to fix 2 of the public
+        // ctors (change them to protected):
+        //
+        // protected QueryParser(CharStream stream)
+        //
+        // protected QueryParser(QueryParserTokenManager tm)
+        //
+        // This test is here as a safety, in case that ant step
+        // doesn't work for some reason.
+        [Test]
+        public void TestProtectedCtors()
+        {
+            try
+            {
+                typeof(QueryParser).GetConstructor(new Type[] { typeof(ICharStream) });
+                fail("please switch public QueryParser(CharStream) to be protected");
+            }
+            catch (Exception nsme)
+            {
+                // expected
+            }
+            try
+            {
+                typeof(QueryParser).GetConstructor(new Type[] { typeof(QueryParserTokenManager) });
+                fail("please switch public QueryParser(QueryParserTokenManager) to be protected");
+            }
+            catch (Exception nsme)
+            {
+                // expected
+            }
+        }
+
+        private class TestFuzzySlopeExtendabilityQueryParser : QueryParser
+        {
+            public TestFuzzySlopeExtendabilityQueryParser()
+                : base(TEST_VERSION_CURRENT, "a", new MockAnalyzer(Random(), MockTokenizer.WHITESPACE, false))
+            {}
+
+            protected override Query HandleBareFuzzy(string qfield, Token fuzzySlop, string termImage)
+            {
+                if (fuzzySlop.image.EndsWith("\u20ac"))
+                {
+                    float fms = FuzzyMinSim;
+                    try
+                    {
+                        fms = float.Parse(fuzzySlop.image.Substring(1, fuzzySlop.image.Length - 1));
+                    }
+                    catch (Exception ignored) { }
+                    float value = float.Parse(termImage);
+                    return GetRangeQuery(qfield, (value - fms / 2.0f).ToString(), (value + fms / 2.0f).ToString(), true, true);
+                }
+                return base.HandleBareFuzzy(qfield, fuzzySlop, termImage);
+            }
+        }
+
+        [Test]
+        public void TestFuzzySlopeExtendability()
+        {
+            QueryParser qp = new TestFuzzySlopeExtendabilityQueryParser();
+            assertEquals(qp.Parse("a:[11.95 TO 12.95]"), qp.Parse("12.45~1\u20ac"));
+        }
+
+        private class TestStarParsingQueryParser : QueryParser
+        {
+            public readonly int[] type = new int[1];
+
+            public TestStarParsingQueryParser()
+                : base(TEST_VERSION_CURRENT, "field", new MockAnalyzer(Random(), MockTokenizer.WHITESPACE, false))
+            { }
+
+            protected override Query GetWildcardQuery(string field, string termStr)
+            {
+                // override error checking of superclass
+                type[0] = 1;
+                return new TermQuery(new Index.Term(field, termStr));
+            }
+
+            protected override Query GetPrefixQuery(string field, string termStr)
+            {
+                // override error checking of superclass
+                type[0] = 2;
+                return new TermQuery(new Index.Term(field, termStr));
+            }
+
+            protected override Query GetFieldQuery(string field, string queryText, bool quoted)
+            {
+                type[0] = 3;
+                return base.GetFieldQuery(field, queryText, quoted);
+            }
+        }
+
+        [Test]
+        public override void TestStarParsing()
+        {
+            TestStarParsingQueryParser qp = new TestStarParsingQueryParser();
+
+            TermQuery tq;
+
+            tq = (TermQuery)qp.Parse("foo:zoo*");
+            assertEquals("zoo", tq.Term.Text());
+            assertEquals(2, qp.type[0]);
+
+            tq = (TermQuery)qp.Parse("foo:zoo*^2");
+            assertEquals("zoo", tq.Term.Text());
+            assertEquals(2, qp.type[0]);
+            assertEquals(tq.Boost, 2, 0);
+
+            tq = (TermQuery)qp.Parse("foo:*");
+            assertEquals("*", tq.Term.Text());
+            assertEquals(1, qp.type[0]); // could be a valid prefix query in the future too
+
+            tq = (TermQuery)qp.Parse("foo:*^2");
+            assertEquals("*", tq.Term.Text());
+            assertEquals(1, qp.type[0]);
+            assertEquals(tq.Boost, 2, 0);
+
+            tq = (TermQuery)qp.Parse("*:foo");
+            assertEquals("*", tq.Term.Field);
+            assertEquals("foo", tq.Term.Text());
+            assertEquals(3, qp.type[0]);
+
+            tq = (TermQuery)qp.Parse("*:*");
+            assertEquals("*", tq.Term.Field);
+            assertEquals("*", tq.Term.Text());
+            assertEquals(1, qp.type[0]); // could be handled as a prefix query in the
+            // future
+
+            tq = (TermQuery)qp.Parse("(*:*)");
+            assertEquals("*", tq.Term.Field);
+            assertEquals("*", tq.Term.Text());
+            assertEquals(1, qp.type[0]);
+        }
+
+        [Test]
+        public void TestCustomQueryParserWildcard()
+        {
+            try
+            {
+                new QPTestParser("contents", new MockAnalyzer(Random(),
+                    MockTokenizer.WHITESPACE, false)).Parse("a?t");
+                fail("Wildcard queries should not be allowed");
+            }
+            catch (ParseException expected)
+            {
+                // expected exception
+            }
+        }
+
+        [Test]
+        public void TestCustomQueryParserFuzzy()
+        {
+            try
+            {
+                new QPTestParser("contents", new MockAnalyzer(Random(),
+                    MockTokenizer.WHITESPACE, false)).Parse("xunit~");
+                fail("Fuzzy queries should not be allowed");
+            }
+            catch (ParseException expected)
+            {
+                // expected exception
+            }
+        }
+
+        /// <summary>
+        /// query parser that doesn't expand synonyms when users use double quotes
+        /// </summary>
+        private class SmartQueryParser : QueryParser
+        {
+            Analyzer morePrecise = new Analyzer2();
+
+            public SmartQueryParser()
+                : base(TEST_VERSION_CURRENT, "field", new Analyzer1())
+            {
+            }
+
+            protected override Query GetFieldQuery(string field, string queryText, bool quoted)
+            {
+                if (quoted) return NewFieldQuery(morePrecise, field, queryText, quoted);
+                else return base.GetFieldQuery(field, queryText, quoted);
+            }
+        }
+
+        public override void TestNewFieldQuery()
+        {
+            /** ordinary behavior, synonyms form uncoordinated boolean query */
+            QueryParser dumb = new QueryParser(TEST_VERSION_CURRENT, "field",
+                new Analyzer1());
+            BooleanQuery expanded = new BooleanQuery(true);
+            expanded.Add(new TermQuery(new Index.Term("field", "dogs")),
+                BooleanClause.Occur.SHOULD);
+            expanded.Add(new TermQuery(new Index.Term("field", "dog")),
+                BooleanClause.Occur.SHOULD);
+            assertEquals(expanded, dumb.Parse("\"dogs\""));
+            /** even with the phrase operator the behavior is the same */
+            assertEquals(expanded, dumb.Parse("dogs"));
+
+            /**
+             * custom behavior, the synonyms are expanded, unless you use quote operator
+             */
+            QueryParser smart = new SmartQueryParser();
+            assertEquals(expanded, smart.Parse("dogs"));
+
+            Query unexpanded = new TermQuery(new Index.Term("field", "dogs"));
+            assertEquals(unexpanded, smart.Parse("\"dogs\""));
+        }
+
+        // LUCENETODO: fold these into QueryParserTestBase
+
+        /// <summary>
+        /// adds synonym of "dog" for "dogs".
+        /// </summary>
+        public class MockSynonymAnalyzer : Analyzer
+        {
+            public override TokenStreamComponents CreateComponents(string fieldName, System.IO.TextReader reader)
+            {
+                MockTokenizer tokenizer = new MockTokenizer(reader);
+                return new TokenStreamComponents(tokenizer, new MockSynonymFilter(tokenizer));
+            }
+        }
+
+        /// <summary>
+        /// simple synonyms test
+        /// </summary>
+        [Test]
+        public void TestSynonyms()
+        {
+            BooleanQuery expected = new BooleanQuery(true);
+            expected.Add(new TermQuery(new Index.Term("field", "dogs")), BooleanClause.Occur.SHOULD);
+            expected.Add(new TermQuery(new Index.Term("field", "dog")), BooleanClause.Occur.SHOULD);
+            QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new MockSynonymAnalyzer());
+            assertEquals(expected, qp.Parse("dogs"));
+            assertEquals(expected, qp.Parse("\"dogs\""));
+            qp.DefaultOperator = (QueryParserBase.Operator.AND);
+            assertEquals(expected, qp.Parse("dogs"));
+            assertEquals(expected, qp.Parse("\"dogs\""));
+            expected.Boost = (2.0f);
+            assertEquals(expected, qp.Parse("dogs^2"));
+            assertEquals(expected, qp.Parse("\"dogs\"^2"));
+        }
+
+        /// <summary>
+        /// forms multiphrase query
+        /// </summary>
+        [Test]
+        public void TestSynonymsPhrase()
+        {
+            MultiPhraseQuery expected = new MultiPhraseQuery();
+            expected.Add(new Index.Term("field", "old"));
+            expected.Add(new Index.Term[] { new Index.Term("field", "dogs"), new Index.Term("field", "dog") });
+            QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new MockSynonymAnalyzer());
+            assertEquals(expected, qp.Parse("\"old dogs\""));
+            qp.DefaultOperator = (QueryParserBase.Operator.AND);
+            assertEquals(expected, qp.Parse("\"old dogs\""));
+            expected.Boost = (2.0f);
+            assertEquals(expected, qp.Parse("\"old dogs\"^2"));
+            expected.Slop = (3);
+            assertEquals(expected, qp.Parse("\"old dogs\"~3^2"));
+        }
+
+        /// <summary>
+        /// adds synonym of "\u570b" for "\u56fd".
+        /// </summary>
+        protected class MockCJKSynonymFilter : TokenFilter
+        {
+            ICharTermAttribute termAtt;
+            IPositionIncrementAttribute posIncAtt;
+            bool addSynonym = false;
+
+            public MockCJKSynonymFilter(TokenStream input)
+                : base(input)
+            {
+                ICharTermAttribute termAtt = AddAttribute<ICharTermAttribute>();
+                IPositionIncrementAttribute posIncAtt = AddAttribute<IPositionIncrementAttribute>();
+            }
+
+            public override bool IncrementToken()
+            {
+                if (addSynonym)
+                { // inject our synonym
+                    ClearAttributes();
+                    termAtt.SetEmpty().Append("\u570b");
+                    posIncAtt.PositionIncrement = (0);
+                    addSynonym = false;
+                    return true;
+                }
+
+                if (input.IncrementToken())
+                {
+                    addSynonym = termAtt.toString().equals("\u56fd");
+                    return true;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+        }
+
+        protected class MockCJKSynonymAnalyzer : Analyzer
+        {
+            public override TokenStreamComponents CreateComponents(string fieldName, System.IO.TextReader reader)
+            {
+                Tokenizer tokenizer = new SimpleCJKTokenizer(reader);
+                return new TokenStreamComponents(tokenizer, new MockCJKSynonymFilter(tokenizer));
+            }
+        }
+
+        /// <summary>
+        /// simple CJK synonym test
+        /// </summary>
+        [Test]
+        public void TestCJKSynonym()
+        {
+            BooleanQuery expected = new BooleanQuery(true);
+            expected.Add(new TermQuery(new Index.Term("field", "\u56fd")), BooleanClause.Occur.SHOULD);
+            expected.Add(new TermQuery(new Index.Term("field", "\u570b")), BooleanClause.Occur.SHOULD);
+            QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new MockCJKSynonymAnalyzer());
+            assertEquals(expected, qp.Parse("\u56fd"));
+            qp.DefaultOperator = (QueryParserBase.Operator.AND);
+            assertEquals(expected, qp.Parse("\u56fd"));
+            expected.Boost = (2.0f);
+            assertEquals(expected, qp.Parse("\u56fd^2"));
+        }
+
+        /// <summary>
+        /// synonyms with default OR operator 
+        /// </summary>
+        [Test]
+        public void TestCJKSynonymsOR()
+        {
+            BooleanQuery expected = new BooleanQuery();
+            expected.Add(new TermQuery(new Index.Term("field", "\u4e2d")), BooleanClause.Occur.SHOULD);
+            BooleanQuery inner = new BooleanQuery(true);
+            inner.Add(new TermQuery(new Index.Term("field", "\u56fd")), BooleanClause.Occur.SHOULD);
+            inner.Add(new TermQuery(new Index.Term("field", "\u570b")), BooleanClause.Occur.SHOULD);
+            expected.Add(inner, BooleanClause.Occur.SHOULD);
+            QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new MockCJKSynonymAnalyzer());
+            assertEquals(expected, qp.Parse("\u4e2d\u56fd"));
+            expected.Boost = (2.0f);
+            assertEquals(expected, qp.Parse("\u4e2d\u56fd^2"));
+        }
+
+        /// <summary>
+        /// more complex synonyms with default OR operator
+        /// </summary>
+        [Test]
+        public void TestCJKSynonymsOR2()
+        {
+            BooleanQuery expected = new BooleanQuery();
+            expected.Add(new TermQuery(new Index.Term("field", "\u4e2d")), BooleanClause.Occur.SHOULD);
+            BooleanQuery inner = new BooleanQuery(true);
+            inner.Add(new TermQuery(new Index.Term("field", "\u56fd")), BooleanClause.Occur.SHOULD);
+            inner.Add(new TermQuery(new Index.Term("field", "\u570b")), BooleanClause.Occur.SHOULD);
+            expected.Add(inner, BooleanClause.Occur.SHOULD);
+            BooleanQuery inner2 = new BooleanQuery(true);
+            inner2.Add(new TermQuery(new Index.Term("field", "\u56fd")), BooleanClause.Occur.SHOULD);
+            inner2.Add(new TermQuery(new Index.Term("field", "\u570b")), BooleanClause.Occur.SHOULD);
+            expected.Add(inner2, BooleanClause.Occur.SHOULD);
+            QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new MockCJKSynonymAnalyzer());
+            assertEquals(expected, qp.Parse("\u4e2d\u56fd\u56fd"));
+            expected.Boost = (2.0f);
+            assertEquals(expected, qp.Parse("\u4e2d\u56fd\u56fd^2"));
+        }
+
+        /// <summary>
+        /// synonyms with default AND operator
+        /// </summary>
+        [Test]
+        public void TestCJKSynonymsAND()
+        {
+            BooleanQuery expected = new BooleanQuery();
+            expected.Add(new TermQuery(new Index.Term("field", "\u4e2d")), BooleanClause.Occur.MUST);
+            BooleanQuery inner = new BooleanQuery(true);
+            inner.Add(new TermQuery(new Index.Term("field", "\u56fd")), BooleanClause.Occur.SHOULD);
+            inner.Add(new TermQuery(new Index.Term("field", "\u570b")), BooleanClause.Occur.SHOULD);
+            expected.Add(inner, BooleanClause.Occur.MUST);
+            QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new MockCJKSynonymAnalyzer());
+            qp.DefaultOperator = (QueryParserBase.Operator.AND);
+            assertEquals(expected, qp.Parse("\u4e2d\u56fd"));
+            expected.Boost = (2.0f);
+            assertEquals(expected, qp.Parse("\u4e2d\u56fd^2"));
+        }
+
+        /// <summary>
+        /// more complex synonyms with default AND operator
+        /// </summary>
+        [Test]
+        public void TestCJKSynonymsAND2()
+        {
+            BooleanQuery expected = new BooleanQuery();
+            expected.Add(new TermQuery(new Index.Term("field", "\u4e2d")), BooleanClause.Occur.MUST);
+            BooleanQuery inner = new BooleanQuery(true);
+            inner.Add(new TermQuery(new Index.Term("field", "\u56fd")), BooleanClause.Occur.SHOULD);
+            inner.Add(new TermQuery(new Index.Term("field", "\u570b")), BooleanClause.Occur.SHOULD);
+            expected.Add(inner, BooleanClause.Occur.MUST);
+            BooleanQuery inner2 = new BooleanQuery(true);
+            inner2.Add(new TermQuery(new Index.Term("field", "\u56fd")), BooleanClause.Occur.SHOULD);
+            inner2.Add(new TermQuery(new Index.Term("field", "\u570b")), BooleanClause.Occur.SHOULD);
+            expected.Add(inner2, BooleanClause.Occur.MUST);
+            QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new MockCJKSynonymAnalyzer());
+            qp.DefaultOperator = (QueryParserBase.Operator.AND);
+            assertEquals(expected, qp.Parse("\u4e2d\u56fd\u56fd"));
+            expected.Boost = (2.0f);
+            assertEquals(expected, qp.Parse("\u4e2d\u56fd\u56fd^2"));
+        }
+
+        [Test]
+        public void TestCJKSynonymsPhrase()
+        {
+            MultiPhraseQuery expected = new MultiPhraseQuery();
+            expected.Add(new Index.Term("field", "\u4e2d"));
+            expected.Add(new Index.Term[] { new Index.Term("field", "\u56fd"), new Index.Term("field", "\u570b") });
+            QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new MockCJKSynonymAnalyzer());
+            qp.DefaultOperator = (QueryParserBase.Operator.AND);
+            assertEquals(expected, qp.Parse("\"\u4e2d\u56fd\""));
+            expected.Boost = (2.0f);
+            assertEquals(expected, qp.Parse("\"\u4e2d\u56fd\"^2"));
+            expected.Slop = (3);
+            assertEquals(expected, qp.Parse("\"\u4e2d\u56fd\"~3^2"));
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6d711567/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj b/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
new file mode 100644
index 0000000..99a5e91
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
@@ -0,0 +1,82 @@
+\ufeff<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{5719FB4F-BF80-40E5-BACC-37E8E18FCA2E}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Lucene.Net.Tests.QueryParser</RootNamespace>
+    <AssemblyName>Lucene.Net.Tests.QueryParser</AssemblyName>
+    <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
+      <HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Classic\TestMultiFieldQueryParser.cs" />
+    <Compile Include="Classic\TestMultiPhraseQueryParsing.cs" />
+    <Compile Include="Classic\TestQueryParser.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Classic\TestMultiAnalyzer.cs" />
+    <Compile Include="Util\QueryParserTestBase.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Lucene.Net.QueryParser\Lucene.Net.QueryParser.csproj">
+      <Project>{949ba34b-6ae6-4ce3-b578-61e13e4d76bf}</Project>
+      <Name>Lucene.Net.QueryParser</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\src\Lucene.Net.Analysis.Common\Lucene.Net.Analysis.Common.csproj">
+      <Project>{4add0bbc-b900-4715-9526-d871de8eea64}</Project>
+      <Name>Lucene.Net.Analysis.Common</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\src\Lucene.Net.Core\Lucene.Net.csproj">
+      <Project>{5d4ad9be-1ffb-41ab-9943-25737971bf57}</Project>
+      <Name>Lucene.Net</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\src\Lucene.Net.TestFramework\Lucene.Net.TestFramework.csproj">
+      <Project>{b2c0d749-ce34-4f62-a15e-00cb2ff5ddb3}</Project>
+      <Name>Lucene.Net.TestFramework</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6d711567/Lucene.Net.Tests.QueryParser/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Properties/AssemblyInfo.cs b/Lucene.Net.Tests.QueryParser/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..549c7bf
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+\ufeffusing System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Lucene.Net.Tests.QueryParser")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Lucene.Net.Tests.QueryParser")]
+[assembly: AssemblyCopyright("Copyright �  2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("27d0ae76-3e51-454c-9c4a-f913fde0ed0a")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]