Return-Path: Delivered-To: apmail-lucene-solr-commits-archive@locus.apache.org Received: (qmail 80767 invoked from network); 22 Oct 2007 13:43:33 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 22 Oct 2007 13:43:33 -0000 Received: (qmail 96020 invoked by uid 500); 22 Oct 2007 13:43:21 -0000 Delivered-To: apmail-lucene-solr-commits-archive@lucene.apache.org Received: (qmail 95943 invoked by uid 500); 22 Oct 2007 13:43:21 -0000 Mailing-List: contact solr-commits-help@lucene.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: solr-dev@lucene.apache.org Delivered-To: mailing list solr-commits@lucene.apache.org Received: (qmail 95934 invoked by uid 99); 22 Oct 2007 13:43:21 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 22 Oct 2007 06:43:20 -0700 X-ASF-Spam-Status: No, hits=-97.2 required=10.0 tests=ALL_TRUSTED,WEIRD_QUOTING X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 22 Oct 2007 13:43:31 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 03F301A9838; Mon, 22 Oct 2007 06:43:11 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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/ Date: Mon, 22 Oct 2007 13:43:08 -0000 To: solr-commits@lucene.apache.org From: yonik@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20071022134311.03F301A9838@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 qParserPlugins = new HashMap(); + + /** Configure the query parsers. */ + private void initQParsers() { + String xpath = "queryParser"; + NodeList nodes = (NodeList) solrConfig.evaluate(xpath, XPathConstants.NODESET); + + NamedListPluginLoader loader = + new NamedListPluginLoader( "[solrconfig.xml] "+xpath, qParserPlugins); + + loader.load( solrConfig, nodes ); + + // default parsers + for (int i=0; i clazz = (Class)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 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 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 args) { } + protected String getArg(String n, Map 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 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 loader = new AbstractPluginLoader( "[schema.xml] fieldType" ) { + AbstractPluginLoader loader = new AbstractPluginLoader( "[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. + *
Other parameters: b, the function query to use as the boost. + *
Example: <!boost b=log(popularity)>foo creates a query "foo" + * which is boosted (scores are multiplied) by the function query log(popularity. + * 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. + *
Other parameters: all main query related parameters from the {@link org.apache.solr.handler.DisMaxRequestHandler} are supported. + * localParams are checked before global request params. + *
Example: <!dismax qf=myfield,mytitle^2>foo 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 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 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 boostQueries = U.parseQueryStrings(req, boostParams); + List boostQueries=null; + if (boostParams!=null && boostParams.length>0) { + boostQueries = new ArrayList(); + 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 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. + *
Other parameters: f, the field + *
Example: <!field f=myfield>Foo Bar 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 myfield:"Foo Bar" + */ +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 lst = new ArrayList(); + 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 vsParsers = new HashMap(); + 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 sources = fp.parseValueSourceList(); + return new SumFloatFunction(sources.toArray(new ValueSource[sources.size()])); + } + }); + vsParsers.put("product", new VSParser() { + ValueSource parse(FunctionQParser fp) throws ParseException { + List 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 parseValueSourceList() throws ParseException { + List sources = new ArrayList(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(); + 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 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. + *
Other parameters: none + *
Example: <!func>log(foo) + */ +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. + *
Other parameters:
    + *
  • q.op - the default operator "OR" or "AND"
  • + *
  • df - the default field name
  • + *
  • df - the default field name
  • + *
+ *
Example: <!lucene q.op=AND df=text sort='price asc'>myfield:foo +bar -baz + */ +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 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. + *
Example: <!lucenePlusSort>myfield:foo +bar -baz;price asc + */ +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). + *
Other parameters: f, the field + *
Example: <!prefix f=myfield>foo is generally equivalent + * to the lucene query parser expression myfield:foo* + */ +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 Query object represented by qstr */ + 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 QParser to parse qstr, + * assuming that the default query type is defaultType. + * The query type may be overridden by local parameters in the query + * string itself. For example if defaultType="dismax" + * and qstr=foo, then the dismax query parser will be used + * to parse and construct the query object. However + * if qstr=<!prefix f=myfield>foo + * 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 target, SolrParams params) throws ParseException { + int off=start; + if (!txt.startsWith("=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 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 + * "yes" returns type="prefix",f="myfield",v="yes" + * "" returns type="prefix",f="myfield",v=params.get("p") + */ + public static SolrParams getLocalParams(String txt, SolrParams params) throws ParseException { + if (!txt.startsWith(" localParams = new HashMap(); + 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) { + throw new ParseException("Missing end quote for string at pos " + (val_start-1) + " str='"+val+"'"); + } + char ch = val.charAt(pos); + if (ch=='\\') { + ch = pos 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 sources = parseValueSourceList(sp,schema); - return new ProductFloatFunction(sources.toArray(new ValueSource[sources.size()])); - } - }); - } - - private static List parseValueSourceList(StrParser sp, IndexSchema schema) throws ParseException { - List sources = new ArrayList(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 @@ *
    * // 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 @@
    * 
*/ 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. + *
Other parameters: f, the field + *
Example: <!raw f=myfield>Foo Bar creates TermQuery(Term("myfield","Foo Bar")) + */ +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