You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by yo...@apache.org on 2007/10/22 15:43:08 UTC

svn commit: r587090 [1/2] - in /lucene/solr/trunk: ./ src/java/org/apache/solr/core/ src/java/org/apache/solr/handler/ src/java/org/apache/solr/schema/ src/java/org/apache/solr/search/ src/test/org/apache/solr/search/

Author: yonik
Date: Mon Oct 22 06:43:07 2007
New Revision: 587090

URL: http://svn.apache.org/viewvc?rev=587090&view=rev
Log:
SOLR-334: pluggable query parsers

Added:
    lucene/solr/trunk/src/java/org/apache/solr/search/BoostQParserPlugin.java   (with props)
    lucene/solr/trunk/src/java/org/apache/solr/search/DisMaxQParserPlugin.java   (with props)
    lucene/solr/trunk/src/java/org/apache/solr/search/FieldQParserPlugin.java
    lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParser.java   (with props)
    lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParserPlugin.java
    lucene/solr/trunk/src/java/org/apache/solr/search/LuceneQParserPlugin.java   (with props)
    lucene/solr/trunk/src/java/org/apache/solr/search/OldLuceneQParserPlugin.java   (with props)
    lucene/solr/trunk/src/java/org/apache/solr/search/PrefixQParserPlugin.java   (with props)
    lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java   (with props)
    lucene/solr/trunk/src/java/org/apache/solr/search/QParserPlugin.java   (with props)
    lucene/solr/trunk/src/java/org/apache/solr/search/RawQParserPlugin.java
    lucene/solr/trunk/src/test/org/apache/solr/search/TestQueryTypes.java   (with props)
Modified:
    lucene/solr/trunk/CHANGES.txt
    lucene/solr/trunk/src/java/org/apache/solr/core/SolrCore.java
    lucene/solr/trunk/src/java/org/apache/solr/handler/StandardRequestHandler.java
    lucene/solr/trunk/src/java/org/apache/solr/schema/FieldType.java
    lucene/solr/trunk/src/java/org/apache/solr/schema/IndexSchema.java
    lucene/solr/trunk/src/java/org/apache/solr/search/QueryParsing.java
    lucene/solr/trunk/src/java/org/apache/solr/search/SolrQueryParser.java

Modified: lucene/solr/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/CHANGES.txt?rev=587090&r1=587089&r2=587090&view=diff
==============================================================================
--- lucene/solr/trunk/CHANGES.txt (original)
+++ lucene/solr/trunk/CHANGES.txt Mon Oct 22 06:43:07 2007
@@ -136,6 +136,9 @@
     to the detailed field information from the solrj client API.
     (Grant Ingersoll via ehatcher)
 
+26. SOLR-334L Pluggable query parsers.  Allows specification of query
+    type and arguments as a prefix on a query string. (yonik)
+
 Changes in runtime behavior
 
 Optimizations

Modified: lucene/solr/trunk/src/java/org/apache/solr/core/SolrCore.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/core/SolrCore.java?rev=587090&r1=587089&r2=587090&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/core/SolrCore.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/core/SolrCore.java Mon Oct 22 06:43:07 2007
@@ -53,6 +53,9 @@
 import org.apache.solr.request.XMLResponseWriter;
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.search.QParserPlugin;
+import org.apache.solr.search.LuceneQParserPlugin;
+import org.apache.solr.search.OldLuceneQParserPlugin;
 import org.apache.solr.update.DirectUpdateHandler;
 import org.apache.solr.update.SolrIndexWriter;
 import org.apache.solr.update.UpdateHandler;
@@ -289,7 +292,8 @@
       initIndex();
       
       initWriters();
-      
+      initQParsers();
+
       // Processors initialized before the handlers
       updateProcessors = loadUpdateProcessors();
       reqHandlers = new RequestHandlers(this);
@@ -908,6 +912,38 @@
    */
   public final QueryResponseWriter getQueryResponseWriter(SolrQueryRequest request) {
     return getQueryResponseWriter(request.getParam("wt")); 
+  }
+
+  private final Map<String, QParserPlugin> qParserPlugins = new HashMap<String, QParserPlugin>();
+
+  /** Configure the query parsers. */
+  private void initQParsers() {
+    String xpath = "queryParser";
+    NodeList nodes = (NodeList) solrConfig.evaluate(xpath, XPathConstants.NODESET);
+
+    NamedListPluginLoader<QParserPlugin> loader =
+      new NamedListPluginLoader<QParserPlugin>( "[solrconfig.xml] "+xpath, qParserPlugins);
+
+    loader.load( solrConfig, nodes );
+
+    // default parsers
+    for (int i=0; i<QParserPlugin.standardPlugins.length; i+=2) {
+     try {
+       String name = (String)QParserPlugin.standardPlugins[i];
+       Class<QParserPlugin> clazz = (Class<QParserPlugin>)QParserPlugin.standardPlugins[i+1];
+       QParserPlugin plugin = clazz.newInstance();
+       qParserPlugins.put(name, plugin);
+       plugin.init(null);
+     } catch (Exception e) {
+       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
+     }
+    }
+  }
+
+  public QParserPlugin getQueryPlugin(String parserName) {
+    QParserPlugin plugin = qParserPlugins.get(parserName);
+    if (plugin != null) return plugin;
+    throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown query type '"+parserName+"'");
   }
 }
 

Modified: lucene/solr/trunk/src/java/org/apache/solr/handler/StandardRequestHandler.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/handler/StandardRequestHandler.java?rev=587090&r1=587089&r2=587090&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/handler/StandardRequestHandler.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/handler/StandardRequestHandler.java Mon Oct 22 06:43:07 2007
@@ -35,7 +35,6 @@
 import org.apache.solr.common.params.MoreLikeThisParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.NamedList;
-import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.highlight.SolrHighlighter;
 
@@ -72,58 +71,31 @@
       SolrParams p = req.getParams();
       String qstr = p.required().get(CommonParams.Q);
 
-      String defaultField = p.get(CommonParams.DF);
-
       // find fieldnames to return (fieldlist)
+      // TODO: make this per-query and add method to QParser to get?
       String fl = p.get(CommonParams.FL);
       int flags = 0; 
       if (fl != null) {
         flags |= U.setReturnFields(fl, rsp);
       }
-      
-      String sortStr = p.get(CommonParams.SORT);
-      if( sortStr == null ) {  
-        // TODO? should we disable the ';' syntax with config?
-        // legacy mode, where sreq is query;sort
-        List<String> commands = StrUtils.splitSmart(qstr,';');
-        if( commands.size() == 2 ) {
-          // TODO? add a deprication warning to the response header
-          qstr = commands.get( 0 );
-          sortStr = commands.get( 1 );
-        }
-        else if( commands.size() == 1 ) {
-          // This is need to support the case where someone sends: "q=query;"
-          qstr = commands.get( 0 );
-        }
-        else if( commands.size() > 2 ) {
-          throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "If you want to use multiple ';' in the query, use the 'sort' param." );
-        }
-      }
 
-      Sort sort = null;
-      if( sortStr != null ) {
-        QueryParsing.SortSpec sortSpec = QueryParsing.parseSort(sortStr, req.getSchema());
-        if (sortSpec != null) {
-          sort = sortSpec.getSort();
-        }
-      }
+      QParser parser = QParser.getParser(qstr, OldLuceneQParserPlugin.NAME, req);
+      Query query = parser.getQuery();
+      QueryParsing.SortSpec sortSpec = parser.getSort(true);
 
