Query DSL: queryString - allow to run against multiple fields, closes #48.

This commit is contained in:
kimchy 2010-03-02 00:42:53 +02:00
parent fbf9197b85
commit fdd221e8ea
14 changed files with 652 additions and 65 deletions

View File

@ -35,7 +35,7 @@ import java.io.IOException;
import java.util.List; import java.util.List;
import static com.google.common.collect.Lists.*; import static com.google.common.collect.Lists.*;
import static org.elasticsearch.index.query.support.QueryParsers.*; import static org.elasticsearch.util.lucene.search.Queries.*;
/** /**
* @author kimchy (Shay Banon) * @author kimchy (Shay Banon)

View File

@ -77,6 +77,8 @@ public class DisMaxJsonQueryParser extends AbstractIndexComponent implements Jso
boost = jp.getFloatValue(); boost = jp.getFloatValue();
} else if ("tieBreakerMultiplier".equals(currentFieldName)) { } else if ("tieBreakerMultiplier".equals(currentFieldName)) {
tieBreakerMultiplier = jp.getFloatValue(); tieBreakerMultiplier = jp.getFloatValue();
} else if ("tieBreaker".equals(currentFieldName)) {
tieBreakerMultiplier = jp.getFloatValue();
} }
} }
} }

View File

@ -37,7 +37,7 @@ import org.elasticsearch.util.settings.Settings;
import java.io.IOException; import java.io.IOException;
import static org.elasticsearch.index.query.support.QueryParsers.*; import static org.elasticsearch.util.lucene.search.Queries.*;
/** /**
* @author kimchy (shay.banon) * @author kimchy (shay.banon)

View File

@ -20,8 +20,12 @@
package org.elasticsearch.index.query.json; package org.elasticsearch.index.query.json;
import org.elasticsearch.util.json.JsonBuilder; import org.elasticsearch.util.json.JsonBuilder;
import org.elasticsearch.util.trove.ExtTObjectFloatHashMap;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import static com.google.common.collect.Lists.*;
/** /**
* @author kimchy (Shay Banon) * @author kimchy (Shay Banon)
@ -55,6 +59,14 @@ public class QueryStringJsonQueryBuilder extends BaseJsonQueryBuilder {
private int phraseSlop = -1; private int phraseSlop = -1;
private List<String> fields;
private ExtTObjectFloatHashMap<String> fieldsBoosts;
private Boolean useDisMax;
private float tieBreaker = -1;
public QueryStringJsonQueryBuilder(String queryString) { public QueryStringJsonQueryBuilder(String queryString) {
this.queryString = queryString; this.queryString = queryString;
} }
@ -64,6 +76,50 @@ public class QueryStringJsonQueryBuilder extends BaseJsonQueryBuilder {
return this; return this;
} }
/**
* Adds a field to run the query string against.
*/
public QueryStringJsonQueryBuilder field(String field) {
if (fields == null) {
fields = newArrayList();
}
fields.add(field);
return this;
}
/**
* Adds a field to run the query string against with a specific boost.
*/
public QueryStringJsonQueryBuilder field(String field, float boost) {
if (fields == null) {
fields = newArrayList();
}
fields.add(field);
if (fieldsBoosts == null) {
fieldsBoosts = new ExtTObjectFloatHashMap<String>().defaultReturnValue(-1);
}
fieldsBoosts.put(field, boost);
return this;
}
/**
* When more than one field is used with the query string, should queries be combined using
* dis max, or boolean query. Defaults to dis max (<tt>true</tt>).
*/
public QueryStringJsonQueryBuilder useDisMax(boolean useDisMax) {
this.useDisMax = useDisMax;
return this;
}
/**
* When more than one field is used with the query string, and combined queries are using
* dis max, control the tie breaker for it.
*/
public QueryStringJsonQueryBuilder tieBreaker(float tieBreaker) {
this.tieBreaker = tieBreaker;
return this;
}
public QueryStringJsonQueryBuilder defaultOperator(Operator defaultOperator) { public QueryStringJsonQueryBuilder defaultOperator(Operator defaultOperator) {
this.defaultOperator = defaultOperator; this.defaultOperator = defaultOperator;
return this; return this;
@ -115,6 +171,26 @@ public class QueryStringJsonQueryBuilder extends BaseJsonQueryBuilder {
if (defaultField != null) { if (defaultField != null) {
builder.field("defaultField", defaultField); builder.field("defaultField", defaultField);
} }
if (fields != null) {
builder.startArray("fields");
for (String field : fields) {
float boost = -1;
if (fieldsBoosts != null) {
boost = fieldsBoosts.get(field);
}
if (boost != -1) {
field += "^" + boost;
}
builder.string(field);
}
builder.endArray();
}
if (useDisMax != null) {
builder.field("useDisMax", useDisMax);
}
if (tieBreaker != -1) {
builder.field("tieBreaker", tieBreaker);
}
if (defaultOperator != null) { if (defaultOperator != null) {
builder.field("defaultOperator", defaultOperator.name().toLowerCase()); builder.field("defaultOperator", defaultOperator.name().toLowerCase());
} }

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.query.json; package org.elasticsearch.index.query.json;
import com.google.common.collect.Lists;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.ParseException;
@ -32,12 +33,15 @@ import org.elasticsearch.index.Index;
import org.elasticsearch.index.analysis.AnalysisService; import org.elasticsearch.index.analysis.AnalysisService;
import org.elasticsearch.index.query.QueryParsingException; import org.elasticsearch.index.query.QueryParsingException;
import org.elasticsearch.index.query.support.MapperQueryParser; import org.elasticsearch.index.query.support.MapperQueryParser;
import org.elasticsearch.index.query.support.MultiFieldMapperQueryParser;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
import org.elasticsearch.util.settings.Settings; import org.elasticsearch.util.settings.Settings;
import org.elasticsearch.util.trove.ExtTObjectFloatHashMap;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import static org.elasticsearch.index.query.support.QueryParsers.*; import static org.elasticsearch.util.lucene.search.Queries.*;
/** /**
* @author kimchy (Shay Banon) * @author kimchy (Shay Banon)
@ -74,12 +78,46 @@ public class QueryStringJsonQueryParser extends AbstractIndexComponent implement
float boost = 1.0f; float boost = 1.0f;
boolean escape = false; boolean escape = false;
Analyzer analyzer = null; Analyzer analyzer = null;
List<String> fields = null;
ExtTObjectFloatHashMap<String> boosts = null;
float tieBreaker = 0.0f;
boolean useDisMax = true;
String currentFieldName = null; String currentFieldName = null;
JsonToken token; JsonToken token;
while ((token = jp.nextToken()) != JsonToken.END_OBJECT) { while ((token = jp.nextToken()) != JsonToken.END_OBJECT) {
if (token == JsonToken.FIELD_NAME) { if (token == JsonToken.FIELD_NAME) {
currentFieldName = jp.getCurrentName(); currentFieldName = jp.getCurrentName();
} else if (token == JsonToken.START_ARRAY) {
if ("fields".equals(currentFieldName)) {
while ((token = jp.nextToken()) != JsonToken.END_ARRAY) {
String fField = null;
float fBoost = -1;
char[] text = jp.getTextCharacters();
int end = jp.getTextOffset() + jp.getTextLength();
for (int i = jp.getTextOffset(); i < end; i++) {
if (text[i] == '^') {
int relativeLocation = i - jp.getTextOffset();
fField = new String(text, jp.getTextOffset(), relativeLocation);
fBoost = Float.parseFloat(new String(text, i + 1, jp.getTextLength() - relativeLocation - 1));
break;
}
}
if (fField == null) {
fField = jp.getText();
}
if (fields == null) {
fields = Lists.newArrayList();
}
fields.add(fField);
if (fBoost != -1) {
if (boosts == null) {
boosts = new ExtTObjectFloatHashMap<String>();
}
boosts.put(fField, fBoost);
}
}
}
} else if (token == JsonToken.VALUE_STRING) { } else if (token == JsonToken.VALUE_STRING) {
if ("query".equals(currentFieldName)) { if ("query".equals(currentFieldName)) {
queryString = jp.getText(); queryString = jp.getText();
@ -106,12 +144,16 @@ public class QueryStringJsonQueryParser extends AbstractIndexComponent implement
enablePositionIncrements = token == JsonToken.VALUE_TRUE; enablePositionIncrements = token == JsonToken.VALUE_TRUE;
} else if ("escape".equals(currentFieldName)) { } else if ("escape".equals(currentFieldName)) {
escape = token == JsonToken.VALUE_TRUE; escape = token == JsonToken.VALUE_TRUE;
} else if ("useDisMax".equals(currentFieldName)) {
useDisMax = token == JsonToken.VALUE_TRUE;
} }
} else if (token == JsonToken.VALUE_NUMBER_FLOAT) { } else if (token == JsonToken.VALUE_NUMBER_FLOAT) {
if ("fuzzyMinSim".equals(currentFieldName)) { if ("fuzzyMinSim".equals(currentFieldName)) {
fuzzyMinSim = jp.getFloatValue(); fuzzyMinSim = jp.getFloatValue();
} else if ("boost".equals(currentFieldName)) { } else if ("boost".equals(currentFieldName)) {
boost = jp.getFloatValue(); boost = jp.getFloatValue();
} else if ("tieBreaker".equals(currentFieldName)) {
tieBreaker = jp.getFloatValue();
} }
} else if (token == JsonToken.VALUE_NUMBER_INT) { } else if (token == JsonToken.VALUE_NUMBER_INT) {
if ("fuzzyPrefixLength".equals(currentFieldName)) { if ("fuzzyPrefixLength".equals(currentFieldName)) {
@ -130,6 +172,10 @@ public class QueryStringJsonQueryParser extends AbstractIndexComponent implement
enablePositionIncrements = jp.getIntValue() != 0; enablePositionIncrements = jp.getIntValue() != 0;
} else if ("escape".equals(currentFieldName)) { } else if ("escape".equals(currentFieldName)) {
escape = jp.getIntValue() != 0; escape = jp.getIntValue() != 0;
} else if ("useDisMax".equals(currentFieldName)) {
escape = jp.getIntValue() != 0;
} else if ("tieBreaker".equals(currentFieldName)) {
tieBreaker = jp.getFloatValue();
} }
} }
} }
@ -140,7 +186,19 @@ public class QueryStringJsonQueryParser extends AbstractIndexComponent implement
analyzer = parseContext.mapperService().searchAnalyzer(); analyzer = parseContext.mapperService().searchAnalyzer();
} }
MapperQueryParser queryParser = new MapperQueryParser(defaultField, analyzer, parseContext.mapperService(), parseContext.filterCache()); MapperQueryParser queryParser;
if (fields != null) {
if (fields.size() == 1) {
queryParser = new MapperQueryParser(fields.get(0), analyzer, parseContext.mapperService(), parseContext.filterCache());
} else {
MultiFieldMapperQueryParser mQueryParser = new MultiFieldMapperQueryParser(fields, boosts, analyzer, parseContext.mapperService(), parseContext.filterCache());
mQueryParser.setTieBreaker(tieBreaker);
mQueryParser.setUseDisMax(useDisMax);
queryParser = mQueryParser;
}
} else {
queryParser = new MapperQueryParser(defaultField, analyzer, parseContext.mapperService(), parseContext.filterCache());
}
queryParser.setEnablePositionIncrements(enablePositionIncrements); queryParser.setEnablePositionIncrements(enablePositionIncrements);
queryParser.setLowercaseExpandedTerms(lowercaseExpandedTerms); queryParser.setLowercaseExpandedTerms(lowercaseExpandedTerms);
queryParser.setAllowLeadingWildcard(allowLeadingWildcard); queryParser.setAllowLeadingWildcard(allowLeadingWildcard);

View File

@ -35,6 +35,7 @@ import org.elasticsearch.util.Nullable;
import java.util.List; import java.util.List;
import static org.elasticsearch.index.query.support.QueryParsers.*; import static org.elasticsearch.index.query.support.QueryParsers.*;
import static org.elasticsearch.util.lucene.search.Queries.*;
/** /**
* A query parser that uses the {@link MapperService} in order to build smarter * A query parser that uses the {@link MapperService} in order to build smarter
@ -145,7 +146,7 @@ public class MapperQueryParser extends QueryParser {
if (q == null) { if (q == null) {
return null; return null;
} }
return fixNegativeQueryIfNeeded(q); return optimizeQuery(fixNegativeQueryIfNeeded(q));
} }
protected FieldMapper fieldMapper(String smartName) { protected FieldMapper fieldMapper(String smartName) {

View File

@ -0,0 +1,251 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.index.query.support;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.*;
import org.elasticsearch.index.cache.filter.FilterCache;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.util.Nullable;
import org.elasticsearch.util.trove.ExtTObjectFloatHashMap;
import java.util.ArrayList;
import java.util.List;
/**
* @author kimchy (shay.banon)
*/
public class MultiFieldMapperQueryParser extends MapperQueryParser {
private List<String> fields;
private ExtTObjectFloatHashMap<String> boosts;
private float tieBreaker = 0.0f;
private boolean useDisMax = true;
public MultiFieldMapperQueryParser(List<String> fields, @Nullable ExtTObjectFloatHashMap<String> boosts, Analyzer analyzer, @Nullable MapperService mapperService, @Nullable FilterCache filterCache) {
super(null, analyzer, mapperService, filterCache);
this.fields = fields;
this.boosts = boosts;
if (this.boosts != null) {
boosts.defaultReturnValue(1.0f);
}
}
public void setTieBreaker(float tieBreaker) {
this.tieBreaker = tieBreaker;
}
public void setUseDisMax(boolean useDisMax) {
this.useDisMax = useDisMax;
}
@Override public Query getFieldQuery(String field, String queryText) throws ParseException {
return getFieldQuery(field, queryText, 0);
}
@Override public Query getFieldQuery(String xField, String queryText, int slop) throws ParseException {
if (xField != null) {
Query q = super.getFieldQuery(xField, queryText);
applySlop(q, slop);
return q;
}
if (useDisMax) {
DisjunctionMaxQuery disMaxQuery = new DisjunctionMaxQuery(tieBreaker);
boolean added = false;
for (String field : fields) {
Query q = super.getFieldQuery(field, queryText);
if (q != null) {
added = true;
applyBoost(field, q);
applySlop(q, slop);
disMaxQuery.add(q);
}
}
if (!added) {
return null;
}
return disMaxQuery;
} else {
List<BooleanClause> clauses = new ArrayList<BooleanClause>();
for (String field : fields) {
Query q = super.getFieldQuery(field, queryText);
if (q != null) {
applyBoost(field, q);
applySlop(q, slop);
clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD));
}
}
if (clauses.size() == 0) // happens for stopwords
return null;
return getBooleanQuery(clauses, true);
}
}
@Override protected Query getRangeQuery(String xField, String part1, String part2, boolean inclusive) throws ParseException {
if (xField != null) {
return super.getRangeQuery(xField, part1, part2, inclusive);
}
if (useDisMax) {
DisjunctionMaxQuery disMaxQuery = new DisjunctionMaxQuery(tieBreaker);
boolean added = false;
for (String field : fields) {
Query q = super.getRangeQuery(field, part1, part2, inclusive);
if (q != null) {
added = true;
applyBoost(field, q);
disMaxQuery.add(q);
}
}
if (!added) {
return null;
}
return disMaxQuery;
} else {
List<BooleanClause> clauses = new ArrayList<BooleanClause>();
for (String field : fields) {
Query q = super.getRangeQuery(field, part1, part2, inclusive);
if (q != null) {
applyBoost(field, q);
clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD));
}
}
if (clauses.size() == 0) // happens for stopwords
return null;
return getBooleanQuery(clauses, true);
}
}
@Override protected Query getPrefixQuery(String xField, String termStr) throws ParseException {
if (xField != null) {
return super.getPrefixQuery(xField, termStr);
}
if (useDisMax) {
DisjunctionMaxQuery disMaxQuery = new DisjunctionMaxQuery(tieBreaker);
boolean added = false;
for (String field : fields) {
Query q = super.getPrefixQuery(field, termStr);
if (q != null) {
added = true;
applyBoost(field, q);
disMaxQuery.add(q);
}
}
if (!added) {
return null;
}
return disMaxQuery;
} else {
List<BooleanClause> clauses = new ArrayList<BooleanClause>();
for (String field : fields) {
Query q = super.getPrefixQuery(field, termStr);
if (q != null) {
applyBoost(field, q);
clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD));
}
}
if (clauses.size() == 0) // happens for stopwords
return null;
return getBooleanQuery(clauses, true);
}
}
@Override protected Query getWildcardQuery(String xField, String termStr) throws ParseException {
if (xField != null) {
return super.getWildcardQuery(xField, termStr);
}
if (useDisMax) {
DisjunctionMaxQuery disMaxQuery = new DisjunctionMaxQuery(tieBreaker);
boolean added = false;
for (String field : fields) {
Query q = super.getWildcardQuery(field, termStr);
if (q != null) {
added = true;
applyBoost(field, q);
disMaxQuery.add(q);
}
}
if (!added) {
return null;
}
return disMaxQuery;
} else {
List<BooleanClause> clauses = new ArrayList<BooleanClause>();
for (String field : fields) {
Query q = super.getWildcardQuery(field, termStr);
if (q != null) {
applyBoost(field, q);
clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD));
}
}
if (clauses.size() == 0) // happens for stopwords
return null;
return getBooleanQuery(clauses, true);
}
}
@Override protected Query getFuzzyQuery(String xField, String termStr, float minSimilarity) throws ParseException {
if (xField != null) {
return super.getFuzzyQuery(xField, termStr, minSimilarity);
}
if (useDisMax) {
DisjunctionMaxQuery disMaxQuery = new DisjunctionMaxQuery(tieBreaker);
boolean added = false;
for (String field : fields) {
Query q = super.getFuzzyQuery(field, termStr, minSimilarity);
if (q != null) {
added = true;
applyBoost(field, q);
disMaxQuery.add(q);
}
}
if (!added) {
return null;
}
return disMaxQuery;
} else {
List<BooleanClause> clauses = new ArrayList<BooleanClause>();
for (String field : fields) {
Query q = super.getFuzzyQuery(field, termStr, minSimilarity);
applyBoost(field, q);
clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD));
}
return getBooleanQuery(clauses, true);
}
}
private void applyBoost(String field, Query q) {
if (boosts != null) {
float boost = boosts.get(field);
q.setBoost(boost);
}
}
private void applySlop(Query q, int slop) {
if (q instanceof PhraseQuery) {
((PhraseQuery) q).setSlop(slop);
} else if (q instanceof MultiPhraseQuery) {
((MultiPhraseQuery) q).setSlop(slop);
}
}
}

