You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2014/01/22 17:50:07 UTC

svn commit: r1560412 - in /lucene/dev/trunk/solr: ./ core/src/java/org/apache/solr/parser/ core/src/java/org/apache/solr/schema/ core/src/java/org/apache/solr/search/ core/src/test-files/solr/collection1/conf/ core/src/test/org/apache/solr/schema/ core...

Author: hossman
Date: Wed Jan 22 16:50:07 2014
New Revision: 1560412

URL: http://svn.apache.org/r1560412
Log:
SOLR-5594: Allow FieldTypes to specify custom PrefixQuery behavior

Added:
    lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-customfield.xml   (with props)
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/MyCrazyCustomField.java   (with props)
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/TrieIntPrefixActsAsRangeQueryFieldType.java   (with props)
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestOverriddenPrefixQueryForCustomFieldType.java   (with props)
Modified:
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/BinaryField.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/FieldType.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/PrefixQParserPlugin.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1560412&r1=1560411&r2=1560412&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Wed Jan 22 16:50:07 2014
@@ -151,6 +151,9 @@ New Features
 
 * SOLR-5476: Overseer Role for nodes (Noble Paul)
 
+* SOLR-5594: Allow FieldTypes to specify custom PrefixQuery behavior
+  (Anshum Gupta via hossman)
+
 Bug Fixes
 ----------------------
 

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java?rev=1560412&r1=1560411&r2=1560412&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java Wed Jan 22 16:50:07 2014
@@ -28,7 +28,6 @@ import org.apache.lucene.search.MatchAll
 import org.apache.lucene.search.MultiPhraseQuery;
 import org.apache.lucene.search.MultiTermQuery;
 import org.apache.lucene.search.PhraseQuery;
-import org.apache.lucene.search.PrefixQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.RegexpQuery;
 import org.apache.lucene.search.WildcardQuery;
@@ -419,10 +418,8 @@ public abstract class SolrQueryParserBas
    * @return new PrefixQuery instance
    */
   protected Query newPrefixQuery(Term prefix){
-    PrefixQuery query = new PrefixQuery(prefix);
     SchemaField sf = schema.getField(prefix.field());
-    query.setRewriteMethod(sf.getType().getRewriteMethod(parser, sf));
-    return query;
+    return sf.getType().getPrefixQuery(parser, sf, prefix.text());
   }
 
   /**

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/BinaryField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/BinaryField.java?rev=1560412&r1=1560411&r2=1560412&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/BinaryField.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/BinaryField.java Wed Jan 22 16:50:07 2014
@@ -21,8 +21,6 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 
 import org.apache.lucene.document.Field;
-import org.apache.lucene.index.GeneralField;
-import org.apache.lucene.index.IndexableField;
 import org.apache.lucene.index.StorableField;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.util.BytesRef;

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/FieldType.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/FieldType.java?rev=1560412&r1=1560411&r2=1560412&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/FieldType.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/FieldType.java Wed Jan 22 16:50:07 2014
@@ -35,6 +35,7 @@ import org.apache.lucene.search.DocTermO
 import org.apache.lucene.search.FieldCacheRangeFilter;
 import org.apache.lucene.search.FieldCacheRewriteMethod;
 import org.apache.lucene.search.MultiTermQuery;
+import org.apache.lucene.search.PrefixQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.TermQuery;
@@ -427,6 +428,25 @@ public abstract class FieldType extends 
     return getClass().getName();
   }
 
+
+  /**
+   * Returns a Query instance for doing prefix searches on this field type.
+   * Also, other QueryParser implementations may have different semantics.
+   * <p/>
+   * Sub-classes should override this method to provide their own range query implementation.
+   *
+   * @param parser       the {@link org.apache.solr.search.QParser} calling the method
+   * @param sf           the schema field
+   * @param termStr      the term string for prefix query
+   * @return a Query instance to perform prefix search
+   *
+   */
+  public Query getPrefixQuery(QParser parser, SchemaField sf, String termStr) {
+    PrefixQuery query = new PrefixQuery(new Term(sf.getName(), termStr));
+    query.setRewriteMethod(sf.getType().getRewriteMethod(parser, sf));
+    return query;
+  }
+
   /**
    * Default analyzer for types that only produce 1 verbatim token...
    * A maximum size of chars to be read must be specified

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/PrefixQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/PrefixQParserPlugin.java?rev=1560412&r1=1560411&r2=1560412&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/PrefixQParserPlugin.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/PrefixQParserPlugin.java Wed Jan 22 16:50:07 2014
@@ -22,6 +22,8 @@ import org.apache.lucene.search.Query;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.schema.SchemaField;
+
 /**
  * Create a prefix query from the input value.  Currently no analysis or
  * value transformation is done to create this prefix query (subject to change).
@@ -41,7 +43,8 @@ public class PrefixQParserPlugin extends
     return new QParser(qstr, localParams, params, req) {
       @Override
       public Query parse() {
-        return new PrefixQuery(new Term(localParams.get(QueryParsing.F), localParams.get(QueryParsing.V)));
+        SchemaField sf = req.getSchema().getField(localParams.get(QueryParsing.F));
+        return sf.getType().getPrefixQuery(this, sf, localParams.get(QueryParsing.V));
       }
     };
   }

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java?rev=1560412&r1=1560411&r2=1560412&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java Wed Jan 22 16:50:07 2014
@@ -32,6 +32,7 @@ import org.apache.solr.parser.QueryParse
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.schema.FieldType;
 import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.schema.SchemaField;
 import org.apache.solr.schema.TextField;
 import org.apache.solr.util.SolrPluginUtils;
 
@@ -112,97 +113,122 @@ public class SimpleQParserPlugin extends
   /** Returns a QParser that will create a query by using Lucene's SimpleQueryParser. */
   @Override
   public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