-      // parse the query from the 'q' parameter (sort has been striped)
-      Query query = QueryParsing.parseQuery(qstr, defaultField, p, req.getSchema());
-      
       DocListAndSet results = new DocListAndSet();
       NamedList facetInfo = null;
       List<Query> filters = U.parseFilterQueries(req);
       SolrIndexSearcher s = req.getSearcher();
 
       if (p.getBool(FacetParams.FACET,false)) {
-        results = s.getDocListAndSet(query, filters, sort,
-                                     p.getInt(CommonParams.START,0), p.getInt(CommonParams.ROWS,10),
+        results = s.getDocListAndSet(query, filters, sortSpec.getSort(),
+                                     sortSpec.getOffset(), sortSpec.getCount(),
                                      flags);
         facetInfo = getFacetInfo(req, rsp, results.docSet);
       } else {
-        results.docList = s.getDocList(query, filters, sort,
-                                       p.getInt(CommonParams.START,0), p.getInt(CommonParams.ROWS,10),
+        results.docList = s.getDocList(query, filters, sortSpec.getSort(),
+                                       sortSpec.getOffset(), sortSpec.getCount(),
                                        flags);
       }
 
@@ -163,7 +135,10 @@
 
       SolrHighlighter highlighter = req.getCore().getHighlighter();
       NamedList sumData = highlighter.doHighlighting(
-        results.docList, query.rewrite(req.getSearcher().getReader()), req, new String[]{defaultField});
+            results.docList,
+            parser.getHighlightQuery().rewrite(req.getSearcher().getReader()),
+            req,
+            parser.getDefaultHighlightFields());
       if(sumData != null)
         rsp.add("highlighting", sumData);
   }

Modified: lucene/solr/trunk/src/java/org/apache/solr/schema/FieldType.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/schema/FieldType.java?rev=587090&r1=587089&r2=587090&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/schema/FieldType.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/schema/FieldType.java Mon Oct 22 06:43:07 2007
@@ -27,6 +27,7 @@
 import org.apache.solr.search.function.ValueSource;
 import org.apache.solr.search.function.OrdFieldSource;
 import org.apache.solr.search.Sorting;
+import org.apache.solr.search.QParser;
 import org.apache.solr.request.XMLWriter;
 import org.apache.solr.request.TextResponseWriter;
 import org.apache.solr.analysis.SolrAnalyzer;
@@ -69,6 +70,14 @@
   protected void init(IndexSchema schema, Map<String,String> args) {
   }
 
+  protected String getArg(String n, Map<String,String> args) {
+    String s = args.remove(n);
+    if (s == null) {
+      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Mising parameter '"+n+"' for FieldType=" + typeName +args);
+    }
+    return s;
+  }
+
   // Handle additional arguments...
   void setArgs(IndexSchema schema, Map<String,String> args) {
     // default to STORED and INDEXED, and MULTIVALUED depending on schema version
@@ -394,7 +403,13 @@
   /** called to get the default value source (normally, from the
    *  Lucene FieldCache.)
    */
+  public ValueSource getValueSource(SchemaField field, QParser parser) {
+    return getValueSource(field);
+  }
+
+  @Deprecated
   public ValueSource getValueSource(SchemaField field) {
     return new OrdFieldSource(field.name);
   }
+
 }

Modified: lucene/solr/trunk/src/java/org/apache/solr/schema/IndexSchema.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/schema/IndexSchema.java?rev=587090&r1=587089&r2=587090&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/schema/IndexSchema.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/schema/IndexSchema.java Mon Oct 22 06:43:07 2007
@@ -317,7 +317,7 @@
       version = schemaConf.getFloat("/schema/@version", 1.0f);
 
       final IndexSchema schema = this;
