Query: boosting query, closes #683.

This commit is contained in:
kimchy 2011-02-11 19:59:15 +02:00
parent 65197ba39d
commit 1b5cdb181a
6 changed files with 226 additions and 0 deletions

View File

@ -225,6 +225,7 @@ public class IndexQueryParserModule extends AbstractModule {
bindings.processXContentQueryParser(DisMaxQueryParser.NAME, DisMaxQueryParser.class);
bindings.processXContentQueryParser(MatchAllQueryParser.NAME, MatchAllQueryParser.class);
bindings.processXContentQueryParser(QueryStringQueryParser.NAME, QueryStringQueryParser.class);
bindings.processXContentQueryParser(BoostingQueryParser.NAME, BoostingQueryParser.class);
bindings.processXContentQueryParser(BoolQueryParser.NAME, BoolQueryParser.class);
bindings.processXContentQueryParser(TermQueryParser.NAME, TermQueryParser.class);
bindings.processXContentQueryParser(TermsQueryParser.NAME, TermsQueryParser.class);

View File

@ -0,0 +1,95 @@
/*
* 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.xcontent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.QueryBuilderException;
import java.io.IOException;
/**
* The BoostingQuery class can be used to effectively demote results that match a given query.
* Unlike the "NOT" clause, this still selects documents that contain undesirable terms,
* but reduces their overall score:
*
* Query balancedQuery = new BoostingQuery(positiveQuery, negativeQuery, 0.01f);
* In this scenario the positiveQuery contains the mandatory, desirable criteria which is used to
* select all matching documents, and the negativeQuery contains the undesirable elements which
* are simply used to lessen the scores. Documents that match the negativeQuery have their score
* multiplied by the supplied "boost" parameter, so this should be less than 1 to achieve a
* demoting effect
*/
public class BoostingQueryBuilder extends BaseQueryBuilder {
private XContentQueryBuilder positiveQuery;
private XContentQueryBuilder negativeQuery;
private float negativeBoost = -1;
private float boost = -1;
public BoostingQueryBuilder() {
}
public BoostingQueryBuilder positive(XContentQueryBuilder positiveQuery) {
this.positiveQuery = positiveQuery;
return this;
}
public BoostingQueryBuilder negative(XContentQueryBuilder negativeQuery) {
this.negativeQuery = negativeQuery;
return this;
}
public BoostingQueryBuilder negativeBoost(float negativeBoost) {
this.negativeBoost = negativeBoost;
return this;
}
public BoostingQueryBuilder boost(float boost) {
this.boost = boost;
return this;
}
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
if (positiveQuery == null) {
throw new QueryBuilderException("boosting query requires positive query to be set");
}
if (negativeQuery == null) {
throw new QueryBuilderException("boosting query requires negative query to be set");
}
if (negativeBoost == -1) {
throw new QueryBuilderException("boosting query requires negativeBoost to be set");
}
builder.startObject(BoostingQueryParser.NAME);
builder.field("positive");
positiveQuery.toXContent(builder, params);
builder.field("negative");
negativeQuery.toXContent(builder, params);
builder.field("negative_boost", negativeBoost);
if (boost != -1) {
builder.field("boost", boost);
}
}
}

View File

@ -0,0 +1,93 @@
/*
* 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.xcontent;
import org.apache.lucene.search.BoostingQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.AbstractIndexComponent;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.query.QueryParsingException;
import org.elasticsearch.index.settings.IndexSettings;
import java.io.IOException;
/**
*
*/
public class BoostingQueryParser extends AbstractIndexComponent implements XContentQueryParser {
public static final String NAME = "boosting";
@Inject public BoostingQueryParser(Index index, @IndexSettings Settings settings) {
super(index, settings);
}
@Override public String[] names() {
return new String[]{NAME};
}
@Override public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser();
Query positiveQuery = null;
Query negativeQuery = null;
float boost = -1;
float negativeBoost = -1;
String currentFieldName = null;
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
if ("positive".equals(currentFieldName)) {
positiveQuery = parseContext.parseInnerQuery();
} else if ("negative".equals(currentFieldName)) {
negativeQuery = parseContext.parseInnerQuery();
}
} else if (token.isValue()) {
if ("negative_boost".equals(currentFieldName) || "negativeBoost".equals(currentFieldName)) {
negativeBoost = parser.floatValue();
} else if ("boost".equals(currentFieldName)) {
boost = parser.floatValue();
}
}
}
if (positiveQuery == null) {
throw new QueryParsingException(index, "[boosting] query requires 'positive' query to be set'");
}
if (negativeQuery == null) {
throw new QueryParsingException(index, "[boosting] query requires 'negative' query to be set'");
}
if (negativeBoost == -1) {
throw new QueryParsingException(index, "[boosting] query requires 'negative_boost' to be set'");
}
BoostingQuery boostingQuery = new BoostingQuery(positiveQuery, negativeQuery, negativeBoost);
if (boost != -1) {
boostingQuery.setBoost(boost);
}
return boostingQuery;
}
}

View File

@ -252,6 +252,15 @@ public abstract class QueryBuilders {
return new QueryStringQueryBuilder(queryString);
}
/**
* The BoostingQuery class can be used to effectively demote results that match a given query.
* Unlike the "NOT" clause, this still selects documents that contain undesirable terms,
* but reduces their overall score:
*/
public static BoostingQueryBuilder boostingQuery() {
return new BoostingQueryBuilder();
}
/**
* A Query that matches documents matching boolean combinations of other queries.
*/

View File

@ -713,6 +713,19 @@ public class SimpleIndexQueryParserTests {
assertThat(((TermFilter) notFilter.filter()).getTerm(), equalTo(new Term("name.first", "shay1")));
}
@Test public void testBoostingQueryBuilder() throws IOException {
IndexQueryParser queryParser = queryParser();
Query parsedQuery = queryParser.parse(boostingQuery().positive(termQuery("field1", "value1")).negative(termQuery("field1", "value2")).negativeBoost(0.2f)).query();
assertThat(parsedQuery, instanceOf(BoostingQuery.class));
}
@Test public void testBoostingQuery() throws IOException {
IndexQueryParser queryParser = queryParser();
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/boosting-query.json");
Query parsedQuery = queryParser.parse(query).query();
assertThat(parsedQuery, instanceOf(BoostingQuery.class));
}
@Test public void testBoolQueryBuilder() throws IOException {
IndexQueryParser queryParser = queryParser();
Query parsedQuery = queryParser.parse(boolQuery().must(termQuery("content", "test1")).must(termQuery("content", "test4")).mustNot(termQuery("content", "test2")).should(termQuery("content", "test3"))).query();

View File

@ -0,0 +1,15 @@
{
"boosting" : {
"positive" : {
"term" : {
"field1" : "value1"
}
},
"negative" : {
"term" : {
"field2" : "value2"
}
},
"negative_boost" : 0.2
}
}