-    // Some of the parameters may come in through localParams, so combine them with params.
-    SolrParams defaultParams = SolrParams.wrapDefaults(localParams, params);
+    return new SimpleQParser(qstr, localParams, params, req);
+  }
 
-    // This will be used to specify what fields and boosts will be used by SimpleQueryParser.
-    Map<String, Float> queryFields = SolrPluginUtils.parseFieldBoosts(defaultParams.get(SimpleParams.QF));
+  private static class SimpleQParser extends QParser {
+    private SimpleQueryParser parser;
 
-    if (queryFields.isEmpty()) {
-      // It qf is not specified setup up the queryFields map to use the defaultField.
-      String defaultField = QueryParsing.getDefaultField(req.getSchema(), defaultParams.get(CommonParams.DF));
-
-      if (defaultField == null) {
-        // A query cannot be run without having a field or set of fields to run against.
-        throw new IllegalStateException("Neither " + SimpleParams.QF + ", " + CommonParams.DF
-            + ", nor the default search field are present.");
-      }
+    public SimpleQParser (String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
 
-      queryFields.put(defaultField, 1.0F);
-    }
-    else {
-      for (Map.Entry<String, Float> queryField : queryFields.entrySet()) {
-        if (queryField.getValue() == null) {
-          // Some fields may be specified without a boost, so default the boost to 1.0 since a null value
-          // will not be accepted by SimpleQueryParser.
-          queryField.setValue(1.0F);
+      super(qstr, localParams, params, req);
+      // Some of the parameters may come in through localParams, so combine them with params.
+      SolrParams defaultParams = SolrParams.wrapDefaults(localParams, params);
+
+      // This will be used to specify what fields and boosts will be used by SimpleQueryParser.
+      Map<String, Float> queryFields = SolrPluginUtils.parseFieldBoosts(defaultParams.get(SimpleParams.QF));
+
+      if (queryFields.isEmpty()) {
+        // It qf is not specified setup up the queryFields map to use the defaultField.
+        String defaultField = QueryParsing.getDefaultField(req.getSchema(), defaultParams.get(CommonParams.DF));
+
+        if (defaultField == null) {
+          // A query cannot be run without having a field or set of fields to run against.
+          throw new IllegalStateException("Neither " + SimpleParams.QF + ", " + CommonParams.DF
+              + ", nor the default search field are present.");
+        }
+
+        queryFields.put(defaultField, 1.0F);
+      }
+      else {
+        for (Map.Entry<String, Float> queryField : queryFields.entrySet()) {
+          if (queryField.getValue() == null) {
+            // Some fields may be specified without a boost, so default the boost to 1.0 since a null value
+            // will not be accepted by SimpleQueryParser.
+            queryField.setValue(1.0F);
+          }
         }
       }
-    }
 
-    // Setup the operations that are enabled for the query.
-    int enabledOps = 0;
-    String opParam = defaultParams.get(SimpleParams.QO);
-
-    if (opParam == null) {
-      // All operations will be enabled.
-      enabledOps = -1;
-    } else {
-      // Parse the specified enabled operations to be used by the query.
-      String[] operations = opParam.split(",");
+      // Setup the operations that are enabled for the query.
+      int enabledOps = 0;
+      String opParam = defaultParams.get(SimpleParams.QO);
+
+      if (opParam == null) {
+        // All operations will be enabled.
+        enabledOps = -1;
+      } else {
+        // Parse the specified enabled operations to be used by the query.
+        String[] operations = opParam.split(",");
 
-      for (String operation : operations) {
-        Integer enabledOp = OPERATORS.get(operation.trim().toUpperCase(Locale.ROOT));
+        for (String operation : operations) {
+          Integer enabledOp = OPERATORS.get(operation.trim().toUpperCase(Locale.ROOT));
 
-        if (enabledOp != null) {
-          enabledOps |= enabledOp;
+          if (enabledOp != null) {
+            enabledOps |= enabledOp;
+          }
         }
       }
-    }
 
-    // Create a SimpleQueryParser using the analyzer from the schema.
-    final IndexSchema schema = req.getSchema();
-    final SimpleQueryParser parser = new SimpleQueryParser(req.getSchema().getAnalyzer(), queryFields, enabledOps) {
-      // Override newPrefixQuery to provide a multi term analyzer for prefix queries run against TextFields.
-      @Override
-      protected Query newPrefixQuery(String text) {
-        BooleanQuery bq = new BooleanQuery(true);
-
-        for (Map.Entry<String, Float> entry : weights.entrySet()) {
-          String field = entry.getKey();
-          FieldType type = schema.getFieldType(field);
-          Query prefix;
-
-          if (type instanceof TextField) {
-            // If the field type is a TextField then use the multi term analyzer.
-            Analyzer analyzer = ((TextField)type).getMultiTermAnalyzer();
-            String term = TextField.analyzeMultiTerm(field, text, analyzer).utf8ToString();
-            prefix = new PrefixQuery(new Term(field, term));
-          } else {
-            // If the type is *not* a TextField don't do any analysis.
-            prefix = new PrefixQuery(new Term(entry.getKey(), text));
-          }
+      // Create a SimpleQueryParser using the analyzer from the schema.
+      final IndexSchema schema = req.getSchema();
+      parser = new SolrSimpleQueryParser(req.getSchema().getAnalyzer(), queryFields, enabledOps, this, schema);
 
-          prefix.setBoost(entry.getValue());
-          bq.add(prefix, BooleanClause.Occur.SHOULD);
-        }
+      // Set the default operator to be either 'AND' or 'OR' for the query.
+      QueryParser.Operator defaultOp = QueryParsing.getQueryParserDefaultOperator(req.getSchema(), defaultParams.get(QueryParsing.OP));
 
-        return simplify(bq);
+      if (defaultOp == QueryParser.Operator.AND) {
+        parser.setDefaultOperator(BooleanClause.Occur.MUST);
       }
-    };
+    }
+
+    @Override
+    public Query parse() throws SyntaxError {
+      return parser.parse(qstr);
+    }
 
-    // Set the default operator to be either 'AND' or 'OR' for the query.
-    QueryParser.Operator defaultOp = QueryParsing.getQueryParserDefaultOperator(req.getSchema(), defaultParams.get(QueryParsing.OP));
+  }
 
-    if (defaultOp == QueryParser.Operator.AND) {
-      parser.setDefaultOperator(BooleanClause.Occur.MUST);
+  private static class SolrSimpleQueryParser extends SimpleQueryParser {
+    QParser qParser;
+    IndexSchema schema;
+
+    public SolrSimpleQueryParser(Analyzer analyzer, Map<String, Float> weights, int flags,
+                                 QParser qParser, IndexSchema schema) {
+      super(analyzer, weights, flags);
+      this.qParser = qParser;
+      this.schema = schema;
     }
 
-    // Return a QParser that wraps a SimpleQueryParser.
-    return new QParser(qstr, localParams, params, req) {
-      public Query parse() throws SyntaxError {
-        return parser.parse(qstr);
+    @Override
+    protected Query newPrefixQuery(String text) {
+      BooleanQuery bq = new BooleanQuery(true);
+
+      for (Map.Entry<String, Float> entry : weights.entrySet()) {
+        String field = entry.getKey();
+        FieldType type = schema.getFieldType(field);
+        Query prefix;
+
+        if (type instanceof TextField) {
+          // If the field type is a TextField then use the multi term analyzer.
+          Analyzer analyzer = ((TextField)type).getMultiTermAnalyzer();
+          String term = TextField.analyzeMultiTerm(field, text, analyzer).utf8ToString();
+          SchemaField sf = schema.getField(field);
+          prefix = sf.getType().getPrefixQuery(qParser, sf, term);
+        } else {
+          // If the type is *not* a TextField don't do any analysis.
+          SchemaField sf = schema.getField(field);
+          prefix = type.getPrefixQuery(qParser, sf, text);
+        }
+
+        prefix.setBoost(entry.getValue());
+        bq.add(prefix, BooleanClause.Occur.SHOULD);
       }
-    };
+
+      return simplify(bq);
+    }
+
+
   }
 }
+

Added: lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-customfield.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-customfield.xml?rev=1560412&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-customfield.xml (added)
+++ lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-customfield.xml Wed Jan 22 16:50:07 2014
@@ -0,0 +1,69 @@
+<?xml version="1.0" ?>
+<!--
+ 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.
+-->
+
+<!-- The Solr schema file. This file should be named "schema.xml" and
+     should be located where the classloader for the Solr webapp can find it.
+
+     This schema is used for testing, and as such has everything and the
+     kitchen sink thrown in. See example/solr/conf/schema.xml for a
+     more concise example.
+
+  -->
+
+<schema name="test" version="1.2">
+  <types>
+
+    <!-- field type definitions... note that the "name" attribute is
+         just a label to be used by field definitions.  The "class"
+         attribute and any other attributes determine the real type and
+         behavior of the fieldtype.
+      -->
+
+    <!-- numeric field types that store and index the text
+         value verbatim (and hence don't sort correctly or support range queries.)
+         These are provided more for backward compatability, allowing one
+         to create a schema that matches an existing lucene index.
+    -->
+    <fieldType name="integer" class="solr.IntField"/>
+    <fieldType name="long" class="solr.LongField"/>
+    <fieldtype name="float" class="solr.FloatField"/>
+    <fieldType name="double" class="solr.DoubleField"/>
+    <fieldType name="customfield" class="org.apache.solr.schema.MyCrazyCustomField" multiValued="true" positionIncrementGap="100">
+    <analyzer>
+      <tokenizer class="solr.StandardTokenizerFactory"/>
+    </analyzer>
+    </fieldType>
+
+    <fieldType name="customtrieintfield" class="org.apache.solr.schema.TrieIntPrefixActsAsRangeQueryFieldType"/>
+
+ </types>
+
+ <fields>
+   <field name="id" type="integer" indexed="true" stored="true" multiValued="false" required="true"/>
+   <field name="intfield" type="integer" indexed="true" stored="true"/>
+   <field name="swap_foo_bar_in_prefix_query" type="customfield" indexed="true" stored="true" multiValued="true"/>
+   <field name="int_prefix_as_range" type="customtrieintfield" indexed="true" stored="true"/>
+
+   <field name="_version_" type="long" indexed="true" stored="true" multiValued="false" />
+
+ </fields>
+
+ <uniqueKey>id</uniqueKey>
+
+
+</schema>

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/MyCrazyCustomField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/MyCrazyCustomField.java?rev=1560412&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/MyCrazyCustomField.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/MyCrazyCustomField.java Wed Jan 22 16:50:07 2014
@@ -0,0 +1,61 @@
+package org.apache.solr.schema;
+
+/*
+ * 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.
+ */
+
+import org.apache.lucene.index.StorableField;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.PrefixQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.SortField;
+import org.apache.solr.response.TextResponseWriter;
+import org.apache.solr.search.QParser;
+
+import java.io.IOException;
+
+/**
+ * Custom field that overrides the PrefixQuery behaviour to map queries such that:
+ * (foo* becomes bar*) and (bar* becomes foo*).
+ * This is used for testing overridded prefix query for custom fields in TestOverriddenPrefixQueryForCustomFieldType
+ */
+public class MyCrazyCustomField extends TextField {
+
+
+  @Override
+  public void write(TextResponseWriter writer, String name, StorableField f) throws IOException {
+    writer.writeStr(name, f.stringValue(), true);
+  }
+
+  @Override
+  public SortField getSortField(final SchemaField field, final boolean reverse) {
+    field.checkSortability();
+    return getStringSort(field, reverse);
+  }
+
+  @Override
+  public Query getPrefixQuery(QParser parser, SchemaField sf, String termStr) {
+    if(termStr.equals("foo")) {
+      termStr = "bar";
+    } else if (termStr.equals("bar")) {
+      termStr = "foo";
+    }
+
+    PrefixQuery query = new PrefixQuery(new Term(sf.getName(), termStr));
+    query.setRewriteMethod(sf.getType().getRewriteMethod(parser, sf));
+    return query;
+  }
+}

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/TrieIntPrefixActsAsRangeQueryFieldType.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/TrieIntPrefixActsAsRangeQueryFieldType.java?rev=1560412&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/TrieIntPrefixActsAsRangeQueryFieldType.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/TrieIntPrefixActsAsRangeQueryFieldType.java Wed Jan 22 16:50:07 2014
@@ -0,0 +1,33 @@
+package org.apache.solr.schema;
+
+/*
+ * 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.
+ */
+
+import org.apache.lucene.search.Query;
+import org.apache.solr.search.QParser;
+
+/**
+ * Custom field type that overrides the prefix query behavior to map "X*" to [X TO Integer.MAX_VALUE].
+ * * This is used for testing overridded prefix query for custom fields in TestOverriddenPrefixQueryForCustomFieldType
+ */
+public class TrieIntPrefixActsAsRangeQueryFieldType extends TrieIntField {
+
+  public Query getPrefixQuery(QParser parser, SchemaField sf, String termStr) {
+    return getRangeQuery(parser, sf, termStr, new String(Integer.MAX_VALUE + ""), true, false);
+  }
+
+}

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestOverriddenPrefixQueryForCustomFieldType.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestOverriddenPrefixQueryForCustomFieldType.java?rev=1560412&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestOverriddenPrefixQueryForCustomFieldType.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestOverriddenPrefixQueryForCustomFieldType.java Wed Jan 22 16:50:07 2014
@@ -0,0 +1,152 @@
+package org.apache.solr.search;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.*;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.request.SolrRequestInfo;
+import org.apache.solr.response.SolrQueryResponse;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.Random;
+
+public class TestOverriddenPrefixQueryForCustomFieldType extends SolrTestCaseJ4 {
+
+  private static int[] counts= new int[2];
+  private static int otherCounts;
+  String[] otherTerms = {"this", "that", "those", "randomness"};
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("solrconfig-basic.xml", "schema-customfield.xml");
+  }
+
+  @Override
+  @Before
+  public void setUp() throws Exception {
+    // if you override setUp or tearDown, you better call
+    // the super classes version
+    super.setUp();
+    clearIndex();
+    assertU(commit());
+  }
+
+  public void createIndex(int nDocs) {
+    Random r = random();
+
+    for (int i=0; i<nDocs; i++) {
+      SolrInputDocument doc = new SolrInputDocument();
+      doc.addField("id", ""+i);
+      int t = r.nextInt(1000);
+      if(t%3 == 0) {
+        doc.addField("swap_foo_bar_in_prefix_query", "foo" + i);
+        counts[0]++;
+      } else if(t%3 == 1) {
+        doc.addField("swap_foo_bar_in_prefix_query", "foo" + i);
+        doc.addField("swap_foo_bar_in_prefix_query", "spam" + i);
+        otherCounts++;
+        counts[0]++;
+      } else {
+        doc.addField("swap_foo_bar_in_prefix_query", "bar" + i);
+        counts[1]++;
+      }
+      //Randomly add noise
+
+      doc.addField("int_prefix_as_range", i);
+      doc.addField("intfield", i);
+
+      assertU(adoc(doc));
+    }
+    assertU(commit());
+  }
+
+  @Test
+  public void testPrefixQueries() throws Exception {
+    createIndex(100);
+    assertQ(req("fl", "id", "q", "*:*"), "//*[@numFound='100']");
+
+    // Test that prefix query actually transforms foo <-> bar.
+    assertQ(req("q", "swap_foo_bar_in_prefix_query:foo*"), "//*[@numFound='" + counts[1] + "']");
+
+    assertQ(req("q", "swap_foo_bar_in_prefix_query:bar*"), "//*[@numFound='" + counts[0] + "']");
+    assertQ(req("q", "swap_foo_bar_in_prefix_query:spam*"), "//*[@numFound='" + otherCounts + "']");
+
+    assertQ(req("q", "intfield:2*"), "//*[@numFound='11']"); //2 and the 10 in twenties
+
+    //Custom field should query for the range [2,MAX_INT)
+    assertQ(req("q", "int_prefix_as_range:2*"),"//*[@numFound='98']");
+
+  }
+
+  @Test
+  public void testQuery() throws Exception {
+    SolrQueryRequest req = req("myField","swap_foo_bar_in_prefix_query");
+
+    try {
+      assertQueryEquals(req,
+          "{!simple qf=$myField}foo*",
+          "{!simple qf=$myField}foo*",
+          "{!prefix f=swap_foo_bar_in_prefix_query}foo",
+          "{!lucene df=$myField v=foo*}",
+          "{!lucene}swap_foo_bar_in_prefix_query:foo*");
+
+      req.close();
+      req = req("myField", "int_prefix_as_range");
+      assertQueryEquals(req,
+          "{!lucene}int_prefix_as_range:[42 TO 2147483647}",
+          "{!lucene}int_prefix_as_range:42*",
+          "{!prefix f=int_prefix_as_range}42",
+          "{!simple qf=int_prefix_as_range}42*",
+          "{!simple df=int_prefix_as_range}42*");
+
+    } finally {
+      req.close();
+    }
+  }
+
+  /**
+   * @see org.apache.lucene.search.QueryUtils#check
+   * @see org.apache.lucene.search.QueryUtils#checkEqual
+   */
+  protected void assertQueryEquals(final SolrQueryRequest req,
+                                   final String... inputs) throws Exception {
+
+    final Query[] queries = new Query[inputs.length];
+
+    try {
+      SolrQueryResponse rsp = new SolrQueryResponse();
+      SolrRequestInfo.setRequestInfo(new SolrRequestInfo(req, rsp));
+      for (int i = 0; i < inputs.length; i++) {
+        queries[i] = (QParser.getParser(inputs[i], null, req).getQuery());
+      }
+    } finally {
+      SolrRequestInfo.clearRequestInfo();
+    }
+
+    for (int i = 0; i < queries.length; i++) {
+      org.apache.lucene.search.QueryUtils.check(queries[i]);
+      for (int j = i; j < queries.length; j++) {
+        org.apache.lucene.search.QueryUtils.checkEqual(queries[i], queries[j]);
+      }
+    }
+  }
+}