-      AbstractPluginLoader<FieldType> loader = new AbstractPluginLoader<FieldType>( "[schema.xml] fieldType" ) {
+      AbstractPluginLoader<FieldType> loader = new AbstractPluginLoader<FieldType>( "[schema.xml] fieldType", true, true) {
 
         @Override
         protected FieldType create( Config config, String name, String className, Node node ) throws Exception

Added: lucene/solr/trunk/src/java/org/apache/solr/search/BoostQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/BoostQParserPlugin.java?rev=587090&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/BoostQParserPlugin.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/BoostQParserPlugin.java Mon Oct 22 06:43:07 2007
@@ -0,0 +1,70 @@
+/**
+ * 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.
+ */
+package org.apache.solr.search;
+
+import org.apache.lucene.queryParser.ParseException;
+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.search.function.BoostedQuery;
+import org.apache.solr.search.function.FunctionQuery;
+import org.apache.solr.search.function.QueryValueSource;
+import org.apache.solr.search.function.ValueSource;
+
+/**
+ * Create a boosted query from the input value.  The main value is the query to be boosted.
+ * <br>Other parameters: <code>b</code>, the function query to use as the boost.
+ * <br>Example: <code>&lt;!boost b=log(popularity)&gt;foo</code> creates a query "foo"
+ * which is boosted (scores are multiplied) by the function query <code>log(popularity</code>.
+ * The query to be boosted may be of any type.
+ */
+public class BoostQParserPlugin extends QParserPlugin {
+  public static String NAME = "boost";
+  public static String BOOSTFUNC = "b";
+
+  public void init(NamedList args) {
+  }
+
+  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    return new QParser(qstr, localParams, params, req) {
+      QParser baseParser;
+
+      public Query parse() throws ParseException {
+        String b = localParams.get(BOOSTFUNC);
+        baseParser = subQuery(localParams.get(QueryParsing.V), null);
+        Query q = baseParser.parse();
+
+        if (b == null) return q;
+        Query bq = subQuery(b, FunctionQParserPlugin.NAME).parse();
+        ValueSource vs;
+        if (bq instanceof FunctionQuery) {
+          vs = ((FunctionQuery)bq).getValueSource();
+        } else {
+          vs = new QueryValueSource(q, 0.0f);
+        }
+        return new BoostedQuery(q, vs);
+      }
+
+
+      public String[] getDefaultHighlightFields() {
+        return baseParser.getDefaultHighlightFields();
+      }
+    };
+  }
+
+}

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/BoostQParserPlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/BoostQParserPlugin.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/BoostQParserPlugin.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: lucene/solr/trunk/src/java/org/apache/solr/search/DisMaxQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/DisMaxQParserPlugin.java?rev=587090&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/DisMaxQParserPlugin.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/DisMaxQParserPlugin.java Mon Oct 22 06:43:07 2007
@@ -0,0 +1,231 @@
+/**
+ * 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.
+ */
+package org.apache.solr.search;
+
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.DefaultSolrParams;
+import org.apache.solr.common.params.DisMaxParams;
+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.IndexSchema;
+import org.apache.solr.util.SolrPluginUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+/**
+ * Create a dismax query from the input value.
+ * <br>Other parameters: all main query related parameters from the {@link org.apache.solr.handler.DisMaxRequestHandler} are supported.
+ * localParams are checked before global request params.
+ * <br>Example: <code>&lt;!dismax qf=myfield,mytitle^2&gt;foo</code> creates a dismax query across
+ * across myfield and mytitle, with a higher weight on mytitle.
+ */
+public class DisMaxQParserPlugin extends QParserPlugin {
+  public static String NAME = "dismax";
+
+  public void init(NamedList args) {
+  }
+
+  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    return new DismaxQParser(qstr, localParams, params, req);
+  }
+}
+
+
+class DismaxQParser extends QParser {
+
+  /**
+   * A field we can't ever find in any schema, so we can safely tell
+   * DisjunctionMaxQueryParser to use it as our defaultField, and
+   * map aliases from it to any field in our schema.
+   */
+  private static String IMPOSSIBLE_FIELD_NAME = "\uFFFC\uFFFC\uFFFC";
+
+  /** shorten the class references for utilities */
+  private static class U extends SolrPluginUtils {
+    /* :NOOP */
+  }
+
+  /** shorten the class references for utilities */
+  private static interface DMP extends DisMaxParams {
+    /* :NOOP */
+  }
+
+
+  public DismaxQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    super(qstr, localParams, params, req);
+  }
+
+  Map<String,Float> queryFields;
+  Query parsedUserQuery;
+
+  public Query parse() throws ParseException {
+    SolrParams solrParams = localParams == null ? params : new DefaultSolrParams(localParams, params);
+
+    IndexSchema schema = req.getSchema();
+
+    queryFields = U.parseFieldBoosts(solrParams.getParams(DMP.QF));
+    Map<String,Float> phraseFields = U.parseFieldBoosts(solrParams.getParams(DMP.PF));
+
+    float tiebreaker = solrParams.getFloat(DMP.TIE, 0.0f);
+
+    int pslop = solrParams.getInt(DMP.PS, 0);
+    int qslop = solrParams.getInt(DMP.QS, 0);
+
+    /* a generic parser for parsing regular lucene queries */
+    QueryParser p = schema.getSolrQueryParser(null);
+
+    /* a parser for dealing with user input, which will convert
+     * things to DisjunctionMaxQueries
+     */
+    U.DisjunctionMaxQueryParser up =
+      new U.DisjunctionMaxQueryParser(schema, IMPOSSIBLE_FIELD_NAME);
+    up.addAlias(IMPOSSIBLE_FIELD_NAME,
+                tiebreaker, queryFields);
+    up.setPhraseSlop(qslop);
+
+    /* for parsing sloppy phrases using DisjunctionMaxQueries */
+    U.DisjunctionMaxQueryParser pp =
+      new U.DisjunctionMaxQueryParser(schema, IMPOSSIBLE_FIELD_NAME);
+    pp.addAlias(IMPOSSIBLE_FIELD_NAME,
+                tiebreaker, phraseFields);
+    pp.setPhraseSlop(pslop);
+
+
+    /* the main query we will execute.  we disable the coord because
+     * this query is an artificial construct
+     */
+    BooleanQuery query = new BooleanQuery(true);
+
+    /* * * Main User Query * * */
+    parsedUserQuery = null;
+    String userQuery = getString();
+    Query altUserQuery = null;
+    if( userQuery == null || userQuery.trim().length() < 1 ) {
+      // If no query is specified, we may have an alternate
+      String altQ = solrParams.get( DMP.ALTQ );
+      if (altQ != null) {
+        altUserQuery = p.parse(altQ);
+        query.add( altUserQuery , BooleanClause.Occur.MUST );
+      } else {
+        throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "missing query string" );
+      }
+    }
+    else {
+      // There is a valid query string
+      userQuery = U.partialEscape(U.stripUnbalancedQuotes(userQuery)).toString();
+
+      String minShouldMatch = solrParams.get(DMP.MM, "100%");
+      Query dis = up.parse(userQuery);
+      parsedUserQuery = dis;
+
+      if (dis instanceof BooleanQuery) {
+        BooleanQuery t = new BooleanQuery();
+        U.flattenBooleanQuery(t, (BooleanQuery)dis);
+        U.setMinShouldMatch(t, minShouldMatch);
+        parsedUserQuery = t;
+      }
+      query.add(parsedUserQuery, BooleanClause.Occur.MUST);
+
+
+      /* * * Add on Phrases for the Query * * */
+
+      /* build up phrase boosting queries */
+
+      /* if the userQuery already has some quotes, stip them out.
+       * we've already done the phrases they asked for in the main
+       * part of the query, this is to boost docs that may not have
+       * matched those phrases but do match looser phrases.
+       */
+      String userPhraseQuery = userQuery.replace("\"","");
+      Query phrase = pp.parse("\"" + userPhraseQuery + "\"");
+      if (null != phrase) {
+        query.add(phrase, BooleanClause.Occur.SHOULD);
+      }
+    }
+
+
+    /* * * Boosting Query * * */
+    String[] boostParams = solrParams.getParams(DMP.BQ);
+    //List<Query> boostQueries = U.parseQueryStrings(req, boostParams);
+    List<Query> boostQueries=null;
+    if (boostParams!=null && boostParams.length>0) {
+      boostQueries = new ArrayList<Query>();
+      for (String qs : boostParams) {
+        Query q = subQuery(qs, null).parse();
+        boostQueries.add(q);
+      }
+    }
+    if (null != boostQueries) {
+      if(1 == boostQueries.size() && 1 == boostParams.length) {
+        /* legacy logic */
+        Query f = boostQueries.get(0);
+        if (1.0f == f.getBoost() && f instanceof BooleanQuery) {
+          /* if the default boost was used, and we've got a BooleanQuery
+           * extract the subqueries out and use them directly
+           */
+          for (Object c : ((BooleanQuery)f).clauses()) {
+            query.add((BooleanClause)c);
+          }
+        } else {
+          query.add(f, BooleanClause.Occur.SHOULD);
+        }
+      } else {
+        for(Query f : boostQueries) {
+          query.add(f, BooleanClause.Occur.SHOULD);
+        }
+      }
+    }
+
+    /* * * Boosting Functions * * */
+
+    String[] boostFuncs = solrParams.getParams(DMP.BF);
+    if (null != boostFuncs && 0 != boostFuncs.length) {
+      for (String boostFunc : boostFuncs) {
+        if(null == boostFunc || "".equals(boostFunc)) continue;
+        Map<String,Float> ff = SolrPluginUtils.parseFieldBoosts(boostFunc);
+        for (String f : ff.keySet()) {
+          Query fq = subQuery(f, FunctionQParserPlugin.NAME).parse();
+          Float b = ff.get(f);
+          if (null != b) {
+            fq.setBoost(b);
+          }
+          query.add(fq, BooleanClause.Occur.SHOULD);
+        }
+      }
+    }
+
+    return query;
+  }
+
+  @Override
+  public String[] getDefaultHighlightFields() {
+    String[] highFields = queryFields.keySet().toArray(new String[0]);
+    return highFields;
+  }
+
+  @Override
+  public Query getHighlightQuery() throws ParseException {
+    return parsedUserQuery;
+  }
+}

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/DisMaxQParserPlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/DisMaxQParserPlugin.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/DisMaxQParserPlugin.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: lucene/solr/trunk/src/java/org/apache/solr/search/FieldQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/FieldQParserPlugin.java?rev=587090&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/FieldQParserPlugin.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/FieldQParserPlugin.java Mon Oct 22 06:43:07 2007
@@ -0,0 +1,143 @@
+/**
+ * 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.
+ */
+package org.apache.solr.search;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.Token;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.search.*;
+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.FieldType;
+import org.apache.solr.schema.TextField;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+
+
+/**
+ * Create a field query from the input value, applying text analysis and constructing a phrase query if appropriate.
+ * <br>Other parameters: <code>f</code>, the field
+ * <br>Example: <code>&lt;!field f=myfield&gt;Foo Bar</code> creates a phrase query with "foo" followed by "bar"
+ * if the analyzer for myfield is a text field with an analyzer that splits on whitespace and lowercases terms.
+ * This is generally equivalent to the lucene query parser expression <code>myfield:"Foo Bar"</code>
+ */
+public class FieldQParserPlugin extends QParserPlugin {
+  public static String NAME = "field";
+
+  public void init(NamedList args) {
+  }
+
+  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    return new QParser(qstr, localParams, params, req) {
+      public Query parse() throws ParseException {
+        String field = localParams.get(QueryParsing.F);
+        String queryText = localParams.get(QueryParsing.V);
+        FieldType ft = req.getSchema().getFieldType(field);
+        if (!(ft instanceof TextField)) {
+          String internal = ft.toInternal(queryText);
+          return new TermQuery(new Term(field, internal));
+        }
+
+        int phraseSlop = 0;
+        Analyzer analyzer = req.getSchema().getQueryAnalyzer();
+
+        // most of the following code is taken from the Lucene QueryParser
+
+        // Use the analyzer to get all the tokens, and then build a TermQuery,
+        // PhraseQuery, or nothing based on the term count
+
+        TokenStream source = analyzer.tokenStream(field, new StringReader(queryText));
+        ArrayList<Token> lst = new ArrayList<Token>();
+        Token t;
+        int positionCount = 0;
+        boolean severalTokensAtSamePosition = false;
+
+        while (true) {
+          try {
+            t = source.next();
+          }
+          catch (IOException e) {
+            t = null;
+          }
+          if (t == null)
+            break;
+          lst.add(t);
+          if (t.getPositionIncrement() != 0)
+            positionCount += t.getPositionIncrement();
+          else
+            severalTokensAtSamePosition = true;
+        }
+        try {
+          source.close();
+        }
+        catch (IOException e) {
+          // ignore
+        }
+
+        if (lst.size() == 0)
+          return null;
+        else if (lst.size() == 1) {
+          t = lst.get(0);
+          return new TermQuery(new Term(field, t.termText()));
+        } else {
+          if (severalTokensAtSamePosition) {
+            if (positionCount == 1) {
+              // no phrase query:
+              BooleanQuery q = new BooleanQuery(true);
+              for (int i = 0; i < lst.size(); i++) {
+                t = (org.apache.lucene.analysis.Token) lst.get(i);
+                TermQuery currentQuery = new TermQuery(
+                        new Term(field, t.termText()));
+                q.add(currentQuery, BooleanClause.Occur.SHOULD);
+              }
+              return q;
+            }
+            else {
+              // phrase query:
+              MultiPhraseQuery mpq = new MultiPhraseQuery();
+              mpq.setSlop(phraseSlop);
+              ArrayList multiTerms = new ArrayList();
+              for (int i = 0; i < lst.size(); i++) {
+                t = (org.apache.lucene.analysis.Token) lst.get(i);
+                if (t.getPositionIncrement() == 1 && multiTerms.size() > 0) {
+                  mpq.add((Term[])multiTerms.toArray(new Term[0]));
+                  multiTerms.clear();
+                }
+                multiTerms.add(new Term(field, t.termText()));
+              }
+              mpq.add((Term[])multiTerms.toArray(new Term[0]));
+              return mpq;
+            }
+          }
+          else {
+            PhraseQuery q = new PhraseQuery();
+            q.setSlop(phraseSlop);
+            for (int i = 0; i < lst.size(); i++) {
+              q.add(new Term(field, lst.get(i).termText()));
+            }
+            return q;
+          }
+        }
+      }
+    };
+  }
+}

