have index level query boost part of the search source

This commit is contained in:
kimchy 2010-02-27 01:26:56 +02:00
parent 284a35131c
commit 929bb3f2be
17 changed files with 141 additions and 89 deletions

View File

@ -26,8 +26,6 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.util.Required;
import org.elasticsearch.util.Strings;
import org.elasticsearch.util.TimeValue;
import org.elasticsearch.util.gnu.trove.TObjectFloatHashMap;
import org.elasticsearch.util.gnu.trove.TObjectFloatIterator;
import java.io.DataInput;
import java.io.DataOutput;
@ -42,8 +40,6 @@ import static org.elasticsearch.util.TimeValue.*;
*/
public class SearchRequest implements ActionRequest {
private static TObjectFloatHashMap<String> EMPTY = new TObjectFloatHashMap<String>();
private SearchType searchType = SearchType.QUERY_THEN_FETCH;
private String[] indices;
@ -60,8 +56,6 @@ public class SearchRequest implements ActionRequest {
private String[] types = Strings.EMPTY_ARRAY;
private TObjectFloatHashMap<String> indexBoost = EMPTY;
private TimeValue timeout;
private boolean listenerThreaded = false;
@ -188,22 +182,6 @@ public class SearchRequest implements ActionRequest {
return this;
}
/**
* Allows to set a dynamic query boost on an index level query. Very handy when, for example, each user has
* his own index, and friends matter more than friends of friends.
*/
public TObjectFloatHashMap<String> indexBoost() {
return indexBoost;
}
public SearchRequest indexBoost(String index, float indexBoost) {
if (this.indexBoost == EMPTY) {
this.indexBoost = new TObjectFloatHashMap<String>();
}
this.indexBoost.put(index, indexBoost);
return this;
}
public int size() {
return size;
}
@ -237,16 +215,6 @@ public class SearchRequest implements ActionRequest {
source = new byte[in.readInt()];
in.readFully(source);
int size = in.readInt();
if (size == 0) {
indexBoost = EMPTY;
} else {
indexBoost = new TObjectFloatHashMap<String>(size);
for (int i = 0; i < size; i++) {
indexBoost.put(in.readUTF(), in.readFloat());
}
}
int typesSize = in.readInt();
if (typesSize > 0) {
types = new String[typesSize];
@ -288,16 +256,6 @@ public class SearchRequest implements ActionRequest {
}
out.writeInt(source.length);
out.write(source);
if (indexBoost == null) {
out.writeInt(0);
} else {
out.writeInt(indexBoost.size());
for (TObjectFloatIterator<String> it = indexBoost.iterator(); it.hasNext();) {
it.advance();
out.writeUTF(it.key());
out.writeFloat(it.value());
}
}
out.writeInt(types.length);
for (String type : types) {
out.writeUTF(type);

View File

@ -63,11 +63,6 @@ public abstract class TransportSearchHelper {
InternalSearchRequest internalRequest = new InternalSearchRequest(shardRouting, request.source());
internalRequest.from(request.from()).size(request.size());
internalRequest.scroll(request.scroll());
if (request.indexBoost() != null) {
if (request.indexBoost().containsKey(shardRouting.index())) {
internalRequest.queryBoost(request.indexBoost().get(shardRouting.index()));
}
}
internalRequest.timeout(request.timeout());
internalRequest.types(request.types());
return internalRequest;

View File

@ -148,6 +148,14 @@ public class NettyHttpRequest implements HttpRequest {
return sValue.equals("true") || sValue.equals("1") || sValue.equals("on");
}
@Override public Boolean paramAsBoolean(String key, Boolean defaultValue) {
String sValue = param(key);
if (sValue == null) {
return defaultValue;
}
return sValue.equals("true") || sValue.equals("1") || sValue.equals("on");
}
@Override public TimeValue paramAsTime(String key, TimeValue defaultValue) {
return parseTimeValue(param(key), defaultValue);
}

View File

@ -67,6 +67,8 @@ public interface RestRequest extends ToJson.Params {
boolean paramAsBoolean(String key, boolean defaultValue);
Boolean paramAsBoolean(String key, Boolean defaultValue);
TimeValue paramAsTime(String key, TimeValue defaultValue);
SizeValue paramAsSize(String key, SizeValue defaultValue);

View File

@ -143,24 +143,6 @@ public class RestSearchAction extends BaseRestHandler {
searchRequest.size(Integer.parseInt(size));
}
String sIndicesBoost = request.param("indicesBoost");
if (sIndicesBoost != null) {
String[] indicesBoost = indicesBoostPattern.split(sIndicesBoost);
for (String indexBoost : indicesBoost) {
int divisor = indexBoost.indexOf(',');
if (divisor == -1) {
throw new ElasticSearchIllegalArgumentException("Illegal index boost [" + indexBoost + "], no ','");
}
String indexName = indexBoost.substring(0, divisor);
String sBoost = indexBoost.substring(divisor + 1);
try {
searchRequest.indexBoost(indexName, Float.parseFloat(sBoost));
} catch (NumberFormatException e) {
throw new ElasticSearchIllegalArgumentException("Illegal index boost [" + indexBoost + "], boost not a float number");
}
}
}
String scroll = request.param("scroll");
if (scroll != null) {
searchRequest.scroll(new Scroll(parseTimeValue(scroll, null)));
@ -237,6 +219,24 @@ public class RestSearchAction extends BaseRestHandler {
}
}
String sIndicesBoost = request.param("indicesBoost");
if (sIndicesBoost != null) {
String[] indicesBoost = indicesBoostPattern.split(sIndicesBoost);
for (String indexBoost : indicesBoost) {
int divisor = indexBoost.indexOf(',');
if (divisor == -1) {
throw new ElasticSearchIllegalArgumentException("Illegal index boost [" + indexBoost + "], no ','");
}
String indexName = indexBoost.substring(0, divisor);
String sBoost = indexBoost.substring(divisor + 1);
try {
searchSourceBuilder.indexBoost(indexName, Float.parseFloat(sBoost));
} catch (NumberFormatException e) {
throw new ElasticSearchIllegalArgumentException("Illegal index boost [" + indexBoost + "], boost not a float number");
}
}
}
// TODO add different parameters to the source
return searchSourceBuilder.build();
}

View File

@ -31,5 +31,10 @@ public interface SearchPhase {
Map<String, ? extends SearchParseElement> parseElements();
/**
* Performs pre processing of the search context before the execute.
*/
void preProcess(SearchContext context);
void execute(SearchContext context) throws ElasticSearchException;
}

View File

@ -47,6 +47,7 @@ import org.elasticsearch.search.query.QuerySearchRequest;
import org.elasticsearch.search.query.QuerySearchResult;
import org.elasticsearch.timer.TimerService;
import org.elasticsearch.util.TimeValue;
import org.elasticsearch.util.Unicode;
import org.elasticsearch.util.component.AbstractComponent;
import org.elasticsearch.util.component.Lifecycle;
import org.elasticsearch.util.component.LifecycleComponent;
@ -245,7 +246,7 @@ public class SearchService extends AbstractComponent implements LifecycleCompone
SearchShardTarget shardTarget = new SearchShardTarget(clusterService.state().nodes().localNodeId(), request.index(), request.shardId());
SearchContext context = new SearchContext(idGenerator.incrementAndGet(), shardTarget, request.timeout(),
request.queryBoost(), request.source(), request.types(), engineSearcher, indexService);
request.source(), request.types(), engineSearcher, indexService);
// init the from and size
context.from(request.from());
@ -263,6 +264,11 @@ public class SearchService extends AbstractComponent implements LifecycleCompone
context.size(10);
}
// pre process
dfsPhase.preProcess(context);
queryPhase.preProcess(context);
fetchPhase.preProcess(context);
// compute the context keep alive
TimeValue keepAlive = defaultKeepAlive;
if (request.scroll() != null && request.scroll().keepAlive() != null) {
@ -305,7 +311,7 @@ public class SearchService extends AbstractComponent implements LifecycleCompone
}
}
} catch (Exception e) {
throw new SearchParseException(context, "Failed to parse [" + context.source() + "]", e);
throw new SearchParseException(context, "Failed to parse [" + Unicode.fromBytes(context.source()) + "]", e);
}
}

View File

@ -21,6 +21,8 @@ package org.elasticsearch.search.builder;
import org.elasticsearch.index.query.json.JsonQueryBuilder;
import org.elasticsearch.search.SearchException;
import org.elasticsearch.util.gnu.trove.TObjectFloatHashMap;
import org.elasticsearch.util.gnu.trove.TObjectFloatIterator;
import org.elasticsearch.util.json.JsonBuilder;
import org.elasticsearch.util.json.ToJson;
@ -59,6 +61,9 @@ public class SearchSourceBuilder {
private SearchSourceFacetsBuilder facetsBuilder;
private TObjectFloatHashMap<String> indexBoost = null;
public SearchSourceBuilder() {
}
@ -125,6 +130,15 @@ public class SearchSourceBuilder {
return this;
}
public SearchSourceBuilder indexBoost(String index, float indexBoost) {
if (this.indexBoost == null) {
this.indexBoost = new TObjectFloatHashMap<String>();
}
this.indexBoost.put(index, indexBoost);
return this;
}
public byte[] build() throws SearchException {
try {
JsonBuilder builder = binaryJsonBuilder();
@ -176,6 +190,15 @@ public class SearchSourceBuilder {
builder.endObject();
}
if (indexBoost != null) {
builder.startObject("queryBoost");
for (TObjectFloatIterator<String> it = indexBoost.iterator(); it.hasNext();) {
it.advance();
builder.field(it.key(), it.value());
}
builder.endObject();
}
if (facetsBuilder != null) {
facetsBuilder.toJson(builder, ToJson.EMPTY_PARAMS);
}

View File

@ -37,6 +37,9 @@ public class DfsPhase implements SearchPhase {
return ImmutableMap.of();
}
@Override public void preProcess(SearchContext context) {
}
public void execute(SearchContext context) {
try {
context.rewriteQuery();

View File

@ -46,6 +46,9 @@ public class FacetsPhase implements SearchPhase {
return ImmutableMap.of("facets", new FacetsParseElement());
}
@Override public void preProcess(SearchContext context) {
}
@Override public void execute(SearchContext context) throws ElasticSearchException {
if (context.facets() == null) {
return;

View File

@ -47,6 +47,9 @@ public class FetchPhase implements SearchPhase {
return ImmutableMap.of("explain", new ExplainParseElement(), "fields", new FieldsParseElement());
}
@Override public void preProcess(SearchContext context) {
}
public void execute(SearchContext context) {
FieldSelector fieldSelector = buildFieldSelectors(context);

View File

@ -64,8 +64,6 @@ public class InternalSearchRequest implements Streamable {
private int size = -1;
private float queryBoost = 1.0f;
private TimeValue timeout;
private String[] types = Strings.EMPTY_ARRAY;
@ -124,19 +122,6 @@ public class InternalSearchRequest implements Streamable {
return this;
}
/**
* Allows to set a dynamic query boost on an index level query. Very handy when, for example, each user has
* his own index, and friends matter more than friends of friends.
*/
public float queryBoost() {
return queryBoost;
}
public InternalSearchRequest queryBoost(float queryBoost) {
this.queryBoost = queryBoost;
return this;
}
public int size() {
return size;
}
@ -167,7 +152,6 @@ public class InternalSearchRequest implements Streamable {
}
source = new byte[in.readInt()];
in.readFully(source);
queryBoost = in.readFloat();
int typesSize = in.readInt();
if (typesSize > 0) {
types = new String[typesSize];
@ -196,7 +180,6 @@ public class InternalSearchRequest implements Streamable {
}
out.writeInt(source.length);
out.write(source);
out.writeFloat(queryBoost);
out.writeInt(types.length);
for (String type : types) {
out.writeUTF(type);

View File

@ -66,7 +66,7 @@ public class SearchContext implements Releasable {
private final TimeValue timeout;
private final float queryBoost;
private float queryBoost = 1.0f;
private Scroll scroll;
@ -100,12 +100,11 @@ public class SearchContext implements Releasable {
private volatile Timeout keepAliveTimeout;
public SearchContext(long id, SearchShardTarget shardTarget, TimeValue timeout, float queryBoost, byte[] source,
public SearchContext(long id, SearchShardTarget shardTarget, TimeValue timeout, byte[] source,
String[] types, Engine.Searcher engineSearcher, IndexService indexService) {
this.id = id;
this.shardTarget = shardTarget;
this.timeout = timeout;
this.queryBoost = queryBoost;
this.source = source;
this.types = types;
this.engineSearcher = engineSearcher;
@ -150,6 +149,11 @@ public class SearchContext implements Releasable {
return queryBoost;
}
public SearchContext queryBoost(float queryBoost) {
this.queryBoost = queryBoost;
return this;
}
public Scroll scroll() {
return this.scroll;
}

View File

@ -0,0 +1,54 @@
/*
* 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.search.query;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.elasticsearch.search.SearchParseElement;
import org.elasticsearch.search.internal.SearchContext;
/**
* <pre>
* {
* queryBoost : {
* "index1" : 1.4,
* "index2" : 1.5
* }
* }
* </pre>
*
* @author kimchy (shay.banon)
*/
public class QueryBoostParseElement implements SearchParseElement {
@Override public void parse(JsonParser jp, SearchContext context) throws Exception {
JsonToken token;
while ((token = jp.nextToken()) != JsonToken.END_OBJECT) {
if (token == JsonToken.FIELD_NAME) {
String indexName = jp.getCurrentName();
if (indexName.equals(context.shardTarget().index())) {
jp.nextToken(); // move to the value
// we found our query boost
context.queryBoost(jp.getFloatValue());
}
}
}
}
}

View File

@ -33,7 +33,6 @@ public class QueryParseElement implements SearchParseElement {
@Override public void parse(JsonParser jp, SearchContext context) throws Exception {
JsonIndexQueryParser indexQueryParser = (JsonIndexQueryParser) context.queryParser();
Query query = indexQueryParser.parse(jp);
query.setBoost(query.getBoost() * context.queryBoost());
context.query(query);
}
}

View File

@ -46,12 +46,18 @@ public class QueryPhase implements SearchPhase {
ImmutableMap.Builder<String, SearchParseElement> parseElements = ImmutableMap.builder();
parseElements.put("from", new FromParseElement()).put("size", new SizeParseElement())
.put("queryParserName", new QueryParserNameParseElement())
.put("queryBoost", new QueryBoostParseElement())
.put("query", new QueryParseElement())
.put("sort", new SortParseElement())
.putAll(facetsPhase.parseElements());
return parseElements.build();
}
@Override public void preProcess(SearchContext context) {
context.query().setBoost(context.query().getBoost() * context.queryBoost());
facetsPhase.preProcess(context);
}
public void execute(SearchContext searchContext) throws QueryPhaseExecutionException {
try {
searchContext.queryResult().from(searchContext.from());

View File

@ -106,7 +106,7 @@ public class TwoInstanceEmbeddedSearchTests extends AbstractServersTests {
@Test public void testDfsQueryFetch() throws Exception {
SearchSourceBuilder sourceBuilder = searchSource()
.query(termQuery("multi", "test"))
.from(0).size(60).explain(true);
.from(0).size(60).explain(true).indexBoost("test", 1.0f).indexBoost("test2", 2.0f);
List<DfsSearchResult> dfsResults = newArrayList();
for (ShardsIterator shardsIt : indicesService.searchShards(clusterService.state(), new String[]{"test"}, null)) {