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:44 UTC
[13/50] [abbrv] lucenenet git commit: Ported QueryParser.Ext
namespace + tests.
Ported QueryParser.Ext namespace + tests.
Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/e45f3289
Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/e45f3289
Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/e45f3289
Branch: refs/heads/master
Commit: e45f3289d5710bbcf3f9a4cbe995f31d05af9530
Parents: 071b60c
Author: Shad Storhaug <sh...@shadstorhaug.com>
Authored: Mon Aug 1 05:54:43 2016 +0700
Committer: Shad Storhaug <sh...@shadstorhaug.com>
Committed: Fri Sep 2 22:30:29 2016 +0700
----------------------------------------------------------------------
.../Ext/ExtendableQueryParser.cs | 131 +++++++++++++++
Lucene.Net.QueryParser/Ext/ExtensionQuery.cs | 54 ++++++
Lucene.Net.QueryParser/Ext/Extensions.cs | 167 +++++++++++++++++++
Lucene.Net.QueryParser/Ext/ParserExtension.cs | 50 ++++++
.../Lucene.Net.QueryParser.csproj | 4 +
.../Ext/ExtensionStub.cs | 30 ++++
.../Ext/TestExtendableQueryParser.cs | 145 ++++++++++++++++
.../Ext/TestExtensions.cs | 97 +++++++++++
.../Lucene.Net.Tests.QueryParser.csproj | 3 +
.../Util/QueryParserTestBase.cs | 2 +-
10 files changed, 682 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.QueryParser/Ext/ExtendableQueryParser.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.QueryParser/Ext/ExtendableQueryParser.cs b/Lucene.Net.QueryParser/Ext/ExtendableQueryParser.cs
new file mode 100644
index 0000000..6418f87
--- /dev/null
+++ b/Lucene.Net.QueryParser/Ext/ExtendableQueryParser.cs
@@ -0,0 +1,131 @@
+\ufeffusing Lucene.Net.Analysis;
+using Lucene.Net.Search;
+using Lucene.Net.Util;
+using System;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+ /*
+ * 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>
+ /// The <see cref="ExtendableQueryParser"/> enables arbitrary query parser extension
+ /// based on a customizable field naming scheme. The lucene query syntax allows
+ /// implicit and explicit field definitions as query prefix followed by a colon
+ /// (':') character. The <see cref="ExtendableQueryParser"/> allows to encode extension
+ /// keys into the field symbol associated with a registered instance of
+ /// <see cref="ParserExtension"/>. A customizable separation character separates the
+ /// extension key from the actual field symbol. The <see cref="ExtendableQueryParser"/>
+ /// splits (<see cref="Extensions.SplitExtensionField(String, String)"/>) the
+ /// extension key from the field symbol and tries to resolve the associated
+ /// <see cref="ParserExtension"/>. If the parser can't resolve the key or the field
+ /// token does not contain a separation character, <see cref="ExtendableQueryParser"/>
+ /// yields the same behavior as its super class <see cref="QueryParser"/>. Otherwise,
+ /// if the key is associated with a <see cref="ParserExtension"/> instance, the parser
+ /// builds an instance of <see cref="ExtensionQuery"/> to be processed by
+ /// <see cref="ParserExtension.Parse(ExtensionQuery)"/>.If a extension field does not
+ /// contain a field part the default field for the query will be used.
+ /// <p>
+ /// To guarantee that an extension field is processed with its associated
+ /// extension, the extension query part must escape any special characters like
+ /// '*' or '['. If the extension query contains any whitespace characters, the
+ /// extension query part must be enclosed in quotes.
+ /// Example ('_' used as separation character):
+ /// <pre>
+ /// title_customExt:"Apache Lucene\?" OR content_customExt:prefix\*
+ /// </pre>
+ ///
+ /// Search on the default field:
+ /// <pre>
+ /// _customExt:"Apache Lucene\?" OR _customExt:prefix\*
+ /// </pre>
+ /// </p>
+ /// <p>
+ /// The <see cref="ExtendableQueryParser"/> itself does not implement the logic how
+ /// field and extension key are separated or ordered. All logic regarding the
+ /// extension key and field symbol parsing is located in <see cref="Extensions"/>.
+ /// Customized extension schemes should be implemented by sub-classing
+ /// <see cref="Extensions"/>.
+ /// </p>
+ /// <p>
+ /// For details about the default encoding scheme see <see cref="Extensions"/>.
+ /// </p>
+ ///
+ /// <see cref="Extensions"/>
+ /// <see cref="ParserExtension"/>
+ /// <see cref="ExtensionQuery"/>
+ /// </summary>
+ public class ExtendableQueryParser : Classic.QueryParser
+ {
+ private readonly string defaultField;
+ private readonly Extensions extensions;
+
+
+ /// <summary>
+ /// Default empty extensions instance
+ /// </summary>
+ private static readonly Extensions DEFAULT_EXTENSION = new Extensions();
+
+ /// <summary>
+ /// Creates a new <see cref="ExtendableQueryParser"/> instance
+ /// </summary>
+ /// <param name="matchVersion">the lucene version to use.</param>
+ /// <param name="f">the default query field</param>
+ /// <param name="a">the analyzer used to find terms in a query string</param>
+ public ExtendableQueryParser(LuceneVersion matchVersion, string f, Analyzer a)
+ : base(matchVersion, f, a)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="ExtendableQueryParser"/> instance
+ /// </summary>
+ /// <param name="matchVersion">the lucene version to use.</param>
+ /// <param name="f">the default query field</param>
+ /// <param name="a">the analyzer used to find terms in a query string</param>
+ /// <param name="ext">the query parser extensions</param>
+ public ExtendableQueryParser(LuceneVersion matchVersion, string f, Analyzer a, Extensions ext)
+ : base(matchVersion, f, a)
+ {
+ this.defaultField = f;
+ this.extensions = ext;
+ }
+
+ /// <summary>
+ /// Returns the extension field delimiter character.
+ /// </summary>
+ /// <returns>the extension field delimiter character.</returns>
+ public char ExtensionFieldDelimiter
+ {
+ get { return extensions.ExtensionFieldDelimiter; }
+ }
+
+ protected internal override Query GetFieldQuery(string field, string queryText, bool quoted)
+ {
+ Tuple<string, string> splitExtensionField = this.extensions
+ .SplitExtensionField(defaultField, field);
+ ParserExtension extension = this.extensions
+ .GetExtension(splitExtensionField.Item2);
+ if (extension != null)
+ {
+ return extension.Parse(new ExtensionQuery(this, splitExtensionField.Item1,
+ queryText));
+ }
+ return base.GetFieldQuery(field, queryText, quoted);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.QueryParser/Ext/ExtensionQuery.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.QueryParser/Ext/ExtensionQuery.cs b/Lucene.Net.QueryParser/Ext/ExtensionQuery.cs
new file mode 100644
index 0000000..610e4ad
--- /dev/null
+++ b/Lucene.Net.QueryParser/Ext/ExtensionQuery.cs
@@ -0,0 +1,54 @@
+\ufeffnamespace Lucene.Net.QueryParser.Ext
+{
+ /*
+ * 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>
+ /// <see cref="ExtensionQuery"/> holds all query components extracted from the original
+ /// query string like the query field and the extension query string.
+ /// </summary>
+ public class ExtensionQuery
+ {
+ /// <summary>
+ /// Creates a new <see cref="ExtensionQuery"/>
+ /// </summary>
+ /// <param name="topLevelParser"></param>
+ /// <param name="field">the query field</param>
+ /// <param name="rawQueryString">the raw extension query string</param>
+ public ExtensionQuery(Classic.QueryParser topLevelParser, string field, string rawQueryString)
+ {
+ this.Field = field;
+ this.RawQueryString = rawQueryString;
+ this.TopLevelParser = topLevelParser;
+ }
+
+ /// <summary>
+ /// Returns the query field
+ /// </summary>
+ public string Field { get; protected set; }
+
+ /// <summary>
+ /// Returns the raw extension query string
+ /// </summary>
+ public string RawQueryString { get; protected set; }
+
+ /// <summary>
+ /// Returns the top level parser which created this <see cref="ExtensionQuery"/>
+ /// </summary>
+ public Classic.QueryParser TopLevelParser { get; protected set; }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.QueryParser/Ext/Extensions.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.QueryParser/Ext/Extensions.cs b/Lucene.Net.QueryParser/Ext/Extensions.cs
new file mode 100644
index 0000000..6895268
--- /dev/null
+++ b/Lucene.Net.QueryParser/Ext/Extensions.cs
@@ -0,0 +1,167 @@
+\ufeffusing Lucene.Net.QueryParser.Classic;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+ /*
+ * 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.
+ */
+
+ public class Extensions
+ {
+ private readonly IDictionary<string, ParserExtension> extensions = new Dictionary<string, ParserExtension>();
+ private readonly char extensionFieldDelimiter;
+
+ /// <summary>
+ /// The default extension field delimiter character. This constant is set to ':'
+ /// </summary>
+ public static readonly char DEFAULT_EXTENSION_FIELD_DELIMITER = ':';
+
+ /// <summary>
+ /// Creates a new <see cref="Extensions"/> instance with the
+ /// <see cref="#DEFAULT_EXTENSION_FIELD_DELIMITER"/> as a delimiter character.
+ /// </summary>
+ public Extensions()
+ : this(DEFAULT_EXTENSION_FIELD_DELIMITER)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Extensions"/> instance
+ /// </summary>
+ /// <param name="extensionFieldDelimiter">the extensions field delimiter character</param>
+ public Extensions(char extensionFieldDelimiter)
+ {
+ this.extensionFieldDelimiter = extensionFieldDelimiter;
+ }
+
+ /// <summary>
+ /// Adds a new <see cref="ParserExtension"/> instance associated with the given key.
+ /// </summary>
+ /// <param name="key">the parser extension key</param>
+ /// <param name="extension">the parser extension</param>
+ public virtual void Add(string key, ParserExtension extension)
+ {
+ this.extensions[key] = extension;
+ }
+
+ /// <summary>
+ /// Returns the <see cref="ParserExtension"/> instance for the given key or
+ /// <code>null</code> if no extension can be found for the key.
+ /// </summary>
+ /// <param name="key">the extension key</param>
+ /// <returns>the <see cref="ParserExtension"/> instance for the given key or
+ /// <code>null</code> if no extension can be found for the key.</returns>
+ public ParserExtension GetExtension(string key)
+ {
+ if (key == null || !this.extensions.ContainsKey(key)) return null;
+ return this.extensions[key];
+ }
+
+ /// <summary>
+ /// Returns the extension field delimiter
+ /// </summary>
+ public virtual char ExtensionFieldDelimiter
+ {
+ get { return extensionFieldDelimiter; }
+ }
+
+ /// <summary>
+ /// Splits a extension field and returns the field / extension part as a
+ /// <see cref="Tuple{String,String}"/>. This method tries to split on the first occurrence of the
+ /// extension field delimiter, if the delimiter is not present in the string
+ /// the result will contain a <code>null</code> value for the extension key and
+ /// the given field string as the field value. If the given extension field
+ /// string contains no field identifier the result pair will carry the given
+ /// default field as the field value.
+ /// </summary>
+ /// <param name="defaultField">the default query field</param>
+ /// <param name="field">the extension field string</param>
+ /// <returns>a {<see cref="Tuple{String,String}"/> with the field name as the <see cref="Tuple{String,String}.Item1"/> and the
+ /// extension key as the <see cref="Tuple{String,String}.Item2"/></returns>
+ public Tuple<string, string> SplitExtensionField(string defaultField, string field)
+ {
+ int indexOf = field.IndexOf(this.extensionFieldDelimiter);
+ if (indexOf < 0)
+ return new Tuple<string, string>(field, null);
+ string indexField = indexOf == 0 ? defaultField : field.Substring(0, indexOf);
+ string extensionKey = field.Substring(indexOf + 1);
+ return new Tuple<string, string>(indexField, extensionKey);
+ }
+
+ /// <summary>
+ /// Escapes an extension field. The default implementation is equivalent to
+ /// <see cref="QueryParser.Escape(String)"/>.
+ /// </summary>
+ /// <param name="extfield">the extension field identifier</param>
+ /// <returns>the extension field identifier with all special chars escaped with
+ /// a backslash character.</returns>
+ public string EscapeExtensionField(string extfield)
+ {
+ return QueryParserBase.Escape(extfield);
+ }
+
+ /// <summary>
+ /// Builds an extension field string from a given extension key and the default
+ /// query field. The default field and the key are delimited with the extension
+ /// field delimiter character. This method makes no assumption about the order
+ /// of the extension key and the field. By default the extension key is
+ /// appended to the end of the returned string while the field is added to the
+ /// beginning. Special Query characters are escaped in the result.
+ /// <p>
+ /// Note: <see cref="Extensions"/> subclasses must maintain the contract between
+ /// <see cref="M:BuildExtensionField(String)"/> and
+ /// <see cref="M:BuildExtensionField(String, String)"/> where the latter inverts the
+ /// former.
+ /// </p>
+ /// </summary>
+ /// <param name="extensionKey">the extension key</param>
+ /// <returns>escaped extension field identifier</returns>
+ public string BuildExtensionField(string extensionKey)
+ {
+ return BuildExtensionField(extensionKey, "");
+ }
+
+ /// <summary>
+ /// Builds an extension field string from a given extension key and the default
+ /// query field. The default field and the key are delimited with the extension
+ /// field delimiter character. This method makes no assumption about the order
+ /// of the extension key and the field. By default the extension key is
+ /// appended to the end of the returned string while the field is added to the
+ /// beginning. Special Query characters are escaped in the result.
+ /// <p>
+ /// Note: <see cref="Extensions"/> subclasses must maintain the contract between
+ /// <see cref="M:BuildExtensionField(String)"/> and
+ /// <see cref="M:BuildExtensionField(String, String)"/> where the latter inverts the
+ /// former.
+ /// </summary>
+ /// <param name="extensionKey">the extension key</param>
+ /// <param name="field">the field to apply the extension on.</param>
+ /// <returns>escaped extension field identifier</returns>
+ /// <remarks>See <see cref="M:BuildExtensionField(String)"/> to use the default query field</remarks>
+ public string BuildExtensionField(string extensionKey, string field)
+ {
+ StringBuilder builder = new StringBuilder(field);
+ builder.Append(this.extensionFieldDelimiter);
+ builder.Append(extensionKey);
+ return EscapeExtensionField(builder.ToString());
+ }
+
+ // NOTE: Pair<T, T> was eliminated in favor of the built in Tuple<T, T> type.
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.QueryParser/Ext/ParserExtension.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.QueryParser/Ext/ParserExtension.cs b/Lucene.Net.QueryParser/Ext/ParserExtension.cs
new file mode 100644
index 0000000..27b9212
--- /dev/null
+++ b/Lucene.Net.QueryParser/Ext/ParserExtension.cs
@@ -0,0 +1,50 @@
+\ufeffusing Lucene.Net.QueryParser.Classic;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+ /*
+ * 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>
+ /// This class represents an extension base class to the Lucene standard
+ /// <see cref="Classic.QueryParser"/>. The
+ /// <see cref="Classic.QueryParser"/> is generated by the JavaCC
+ /// parser generator. Changing or adding functionality or syntax in the standard
+ /// query parser requires changes to the JavaCC source file. To enable extending
+ /// the standard query parser without changing the JavaCC sources and re-generate
+ /// the parser the <see cref="ParserExtension"/> can be customized and plugged into an
+ /// instance of <see cref="ExtendableQueryParser"/>, a direct subclass of
+ /// <see cref="Classic.QueryParser"/>.
+ ///
+ /// <see cref="Extensions"/>
+ /// <see cref="ExtendableQueryParser"/>
+ /// </summary>
+ public abstract class ParserExtension
+ {
+ /// <summary>
+ /// Processes the given <see cref="ExtensionQuery"/> and returns a corresponding
+ /// <see cref="Query"/> instance. Subclasses must either return a <see cref="Query"/>
+ /// instance or raise a <see cref="ParseException"/>. This method must not return
+ /// <code>null</code>.
+ /// </summary>
+ /// <param name="query">the extension query</param>
+ /// <returns>a new query instance</returns>
+ /// <exception cref="ParseException">if the query can not be parsed.</exception>
+ public abstract Query Parse(ExtensionQuery query);
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
----------------------------------------------------------------------
diff --git a/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj b/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
index 0b18336..b42ed22 100644
--- a/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
+++ b/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
@@ -51,6 +51,10 @@
<Compile Include="Classic\Token.cs" />
<Compile Include="Classic\TokenMgrError.cs" />
<Compile Include="ComplexPhrase\ComplexPhraseQueryParser.cs" />
+ <Compile Include="Ext\ExtendableQueryParser.cs" />
+ <Compile Include="Ext\ExtensionQuery.cs" />
+ <Compile Include="Ext\Extensions.cs" />
+ <Compile Include="Ext\ParserExtension.cs" />
<Compile Include="Flexible\Standard\CommonQueryParserConfiguration.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.Tests.QueryParser/Ext/ExtensionStub.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Ext/ExtensionStub.cs b/Lucene.Net.Tests.QueryParser/Ext/ExtensionStub.cs
new file mode 100644
index 0000000..cbef5d8
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Ext/ExtensionStub.cs
@@ -0,0 +1,30 @@
+\ufeffusing Lucene.Net.Index;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+ /*
+ * 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.
+ */
+
+ internal class ExtensionStub : ParserExtension
+ {
+ public override Query Parse(ExtensionQuery components)
+ {
+ return new TermQuery(new Term(components.Field, components.RawQueryString));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.Tests.QueryParser/Ext/TestExtendableQueryParser.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Ext/TestExtendableQueryParser.cs b/Lucene.Net.Tests.QueryParser/Ext/TestExtendableQueryParser.cs
new file mode 100644
index 0000000..7e2e99e
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Ext/TestExtendableQueryParser.cs
@@ -0,0 +1,145 @@
+\ufeffusing Lucene.Net.Analysis;
+using Lucene.Net.QueryParser.Classic;
+using Lucene.Net.Search;
+using NUnit.Framework;
+using System.Globalization;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+ /*
+ * 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>
+ /// Testcase for the class <see cref="ExtendableQueryParser"/>
+ /// </summary>
+ [TestFixture]
+ public class TestExtendableQueryParser : TestQueryParser
+ {
+ private static char[] DELIMITERS = new char[] {
+ Extensions.DEFAULT_EXTENSION_FIELD_DELIMITER, '-', '|' };
+
+ public override Classic.QueryParser GetParser(Analyzer a)
+ {
+ return GetParser(a, null);
+ }
+
+ public Classic.QueryParser GetParser(Analyzer a, Extensions extensions)
+ {
+ if (a == null)
+ a = new MockAnalyzer(Random(), MockTokenizer.SIMPLE, true);
+ Classic.QueryParser qp = extensions == null ? new ExtendableQueryParser(
+ TEST_VERSION_CURRENT, DefaultField, a) : new ExtendableQueryParser(
+ TEST_VERSION_CURRENT, DefaultField, a, extensions);
+ qp.DefaultOperator = QueryParserBase.OR_OPERATOR;
+ return qp;
+ }
+
+ [Test]
+ public void TestUnescapedExtDelimiter()
+ {
+ Extensions ext = NewExtensions(':');
+ ext.Add("testExt", new ExtensionStub());
+ ExtendableQueryParser parser = (ExtendableQueryParser)GetParser(null, ext);
+ try
+ {
+ parser.Parse("aField:testExt:\"foo \\& bar\"");
+ fail("extension field delimiter is not escaped");
+ }
+ catch (ParseException e)
+ {
+ }
+ }
+
+ [Test]
+ public void TestExtFieldUnqoted()
+ {
+ for (int i = 0; i < DELIMITERS.Length; i++)
+ {
+ Extensions ext = NewExtensions(DELIMITERS[i]);
+ ext.Add("testExt", new ExtensionStub());
+ ExtendableQueryParser parser = (ExtendableQueryParser)GetParser(null,
+ ext);
+ string field = ext.BuildExtensionField("testExt", "aField");
+ Query query = parser.Parse(string.Format(CultureInfo.InvariantCulture, "{0}:foo bar", field));
+ assertTrue("expected instance of BooleanQuery but was "
+ + query.GetType(), query is BooleanQuery);
+ BooleanQuery bquery = (BooleanQuery)query;
+ BooleanClause[] clauses = bquery.Clauses;
+ assertEquals(2, clauses.Length);
+ BooleanClause booleanClause = clauses[0];
+ query = booleanClause.Query;
+ assertTrue("expected instance of TermQuery but was " + query.GetType(),
+ query is TermQuery);
+ TermQuery tquery = (TermQuery)query;
+ assertEquals("aField", tquery.Term
+ .Field);
+ assertEquals("foo", tquery.Term.Text());
+
+ booleanClause = clauses[1];
+ query = booleanClause.Query;
+ assertTrue("expected instance of TermQuery but was " + query.GetType(),
+ query is TermQuery);
+ tquery = (TermQuery)query;
+ assertEquals(DefaultField, tquery.Term.Field);
+ assertEquals("bar", tquery.Term.Text());
+ }
+ }
+
+ [Test]
+ public void TestExtDefaultField()
+ {
+ for (int i = 0; i < DELIMITERS.Length; i++)
+ {
+ Extensions ext = NewExtensions(DELIMITERS[i]);
+ ext.Add("testExt", new ExtensionStub());
+ ExtendableQueryParser parser = (ExtendableQueryParser)GetParser(null,
+ ext);
+ string field = ext.BuildExtensionField("testExt");
+ Query parse = parser.Parse(string.Format(CultureInfo.InvariantCulture, "{0}:\"foo \\& bar\"", field));
+ assertTrue("expected instance of TermQuery but was " + parse.GetType(),
+ parse is TermQuery);
+ TermQuery tquery = (TermQuery)parse;
+ assertEquals(DefaultField, tquery.Term.Field);
+ assertEquals("foo & bar", tquery.Term.Text());
+ }
+ }
+
+ public Extensions NewExtensions(char delimiter)
+ {
+ return new Extensions(delimiter);
+ }
+
+ [Test]
+ public void TestExtField()
+ {
+ for (int i = 0; i < DELIMITERS.Length; i++)
+ {
+ Extensions ext = NewExtensions(DELIMITERS[i]);
+ ext.Add("testExt", new ExtensionStub());
+ ExtendableQueryParser parser = (ExtendableQueryParser)GetParser(null,
+ ext);
+ string field = ext.BuildExtensionField("testExt", "afield");
+ Query parse = parser.Parse(string.Format(CultureInfo.InvariantCulture, "{0}:\"foo \\& bar\"", field));
+ assertTrue("expected instance of TermQuery but was " + parse.GetType(),
+ parse is TermQuery);
+ TermQuery tquery = (TermQuery)parse;
+ assertEquals("afield", tquery.Term.Field);
+ assertEquals("foo & bar", tquery.Term.Text());
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.Tests.QueryParser/Ext/TestExtensions.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Ext/TestExtensions.cs b/Lucene.Net.Tests.QueryParser/Ext/TestExtensions.cs
new file mode 100644
index 0000000..4850987
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Ext/TestExtensions.cs
@@ -0,0 +1,97 @@
+\ufeffusing Lucene.Net.Util;
+using NUnit.Framework;
+using System;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+ /*
+ * 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>
+ /// Testcase for the <see cref="Extensions"/> class
+ /// </summary>
+ [TestFixture]
+ public class TestExtensions : LuceneTestCase
+ {
+ private Extensions ext;
+
+ public override void SetUp()
+ {
+ base.SetUp();
+ this.ext = new Extensions();
+ }
+
+ [Test]
+ public void TestBuildExtensionField()
+ {
+ assertEquals("field\\:key", ext.BuildExtensionField("key", "field"));
+ assertEquals("\\:key", ext.BuildExtensionField("key"));
+
+ ext = new Extensions('.');
+ assertEquals("field.key", ext.BuildExtensionField("key", "field"));
+ assertEquals(".key", ext.BuildExtensionField("key"));
+ }
+
+ [Test]
+ public void TestSplitExtensionField()
+ {
+ assertEquals("field\\:key", ext.BuildExtensionField("key", "field"));
+ assertEquals("\\:key", ext.BuildExtensionField("key"));
+
+ ext = new Extensions('.');
+ assertEquals("field.key", ext.BuildExtensionField("key", "field"));
+ assertEquals(".key", ext.BuildExtensionField("key"));
+ }
+
+ [Test]
+ public void TestAddGetExtension()
+ {
+ ParserExtension extension = new ExtensionStub();
+ assertNull(ext.GetExtension("foo"));
+ ext.Add("foo", extension);
+ Assert.AreSame(extension, ext.GetExtension("foo"));
+ ext.Add("foo", null);
+ assertNull(ext.GetExtension("foo"));
+ }
+
+ [Test]
+ public void TestGetExtDelimiter()
+ {
+ assertEquals(Extensions.DEFAULT_EXTENSION_FIELD_DELIMITER, this.ext
+ .ExtensionFieldDelimiter);
+ ext = new Extensions('?');
+ assertEquals('?', this.ext.ExtensionFieldDelimiter);
+ }
+
+ [Test]
+ public void TestEscapeExtension()
+ {
+ assertEquals("abc\\:\\?\\{\\}\\[\\]\\\\\\(\\)\\+\\-\\!\\~", ext
+ .EscapeExtensionField("abc:?{}[]\\()+-!~"));
+ try
+ {
+ ext.EscapeExtensionField(null);
+ fail("should throw NPE - escape string is null");
+ }
+ //catch (NullPointerException e)
+ catch (Exception e)
+ {
+ //
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/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
index b263dc8..adab182 100644
--- a/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
+++ b/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
@@ -48,6 +48,9 @@
<Compile Include="Classic\TestMultiPhraseQueryParsing.cs" />
<Compile Include="Classic\TestQueryParser.cs" />
<Compile Include="ComplexPhrase\TestComplexPhraseQuery.cs" />
+ <Compile Include="Ext\ExtensionStub.cs" />
+ <Compile Include="Ext\TestExtendableQueryParser.cs" />
+ <Compile Include="Ext\TestExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Classic\TestMultiAnalyzer.cs" />
<Compile Include="Util\QueryParserTestBase.cs" />
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.Tests.QueryParser/Util/QueryParserTestBase.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Util/QueryParserTestBase.cs b/Lucene.Net.Tests.QueryParser/Util/QueryParserTestBase.cs
index bcf8792..b879008 100644
--- a/Lucene.Net.Tests.QueryParser/Util/QueryParserTestBase.cs
+++ b/Lucene.Net.Tests.QueryParser/Util/QueryParserTestBase.cs
@@ -114,7 +114,7 @@ namespace Lucene.Net.QueryParser.Util
}
// Moved from TestQueryParser
- public Classic.QueryParser GetParser(Analyzer a)
+ public virtual Classic.QueryParser GetParser(Analyzer a)
{
if (a == null) a = new MockAnalyzer(Random(), MockTokenizer.SIMPLE, true);
Classic.QueryParser qp = new Classic.QueryParser(TEST_VERSION_CURRENT, DefaultField, a);