Added: lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParser.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParser.java?rev=587090&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParser.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParser.java Mon Oct 22 06:43:07 2007
@@ -0,0 +1,287 @@
+/**
+ * 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.
+ */
+package org.apache.solr.search;
+
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.search.Query;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.search.function.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FunctionQParser extends QParser {
+  public FunctionQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    super(qstr, localParams, params, req);
+  }
+
+  QueryParsing.StrParser sp;
+
+  public Query parse() throws ParseException {
+    sp = new QueryParsing.StrParser(getString());
+    ValueSource vs = parseValSource();
+
+    /***  boost promoted to top-level query type to avoid this hack 
+
+    // HACK - if this is a boosted query wrapped in a value-source, return
+    // that boosted query instead of a FunctionQuery
+    if (vs instanceof QueryValueSource) {
+      Query q = ((QueryValueSource)vs).getQuery();
+      if (q instanceof BoostedQuery) return q;
+    }
+    ***/
+
+    return new FunctionQuery(vs);
+  }
+
+  private abstract static class VSParser {
+    abstract ValueSource parse(FunctionQParser fp) throws ParseException;
+  }
+
+  private static Map<String, VSParser> vsParsers = new HashMap<String, VSParser>();
+  static {
+    vsParsers.put("ord", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        String field = fp.sp.getId();
+        return new OrdFieldSource(field);
+      }
+    });
+    vsParsers.put("rord", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        String field = fp.sp.getId();
+        return new ReverseOrdFieldSource(field);
+      }
+    });
+    vsParsers.put("linear", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        ValueSource source = fp.parseValSource();
+        fp.sp.expect(",");
+        float slope = fp.sp.getFloat();
+        fp.sp.expect(",");
+        float intercept = fp.sp.getFloat();
+        return new LinearFloatFunction(source,slope,intercept);
+      }
+    });
+    vsParsers.put("max", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        ValueSource source = fp.parseValSource();
+        fp.sp.expect(",");
+        float val = fp.sp.getFloat();
+        return new MaxFloatFunction(source,val);
+      }
+    });
+    vsParsers.put("recip", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        ValueSource source = fp.parseValSource();
+        fp.sp.expect(",");
+        float m = fp.sp.getFloat();
+        fp.sp.expect(",");
+        float a = fp.sp.getFloat();
+        fp.sp.expect(",");
+        float b = fp.sp.getFloat();
+        return new ReciprocalFloatFunction(source,m,a,b);
+      }
+    });
+    vsParsers.put("scale", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        ValueSource source = fp.parseValSource();
+        fp.sp.expect(",");
+        float min = fp.sp.getFloat();
+        fp.sp.expect(",");
+        float max = fp.sp.getFloat();
+        return new ScaleFloatFunction(source,min,max);
+      }
+    });
+    vsParsers.put("pow", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        ValueSource a = fp.parseValSource();
+        fp.sp.expect(",");
+        ValueSource b = fp.parseValSource();
+        return new PowFloatFunction(a,b);
+      }
+    });
+    vsParsers.put("div", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        ValueSource a = fp.parseValSource();
+        fp.sp.expect(",");
+        ValueSource b = fp.parseValSource();
+        return new DivFloatFunction(a,b);
+      }
+    });
+    vsParsers.put("map", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        ValueSource source = fp.parseValSource();
+        fp.sp.expect(",");
+        float min = fp.sp.getFloat();
+        fp.sp.expect(",");
+        float max = fp.sp.getFloat();
+        fp.sp.expect(",");
+        float target = fp.sp.getFloat();
+        return new RangeMapFloatFunction(source,min,max,target);
+      }
+    });
+    vsParsers.put("sqrt", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        ValueSource source = fp.parseValSource();
+        return new SimpleFloatFunction(source) {
+          protected String name() {
+            return "sqrt";
+          }
+          protected float func(int doc, DocValues vals) {
+            return (float)Math.sqrt(vals.floatVal(doc));
+          }
+        };
+      }
+    });
+    vsParsers.put("log", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        ValueSource source = fp.parseValSource();
+        return new SimpleFloatFunction(source) {
+          protected String name() {
+            return "log";
+          }
+          protected float func(int doc, DocValues vals) {
+            return (float)Math.log10(vals.floatVal(doc));
+          }
+        };
+      }
+    });
+    vsParsers.put("abs", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        ValueSource source = fp.parseValSource();
+        return new SimpleFloatFunction(source) {
+          protected String name() {
+            return "abs";
+          }
+          protected float func(int doc, DocValues vals) {
+            return (float)Math.abs(vals.floatVal(doc));
+          }
+        };
+      }
+    });
+    vsParsers.put("sum", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        List<ValueSource> sources = fp.parseValueSourceList();
+        return new SumFloatFunction(sources.toArray(new ValueSource[sources.size()]));
+      }
+    });
+    vsParsers.put("product", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        List<ValueSource> sources = fp.parseValueSourceList();
+        return new ProductFloatFunction(sources.toArray(new ValueSource[sources.size()]));
+      }
+    });
+    vsParsers.put("query", new VSParser() {
+      // boost(query($q),rating)
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        Query q = fp.getNestedQuery();
+        float defVal = 0.0f;
+        if (fp.sp.opt(",")) {
+          defVal = fp.sp.getFloat();
+        }
+        return new QueryValueSource(q, defVal);
+      }
+    });
+    vsParsers.put("boost", new VSParser() {
+      ValueSource parse(FunctionQParser fp) throws ParseException {
+        Query q = fp.getNestedQuery();
+        fp.sp.expect(",");
+        ValueSource vs = fp.parseValSource();
+        BoostedQuery bq = new BoostedQuery(q, vs);
+System.out.println("Constructed Boostedquery " + bq);
+        return new QueryValueSource(bq, 0.0f);
+      }
+    });
+  }
+
+  private List<ValueSource> parseValueSourceList() throws ParseException {
+    List<ValueSource> sources = new ArrayList<ValueSource>(3);
+    for (;;) {
+      sources.add(parseValSource());
+      char ch = sp.peek();
+      if (ch==')') break;
+      sp.expect(",");
+    }
+    return sources;
+  }
+
+  private ValueSource parseValSource() throws ParseException {
+    int ch = sp.peek();
+    if (ch>='0' && ch<='9'  || ch=='.' || ch=='+' || ch=='-') {
+      return new ConstValueSource(sp.getFloat());
+    }
+
+    String id = sp.getId();
+    if (sp.opt("(")) {
+      // a function... look it up.
+      VSParser argParser = vsParsers.get(id);
+      if (argParser==null) {
+        throw new ParseException("Unknown function " + id + " in FunctionQuery(" + sp + ")");
+      }
+      ValueSource vs = argParser.parse(this);
+      sp.expect(")");
+      return vs;
+    }
+
+    SchemaField f = req.getSchema().getField(id);
+    return f.getType().getValueSource(f, this);
+  }
+
+  private Query getNestedQuery() throws ParseException {
+    if (sp.opt("$")) {
+      String param = sp.getId();
+      sp.pos += param.length();
+      String qstr = getParam(param);
+      qstr = qstr==null ? "" : qstr;
+      return subQuery(qstr, null).parse();
+    }
+
+    int start = sp.pos;
+    int end = sp.pos;
+    String v = sp.val; 
+
+    String qs = v.substring(start);
+    HashMap nestedLocalParams = new HashMap<String,String>();
+    end = QueryParsing.parseLocalParams(qs, start, nestedLocalParams, getParams());
+
+    QParser sub;
+
+    if (end>start) {
+      if (nestedLocalParams.get(QueryParsing.V) != null) {
+        // value specified directly in local params... so the end of the
+        // query should be the end of the local params.
+        sub = subQuery(qs.substring(0, end), null);
+      } else {
+        // value here is *after* the local params... ask the parser.
+        sub = subQuery(qs, null);
+        // int subEnd = sub.findEnd(')');
+        // TODO.. implement functions to find the end of a nested query
+        throw new ParseException("Nested local params must have value in v parameter.  got '" + qs + "'");
+      }
+    } else {
+      throw new ParseException("Nested function query must use $param or <!v=value> forms. got '" + qs + "'");
+    }
+
+    sp.pos += end-start;  // advance past nested query
+    return sub.getQuery();
+  }
+
+}

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParser.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParser.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParserPlugin.java?rev=587090&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParserPlugin.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/FunctionQParserPlugin.java Mon Oct 22 06:43:07 2007
@@ -0,0 +1,37 @@
+/**
+ * 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.
+ */
+package org.apache.solr.search;
+
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.request.SolrQueryRequest;
+
+/**
+ * Create a function query from the input value.
+ * <br>Other parameters: none
+ * <br>Example: <code>&lt;!func&gt;log(foo)</code>
+ */
+public class FunctionQParserPlugin extends QParserPlugin {
+  public static String NAME = "func";
+
+  public void init(NamedList args) {
+  }
+
+  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    return new FunctionQParser(qstr, localParams, params, req);
+  }
+}