View File

@ -26,8 +26,6 @@ import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.util.Nullable; import org.elasticsearch.util.Nullable;
import org.elasticsearch.util.lucene.search.TermFilter; import org.elasticsearch.util.lucene.search.TermFilter;
import java.util.List;
/** /**
* @author kimchy (shay.banon) * @author kimchy (shay.banon)
*/ */
@ -37,58 +35,6 @@ public final class QueryParsers {
} }
/**
* Optimizes the given query and returns the optimized version of it.
*/
public static Query optimizeQuery(Query q) {
if (q instanceof BooleanQuery) {
return optimizeBooleanQuery((BooleanQuery) q);
}
return q;
}
public static BooleanQuery optimizeBooleanQuery(BooleanQuery q) {
BooleanQuery optimized = new BooleanQuery(q.isCoordDisabled());
optimized.setMinimumNumberShouldMatch(q.getMinimumNumberShouldMatch());
optimizeBooleanQuery(optimized, q);
return optimized;
}
public static void optimizeBooleanQuery(BooleanQuery optimized, BooleanQuery q) {
for (BooleanClause clause : q.clauses()) {
Query cq = clause.getQuery();
cq.setBoost(cq.getBoost() * q.getBoost());
if (cq instanceof BooleanQuery && !clause.isRequired() && !clause.isProhibited()) {
optimizeBooleanQuery(optimized, (BooleanQuery) cq);
} else {
optimized.add(clause);
}
}
}
public static boolean isNegativeQuery(Query q) {
if (!(q instanceof BooleanQuery)) {
return false;
}
List<BooleanClause> clauses = ((BooleanQuery) q).clauses();
if (clauses.isEmpty()) {
return false;
}
for (BooleanClause clause : clauses) {
if (!clause.isProhibited()) return false;
}
return true;
}
public static Query fixNegativeQueryIfNeeded(Query q) {
if (isNegativeQuery(q)) {
BooleanQuery newBq = (BooleanQuery) q.clone();
newBq.add(new MatchAllDocsQuery(), BooleanClause.Occur.MUST);
return newBq;
}
return q;
}
public static Query wrapSmartNameQuery(Query query, @Nullable MapperService.SmartNameFieldMappers smartFieldMappers, public static Query wrapSmartNameQuery(Query query, @Nullable MapperService.SmartNameFieldMappers smartFieldMappers,
@Nullable FilterCache filterCache) { @Nullable FilterCache filterCache) {
if (smartFieldMappers == null) { if (smartFieldMappers == null) {

View File

@ -0,0 +1,104 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.util.lucene.search;
import org.apache.lucene.search.*;
import java.lang.reflect.Field;
import java.util.List;
/**
* @author kimchy (shay.banon)
*/
public class Queries {
private final static Field disjuncts;
static {
Field disjunctsX;
try {
disjunctsX = DisjunctionMaxQuery.class.getDeclaredField("disjuncts");
disjunctsX.setAccessible(true);
} catch (Exception e) {
disjunctsX = null;
}
disjuncts = disjunctsX;
}
public static List<Query> disMaxClauses(DisjunctionMaxQuery query) {
try {
return (List<Query>) disjuncts.get(query);
} catch (IllegalAccessException e) {
return null;
}
}
/**
* Optimizes the given query and returns the optimized version of it.
*/
public static Query optimizeQuery(Query q) {
if (q instanceof BooleanQuery) {
return optimizeBooleanQuery((BooleanQuery) q);
}
return q;
}
public static BooleanQuery optimizeBooleanQuery(BooleanQuery q) {
BooleanQuery optimized = new BooleanQuery(q.isCoordDisabled());
optimized.setMinimumNumberShouldMatch(q.getMinimumNumberShouldMatch());
optimizeBooleanQuery(optimized, q);
return optimized;
}
public static void optimizeBooleanQuery(BooleanQuery optimized, BooleanQuery q) {
for (BooleanClause clause : q.clauses()) {
Query cq = clause.getQuery();
cq.setBoost(cq.getBoost() * q.getBoost());
if (cq instanceof BooleanQuery && !clause.isRequired() && !clause.isProhibited()) {
optimizeBooleanQuery(optimized, (BooleanQuery) cq);
} else {
optimized.add(clause);
}
}
}
public static boolean isNegativeQuery(Query q) {
if (!(q instanceof BooleanQuery)) {
return false;
}
List<BooleanClause> clauses = ((BooleanQuery) q).clauses();
if (clauses.isEmpty()) {
return false;
}
for (BooleanClause clause : clauses) {
if (!clause.isProhibited()) return false;
}
return true;
}
public static Query fixNegativeQueryIfNeeded(Query q) {
if (isNegativeQuery(q)) {
BooleanQuery newBq = (BooleanQuery) q.clone();
newBq.add(new MatchAllDocsQuery(), BooleanClause.Occur.MUST);
return newBq;
}
return q;
}
}

View File

@ -0,0 +1,64 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.util.trove;
import org.elasticsearch.util.gnu.trove.TObjectFloatHashMap;
import org.elasticsearch.util.gnu.trove.TObjectHashingStrategy;
/**
* @author kimchy (shay.banon)
*/
public class ExtTObjectFloatHashMap<T> extends TObjectFloatHashMap<T> {
private float defaultReturnValue = 0;
public ExtTObjectFloatHashMap() {
}
public ExtTObjectFloatHashMap(int initialCapacity) {
super(initialCapacity);
}
public ExtTObjectFloatHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
public ExtTObjectFloatHashMap(TObjectHashingStrategy<T> ttObjectHashingStrategy) {
super(ttObjectHashingStrategy);
}
public ExtTObjectFloatHashMap(int initialCapacity, TObjectHashingStrategy<T> ttObjectHashingStrategy) {
super(initialCapacity, ttObjectHashingStrategy);
}
public ExtTObjectFloatHashMap(int initialCapacity, float loadFactor, TObjectHashingStrategy<T> ttObjectHashingStrategy) {
super(initialCapacity, loadFactor, ttObjectHashingStrategy);
}
public ExtTObjectFloatHashMap<T> defaultReturnValue(float defaultReturnValue) {
this.defaultReturnValue = defaultReturnValue;
return this;
}
@Override public float get(T key) {
int index = index(key);
return index < 0 ? defaultReturnValue : _values[index];
}
}

View File

@ -30,6 +30,7 @@ import org.elasticsearch.index.cache.filter.none.NoneFilterCache;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.query.IndexQueryParser; import org.elasticsearch.index.query.IndexQueryParser;
import org.elasticsearch.util.lucene.search.MoreLikeThisQuery; import org.elasticsearch.util.lucene.search.MoreLikeThisQuery;
import org.elasticsearch.util.lucene.search.Queries;
import org.elasticsearch.util.lucene.search.TermFilter; import org.elasticsearch.util.lucene.search.TermFilter;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -71,6 +72,73 @@ public class SimpleJsonIndexQueryParserTests {
assertThat(termQuery.getTerm(), equalTo(new Term("content", "test"))); assertThat(termQuery.getTerm(), equalTo(new Term("content", "test")));
} }
@Test public void testQueryStringFields1Builder() throws Exception {
IndexQueryParser queryParser = newQueryParser();
Query parsedQuery = queryParser.parse(queryString("test").field("content").field("name").useDisMax(false));
assertThat(parsedQuery, instanceOf(BooleanQuery.class));
BooleanQuery bQuery = (BooleanQuery) parsedQuery;
assertThat(bQuery.clauses().size(), equalTo(2));
assertThat(((TermQuery) bQuery.clauses().get(0).getQuery()).getTerm(), equalTo(new Term("content", "test")));
assertThat(((TermQuery) bQuery.clauses().get(1).getQuery()).getTerm(), equalTo(new Term("name", "test")));
}
@Test public void testQueryStringFields1() throws Exception {
IndexQueryParser queryParser = newQueryParser();
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/json/query-fields1.json");
Query parsedQuery = queryParser.parse(query);
assertThat(parsedQuery, instanceOf(BooleanQuery.class));
BooleanQuery bQuery = (BooleanQuery) parsedQuery;
assertThat(bQuery.clauses().size(), equalTo(2));
assertThat(((TermQuery) bQuery.clauses().get(0).getQuery()).getTerm(), equalTo(new Term("content", "test")));
assertThat(((TermQuery) bQuery.clauses().get(1).getQuery()).getTerm(), equalTo(new Term("name", "test")));
}
@Test public void testQueryStringFields2Builder() throws Exception {
IndexQueryParser queryParser = newQueryParser();
Query parsedQuery = queryParser.parse(queryString("test").field("content").field("name").useDisMax(true));
assertThat(parsedQuery, instanceOf(DisjunctionMaxQuery.class));
DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) parsedQuery;
List<Query> disjuncts = Queries.disMaxClauses(disMaxQuery);
assertThat(((TermQuery) disjuncts.get(0)).getTerm(), equalTo(new Term("content", "test")));
assertThat(((TermQuery) disjuncts.get(1)).getTerm(), equalTo(new Term("name", "test")));
}
@Test public void testQueryStringFields2() throws Exception {
IndexQueryParser queryParser = newQueryParser();
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/json/query-fields2.json");
Query parsedQuery = queryParser.parse(query);
assertThat(parsedQuery, instanceOf(DisjunctionMaxQuery.class));
DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) parsedQuery;
List<Query> disjuncts = Queries.disMaxClauses(disMaxQuery);
assertThat(((TermQuery) disjuncts.get(0)).getTerm(), equalTo(new Term("content", "test")));
assertThat(((TermQuery) disjuncts.get(1)).getTerm(), equalTo(new Term("name", "test")));
}
@Test public void testQueryStringFields3Builder() throws Exception {
IndexQueryParser queryParser = newQueryParser();
Query parsedQuery = queryParser.parse(queryString("test").field("content", 2.2f).field("name").useDisMax(true));
assertThat(parsedQuery, instanceOf(DisjunctionMaxQuery.class));
DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) parsedQuery;
List<Query> disjuncts = Queries.disMaxClauses(disMaxQuery);
assertThat(((TermQuery) disjuncts.get(0)).getTerm(), equalTo(new Term("content", "test")));
assertThat((double) disjuncts.get(0).getBoost(), closeTo(2.2, 0.01));
assertThat(((TermQuery) disjuncts.get(1)).getTerm(), equalTo(new Term("name", "test")));
assertThat((double) disjuncts.get(1).getBoost(), closeTo(1, 0.01));
}
@Test public void testQueryStringFields3() throws Exception {
IndexQueryParser queryParser = newQueryParser();
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/json/query-fields3.json");
Query parsedQuery = queryParser.parse(query);
assertThat(parsedQuery, instanceOf(DisjunctionMaxQuery.class));
DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) parsedQuery;
List<Query> disjuncts = Queries.disMaxClauses(disMaxQuery);
assertThat(((TermQuery) disjuncts.get(0)).getTerm(), equalTo(new Term("content", "test")));
assertThat((double) disjuncts.get(0).getBoost(), closeTo(2.2, 0.01));
assertThat(((TermQuery) disjuncts.get(1)).getTerm(), equalTo(new Term("name", "test")));
assertThat((double) disjuncts.get(1).getBoost(), closeTo(1, 0.01));
}
@Test public void testMatchAllBuilder() throws Exception { @Test public void testMatchAllBuilder() throws Exception {
IndexQueryParser queryParser = newQueryParser(); IndexQueryParser queryParser = newQueryParser();
Query parsedQuery = queryParser.parse(matchAllQuery().boost(1.2f).buildAsString()); Query parsedQuery = queryParser.parse(matchAllQuery().boost(1.2f).buildAsString());
@ -95,9 +163,7 @@ public class SimpleJsonIndexQueryParserTests {
DisjunctionMaxQuery disjunctionMaxQuery = (DisjunctionMaxQuery) parsedQuery; DisjunctionMaxQuery disjunctionMaxQuery = (DisjunctionMaxQuery) parsedQuery;
assertThat((double) disjunctionMaxQuery.getBoost(), closeTo(1.2, 0.01)); assertThat((double) disjunctionMaxQuery.getBoost(), closeTo(1.2, 0.01));
Field field = disjunctionMaxQuery.getClass().getDeclaredField("disjuncts"); List<Query> disjuncts = Queries.disMaxClauses(disjunctionMaxQuery);
field.setAccessible(true);
List<Query> disjuncts = (List<Query>) field.get(disjunctionMaxQuery);
assertThat(disjuncts.size(), equalTo(2)); assertThat(disjuncts.size(), equalTo(2));
Query firstQ = disjuncts.get(0); Query firstQ = disjuncts.get(0);
@ -117,9 +183,7 @@ public class SimpleJsonIndexQueryParserTests {
DisjunctionMaxQuery disjunctionMaxQuery = (DisjunctionMaxQuery) parsedQuery; DisjunctionMaxQuery disjunctionMaxQuery = (DisjunctionMaxQuery) parsedQuery;
assertThat((double) disjunctionMaxQuery.getBoost(), closeTo(1.2, 0.01)); assertThat((double) disjunctionMaxQuery.getBoost(), closeTo(1.2, 0.01));
Field field = disjunctionMaxQuery.getClass().getDeclaredField("disjuncts"); List<Query> disjuncts = Queries.disMaxClauses(disjunctionMaxQuery);
field.setAccessible(true);
List<Query> disjuncts = (List<Query>) field.get(disjunctionMaxQuery);
assertThat(disjuncts.size(), equalTo(2)); assertThat(disjuncts.size(), equalTo(2));
Query firstQ = disjuncts.get(0); Query firstQ = disjuncts.get(0);

View File

@ -0,0 +1,7 @@
{
queryString : {
fields : ["content", "name"],
useDisMax : false,
query: "test"
}
}

View File

@ -0,0 +1,7 @@
{
queryString : {
fields : ["content", "name"],
useDisMax : true,
query: "test"
}
}

View File

@ -0,0 +1,7 @@
{
queryString : {
fields : ["content^2.2", "name"],
useDisMax : true,
query: "test"
}
}