Added: lucene/solr/trunk/src/java/org/apache/solr/search/LuceneQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/LuceneQParserPlugin.java?rev=587090&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/LuceneQParserPlugin.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/LuceneQParserPlugin.java Mon Oct 22 06:43:07 2007
@@ -0,0 +1,128 @@
+/**
+ * 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.
+ */
+package org.apache.solr.search;
+
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.Query;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.StrUtils;
+import org.apache.solr.request.SolrQueryRequest;
+
+import java.util.List;
+/**
+ * Parse Solr's variant on the Lucene QueryParser syntax.
+ * <br>Other parameters:<ul>
+ * <li>q.op - the default operator "OR" or "AND"</li>
+ * <li>df - the default field name</li>
+ * <li>df - the default field name</li>
+ * </ul>
+ * <br>Example: <code>&lt;!lucene q.op=AND df=text sort='price asc'&gt;myfield:foo +bar -baz</code>
+ */
+public class LuceneQParserPlugin extends QParserPlugin {
+  public static String NAME = "lucene";
+
+  public void init(NamedList args) {
+  }
+
+  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    return new LuceneQParser(qstr, localParams, params, req);
+  }
+}
+
+class LuceneQParser extends QParser {
+  String sortStr;
+  SolrQueryParser lparser;
+
+  public LuceneQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    super(qstr, localParams, params, req);
+  }
+
+
+  public Query parse() throws ParseException {
+    String qstr = getString();
+
+    String defaultField = getParam(CommonParams.DF);
+    if (defaultField==null) {
+      defaultField = getReq().getSchema().getDefaultSearchFieldName();
+    }
+    lparser = new SolrQueryParser(this, defaultField);
+
+    // these could either be checked & set here, or in the SolrQueryParser constructor
+    String opParam = getParam(QueryParsing.OP);
+    if (opParam != null) {
+      lparser.setDefaultOperator("AND".equals(opParam) ? QueryParser.Operator.AND : QueryParser.Operator.OR);
+    }
+
+    return lparser.parse(qstr);
+  }
+
+
+  public String[] getDefaultHighlightFields() {
+    return new String[]{lparser.getField()};
+  }
+  
+}
+
+
+class OldLuceneQParser extends LuceneQParser {
+  String sortStr;
+
+  public OldLuceneQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    super(qstr, localParams, params, req);
+  }
+
+  public Query parse() throws ParseException {
+    // handle legacy "query;sort" syntax
+    if (getLocalParams() == null) {
+      String qstr = getString();
+      sortStr = getParams().get(CommonParams.SORT);
+      if (sortStr == null) {
+        // sort may be legacy form, included in the query string
+        List<String> commands = StrUtils.splitSmart(qstr,';');
+        if (commands.size() == 2) {
+          qstr = commands.get(0);
+          sortStr = commands.get(1);
+        } else if (commands.size() == 1) {
+          // This is need to support the case where someone sends: "q=query;"
+          qstr = commands.get(0);
+        }
+        else if (commands.size() > 2) {
+          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "If you want to use multiple ';' in the query, use the 'sort' param.");
+        }
+      }
+      setString(qstr);
+    }
+
+    return super.parse();
+  }
+
+  @Override
+  public QueryParsing.SortSpec getSort(boolean useGlobal) throws ParseException {
+    QueryParsing.SortSpec sort = super.getSort(useGlobal);
+    if (sortStr != null && sortStr.length()>0 && sort.getSort()==null) {
+      QueryParsing.SortSpec oldSort = QueryParsing.parseSort(sortStr, getReq().getSchema());
+      sort.sort = oldSort.sort;
+    }
+    return sort;
+  }
+
+}
+

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/LuceneQParserPlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/LuceneQParserPlugin.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/LuceneQParserPlugin.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: lucene/solr/trunk/src/java/org/apache/solr/search/OldLuceneQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/OldLuceneQParserPlugin.java?rev=587090&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/OldLuceneQParserPlugin.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/OldLuceneQParserPlugin.java Mon Oct 22 06:43:07 2007
@@ -0,0 +1,37 @@
+/**
+ * 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.
+ */
+package org.apache.solr.search;
+
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.request.SolrQueryRequest;
+
+/**
+ * Parse Solr's variant of Lucene QueryParser syntax, including the
+ * deprecated sort specification after the query.
+ * <br>Example: <code>&lt;!lucenePlusSort&gt;myfield:foo +bar -baz;price asc</code>
+ */
+public class OldLuceneQParserPlugin extends QParserPlugin {
+  public static String NAME = "lucenePlusSort";
+
+  public void init(NamedList args) {
+  }
+
+  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    return new OldLuceneQParser(qstr, localParams, params, req);
+  }
+}

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/OldLuceneQParserPlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/OldLuceneQParserPlugin.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/OldLuceneQParserPlugin.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: lucene/solr/trunk/src/java/org/apache/solr/search/PrefixQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/PrefixQParserPlugin.java?rev=587090&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/PrefixQParserPlugin.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/PrefixQParserPlugin.java Mon Oct 22 06:43:07 2007
@@ -0,0 +1,47 @@
+/**
+ * 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.
+ */
+package org.apache.solr.search;
+
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.search.PrefixQuery;
+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;
+/**
+ * Create a prefix query from the input value.  Currently no analysis or
+ * value transformation is done to create this prefix query (subject to change).
+ * <br>Other parameters: <code>f</code>, the field
+ * <br>Example: <code>&lt;!prefix f=myfield&gt;foo</code> is generally equivalent
+ * to the lucene query parser expression <code>myfield:foo*</code>
+ */
+public class PrefixQParserPlugin extends QParserPlugin {
+  public static String NAME = "prefix";
+
+  public void init(NamedList args) {
+  }
+
+  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    return new QParser(qstr, localParams, params, req) {
+      public Query parse() throws ParseException {
+        return new PrefixQuery(new Term(localParams.get(QueryParsing.F), localParams.get(QueryParsing.V)));
+      }
+    };
+  }
+}
+

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/PrefixQParserPlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/PrefixQParserPlugin.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/PrefixQParserPlugin.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java?rev=587090&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java Mon Oct 22 06:43:07 2007
@@ -0,0 +1,202 @@
+/**
+ * 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.
+ */
+package org.apache.solr.search;
+
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.search.Query;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.request.SolrQueryRequest;
+
+public abstract class QParser {
+  String qstr;
+  SolrParams params;
+  SolrParams localParams;
+  SolrQueryRequest req;
+  int recurseCount;
+
+  Query query;
+
+  public QParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    this.qstr = qstr;
+    this.localParams = localParams;
+    this.params = params;
+    this.req = req;
+  }
+
+  /** create and return the <code>Query</code> object represented by <code>qstr</code> */
+  protected abstract Query parse() throws ParseException;
+
+  public SolrParams getLocalParams() {
+    return localParams;
+  }
+
+  public void setLocalParams(SolrParams localParams) {
+    this.localParams = localParams;
+  }
+
+  public SolrParams getParams() {
+    return params;
+  }
+
+  public void setParams(SolrParams params) {
+    this.params = params;
+  }
+
+  public SolrQueryRequest getReq() {
+    return req;
+  }
+
+  public void setReq(SolrQueryRequest req) {
+    this.req = req;
+  }
+
+  public String getString() {
+    return qstr;
+  }
+
+  public void setString(String s) {
+    this.qstr = s;
+  }
+
+  public Query getQuery() throws ParseException {
+    if (query==null) {
+      query=parse();
+    }
+    return query;
+  }
+
+  private void checkRecurse() throws ParseException {
+    if (recurseCount++ >= 100) {
+      throw new ParseException("Infinite Recursion detected parsing query '" + qstr + "'");
+    }
+  }
+
+  // TODO: replace with a SolrParams that defaults to checking localParams first?
+  // ideas..
+  //   create params that satisfy field-specific overrides
+  //   overrideable syntax $x=foo  (set global for limited scope) (invariants & security?)
+  //                       $x+=foo (append to global for limited scope)
+
+  /** check both local and global params */
+  protected String getParam(String name) {
+    String val;
+    if (localParams != null) {
+      val = localParams.get(name);
+      if (val != null) return val;
+    }
+    return params.get(name);
+  }
+
+  /** Create a new QParser for parsing an embedded sub-query */
+  public QParser subQuery(String q, String defaultType) throws ParseException {
+    checkRecurse();
+    if (defaultType == null && localParams != null) {
+      // if not passed, try and get the defaultType from local params
+      defaultType = localParams.get(QueryParsing.DEFTYPE);
+    }
+    QParser nestedParser = getParser(q, defaultType, getReq());
+    nestedParser.recurseCount = recurseCount;
+    return nestedParser;
+  }
+
+
+  /**
+   * @param useGlobalParams look up sort, start, rows in global params if not in local params
+   * @return the sort specification
+   */
+  public QueryParsing.SortSpec getSort(boolean useGlobalParams) throws ParseException {
+    getQuery(); // ensure query is parsed first
+
+    String sortStr = null;
+    String startS = null;
+    String rowsS = null;
+
+    if (localParams != null) {
+      sortStr = localParams.get(CommonParams.SORT);
+      startS = localParams.get(CommonParams.START);
+      rowsS = localParams.get(CommonParams.ROWS);
+
+      // if any of these parameters are present, don't go back to the global params
+      if (sortStr != null || startS != null || rowsS != null) {
+        useGlobalParams = false;
+      }
+    }
+
+    if (useGlobalParams) {
+      if (sortStr ==null) {
+          sortStr = params.get(CommonParams.SORT);
+      }
+      if (startS==null) {
+        startS = params.get(CommonParams.START);
+      }
+      if (rowsS==null) {
+        rowsS = params.get(CommonParams.ROWS);
+      }
+    }
+
+    int start = startS != null ? Integer.parseInt(startS) : 0;
+    int rows = rowsS != null ? Integer.parseInt(rowsS) : 10;
+
+    QueryParsing.SortSpec sort;
+    if (sortStr != null) {
+      sort = QueryParsing.parseSort(sortStr, req.getSchema());
+      sort.offset = start;
+      sort.num =  rows;
+    } else {
+      sort = new QueryParsing.SortSpec(null, start, rows);
+    }
+
+    return sort;
+  }
+
+  public String[] getDefaultHighlightFields() {
+    return new String[]{};
+  }
+
+  public Query getHighlightQuery() throws ParseException {
+    return getQuery();
+  }
+
+  /** Create a <code>QParser</code> to parse <code>qstr</code>,
+   * assuming that the default query type is <code>defaultType</code>.
+   * The query type may be overridden by local parameters in the query
+   * string itself.  For example if defaultType=<code>"dismax"</code>
+   * and qstr=<code>foo</code>, then the dismax query parser will be used
+   * to parse and construct the query object.  However
+   * if qstr=<code>&lt;!prefix f=myfield&gt;foo</code>
+   * then the prefix query parser will be used.
+   */
+  public static QParser getParser(String qstr, String defaultType, SolrQueryRequest req) throws ParseException {
+    SolrParams localParams = QueryParsing.getLocalParams(qstr, req.getParams());
+    String type;
+    
+    if (localParams == null) {
+      type = defaultType;
+    } else {
+      type = localParams.get(QueryParsing.TYPE);
+      qstr = localParams.get("v");
+    }
+
+    type = type==null ? QParserPlugin.DEFAULT_QTYPE : type;
+
+    QParserPlugin qplug = req.getCore().getQueryPlugin(type);
+    return qplug.createParser(qstr, localParams, req.getParams(), req);
+  }
+
+}
+

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: lucene/solr/trunk/src/java/org/apache/solr/search/QParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/QParserPlugin.java?rev=587090&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/QParserPlugin.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/QParserPlugin.java Mon Oct 22 06:43:07 2007
@@ -0,0 +1,43 @@
+/**
+ * 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.
+ */
+package org.apache.solr.search;
+
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.util.plugin.NamedListInitializedPlugin;
+
+public abstract class QParserPlugin implements NamedListInitializedPlugin {
+  /** internal use - name of the default parser */
+  public static String DEFAULT_QTYPE="lucene";
+
+  /** internal use - name to class mappings of builtin parsers */
+  public static final Object[] standardPlugins = {
+    DEFAULT_QTYPE, LuceneQParserPlugin.class,
+    OldLuceneQParserPlugin.NAME, OldLuceneQParserPlugin.class,
+    FunctionQParserPlugin.NAME, FunctionQParserPlugin.class,
+    PrefixQParserPlugin.NAME, PrefixQParserPlugin.class,
+    BoostQParserPlugin.NAME, BoostQParserPlugin.class,
+    DisMaxQParserPlugin.NAME, DisMaxQParserPlugin.class,
+    FieldQParserPlugin.NAME, FieldQParserPlugin.class,
+    RawQParserPlugin.NAME, RawQParserPlugin.class,
+  };
+
+  /** return a {@link QParser} */
+  public abstract QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req);
+}
+
+

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/QParserPlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/QParserPlugin.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lucene/solr/trunk/src/java/org/apache/solr/search/QParserPlugin.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: lucene/solr/trunk/src/java/org/apache/solr/search/QueryParsing.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/QueryParsing.java?rev=587090&r1=587089&r2=587090&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/QueryParsing.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/QueryParsing.java Mon Oct 22 06:43:07 2007
@@ -17,26 +17,28 @@
 
 package org.apache.solr.search;
 
-import org.apache.lucene.search.*;
-import org.apache.solr.search.function.*;
-import org.apache.lucene.queryParser.ParseException;
-import org.apache.lucene.queryParser.QueryParser;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.*;
 import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.core.SolrCore;
+import org.apache.solr.request.LocalSolrQueryRequest;
+import org.apache.solr.schema.FieldType;
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.schema.SchemaField;
-import org.apache.solr.schema.FieldType;
+import org.apache.solr.search.function.FunctionQuery;
 
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.HashMap;
-import java.util.regex.Pattern;
 import java.util.logging.Level;
-import java.io.IOException;
+import java.util.regex.Pattern;
 
 /**
  * Collection of static utilities usefull for query parsing.
@@ -46,6 +48,10 @@
 public class QueryParsing {
   /** the SolrParam used to override the QueryParser "default operator" */
   public static final String OP = "q.op";
+  public static final String V = "v";      // value of this parameter
+  public static final String F = "f";      // field that a query or command pertains to
+  public static final String TYPE = "type";// type of this query or command
+  public static final String DEFTYPE = "defType"; // default type for any direct subqueries
 
   /** 
    * Helper utility for parsing a query using the Lucene QueryParser syntax. 
@@ -106,16 +112,114 @@
     }
   }
 
+
+  // note to self: something needs to detect infinite recursion when parsing queries
+  static int parseLocalParams(String txt, int start, Map<String,String> target, SolrParams params) throws ParseException {
+    int off=start;
+    if (!txt.startsWith("<!",off)) return start;
+    StrParser p = new StrParser(txt,start,txt.length());
+    p.pos+=2; // skip over "<!"
+
+    for(;;) {
+      /*
+      if (p.pos>=txt.length()) {
+        throw new ParseException("Missing '>' parsing local params '" + txt + '"');
+      }
+      */
+      char ch = p.peek();
+      if (ch=='>') {
+        return p.pos+1;
+      }
+
+      String id = p.getId();
+      if (id.length()==0) {
+        throw new ParseException("Expected identifier '>' parsing local params '" + txt + '"');
+
+      }
+      String val=null;
+
+      ch = p.peek();
+      if (ch!='=') {
+        // single word... treat <!func> as ""=func for easy lookup
+        val = id;
+        id = TYPE;
+      } else {
+        // saw equals, so read value
+        p.pos++;
+        ch = p.peek();
+        if (ch=='\"' || ch=='\'') {
+          val = p.getQuotedString();
+        } else if (ch=='$') {
+          p.pos++;
+          // dereference parameter
+          String pname = p.getId();
+          if (params!=null) {
+            val = params.get(pname);
+          }
+        } else {
+          // read unquoted literal ended by whitespace or '>'
+          // there is no escaping.
+          int valStart = p.pos;
+          for (;;) {
+            if (p.pos >= p.end) {
+              throw new ParseException("Missing end to unquoted value starting at " + valStart + " str='" + txt +"'");
+            }
+            char c = p.val.charAt(p.pos);
+            if (c=='>' || Character.isWhitespace(c)) {
+              val = p.val.substring(valStart, p.pos);
+              break;
+            }
+            p.pos++;
+          }
+        }
+      }
+      if (target != null) target.put(id,val);
+    }
+  }
+
+  /**
+   *  "foo" returns null
+   *  "<!prefix f=myfield>yes" returns type="prefix",f="myfield",v="yes"
+   *  "<!prefix f=myfield v=$p>" returns type="prefix",f="myfield",v=params.get("p")
+   */
+  public static SolrParams getLocalParams(String txt, SolrParams params) throws ParseException {
+    if (!txt.startsWith("<!")) {
+      return null;      
+    }
+    Map<String,String> localParams = new HashMap<String,String>();
+    int start = QueryParsing.parseLocalParams(txt, 0, localParams, params);
+
+    String val;
+    if (start >= txt.length()) {
+      // if the rest of the string is empty, check for "v" to provide the value
+      val = localParams.get(V);
+      val = val==null ? "" : val;
+    } else {
+      val = txt.substring(start);
+    }
+    localParams.put(V,val);
+    return new MapSolrParams(localParams);
+  }
+
+
+
+
   /***
    * SortSpec encapsulates a Lucene Sort and a count of the number of documents
    * to return.
    */
   public static class SortSpec {
-    private final Sort sort;
-    private final int num;
+     Sort sort;
+     int num;
+     int offset;
 
     SortSpec(Sort sort, int num) {
+      this(sort,0,num);
+    }
+
+    SortSpec(Sort sort, int offset, int num) {
       this.sort=sort;
+      this.offset=offset;
       this.num=num;
     }
 
@@ -126,11 +230,21 @@
     public Sort getSort() { return sort; }
 
     /**
+     * Offset into the list of results.
+     */
+    public int getOffset() { return offset; }
+
+    /**
      * Gets the number of documens to return after sorting.
      *
      * @return number of docs to return, or -1 for no cut off (just sort)
      */
     public int getCount() { return num; }
+
+    public String toString() {
+      return "start="+offset+"&rows="+num
+              + (sort==null ? "" : "sort="+sort); 
+    }
   }
 
 
@@ -407,17 +521,29 @@
 
 
   // simple class to help with parsing a string
-  private static class StrParser {
+  static class StrParser {
     String val;
     int pos;
     int end;
 
-    StrParser(String val) {this.val = val; end=val.length(); }
+    StrParser(String val) {
+      this(val,0,val.length());
+    }
+
+    StrParser(String val, int start, int end) {
+      this.val = val;
+      this.pos = start;
+      this.end = end;
+    }
 
     void eatws() {
       while (pos<end && Character.isWhitespace(val.charAt(pos))) pos++;
     }
 
+    void skip(int nChars) {
+      pos = Math.max(pos+nChars, end);
+    }
+
     boolean opt(String s) {
       eatws();
       int slen=s.length();
@@ -428,6 +554,16 @@
       return false;
     }
 
+    boolean opt(char ch) {
+      eatws();
+      if (val.charAt(pos) == ch) {
+        pos++;
+        return true;
+      }
+      return false;
+    }
+
+
     void expect(String s) throws ParseException {
       eatws();
       int slen=s.length();
@@ -461,15 +597,56 @@
     String getId() throws ParseException {
       eatws();
       int id_start=pos;
-      while (pos<end && Character.isJavaIdentifierPart(val.charAt(pos))) pos++;
-      return val.substring(id_start, pos);
+      if (pos<end && Character.isJavaIdentifierStart(val.charAt(pos))) {
+        pos++;
+        while (pos<end) {
+          char ch = val.charAt(pos);
+          if (!Character.isJavaIdentifierPart(ch) && ch!='.') {
+            break;
+          }
+          pos++;
+        }
+        return val.substring(id_start, pos);
+      }
+      throw new ParseException("Expected identifier at pos " + pos + " str='" + val + "'");
+    }
+
+    // return null if not a string
+    String getQuotedString() throws ParseException {
+      eatws();
+      char delim = peekChar();
+      if (!(delim=='\"' || delim=='\'')) {
+        return null;
+      }
+      int val_start = ++pos;
+      StringBuilder sb = new StringBuilder(); // needed for escaping
+      for(;;) {
+        if (pos>=end) {
+          throw new ParseException("Missing end quote for string at pos " + (val_start-1) + " str='"+val+"'");
+        }
+        char ch = val.charAt(pos);
+        if (ch=='\\') {
+          ch = pos<end ? val.charAt(pos++) : 0;
+        } else if (ch==delim) {
+          pos++;
+          return sb.toString();
+        }
+        sb.append(ch);
+        pos++;
+      }
     }
 
+    // next non-whitespace char
     char peek() {
       eatws();
       return pos<end ? val.charAt(pos) : 0;
     }
 
+    // next char
+    char peekChar() {
+      return pos<end ? val.charAt(pos) : 0;  
+    }
+
     public String toString() {
       return "'" + val + "'" + ", pos=" + pos;
     }
@@ -487,177 +664,6 @@
     return out;
   }
 
-  private abstract static class VSParser {
-    abstract ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException;
-  }
-  private static Map<String, VSParser> vsParsers = new HashMap<String, VSParser>();
-  static {
-    vsParsers.put("ord", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        String field = sp.getId();
-        return new OrdFieldSource(field);
-      }
-    });
-    vsParsers.put("rord", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        String field = sp.getId();
-        return new ReverseOrdFieldSource(field);
-      }
-    });
-    vsParsers.put("linear", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        ValueSource source = parseValSource(sp, schema);
-        sp.expect(",");
-        float slope = sp.getFloat();
-        sp.expect(",");
-        float intercept = sp.getFloat();
-        return new LinearFloatFunction(source,slope,intercept);
-      }
-    });
-    vsParsers.put("max", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        ValueSource source = parseValSource(sp, schema);
-        sp.expect(",");
-        float val = sp.getFloat();
-        return new MaxFloatFunction(source,val);
-      }
-    });
-    vsParsers.put("recip", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {      
-        ValueSource source = parseValSource(sp,schema);
-        sp.expect(",");
-        float m = sp.getFloat();
-        sp.expect(",");
-        float a = sp.getFloat();
-        sp.expect(",");
-        float b = sp.getFloat();
-        return new ReciprocalFloatFunction(source,m,a,b);
-      }
-    });
-    vsParsers.put("scale", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        ValueSource source = parseValSource(sp,schema);
-        sp.expect(",");
-        float min = sp.getFloat();
-        sp.expect(",");
-        float max = sp.getFloat();
-        return new ScaleFloatFunction(source,min,max);
-      }
-    });
-    vsParsers.put("pow", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        ValueSource a = parseValSource(sp,schema);
-        sp.expect(",");
-        ValueSource b = parseValSource(sp,schema);
-        return new PowFloatFunction(a,b);
-      }
-    });
-    vsParsers.put("div", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        ValueSource a = parseValSource(sp,schema);
-        sp.expect(",");
-        ValueSource b = parseValSource(sp,schema);
-        return new DivFloatFunction(a,b);
-      }
-    });
-    vsParsers.put("map", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        ValueSource source = parseValSource(sp,schema);
-        sp.expect(",");
-        float min = sp.getFloat();
-        sp.expect(",");
-        float max = sp.getFloat();
-        sp.expect(",");
-        float target = sp.getFloat();
-        return new RangeMapFloatFunction(source,min,max,target);
-      }
-    });
-    vsParsers.put("sqrt", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        ValueSource source = parseValSource(sp,schema);
-        return new SimpleFloatFunction(source) {
-          protected String name() {
-            return "sqrt";
-          }
-          protected float func(int doc, DocValues vals) {
-            return (float)Math.sqrt(vals.floatVal(doc));
-          }
-        };
-      }
-    });
-    vsParsers.put("log", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        ValueSource source = parseValSource(sp,schema);
-        return new SimpleFloatFunction(source) {
-          protected String name() {
-            return "log";
-          }
-          protected float func(int doc, DocValues vals) {
-            return (float)Math.log10(vals.floatVal(doc));
-          }
-        };
-      }
-    });
-    vsParsers.put("abs", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        ValueSource source = parseValSource(sp,schema);
-        return new SimpleFloatFunction(source) {
-          protected String name() {
-            return "abs";
-          }
-          protected float func(int doc, DocValues vals) {
-            return (float)Math.abs(vals.floatVal(doc));
-          }
-        };
-      }
-    });
-    vsParsers.put("sum", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        List<ValueSource> sources = parseValueSourceList(sp,schema);
-        return new SumFloatFunction(sources.toArray(new ValueSource[sources.size()]));
-      }
-    });
-    vsParsers.put("product", new VSParser() {
-      ValueSource parse(StrParser sp, IndexSchema schema) throws ParseException {
-        List<ValueSource> sources = parseValueSourceList(sp,schema);
-        return new ProductFloatFunction(sources.toArray(new ValueSource[sources.size()]));
-      }
-    });
-  }
-
-  private static List<ValueSource> parseValueSourceList(StrParser sp, IndexSchema schema) throws ParseException {
-    List<ValueSource> sources = new ArrayList<ValueSource>(3);
-    for (;;) {
-      sources.add(parseValSource(sp,schema)); 
-      char ch = sp.peek();
-      if (ch==')') break;
-      sp.expect(",");
-    }
-    return sources;    
-  }
-
-  private static ValueSource parseValSource(StrParser sp, IndexSchema schema) throws ParseException {
-    int ch = sp.peek();
-    if (ch>='0' && ch<='9'  || ch=='.' || ch=='+' || ch=='-') {
-      return new ConstValueSource(sp.getFloat());
-    }
-
-    String id = sp.getId();
-    if (sp.opt("(")) {
-      // a function... look it up.
-      VSParser argParser = vsParsers.get(id);
-      if (argParser==null) {
-        throw new ParseException("Unknown function " + id + " in FunctionQuery(" + sp + ")");
-      }
-      ValueSource vs = argParser.parse(sp, schema);
-      sp.expect(")");
-      return vs;
-    }
-
-    SchemaField f = schema.getField(id);
-    return f.getType().getValueSource(f);
-  }
-
   /** 
    * Parse a function, returning a FunctionQuery
    *
@@ -668,7 +674,7 @@
    * <pre>
    * // Numeric fields default to correct type
    * // (ie: IntFieldSource or FloatFieldSource)
-   * // Others use implicit ord(...) to generate numeric field value
+   * // Others use explicit ord(...) to generate numeric field value
    * myfield
    *
    * // OrdFieldSource
@@ -694,7 +700,9 @@
    * </pre>
    */
   public static FunctionQuery parseFunction(String func, IndexSchema schema) throws ParseException {
-    return new FunctionQuery(parseValSource(new StrParser(func), schema));
+    SolrCore core = SolrCore.getSolrCore();
+    return (FunctionQuery)(QParser.getParser(func,"func",new LocalSolrQueryRequest(core,new HashMap())).parse());
+    // return new FunctionQuery(parseValSource(new StrParser(func), schema));
   }
 
 }

Added: lucene/solr/trunk/src/java/org/apache/solr/search/RawQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/RawQParserPlugin.java?rev=587090&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/RawQParserPlugin.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/RawQParserPlugin.java Mon Oct 22 06:43:07 2007
@@ -0,0 +1,45 @@
+/**
+ * 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.
+ */
+package org.apache.solr.search;
+
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.request.SolrQueryRequest;
+
+/**
+ * Create a term query from the input value without any text analysis or transformation whatsoever.
+ * <br>Other parameters: <code>f</code>, the field
+ * <br>Example: <code>&lt;!raw f=myfield&gt;Foo Bar</code> creates <code>TermQuery(Term("myfield","Foo Bar"))</code>
+ */
+public class RawQParserPlugin extends QParserPlugin {
+  public static String NAME = "raw";
+
+  public void init(NamedList args) {
+  }
+
+  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    return new QParser(qstr, localParams, params, req) {
+      public Query parse() throws ParseException {
+        return new TermQuery(new Term(localParams.get(QueryParsing.F), localParams.get(QueryParsing.V)));
+      }
+    };
+  }
+}

Modified: lucene/solr/trunk/src/java/org/apache/solr/search/SolrQueryParser.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/SolrQueryParser.java?rev=587090&r1=587089&r2=587090&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/SolrQueryParser.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/SolrQueryParser.java Mon Oct 22 06:43:07 2007
@@ -17,12 +17,13 @@
 
 package org.apache.solr.search;
 
-import org.apache.lucene.queryParser.QueryParser;
-import org.apache.lucene.queryParser.ParseException;
-import org.apache.lucene.search.*;
 import org.apache.lucene.index.Term;
-import org.apache.solr.schema.IndexSchema;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.ConstantScoreRangeQuery;
+import org.apache.lucene.search.Query;
 import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.IndexSchema;
 
 // TODO: implement the analysis of simple fields with
 // FieldType.toInternal() instead of going through the
@@ -49,6 +50,7 @@
  */
 public class SolrQueryParser extends QueryParser {
   protected final IndexSchema schema;
+  protected final QParser parser;
 
   /**
    * Constructs a SolrQueryParser using the schema to understand the
@@ -63,14 +65,32 @@
   public SolrQueryParser(IndexSchema schema, String defaultField) {
     super(defaultField == null ? schema.getDefaultSearchFieldName() : defaultField, schema.getQueryAnalyzer());
     this.schema = schema;
+    this.parser  = null;
     setLowercaseExpandedTerms(false);
   }
 
+  public SolrQueryParser(QParser parser, String defaultField) {
+    super(defaultField, parser.getReq().getSchema().getQueryAnalyzer());
+    this.schema = parser.getReq().getSchema();
+    this.parser = parser;
+    setLowercaseExpandedTerms(false);
+  }
+
+
   protected Query getFieldQuery(String field, String queryText) throws ParseException {
     // intercept magic field name of "_" to use as a hook for our
     // own functions.
-    if (field.equals("_val_")) {
-      return QueryParsing.parseFunction(queryText, schema);
+    if (field.charAt(0) == '_') {
+      if ("_val_".equals(field)) {
+        if (parser==null) {
+          return QueryParsing.parseFunction(queryText, schema);
+        } else {
+          QParser nested = parser.subQuery(queryText, "func");
+          return nested.getQuery();
+        }
+      } else if ("_query_".equals(field) && parser != null) {
+        return parser.subQuery(queryText, null).getQuery();
+      }
     }
 
     // default to a normal field query