Added highlighter to percolate api.

The highlighter in the percolate api highlights snippets in the document being percolated. If highlighting is enabled then foreach matching query, highlight snippets will be generated.
All highlight options that are supported via the search api are also supported in the percolate api, since the percolate api embeds the same highlighter infrastructure as the search api.
The `size` option is a required option if highlighting is specified in the percolate api, other than that the `highlight`request part can just be placed in the percolate api request body.

Closes #3574
This commit is contained in:
Martijn van Groningen 2013-08-26 16:28:27 +02:00
parent df3922a22a
commit 3ca0239668
17 changed files with 1954 additions and 642 deletions

View File

@ -41,6 +41,7 @@ import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.shard.service.IndexShard; import org.elasticsearch.index.shard.service.IndexShard;
import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.internal.DefaultSearchContext;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.internal.ShardSearchRequest; import org.elasticsearch.search.internal.ShardSearchRequest;
import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.threadpool.ThreadPool;
@ -169,7 +170,7 @@ public class TransportValidateQueryAction extends TransportBroadcastOperationAct
if (request.querySource().length() == 0) { if (request.querySource().length() == 0) {
valid = true; valid = true;
} else { } else {
SearchContext.setCurrent(new SearchContext(0, SearchContext.setCurrent(new DefaultSearchContext(0,
new ShardSearchRequest().types(request.types()), new ShardSearchRequest().types(request.types()),
null, indexShard.searcher(), indexService, indexShard, null, indexShard.searcher(), indexService, indexShard,
scriptService, cacheRecycler)); scriptService, cacheRecycler));

View File

@ -41,6 +41,7 @@ import org.elasticsearch.index.shard.service.IndexShard;
import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.SearchShardTarget; import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.internal.DefaultSearchContext;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.internal.ShardSearchRequest; import org.elasticsearch.search.internal.ShardSearchRequest;
import org.elasticsearch.search.query.QueryPhaseExecutionException; import org.elasticsearch.search.query.QueryPhaseExecutionException;
@ -151,7 +152,7 @@ public class TransportCountAction extends TransportBroadcastOperationAction<Coun
IndexShard indexShard = indexService.shardSafe(request.shardId()); IndexShard indexShard = indexService.shardSafe(request.shardId());
SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().id(), request.index(), request.shardId()); SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().id(), request.index(), request.shardId());
SearchContext context = new SearchContext(0, SearchContext context = new DefaultSearchContext(0,
new ShardSearchRequest().types(request.types()).filteringAliases(request.filteringAliases()), new ShardSearchRequest().types(request.types()).filteringAliases(request.filteringAliases()),
shardTarget, indexShard.searcher(), indexService, indexShard, shardTarget, indexShard.searcher(), indexService, indexShard,
scriptService, cacheRecycler); scriptService, cacheRecycler);

View File

@ -43,6 +43,7 @@ import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.shard.service.IndexShard; import org.elasticsearch.index.shard.service.IndexShard;
import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.internal.DefaultSearchContext;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.internal.ShardSearchRequest; import org.elasticsearch.search.internal.ShardSearchRequest;
import org.elasticsearch.search.rescore.RescoreSearchContext; import org.elasticsearch.search.rescore.RescoreSearchContext;
@ -98,7 +99,7 @@ public class TransportExplainAction extends TransportShardSingleOperationAction<
return new ExplainResponse(false); return new ExplainResponse(false);
} }
SearchContext context = new SearchContext( SearchContext context = new DefaultSearchContext(
0, 0,
new ShardSearchRequest().types(new String[]{request.type()}) new ShardSearchRequest().types(new String[]{request.type()})
.filteringAliases(request.filteringAlias()), .filteringAliases(request.filteringAlias()),

View File

@ -30,6 +30,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.highlight.HighlightBuilder;
import java.util.Map; import java.util.Map;
@ -178,6 +179,11 @@ public class PercolateRequestBuilder extends BroadcastOperationRequestBuilder<Pe
return this; return this;
} }
public PercolateRequestBuilder setHighlightBuilder(HighlightBuilder highlightBuilder) {
sourceBuilder().setHighlightBuilder(highlightBuilder);
return this;
}
private PercolateSourceBuilder sourceBuilder() { private PercolateSourceBuilder sourceBuilder() {
if (sourceBuilder == null) { if (sourceBuilder == null) {
sourceBuilder = new PercolateSourceBuilder(); sourceBuilder = new PercolateSourceBuilder();

View File

@ -31,11 +31,10 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString; import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.percolator.PercolatorService; import org.elasticsearch.percolator.PercolatorService;
import org.elasticsearch.rest.action.support.RestActions; import org.elasticsearch.rest.action.support.RestActions;
import org.elasticsearch.search.highlight.HighlightField;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.*;
import java.util.Iterator;
import java.util.List;
/** /**
* *
@ -116,17 +115,33 @@ public class PercolateResponse extends BroadcastOperationResponse implements Ite
boolean justIds = "ids".equals(params.param("percolate_format")); boolean justIds = "ids".equals(params.param("percolate_format"));
if (justIds) { if (justIds) {
for (PercolateResponse.Match match : matches) { for (PercolateResponse.Match match : matches) {
builder.value(match.id()); builder.value(match.getId());
} }
} else { } else {
for (PercolateResponse.Match match : matches) { for (PercolateResponse.Match match : matches) {
builder.startObject(); builder.startObject();
builder.field(Fields._INDEX, match.getIndex()); builder.field(Fields._INDEX, match.getIndex());
builder.field(Fields._ID, match.getId()); builder.field(Fields._ID, match.getId());
float score = match.score(); float score = match.getScore();
if (score != PercolatorService.NO_SCORE) { if (score != PercolatorService.NO_SCORE) {
builder.field(Fields._SCORE, match.getScore()); builder.field(Fields._SCORE, match.getScore());
} }
if (match.getHighlightFields() != null) {
builder.startObject(Fields.HIGHLIGHT);
for (HighlightField field : match.getHighlightFields().values()) {
builder.field(field.name());
if (field.fragments() == null) {
builder.nullValue();
} else {
builder.startArray();
for (Text fragment : field.fragments()) {
builder.value(fragment);
}
builder.endArray();
}
}
builder.endObject();
}
builder.endObject(); builder.endObject();
} }
} }
@ -166,6 +181,14 @@ public class PercolateResponse extends BroadcastOperationResponse implements Ite
private Text index; private Text index;
private Text id; private Text id;
private float score; private float score;
private Map<String, HighlightField> hl;
public Match(Text index, Text id, float score, Map<String, HighlightField> hl) {
this.id = id;
this.score = score;
this.index = index;
this.hl = hl;
}
public Match(Text index, Text id, float score) { public Match(Text index, Text id, float score) {
this.id = id; this.id = id;
@ -176,28 +199,20 @@ public class PercolateResponse extends BroadcastOperationResponse implements Ite
Match() { Match() {
} }
public Text index() { public Text getIndex() {
return index; return index;
} }
public Text id() { public Text getId() {
return id; return id;
} }
public float score() { public float getScore() {
return score; return score;
} }
public Text getIndex() { public Map<String, HighlightField> getHighlightFields() {
return index(); return hl;
}
public Text getId() {
return id();
}
public float getScore() {
return score();
} }
@Override @Override
@ -205,6 +220,13 @@ public class PercolateResponse extends BroadcastOperationResponse implements Ite
id = in.readText(); id = in.readText();
index = in.readText(); index = in.readText();
score = in.readFloat(); score = in.readFloat();
int size = in.readVInt();
if (size > 0) {
hl = new HashMap<String, HighlightField>(size);
for (int j = 0; j < size; j++) {
hl.put(in.readString(), HighlightField.readHighlightField(in));
}
}
} }
@Override @Override
@ -212,6 +234,15 @@ public class PercolateResponse extends BroadcastOperationResponse implements Ite
out.writeText(id); out.writeText(id);
out.writeText(index); out.writeText(index);
out.writeFloat(score); out.writeFloat(score);
if (hl != null) {
out.writeVInt(hl.size());
for (Map.Entry<String, HighlightField> entry : hl.entrySet()) {
out.writeString(entry.getKey());
entry.getValue().writeTo(out);
}
} else {
out.writeVInt(0);
}
} }
} }
@ -222,6 +253,7 @@ public class PercolateResponse extends BroadcastOperationResponse implements Ite
static final XContentBuilderString _INDEX = new XContentBuilderString("_index"); static final XContentBuilderString _INDEX = new XContentBuilderString("_index");
static final XContentBuilderString _ID = new XContentBuilderString("_id"); static final XContentBuilderString _ID = new XContentBuilderString("_id");
static final XContentBuilderString _SCORE = new XContentBuilderString("_score"); static final XContentBuilderString _SCORE = new XContentBuilderString("_score");
static final XContentBuilderString HIGHLIGHT = new XContentBuilderString("highlight");
} }
} }

View File

@ -23,9 +23,14 @@ import org.apache.lucene.util.BytesRef;
import org.elasticsearch.action.support.broadcast.BroadcastShardOperationResponse; import org.elasticsearch.action.support.broadcast.BroadcastShardOperationResponse;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.percolator.PercolatorService; import org.elasticsearch.percolator.PercolateContext;
import org.elasticsearch.search.highlight.HighlightField;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
*/ */
@ -36,13 +41,24 @@ public class PercolateShardResponse extends BroadcastShardOperationResponse {
private long count; private long count;
private float[] scores; private float[] scores;
private BytesRef[] matches; private BytesRef[] matches;
private List<Map<String, HighlightField>> hls = new ArrayList<Map<String, HighlightField>>();
private byte percolatorTypeId; private byte percolatorTypeId;
private int requestedSize; private int requestedSize;
public PercolateShardResponse() { PercolateShardResponse() {
} }
public PercolateShardResponse(BytesRef[] matches, long count, float[] scores, PercolatorService.PercolateContext context, String index, int shardId) { public PercolateShardResponse(BytesRef[] matches, List<Map<String, HighlightField>> hls, long count, float[] scores, PercolateContext context, String index, int shardId) {
super(index, shardId);
this.matches = matches;
this.hls = hls;
this.count = count;
this.scores = scores;
this.percolatorTypeId = context.percolatorTypeId;
this.requestedSize = context.size;
}
public PercolateShardResponse(BytesRef[] matches, long count, float[] scores, PercolateContext context, String index, int shardId) {
super(index, shardId); super(index, shardId);
this.matches = matches; this.matches = matches;
this.count = count; this.count = count;
@ -51,7 +67,7 @@ public class PercolateShardResponse extends BroadcastShardOperationResponse {
this.requestedSize = context.size; this.requestedSize = context.size;
} }
public PercolateShardResponse(BytesRef[] matches, long count, PercolatorService.PercolateContext context, String index, int shardId) { public PercolateShardResponse(BytesRef[] matches, long count, PercolateContext context, String index, int shardId) {
super(index, shardId); super(index, shardId);
this.matches = matches; this.matches = matches;
this.scores = new float[0]; this.scores = new float[0];
@ -60,7 +76,17 @@ public class PercolateShardResponse extends BroadcastShardOperationResponse {
this.requestedSize = context.size; this.requestedSize = context.size;
} }
public PercolateShardResponse(long count, PercolatorService.PercolateContext context, String index, int shardId) { public PercolateShardResponse(BytesRef[] matches, List<Map<String, HighlightField>> hls, long count, PercolateContext context, String index, int shardId) {
super(index, shardId);
this.matches = matches;
this.hls = hls;
this.scores = new float[0];
this.count = count;
this.percolatorTypeId = context.percolatorTypeId;
this.requestedSize = context.size;
}
public PercolateShardResponse(long count, PercolateContext context, String index, int shardId) {
super(index, shardId); super(index, shardId);
this.count = count; this.count = count;
this.matches = EMPTY; this.matches = EMPTY;
@ -69,7 +95,7 @@ public class PercolateShardResponse extends BroadcastShardOperationResponse {
this.requestedSize = context.size; this.requestedSize = context.size;
} }
public PercolateShardResponse(PercolatorService.PercolateContext context, String index, int shardId) { public PercolateShardResponse(PercolateContext context, String index, int shardId) {
super(index, shardId); super(index, shardId);
this.matches = EMPTY; this.matches = EMPTY;
this.scores = new float[0]; this.scores = new float[0];
@ -92,6 +118,10 @@ public class PercolateShardResponse extends BroadcastShardOperationResponse {
return requestedSize; return requestedSize;
} }
public List<Map<String, HighlightField>> hls() {
return hls;
}
public byte percolatorTypeId() { public byte percolatorTypeId() {
return percolatorTypeId; return percolatorTypeId;
} }
@ -103,6 +133,8 @@ public class PercolateShardResponse extends BroadcastShardOperationResponse {
@Override @Override
public void readFrom(StreamInput in) throws IOException { public void readFrom(StreamInput in) throws IOException {
super.readFrom(in); super.readFrom(in);
percolatorTypeId = in.readByte();
requestedSize = in.readVInt();
count = in.readVLong(); count = in.readVLong();
matches = new BytesRef[in.readVInt()]; matches = new BytesRef[in.readVInt()];
for (int i = 0; i < matches.length; i++) { for (int i = 0; i < matches.length; i++) {
@ -112,13 +144,22 @@ public class PercolateShardResponse extends BroadcastShardOperationResponse {
for (int i = 0; i < scores.length; i++) { for (int i = 0; i < scores.length; i++) {
scores[i] = in.readFloat(); scores[i] = in.readFloat();
} }
percolatorTypeId = in.readByte(); int size = in.readVInt();
requestedSize = in.readVInt(); for (int i = 0; i < size; i++) {
int mSize = in.readVInt();
Map<String, HighlightField> fields = new HashMap<String, HighlightField>();
for (int j = 0; j < mSize; j++) {
fields.put(in.readString(), HighlightField.readHighlightField(in));
}
hls.add(fields);
}
} }
@Override @Override
public void writeTo(StreamOutput out) throws IOException { public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out); super.writeTo(out);
out.writeByte(percolatorTypeId);
out.writeVLong(requestedSize);
out.writeVLong(count); out.writeVLong(count);
out.writeVInt(matches.length); out.writeVInt(matches.length);
for (BytesRef match : matches) { for (BytesRef match : matches) {
@ -128,7 +169,13 @@ public class PercolateShardResponse extends BroadcastShardOperationResponse {
for (float score : scores) { for (float score : scores) {
out.writeFloat(score); out.writeFloat(score);
} }
out.writeByte(percolatorTypeId); out.writeVInt(hls.size());
out.writeVLong(requestedSize); for (Map<String, HighlightField> hl : hls) {
out.writeVInt(hl.size());
for (Map.Entry<String, HighlightField> entry : hl.entrySet()) {
out.writeString(entry.getKey());
entry.getValue().writeTo(out);
}
}
} }
} }

View File

@ -26,6 +26,7 @@ import org.elasticsearch.common.xcontent.*;
import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilderException; import org.elasticsearch.search.builder.SearchSourceBuilderException;
import org.elasticsearch.search.highlight.HighlightBuilder;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
@ -41,6 +42,7 @@ public class PercolateSourceBuilder implements ToXContent {
private Integer size; private Integer size;
private Boolean sort; private Boolean sort;
private Boolean score; private Boolean score;
private HighlightBuilder highlightBuilder;
public DocBuilder percolateDocument() { public DocBuilder percolateDocument() {
if (docBuilder == null) { if (docBuilder == null) {
@ -91,6 +93,11 @@ public class PercolateSourceBuilder implements ToXContent {
return this; return this;
} }
public PercolateSourceBuilder setHighlightBuilder(HighlightBuilder highlightBuilder) {
this.highlightBuilder = highlightBuilder;
return this;
}
public BytesReference buildAsBytes(XContentType contentType) throws SearchSourceBuilderException { public BytesReference buildAsBytes(XContentType contentType) throws SearchSourceBuilderException {
try { try {
XContentBuilder builder = XContentFactory.contentBuilder(contentType); XContentBuilder builder = XContentFactory.contentBuilder(contentType);
@ -124,6 +131,9 @@ public class PercolateSourceBuilder implements ToXContent {
if (score != null) { if (score != null) {
builder.field("score", score); builder.field("score", score);
} }
if (highlightBuilder != null) {
highlightBuilder.toXContent(builder, params);
}
builder.endObject(); builder.endObject();
return builder; return builder;
} }

View File

@ -196,10 +196,10 @@ public class TransportPercolateAction extends TransportBroadcastOperationAction<
protected PercolateShardResponse shardOperation(PercolateShardRequest request) throws ElasticSearchException { protected PercolateShardResponse shardOperation(PercolateShardRequest request) throws ElasticSearchException {
try { try {
return percolatorService.percolate(request); return percolatorService.percolate(request);
} catch (Throwable t) { } catch (Throwable e) {
logger.trace("[{}][{}] failed to percolate", t, request.index(), request.shardId()); logger.trace("[{}][{}] failed to percolate", e, request.index(), request.shardId());
ShardId shardId = new ShardId(request.index(), request.shardId()); ShardId shardId = new ShardId(request.index(), request.shardId());
throw new PercolateException(shardId, "failed to percolate", t); throw new PercolateException(shardId, "failed to percolate", e);
} }
} }

View File

@ -21,8 +21,8 @@ package org.elasticsearch.index.mapper;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.elasticsearch.common.bytes.BytesReference;
import org.apache.lucene.document.Field; import org.apache.lucene.document.Field;
import org.elasticsearch.common.bytes.BytesReference;
import java.util.List; import java.util.List;
@ -47,7 +47,7 @@ public class ParsedDocument {
private final Analyzer analyzer; private final Analyzer analyzer;
private final BytesReference source; private BytesReference source;
private boolean mappingsModified; private boolean mappingsModified;
@ -111,6 +111,10 @@ public class ParsedDocument {
return this.source; return this.source;
} }
public void setSource(BytesReference source) {
this.source = source;
}
public ParsedDocument parent(String parent) { public ParsedDocument parent(String parent) {
this.parent = parent; this.parent = parent;
return this; return this;

View File

@ -0,0 +1,656 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch 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.percolator;
import com.google.common.collect.ImmutableList;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.memory.MemoryIndex;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.action.percolate.PercolateShardRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.cache.recycler.CacheRecycler;
import org.elasticsearch.common.lucene.HashedBytesRef;
import org.elasticsearch.common.text.StringText;
import org.elasticsearch.index.analysis.AnalysisService;
import org.elasticsearch.index.cache.docset.DocSetCache;
import org.elasticsearch.index.cache.filter.FilterCache;
import org.elasticsearch.index.cache.id.IdCache;
import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.index.fielddata.IndexFieldDataService;
import org.elasticsearch.index.fieldvisitor.JustSourceFieldsVisitor;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.FieldMappers;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.ParsedDocument;
import org.elasticsearch.index.query.IndexQueryParserService;
import org.elasticsearch.index.query.ParsedFilter;
import org.elasticsearch.index.query.ParsedQuery;
import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.shard.service.IndexShard;
import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHitField;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.dfs.DfsSearchResult;
import org.elasticsearch.search.facet.SearchContextFacets;
import org.elasticsearch.search.fetch.FetchSearchResult;
import org.elasticsearch.search.fetch.FetchSubPhase;
import org.elasticsearch.search.fetch.partial.PartialFieldsContext;
import org.elasticsearch.search.fetch.script.ScriptFieldsContext;
import org.elasticsearch.search.fetch.source.FetchSourceContext;
import org.elasticsearch.search.highlight.SearchContextHighlight;
import org.elasticsearch.search.internal.*;
import org.elasticsearch.search.lookup.SearchLookup;
import org.elasticsearch.search.query.QuerySearchResult;
import org.elasticsearch.search.rescore.RescoreSearchContext;
import org.elasticsearch.search.scan.ScanContext;
import org.elasticsearch.search.suggest.SuggestionSearchContext;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
/**
*/
public class PercolateContext extends SearchContext {
public boolean limit;
public int size;
public boolean score;
public boolean sort;
public byte percolatorTypeId;
private final PercolateShardRequest request;
private final SearchShardTarget searchShardTarget;
private final IndexService indexService;
private final IndexFieldDataService fieldDataService;
private final IndexShard indexShard;
private final ConcurrentMap<HashedBytesRef, Query> percolateQueries;
private String[] types;
private Engine.Searcher docEngineSearcher;
private SearchContextHighlight highlight;
private SearchLookup searchLookup;
private ParsedQuery parsedQuery;
private Query query;
private boolean queryRewritten;
private Query percolateQuery;
private FetchSubPhase.HitContext hitContext;
public PercolateContext(PercolateShardRequest request, SearchShardTarget searchShardTarget, IndexShard indexShard, IndexService indexService) {
this.request = request;
this.indexShard = indexShard;
this.indexService = indexService;
this.fieldDataService = indexService.fieldData();
this.searchShardTarget = searchShardTarget;
this.percolateQueries = indexShard.percolateRegistry().percolateQueries();
this.types = new String[]{request.documentType()};
}
public void initialize(final MemoryIndex memoryIndex, ParsedDocument parsedDocument) {
final IndexSearcher docSearcher = memoryIndex.createSearcher();
final IndexReader topLevelReader = docSearcher.getIndexReader();
AtomicReaderContext readerContext = topLevelReader.leaves().get(0);
docEngineSearcher = new Engine.Searcher() {
@Override
public IndexReader reader() {
return topLevelReader;
}
@Override
public IndexSearcher searcher() {
return docSearcher;
}
@Override
public boolean release() throws ElasticSearchException {
try {
docSearcher.getIndexReader().close();
memoryIndex.reset();
} catch (IOException e) {
throw new ElasticSearchException("failed to close percolator in-memory index", e);
}
return true;
}
};
lookup().setNextReader(readerContext);
lookup().setNextDocId(0);
lookup().source().setNextSource(parsedDocument.source());
Map<String, SearchHitField> fields = new HashMap<String, SearchHitField>();
for (IndexableField field : parsedDocument.rootDoc().getFields()) {
fields.put(field.name(), new InternalSearchHitField(field.name(), ImmutableList.of()));
}
hitContext = new FetchSubPhase.HitContext();
hitContext.reset(new InternalSearchHit(0, "unknown", new StringText(request.documentType()), fields), readerContext, 0, topLevelReader, 0, new JustSourceFieldsVisitor());
}
public IndexSearcher docSearcher() {
return docEngineSearcher.searcher();
}
public IndexShard indexShard() {
return indexShard;
}
public IndexService indexService() {
return indexService;
}
public ConcurrentMap<HashedBytesRef, Query> percolateQueries() {
return percolateQueries;
}
public Query percolateQuery() {
return percolateQuery;
}
public void percolateQuery(Query percolateQuery) {
this.percolateQuery = percolateQuery;
}
public FetchSubPhase.HitContext hitContext() {
return hitContext;
}
@Override
public SearchContextHighlight highlight() {
return highlight;
}
@Override
public void highlight(SearchContextHighlight highlight) {
this.highlight = highlight;
}
@Override
public SearchShardTarget shardTarget() {
return searchShardTarget;
}
@Override
public SearchLookup lookup() {
if (searchLookup == null) {
searchLookup = new SearchLookup(mapperService(), fieldData(), types);
}
return searchLookup;
}
@Override
public boolean release() throws ElasticSearchException {
if (docEngineSearcher != null) {
IndexReader indexReader = docEngineSearcher.reader();
fieldDataService.clear(indexReader);
indexService.cache().clear(indexReader);
return docEngineSearcher.release();
} else {
return false;
}
}
@Override
public MapperService mapperService() {
return indexService.mapperService();
}
@Override
public SearchContext parsedQuery(ParsedQuery query) {
parsedQuery = query;
this.query = query.query();
queryRewritten = false;
return this;
}
@Override
public ParsedQuery parsedQuery() {
return parsedQuery;
}
@Override
public Query query() {
return query;
}
@Override
public boolean queryRewritten() {
return queryRewritten;
}
@Override
public SearchContext updateRewriteQuery(Query rewriteQuery) {
queryRewritten = true;
query = rewriteQuery;
return this;
}
@Override
public String[] types() {
return types;
}
public void types(String[] types) {
this.types = types;
searchLookup = new SearchLookup(mapperService(), fieldData(), types);
}
@Override
public IndexFieldDataService fieldData() {
return fieldDataService;
}
// Unused:
@Override
public void preProcess() {
throw new UnsupportedOperationException();
}
@Override
public Filter searchFilter(String[] types) {
throw new UnsupportedOperationException();
}
@Override
public long id() {
throw new UnsupportedOperationException();
}
@Override
public ShardSearchRequest request() {
throw new UnsupportedOperationException();
}
@Override
public SearchType searchType() {
throw new UnsupportedOperationException();
}
@Override
public SearchContext searchType(SearchType searchType) {
throw new UnsupportedOperationException();
}
@Override
public int numberOfShards() {
throw new UnsupportedOperationException();
}
@Override
public boolean hasTypes() {
throw new UnsupportedOperationException();
}
@Override
public float queryBoost() {
throw new UnsupportedOperationException();
}
@Override
public SearchContext queryBoost(float queryBoost) {
throw new UnsupportedOperationException();
}
@Override
public long nowInMillis() {
throw new UnsupportedOperationException();
}
@Override
public Scroll scroll() {
throw new UnsupportedOperationException();
}
@Override
public SearchContext scroll(Scroll scroll) {
throw new UnsupportedOperationException();
}
@Override
public SearchContextFacets facets() {
throw new UnsupportedOperationException();
}
@Override
public SearchContext facets(SearchContextFacets facets) {
throw new UnsupportedOperationException();
}
@Override
public SuggestionSearchContext suggest() {
throw new UnsupportedOperationException();
}
@Override
public void suggest(SuggestionSearchContext suggest) {
throw new UnsupportedOperationException();
}
@Override
public RescoreSearchContext rescore() {
throw new UnsupportedOperationException();
}
@Override
public void rescore(RescoreSearchContext rescore) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasScriptFields() {
throw new UnsupportedOperationException();
}
@Override
public ScriptFieldsContext scriptFields() {
throw new UnsupportedOperationException();
}
@Override
public boolean hasPartialFields() {
throw new UnsupportedOperationException();
}
@Override
public PartialFieldsContext partialFields() {
throw new UnsupportedOperationException();
}
@Override
public boolean sourceRequested() {
throw new UnsupportedOperationException();
}
@Override
public boolean hasFetchSourceContext() {
throw new UnsupportedOperationException();
}
@Override
public FetchSourceContext fetchSourceContext() {
throw new UnsupportedOperationException();
}
@Override
public SearchContext fetchSourceContext(FetchSourceContext fetchSourceContext) {
throw new UnsupportedOperationException();
}
@Override
public ContextIndexSearcher searcher() {
throw new UnsupportedOperationException();
}
@Override
public AnalysisService analysisService() {
throw new UnsupportedOperationException();
}
@Override
public IndexQueryParserService queryParserService() {
throw new UnsupportedOperationException();
}
@Override
public SimilarityService similarityService() {
throw new UnsupportedOperationException();
}
@Override
public ScriptService scriptService() {
throw new UnsupportedOperationException();
}
@Override
public CacheRecycler cacheRecycler() {
throw new UnsupportedOperationException();
}
@Override
public FilterCache filterCache() {
throw new UnsupportedOperationException();
}
@Override
public DocSetCache docSetCache() {
throw new UnsupportedOperationException();
}
@Override
public IdCache idCache() {
throw new UnsupportedOperationException();
}
@Override
public long timeoutInMillis() {
throw new UnsupportedOperationException();
}
@Override
public void timeoutInMillis(long timeoutInMillis) {
throw new UnsupportedOperationException();
}
@Override
public SearchContext minimumScore(float minimumScore) {
throw new UnsupportedOperationException();
}
@Override
public Float minimumScore() {
throw new UnsupportedOperationException();
}
@Override
public SearchContext sort(Sort sort) {
throw new UnsupportedOperationException();
}
@Override
public Sort sort() {
throw new UnsupportedOperationException();
}
@Override
public SearchContext trackScores(boolean trackScores) {
throw new UnsupportedOperationException();
}
@Override
public boolean trackScores() {
throw new UnsupportedOperationException();
}
@Override
public SearchContext parsedFilter(ParsedFilter filter) {
throw new UnsupportedOperationException();
}
@Override
public ParsedFilter parsedFilter() {
throw new UnsupportedOperationException();
}
@Override
public Filter aliasFilter() {
throw new UnsupportedOperationException();
}
@Override
public int from() {
throw new UnsupportedOperationException();
}
@Override
public SearchContext from(int from) {
throw new UnsupportedOperationException();
}
@Override
public int size() {
throw new UnsupportedOperationException();
}
@Override
public SearchContext size(int size) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasFieldNames() {
throw new UnsupportedOperationException();
}
@Override
public List<String> fieldNames() {
throw new UnsupportedOperationException();
}
@Override
public void emptyFieldNames() {
throw new UnsupportedOperationException();
}
@Override
public boolean explain() {
throw new UnsupportedOperationException();
}
@Override
public void explain(boolean explain) {
throw new UnsupportedOperationException();
}
@Override
public List<String> groupStats() {
throw new UnsupportedOperationException();
}
@Override
public void groupStats(List<String> groupStats) {
throw new UnsupportedOperationException();
}
@Override
public boolean version() {
throw new UnsupportedOperationException();
}
@Override
public void version(boolean version) {
throw new UnsupportedOperationException();
}
@Override
public int[] docIdsToLoad() {
throw new UnsupportedOperationException();
}
@Override
public int docIdsToLoadFrom() {
throw new UnsupportedOperationException();
}
@Override
public int docIdsToLoadSize() {
throw new UnsupportedOperationException();
}
@Override
public SearchContext docIdsToLoad(int[] docIdsToLoad, int docsIdsToLoadFrom, int docsIdsToLoadSize) {
throw new UnsupportedOperationException();
}
@Override
public void accessed(long accessTime) {
throw new UnsupportedOperationException();
}
@Override
public long lastAccessTime() {
throw new UnsupportedOperationException();
}
@Override
public long keepAlive() {
throw new UnsupportedOperationException();
}
@Override
public void keepAlive(long keepAlive) {
throw new UnsupportedOperationException();
}
@Override
public DfsSearchResult dfsResult() {
throw new UnsupportedOperationException();
}
@Override
public QuerySearchResult queryResult() {
throw new UnsupportedOperationException();
}
@Override
public FetchSearchResult fetchResult() {
throw new UnsupportedOperationException();
}
@Override
public void addRewrite(Rewrite rewrite) {
throw new UnsupportedOperationException();
}
@Override
public List<Rewrite> rewrites() {
throw new UnsupportedOperationException();
}
@Override
public ScanContext scanContext() {
throw new UnsupportedOperationException();
}
@Override
public MapperService.SmartNameFieldMappers smartFieldMappers(String name) {
throw new UnsupportedOperationException();
}
@Override
public FieldMappers smartNameFieldMappers(String name) {
throw new UnsupportedOperationException();
}
@Override
public FieldMapper smartNameFieldMapper(String name) {
throw new UnsupportedOperationException();
}
@Override
public MapperService.SmartNameObjectMapper smartNameObjectMapper(String name) {
throw new UnsupportedOperationException();
}
}

View File

@ -18,6 +18,7 @@
package org.elasticsearch.percolator; package org.elasticsearch.percolator;
import com.google.common.collect.ImmutableMap;
import gnu.trove.map.hash.TByteObjectHashMap; import gnu.trove.map.hash.TByteObjectHashMap;
import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.AtomicReaderContext;
@ -34,10 +35,12 @@ import org.elasticsearch.ElasticSearchParseException;
import org.elasticsearch.action.percolate.PercolateResponse; import org.elasticsearch.action.percolate.PercolateResponse;
import org.elasticsearch.action.percolate.PercolateShardRequest; import org.elasticsearch.action.percolate.PercolateShardRequest;
import org.elasticsearch.action.percolate.PercolateShardResponse; import org.elasticsearch.action.percolate.PercolateShardResponse;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.lucene.HashedBytesRef; import org.elasticsearch.common.lucene.HashedBytesRef;
import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.lucene.search.XConstantScoreQuery; import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
@ -48,14 +51,14 @@ import org.elasticsearch.common.text.StringText;
import org.elasticsearch.common.text.Text; import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.cache.IndexCache; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.index.fielddata.BytesValues; import org.elasticsearch.index.fielddata.BytesValues;
import org.elasticsearch.index.fielddata.FieldDataType; import org.elasticsearch.index.fielddata.FieldDataType;
import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.IndexFieldDataService;
import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
@ -63,17 +66,20 @@ import org.elasticsearch.index.mapper.ParsedDocument;
import org.elasticsearch.index.mapper.internal.IdFieldMapper; import org.elasticsearch.index.mapper.internal.IdFieldMapper;
import org.elasticsearch.index.mapper.internal.UidFieldMapper; import org.elasticsearch.index.mapper.internal.UidFieldMapper;
import org.elasticsearch.index.percolator.stats.ShardPercolateService; import org.elasticsearch.index.percolator.stats.ShardPercolateService;
import org.elasticsearch.index.query.ParsedQuery;
import org.elasticsearch.index.service.IndexService; import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.shard.service.IndexShard; import org.elasticsearch.index.shard.service.IndexShard;
import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.search.SearchParseElement;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.highlight.HighlightField;
import org.elasticsearch.search.highlight.HighlightPhase;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.internal.ShardSearchRequest;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import static org.elasticsearch.index.mapper.SourceToParse.source; import static org.elasticsearch.index.mapper.SourceToParse.source;
import static org.elasticsearch.percolator.QueryCollector.*; import static org.elasticsearch.percolator.QueryCollector.*;
@ -88,15 +94,22 @@ public class PercolatorService extends AbstractComponent {
private final IndicesService indicesService; private final IndicesService indicesService;
private final TByteObjectHashMap<PercolatorType> percolatorTypes; private final TByteObjectHashMap<PercolatorType> percolatorTypes;
private final ClusterService clusterService;
private final HighlightPhase highlightPhase;
@Inject @Inject
public PercolatorService(Settings settings, IndicesService indicesService) { public PercolatorService(Settings settings, IndicesService indicesService, HighlightPhase highlightPhase, ClusterService clusterService) {
super(settings); super(settings);
this.indicesService = indicesService; this.indicesService = indicesService;
this.clusterService = clusterService;
this.highlightPhase = highlightPhase;
final long maxReuseBytes = settings.getAsBytesSize("indices.memory.memory_index.size_per_thread", new ByteSizeValue(1, ByteSizeUnit.MB)).bytes(); final long maxReuseBytes = settings.getAsBytesSize("indices.memory.memory_index.size_per_thread", new ByteSizeValue(1, ByteSizeUnit.MB)).bytes();
cache = new CloseableThreadLocal<MemoryIndex>() { cache = new CloseableThreadLocal<MemoryIndex>() {
@Override @Override
protected MemoryIndex initialValue() { protected MemoryIndex initialValue() {
return new ExtendedMemoryIndex(false, maxReuseBytes); return new ExtendedMemoryIndex(true, maxReuseBytes);
} }
}; };
@ -124,12 +137,13 @@ public class PercolatorService extends AbstractComponent {
long startTime = System.nanoTime(); long startTime = System.nanoTime();
try { try {
final PercolateContext context = new PercolateContext(); SearchShardTarget searchShardTarget = new SearchShardTarget(clusterService.localNode().id(), request.index(), request.shardId());
context.percolateQueries = indexShard.percolateRegistry().percolateQueries(); final PercolateContext context = new PercolateContext(
context.indexShard = indexShard; request, searchShardTarget, indexShard, percolateIndexService
context.percolateIndexService = percolateIndexService; );
ParsedDocument parsedDocument = parsePercolate(percolateIndexService, request, context);
if (context.percolateQueries.isEmpty()) { ParsedDocument parsedDocument = parseRequest(percolateIndexService, request, context);
if (context.percolateQueries().isEmpty()) {
return new PercolateShardResponse(context, request.index(), request.shardId()); return new PercolateShardResponse(context, request.index(), request.shardId());
} }
@ -139,7 +153,7 @@ public class PercolatorService extends AbstractComponent {
throw new ElasticSearchIllegalArgumentException("Nothing to percolate"); throw new ElasticSearchIllegalArgumentException("Nothing to percolate");
} }
if (context.query == null && (context.score || context.sort)) { if (context.percolateQuery() == null && (context.score || context.sort)) {
throw new ElasticSearchIllegalArgumentException("Can't sort or score if query isn't specified"); throw new ElasticSearchIllegalArgumentException("Can't sort or score if query isn't specified");
} }
@ -147,6 +161,10 @@ public class PercolatorService extends AbstractComponent {
throw new ElasticSearchIllegalArgumentException("Can't sort if size isn't specified"); throw new ElasticSearchIllegalArgumentException("Can't sort if size isn't specified");
} }
if (context.highlight() != null && !context.limit) {
throw new ElasticSearchIllegalArgumentException("Can't highlight if size isn't specified");
}
if (context.size < 0) { if (context.size < 0) {
context.size = 0; context.size = 0;
} }
@ -177,11 +195,11 @@ public class PercolatorService extends AbstractComponent {
PercolatorType action; PercolatorType action;
if (request.onlyCount()) { if (request.onlyCount()) {
action = context.query != null ? queryCountPercolator : countPercolator; action = context.percolateQuery() != null ? queryCountPercolator : countPercolator;
} else { } else {
if (context.sort) { if (context.sort) {
action = topMatchingPercolator; action = topMatchingPercolator;
} else if (context.query != null) { } else if (context.percolateQuery() != null) {
action = context.score ? scoringPercolator : queryPercolator; action = context.score ? scoringPercolator : queryPercolator;
} else { } else {
action = matchPercolator; action = matchPercolator;
@ -189,39 +207,33 @@ public class PercolatorService extends AbstractComponent {
} }
context.percolatorTypeId = action.id(); context.percolatorTypeId = action.id();
context.docSearcher = memoryIndex.createSearcher(); context.initialize(memoryIndex, parsedDocument);
context.fieldData = percolateIndexService.fieldData();
IndexCache indexCache = percolateIndexService.cache();
try {
return action.doPercolate(request, context); return action.doPercolate(request, context);
} finally { } finally {
// explicitly clear the reader, since we can only register on callback on SegmentReader context.release();
indexCache.clear(context.docSearcher.getIndexReader());
context.fieldData.clear(context.docSearcher.getIndexReader());
}
} finally {
memoryIndex.reset();
} }
} finally { } finally {
shardPercolateService.postPercolate(System.nanoTime() - startTime); shardPercolateService.postPercolate(System.nanoTime() - startTime);
} }
} }
private ParsedDocument parsePercolate(IndexService documentIndexService, PercolateShardRequest request, PercolateContext context) throws ElasticSearchException { private ParsedDocument parseRequest(IndexService documentIndexService, PercolateShardRequest request, PercolateContext context) throws ElasticSearchException {
BytesReference source = request.source(); BytesReference source = request.source();
if (source == null || source.length() == 0) { if (source == null || source.length() == 0) {
return null; return null;
} }
Map<String, ? extends SearchParseElement> hlElements = highlightPhase.parseElements();
ParsedDocument doc = null; ParsedDocument doc = null;
XContentParser parser = null; XContentParser parser = null;
// Some queries (function_score query when for decay functions) rely on SearchContext being set: // Some queries (function_score query when for decay functions) rely on a SearchContext being set:
SearchContext searchContext = new SearchContext(0, // We switch types because this context needs to be in the context of the percolate queries in the shard and
new ShardSearchRequest().types(new String[0]), // not the in memory percolate doc
null, context.indexShard.searcher(), context.percolateIndexService, context.indexShard, String[] previousTypes = context.types();
null, null); context.types(new String[]{Constants.TYPE_NAME});
SearchContext.setCurrent(searchContext); SearchContext.setCurrent(context);
try { try {
parser = XContentFactory.xContent(source).createParser(source); parser = XContentFactory.xContent(source).createParser(source);
String currentFieldName = null; String currentFieldName = null;
@ -241,17 +253,20 @@ public class PercolatorService extends AbstractComponent {
doc = docMapper.parse(source(parser).type(request.documentType()).flyweight(true)); doc = docMapper.parse(source(parser).type(request.documentType()).flyweight(true));
} }
} else if (token == XContentParser.Token.START_OBJECT) { } else if (token == XContentParser.Token.START_OBJECT) {
SearchParseElement element = hlElements.get(currentFieldName);
if ("query".equals(currentFieldName)) { if ("query".equals(currentFieldName)) {
if (context.query != null) { if (context.percolateQuery() != null) {
throw new ElasticSearchParseException("Either specify query or filter, not both"); throw new ElasticSearchParseException("Either specify query or filter, not both");
} }
context.query = documentIndexService.queryParserService().parse(parser).query(); context.percolateQuery(documentIndexService.queryParserService().parse(parser).query());
} else if ("filter".equals(currentFieldName)) { } else if ("filter".equals(currentFieldName)) {
if (context.query != null) { if (context.percolateQuery() != null) {
throw new ElasticSearchParseException("Either specify query or filter, not both"); throw new ElasticSearchParseException("Either specify query or filter, not both");
} }
Filter filter = documentIndexService.queryParserService().parseInnerFilter(parser).filter(); Filter filter = documentIndexService.queryParserService().parseInnerFilter(parser).filter();
context.query = new XConstantScoreQuery(filter); context.percolateQuery(new XConstantScoreQuery(filter));
} else if (element != null) {
element.parse(parser, context);
} }
} else if (token == null) { } else if (token == null) {
break; break;
@ -269,10 +284,39 @@ public class PercolatorService extends AbstractComponent {
} }
} }
} }
} catch (IOException e) {
// We need to get the actual source from the request body for highlighting, so parse the request body again
// and only get the doc source.
if (context.highlight() != null) {
parser.close();
currentFieldName = null;
parser = XContentFactory.xContent(source).createParser(source);
token = parser.nextToken();
assert token == XContentParser.Token.START_OBJECT;
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 ("doc".equals(currentFieldName)) {
BytesStreamOutput bStream = new BytesStreamOutput();
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.SMILE, bStream);
builder.copyCurrentStructure(parser);
builder.close();
doc.setSource(bStream.bytes());
break;
} else {
parser.skipChildren();
}
} else if (token == null) {
break;
}
}
}
} catch (Throwable e) {
throw new ElasticSearchParseException("failed to parse request", e); throw new ElasticSearchParseException("failed to parse request", e);
} finally { } finally {
searchContext.release(); context.types(previousTypes);
SearchContext.removeCurrent(); SearchContext.removeCurrent();
if (parser != null) { if (parser != null) {
parser.close(); parser.close();
@ -290,7 +334,7 @@ public class PercolatorService extends AbstractComponent {
MapperService mapperService = documentIndexService.mapperService(); MapperService mapperService = documentIndexService.mapperService();
DocumentMapper docMapper = mapperService.documentMapperWithAutoCreate(type); DocumentMapper docMapper = mapperService.documentMapperWithAutoCreate(type);
doc = docMapper.parse(source(parser).type(type).flyweight(true)); doc = docMapper.parse(source(parser).type(type).flyweight(true));
} catch (IOException e) { } catch (Throwable e) {
throw new ElasticSearchParseException("failed to parse request", e); throw new ElasticSearchParseException("failed to parse request", e);
} finally { } finally {
if (parser != null) { if (parser != null) {
@ -340,10 +384,10 @@ public class PercolatorService extends AbstractComponent {
public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) { public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) {
long count = 0; long count = 0;
Lucene.ExistsCollector collector = new Lucene.ExistsCollector(); Lucene.ExistsCollector collector = new Lucene.ExistsCollector();
for (Map.Entry<HashedBytesRef, Query> entry : context.percolateQueries.entrySet()) { for (Map.Entry<HashedBytesRef, Query> entry : context.percolateQueries().entrySet()) {
collector.reset(); collector.reset();
try { try {
context.docSearcher.search(entry.getValue(), collector); context.docSearcher().search(entry.getValue(), collector);
} catch (IOException e) { } catch (IOException e) {
logger.warn("[" + entry.getKey() + "] failed to execute query", e); logger.warn("[" + entry.getKey() + "] failed to execute query", e);
} }
@ -372,12 +416,12 @@ public class PercolatorService extends AbstractComponent {
@Override @Override
public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) { public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) {
long count = 0; long count = 0;
Engine.Searcher percolatorSearcher = context.indexShard.searcher(); Engine.Searcher percolatorSearcher = context.indexShard().searcher();
try { try {
Count countCollector = count(logger, context); Count countCollector = count(logger, context);
queryBasedPercolating(percolatorSearcher, context, countCollector); queryBasedPercolating(percolatorSearcher, context, countCollector);
count = countCollector.counter(); count = countCollector.counter();
} catch (IOException e) { } catch (Throwable e) {
logger.warn("failed to execute", e); logger.warn("failed to execute", e);
} finally { } finally {
percolatorSearcher.release(); percolatorSearcher.release();
@ -411,7 +455,8 @@ public class PercolatorService extends AbstractComponent {
for (int i = 0; i < response.matches().length; i++) { for (int i = 0; i < response.matches().length; i++) {
float score = response.scores().length == 0 ? NO_SCORE : response.scores()[i]; float score = response.scores().length == 0 ? NO_SCORE : response.scores()[i];
Text match = new BytesText(new BytesArray(response.matches()[i])); Text match = new BytesText(new BytesArray(response.matches()[i]));
finalMatches.add(new PercolateResponse.Match(index, match, score)); Map<String, HighlightField> hl = response.hls().isEmpty() ? null : response.hls().get(i);
finalMatches.add(new PercolateResponse.Match(index, match, score, hl));
if (requestedSize != 0 && finalMatches.size() == requestedSize) { if (requestedSize != 0 && finalMatches.size() == requestedSize) {
break outer; break outer;
} }
@ -424,24 +469,35 @@ public class PercolatorService extends AbstractComponent {
public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) { public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) {
long count = 0; long count = 0;
List<BytesRef> matches = new ArrayList<BytesRef>(); List<BytesRef> matches = new ArrayList<BytesRef>();
List<Map<String, HighlightField>> hls = new ArrayList<Map<String, HighlightField>>();
Lucene.ExistsCollector collector = new Lucene.ExistsCollector(); Lucene.ExistsCollector collector = new Lucene.ExistsCollector();
for (Map.Entry<HashedBytesRef, Query> entry : context.percolateQueries.entrySet()) { for (Map.Entry<HashedBytesRef, Query> entry : context.percolateQueries().entrySet()) {
collector.reset(); collector.reset();
if (context.highlight() != null) {
context.parsedQuery(new ParsedQuery(entry.getValue(), ImmutableMap.<String, Filter>of()));
context.hitContext().cache().clear();
}
try { try {
context.docSearcher.search(entry.getValue(), collector); context.docSearcher().search(entry.getValue(), collector);
} catch (IOException e) { } catch (Throwable e) {
logger.warn("[" + entry.getKey() + "] failed to execute query", e); logger.warn("[" + entry.getKey() + "] failed to execute query", e);
} }
if (collector.exists()) { if (collector.exists()) {
if (!context.limit || count < context.size) { if (!context.limit || count < context.size) {
matches.add(entry.getKey().bytes); matches.add(entry.getKey().bytes);
if (context.highlight() != null) {
highlightPhase.hitExecute(context, context.hitContext());
hls.add(context.hitContext().hit().getHighlightFields());
}
} }
count++; count++;
} }
} }
return new PercolateShardResponse(matches.toArray(new BytesRef[0]), count, context, request.index(), request.shardId());
BytesRef[] finalMatches = matches.toArray(new BytesRef[matches.size()]);
return new PercolateShardResponse(finalMatches, hls, count, context, request.index(), request.shardId());
} }
}; };
@ -459,16 +515,19 @@ public class PercolatorService extends AbstractComponent {
@Override @Override
public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) { public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) {
Engine.Searcher percolatorSearcher = context.indexShard.searcher(); Engine.Searcher percolatorSearcher = context.indexShard().searcher();
try { try {
Match match = match(logger, context); Match match = match(logger, context, highlightPhase);
queryBasedPercolating(percolatorSearcher, context, match); queryBasedPercolating(percolatorSearcher, context, match);
List<BytesRef> matches = match.matches(); List<BytesRef> matches = match.matches();
List<Map<String, HighlightField>> hls = match.hls();
long count = match.counter(); long count = match.counter();
return new PercolateShardResponse(matches.toArray(new BytesRef[0]), count, context, request.index(), request.shardId());
} catch (IOException e) { BytesRef[] finalMatches = matches.toArray(new BytesRef[matches.size()]);
return new PercolateShardResponse(finalMatches, hls, count, context, request.index(), request.shardId());
} catch (Throwable e) {
logger.debug("failed to execute", e); logger.debug("failed to execute", e);
throw new PercolateException(context.indexShard.shardId(), "failed to execute", e); throw new PercolateException(context.indexShard().shardId(), "failed to execute", e);
} finally { } finally {
percolatorSearcher.release(); percolatorSearcher.release();
} }
@ -489,17 +548,20 @@ public class PercolatorService extends AbstractComponent {
@Override @Override
public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) { public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) {
Engine.Searcher percolatorSearcher = context.indexShard.searcher(); Engine.Searcher percolatorSearcher = context.indexShard().searcher();
try { try {
MatchAndScore matchAndScore = matchAndScore(logger, context); MatchAndScore matchAndScore = matchAndScore(logger, context, highlightPhase);
queryBasedPercolating(percolatorSearcher, context, matchAndScore); queryBasedPercolating(percolatorSearcher, context, matchAndScore);
BytesRef[] matches = matchAndScore.matches().toArray(new BytesRef[0]); List<BytesRef> matches = matchAndScore.matches();
List<Map<String, HighlightField>> hls = matchAndScore.hls();
float[] scores = matchAndScore.scores().toArray(); float[] scores = matchAndScore.scores().toArray();
long count = matchAndScore.counter(); long count = matchAndScore.counter();
return new PercolateShardResponse(matches, count, scores, context, request.index(), request.shardId());
} catch (IOException e) { BytesRef[] finalMatches = matches.toArray(new BytesRef[matches.size()]);
return new PercolateShardResponse(finalMatches, hls, count, scores, context, request.index(), request.shardId());
} catch (Throwable e) {
logger.debug("failed to execute", e); logger.debug("failed to execute", e);
throw new PercolateException(context.indexShard.shardId(), "failed to execute", e); throw new PercolateException(context.indexShard().shardId(), "failed to execute", e);
} finally { } finally {
percolatorSearcher.release(); percolatorSearcher.release();
} }
@ -539,8 +601,13 @@ public class PercolatorService extends AbstractComponent {
for (int i = 0; i < response.matches().length; i++) { for (int i = 0; i < response.matches().length; i++) {
float score = response.scores().length == 0 ? Float.NaN : response.scores()[i]; float score = response.scores().length == 0 ? Float.NaN : response.scores()[i];
Text match = new BytesText(new BytesArray(response.matches()[i])); Text match = new BytesText(new BytesArray(response.matches()[i]));
if (!response.hls().isEmpty()) {
Map<String, HighlightField> hl = response.hls().get(i);
finalMatches.add(new PercolateResponse.Match(index, match, score, hl));
} else {
finalMatches.add(new PercolateResponse.Match(index, match, score)); finalMatches.add(new PercolateResponse.Match(index, match, score));
} }
}
} else { } else {
int[] slots = new int[shardResults.size()]; int[] slots = new int[shardResults.size()];
while (true) { while (true) {
@ -575,7 +642,12 @@ public class PercolatorService extends AbstractComponent {
Text index = new StringText(shardResponse.getIndex()); Text index = new StringText(shardResponse.getIndex());
Text match = new BytesText(new BytesArray(shardResponse.matches()[itemIndex])); Text match = new BytesText(new BytesArray(shardResponse.matches()[itemIndex]));
float score = shardResponse.scores()[itemIndex]; float score = shardResponse.scores()[itemIndex];
if (!shardResponse.hls().isEmpty()) {
Map<String, HighlightField> hl = shardResponse.hls().get(itemIndex);
finalMatches.add(new PercolateResponse.Match(index, match, score, hl));
} else {
finalMatches.add(new PercolateResponse.Match(index, match, score)); finalMatches.add(new PercolateResponse.Match(index, match, score));
}
if (finalMatches.size() == requestedSize) { if (finalMatches.size() == requestedSize) {
break; break;
} }
@ -586,7 +658,7 @@ public class PercolatorService extends AbstractComponent {
@Override @Override
public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) { public PercolateShardResponse doPercolate(PercolateShardRequest request, PercolateContext context) {
Engine.Searcher percolatorSearcher = context.indexShard.searcher(); Engine.Searcher percolatorSearcher = context.indexShard().searcher();
try { try {
MatchAndSort matchAndSort = QueryCollector.matchAndSort(logger, context); MatchAndSort matchAndSort = QueryCollector.matchAndSort(logger, context);
queryBasedPercolating(percolatorSearcher, context, matchAndSort); queryBasedPercolating(percolatorSearcher, context, matchAndSort);
@ -594,24 +666,40 @@ public class PercolatorService extends AbstractComponent {
long count = topDocs.totalHits; long count = topDocs.totalHits;
List<BytesRef> matches = new ArrayList<BytesRef>(topDocs.scoreDocs.length); List<BytesRef> matches = new ArrayList<BytesRef>(topDocs.scoreDocs.length);
float[] scores = new float[topDocs.scoreDocs.length]; float[] scores = new float[topDocs.scoreDocs.length];
List<Map<String, HighlightField>> hls = null;
if (context.highlight() != null) {
hls = new ArrayList<Map<String, HighlightField>>(topDocs.scoreDocs.length);
}
IndexFieldData idFieldData = context.fieldData.getForField( IndexFieldData idFieldData = context.fieldData().getForField(
new FieldMapper.Names(IdFieldMapper.NAME), new FieldMapper.Names(IdFieldMapper.NAME),
new FieldDataType("string", ImmutableSettings.builder().put("format", "paged_bytes")) new FieldDataType("string", ImmutableSettings.builder().put("format", "paged_bytes"))
); );
int i = 0; int i = 0;
final HashedBytesRef spare = new HashedBytesRef(new BytesRef());
for (ScoreDoc scoreDoc : topDocs.scoreDocs) { for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
int segmentIdx = ReaderUtil.subIndex(scoreDoc.doc, percolatorSearcher.reader().leaves()); int segmentIdx = ReaderUtil.subIndex(scoreDoc.doc, percolatorSearcher.reader().leaves());
AtomicReaderContext atomicReaderContext = percolatorSearcher.reader().leaves().get(segmentIdx); AtomicReaderContext atomicReaderContext = percolatorSearcher.reader().leaves().get(segmentIdx);
BytesValues values = idFieldData.load(atomicReaderContext).getBytesValues(); BytesValues values = idFieldData.load(atomicReaderContext).getBytesValues();
BytesRef id = values.getValue(scoreDoc.doc - atomicReaderContext.docBase); spare.hash = values.getValueHashed(scoreDoc.doc - atomicReaderContext.docBase, spare.bytes);
matches.add(values.makeSafe(id)); matches.add(values.makeSafe(spare.bytes));
if (hls != null) {
Query query = context.percolateQueries().get(spare);
context.parsedQuery(new ParsedQuery(query, ImmutableMap.<String, Filter>of()));
context.hitContext().cache().clear();
highlightPhase.hitExecute(context, context.hitContext());
hls.add(i, context.hitContext().hit().getHighlightFields());
}
scores[i++] = scoreDoc.score; scores[i++] = scoreDoc.score;
} }
if (hls != null) {
return new PercolateShardResponse(matches.toArray(new BytesRef[matches.size()]), hls, count, scores, context, request.index(), request.shardId());
} else {
return new PercolateShardResponse(matches.toArray(new BytesRef[matches.size()]), count, scores, context, request.index(), request.shardId()); return new PercolateShardResponse(matches.toArray(new BytesRef[matches.size()]), count, scores, context, request.index(), request.shardId());
} catch (Exception e) { }
} catch (Throwable e) {
logger.debug("failed to execute", e); logger.debug("failed to execute", e);
throw new PercolateException(context.indexShard.shardId(), "failed to execute", e); throw new PercolateException(context.indexShard().shardId(), "failed to execute", e);
} finally { } finally {
percolatorSearcher.release(); percolatorSearcher.release();
} }
@ -620,29 +708,12 @@ public class PercolatorService extends AbstractComponent {
}; };
private static void queryBasedPercolating(Engine.Searcher percolatorSearcher, PercolateContext context, Collector collector) throws IOException { private static void queryBasedPercolating(Engine.Searcher percolatorSearcher, PercolateContext context, Collector collector) throws IOException {
Filter percolatorTypeFilter = context.percolateIndexService.mapperService().documentMapper(Constants.TYPE_NAME).typeFilter(); Filter percolatorTypeFilter = context.indexService().mapperService().documentMapper(Constants.TYPE_NAME).typeFilter();
percolatorTypeFilter = context.percolateIndexService.cache().filter().cache(percolatorTypeFilter); percolatorTypeFilter = context.indexService().cache().filter().cache(percolatorTypeFilter);
FilteredQuery query = new FilteredQuery(context.query, percolatorTypeFilter); FilteredQuery query = new FilteredQuery(context.percolateQuery(), percolatorTypeFilter);
percolatorSearcher.searcher().search(query, collector); percolatorSearcher.searcher().search(query, collector);
} }
public class PercolateContext {
public boolean limit;
public int size;
public boolean score;
public boolean sort;
public byte percolatorTypeId;
Query query;
ConcurrentMap<HashedBytesRef, Query> percolateQueries;
IndexSearcher docSearcher;
IndexShard indexShard;
IndexFieldDataService fieldData;
IndexService percolateIndexService;
}
public final static class ReduceResult { public final static class ReduceResult {
private final long count; private final long count;

View File

@ -1,5 +1,25 @@
/*
* 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.percolator; package org.elasticsearch.percolator;
import com.google.common.collect.ImmutableMap;
import gnu.trove.list.array.TFloatArrayList; import gnu.trove.list.array.TFloatArrayList;
import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.*; import org.apache.lucene.search.*;
@ -13,10 +33,14 @@ import org.elasticsearch.index.fielddata.FieldDataType;
import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.internal.IdFieldMapper; import org.elasticsearch.index.mapper.internal.IdFieldMapper;
import org.elasticsearch.index.query.ParsedQuery;
import org.elasticsearch.search.highlight.HighlightField;
import org.elasticsearch.search.highlight.HighlightPhase;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
/** /**
@ -33,11 +57,11 @@ abstract class QueryCollector extends Collector {
BytesValues values; BytesValues values;
QueryCollector(ESLogger logger, PercolatorService.PercolateContext context) { QueryCollector(ESLogger logger, PercolateContext context) {
this.logger = logger; this.logger = logger;
this.queries = context.percolateQueries; this.queries = context.percolateQueries();
this.searcher = context.docSearcher; this.searcher = context.docSearcher();
this.idFieldData = context.fieldData.getForField( this.idFieldData = context.fieldData().getForField(
new FieldMapper.Names(IdFieldMapper.NAME), new FieldMapper.Names(IdFieldMapper.NAME),
new FieldDataType("string", ImmutableSettings.builder().put("format", "paged_bytes")) new FieldDataType("string", ImmutableSettings.builder().put("format", "paged_bytes"))
); );
@ -59,33 +83,39 @@ abstract class QueryCollector extends Collector {
} }
static Match match(ESLogger logger, PercolatorService.PercolateContext context) { static Match match(ESLogger logger, PercolateContext context, HighlightPhase highlightPhase) {
return new Match(logger, context); return new Match(logger, context, highlightPhase);
} }
static Count count(ESLogger logger, PercolatorService.PercolateContext context) { static Count count(ESLogger logger, PercolateContext context) {
return new Count(logger, context); return new Count(logger, context);
} }
static MatchAndScore matchAndScore(ESLogger logger, PercolatorService.PercolateContext context) { static MatchAndScore matchAndScore(ESLogger logger, PercolateContext context, HighlightPhase highlightPhase) {
return new MatchAndScore(logger, context); return new MatchAndScore(logger, context, highlightPhase);
} }
static MatchAndSort matchAndSort(ESLogger logger, PercolatorService.PercolateContext context) { static MatchAndSort matchAndSort(ESLogger logger, PercolateContext context) {
return new MatchAndSort(logger, context); return new MatchAndSort(logger, context);
} }
final static class Match extends QueryCollector { final static class Match extends QueryCollector {
private final List<BytesRef> matches = new ArrayList<BytesRef>(); final PercolateContext context;
private final boolean limit; final HighlightPhase highlightPhase;
private final int size;
private long counter = 0;
Match(ESLogger logger, PercolatorService.PercolateContext context) { final List<BytesRef> matches = new ArrayList<BytesRef>();
final List<Map<String, HighlightField>> hls = new ArrayList<Map<String, HighlightField>>();
final boolean limit;
final int size;
long counter = 0;
Match(ESLogger logger, PercolateContext context, HighlightPhase highlightPhase) {
super(logger, context); super(logger, context);
this.limit = context.limit; this.limit = context.limit;
this.size = context.size; this.size = context.size;
this.context = context;
this.highlightPhase = highlightPhase;
} }
@Override @Override
@ -99,10 +129,19 @@ abstract class QueryCollector extends Collector {
// run the query // run the query
try { try {
collector.reset(); collector.reset();
if (context.highlight() != null) {
context.parsedQuery(new ParsedQuery(query, ImmutableMap.<String, Filter>of()));
context.hitContext().cache().clear();
}
searcher.search(query, collector); searcher.search(query, collector);
if (collector.exists()) { if (collector.exists()) {
if (!limit || counter < size) { if (!limit || counter < size) {
matches.add(values.makeSafe(spare.bytes)); matches.add(values.makeSafe(spare.bytes));
if (context.highlight() != null) {
highlightPhase.hitExecute(context, context.hitContext());
hls.add(context.hitContext().hit().getHighlightFields());
}
} }
counter++; counter++;
} }
@ -119,13 +158,16 @@ abstract class QueryCollector extends Collector {
return matches; return matches;
} }
List<Map<String, HighlightField>> hls() {
return hls;
}
} }
final static class MatchAndSort extends QueryCollector { final static class MatchAndSort extends QueryCollector {
private final TopScoreDocCollector topDocsCollector; private final TopScoreDocCollector topDocsCollector;
MatchAndSort(ESLogger logger, PercolatorService.PercolateContext context) { MatchAndSort(ESLogger logger, PercolateContext context) {
super(logger, context); super(logger, context);
// TODO: Use TopFieldCollector.create(...) for ascending and decending scoring? // TODO: Use TopFieldCollector.create(...) for ascending and decending scoring?
topDocsCollector = TopScoreDocCollector.create(context.size, false); topDocsCollector = TopScoreDocCollector.create(context.size, false);
@ -170,19 +212,25 @@ abstract class QueryCollector extends Collector {
final static class MatchAndScore extends QueryCollector { final static class MatchAndScore extends QueryCollector {
private final List<BytesRef> matches = new ArrayList<BytesRef>(); final PercolateContext context;
final HighlightPhase highlightPhase;
final List<BytesRef> matches = new ArrayList<BytesRef>();
final List<Map<String, HighlightField>> hls = new ArrayList<Map<String, HighlightField>>();
// TODO: Use thread local in order to cache the scores lists? // TODO: Use thread local in order to cache the scores lists?
private final TFloatArrayList scores = new TFloatArrayList(); final TFloatArrayList scores = new TFloatArrayList();
private final boolean limit; final boolean limit;
private final int size; final int size;
private long counter = 0; long counter = 0;
private Scorer scorer; private Scorer scorer;
MatchAndScore(ESLogger logger, PercolatorService.PercolateContext context) { MatchAndScore(ESLogger logger, PercolateContext context, HighlightPhase highlightPhase) {
super(logger, context); super(logger, context);
this.limit = context.limit; this.limit = context.limit;
this.size = context.size; this.size = context.size;
this.context = context;
this.highlightPhase = highlightPhase;
} }
@Override @Override
@ -196,11 +244,19 @@ abstract class QueryCollector extends Collector {
// run the query // run the query
try { try {
collector.reset(); collector.reset();
if (context.highlight() != null) {
context.parsedQuery(new ParsedQuery(query, ImmutableMap.<String, Filter>of()));
context.hitContext().cache().clear();
}
searcher.search(query, collector); searcher.search(query, collector);
if (collector.exists()) { if (collector.exists()) {
if (!limit || counter < size) { if (!limit || counter < size) {
matches.add(values.makeSafe(spare.bytes)); matches.add(values.makeSafe(spare.bytes));
scores.add(scorer.score()); scores.add(scorer.score());
if (context.highlight() != null) {
highlightPhase.hitExecute(context, context.hitContext());
hls.add(context.hitContext().hit().getHighlightFields());
}
} }
counter++; counter++;
} }
@ -225,13 +281,17 @@ abstract class QueryCollector extends Collector {
TFloatArrayList scores() { TFloatArrayList scores() {
return scores; return scores;
} }
List<Map<String, HighlightField>> hls() {
return hls;
}
} }
final static class Count extends QueryCollector { final static class Count extends QueryCollector {
private long counter = 0; private long counter = 0;
Count(ESLogger logger, PercolatorService.PercolateContext context) { Count(ESLogger logger, PercolateContext context) {
super(logger, context); super(logger, context);
} }

View File

@ -52,6 +52,7 @@ import org.elasticsearch.search.dfs.CachedDfSource;
import org.elasticsearch.search.dfs.DfsPhase; import org.elasticsearch.search.dfs.DfsPhase;
import org.elasticsearch.search.dfs.DfsSearchResult; import org.elasticsearch.search.dfs.DfsSearchResult;
import org.elasticsearch.search.fetch.*; import org.elasticsearch.search.fetch.*;
import org.elasticsearch.search.internal.DefaultSearchContext;
import org.elasticsearch.search.internal.InternalScrollSearchRequest; import org.elasticsearch.search.internal.InternalScrollSearchRequest;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.internal.ShardSearchRequest; import org.elasticsearch.search.internal.ShardSearchRequest;
@ -480,7 +481,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().id(), request.index(), request.shardId()); SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().id(), request.index(), request.shardId());
Engine.Searcher engineSearcher = searcher == null ? indexShard.searcher() : searcher; Engine.Searcher engineSearcher = searcher == null ? indexShard.searcher() : searcher;
SearchContext context = new SearchContext(idGenerator.incrementAndGet(), request, shardTarget, engineSearcher, indexService, indexShard, scriptService, cacheRecycler); SearchContext context = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget, engineSearcher, indexService, indexShard, scriptService, cacheRecycler);
SearchContext.setCurrent(context); SearchContext.setCurrent(context);
try { try {
context.scroll(request.scroll()); context.scroll(request.scroll());

View File

@ -0,0 +1,648 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch 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.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.cache.recycler.CacheRecycler;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.lucene.search.AndFilter;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
import org.elasticsearch.common.lucene.search.XFilteredQuery;
import org.elasticsearch.common.lucene.search.function.BoostScoreFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.analysis.AnalysisService;
import org.elasticsearch.index.cache.docset.DocSetCache;
import org.elasticsearch.index.cache.filter.FilterCache;
import org.elasticsearch.index.cache.id.IdCache;
import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.index.fielddata.IndexFieldDataService;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.FieldMappers;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.query.IndexQueryParserService;
import org.elasticsearch.index.query.ParsedFilter;
import org.elasticsearch.index.query.ParsedQuery;
import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.shard.service.IndexShard;
import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.dfs.DfsSearchResult;
import org.elasticsearch.search.facet.SearchContextFacets;
import org.elasticsearch.search.fetch.FetchSearchResult;
import org.elasticsearch.search.fetch.partial.PartialFieldsContext;
import org.elasticsearch.search.fetch.script.ScriptFieldsContext;
import org.elasticsearch.search.fetch.source.FetchSourceContext;
import org.elasticsearch.search.highlight.SearchContextHighlight;
import org.elasticsearch.search.lookup.SearchLookup;
import org.elasticsearch.search.query.QuerySearchResult;
import org.elasticsearch.search.rescore.RescoreSearchContext;
import org.elasticsearch.search.scan.ScanContext;
import org.elasticsearch.search.suggest.SuggestionSearchContext;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
public class DefaultSearchContext extends SearchContext {
private final long id;
private final ShardSearchRequest request;
private final SearchShardTarget shardTarget;
private SearchType searchType;
private final Engine.Searcher engineSearcher;
private final ScriptService scriptService;
private final CacheRecycler cacheRecycler;
private final IndexShard indexShard;
private final IndexService indexService;
private final ContextIndexSearcher searcher;
private final DfsSearchResult dfsResult;
private final QuerySearchResult queryResult;
private final FetchSearchResult fetchResult;
// lazy initialized only if needed
private ScanContext scanContext;
private float queryBoost = 1.0f;
// timeout in millis
private long timeoutInMillis = -1;
private List<String> groupStats;
private Scroll scroll;
private boolean explain;
private boolean version = false; // by default, we don't return versions
private List<String> fieldNames;
private ScriptFieldsContext scriptFields;
private PartialFieldsContext partialFields;
private FetchSourceContext fetchSourceContext;
private int from = -1;
private int size = -1;
private Sort sort;
private Float minimumScore;
private boolean trackScores = false; // when sorting, track scores as well...
private ParsedQuery originalQuery;
private Query query;
private ParsedFilter filter;
private Filter aliasFilter;
private int[] docIdsToLoad;
private int docsIdsToLoadFrom;
private int docsIdsToLoadSize;
private SearchContextFacets facets;
private SearchContextHighlight highlight;
private SuggestionSearchContext suggest;
private RescoreSearchContext rescore;
private SearchLookup searchLookup;
private boolean queryRewritten;
private volatile long keepAlive;
private volatile long lastAccessTime;
private List<SearchContext.Rewrite> rewrites = null;
public DefaultSearchContext(long id, ShardSearchRequest request, SearchShardTarget shardTarget,
Engine.Searcher engineSearcher, IndexService indexService, IndexShard indexShard,
ScriptService scriptService, CacheRecycler cacheRecycler) {
this.id = id;
this.request = request;
this.searchType = request.searchType();
this.shardTarget = shardTarget;
this.engineSearcher = engineSearcher;
this.scriptService = scriptService;
this.cacheRecycler = cacheRecycler;
this.dfsResult = new DfsSearchResult(id, shardTarget);
this.queryResult = new QuerySearchResult(id, shardTarget);
this.fetchResult = new FetchSearchResult(id, shardTarget);
this.indexShard = indexShard;
this.indexService = indexService;
this.searcher = new ContextIndexSearcher(this, engineSearcher);
// initialize the filtering alias based on the provided filters
aliasFilter = indexService.aliasesService().aliasFilter(request.filteringAliases());
}
@Override
public boolean release() throws ElasticSearchException {
if (scanContext != null) {
scanContext.clear();
}
// clear and scope phase we have
if (rewrites != null) {
for (SearchContext.Rewrite rewrite : rewrites) {
rewrite.contextClear();
}
}
searcher.release();
engineSearcher.release();
return true;
}
/**
* Should be called before executing the main query and after all other parameters have been set.
*/
public void preProcess() {
if (query() == null) {
parsedQuery(ParsedQuery.parsedMatchAllQuery());
}
if (queryBoost() != 1.0f) {
parsedQuery(new ParsedQuery(new FunctionScoreQuery(query(), new BoostScoreFunction(queryBoost)), parsedQuery()));
}
Filter searchFilter = searchFilter(types());
if (searchFilter != null) {
if (Queries.isConstantMatchAllQuery(query())) {
Query q = new XConstantScoreQuery(searchFilter);
q.setBoost(query().getBoost());
parsedQuery(new ParsedQuery(q, parsedQuery()));
} else {
parsedQuery(new ParsedQuery(new XFilteredQuery(query(), searchFilter), parsedQuery()));
}
}
}
public Filter searchFilter(String[] types) {
Filter filter = mapperService().searchFilter(types);
if (filter == null) {
return aliasFilter;
} else {
filter = filterCache().cache(filter);
if (aliasFilter != null) {
return new AndFilter(ImmutableList.of(filter, aliasFilter));
}
return filter;
}
}
public long id() {
return this.id;
}
public ShardSearchRequest request() {
return this.request;
}
public SearchType searchType() {
return this.searchType;
}
public SearchContext searchType(SearchType searchType) {
this.searchType = searchType;
return this;
}
public SearchShardTarget shardTarget() {
return this.shardTarget;
}
public int numberOfShards() {
return request.numberOfShards();
}
public boolean hasTypes() {
return request.types() != null && request.types().length > 0;
}
public String[] types() {
return request.types();
}
public float queryBoost() {
return queryBoost;
}
public SearchContext queryBoost(float queryBoost) {
this.queryBoost = queryBoost;
return this;
}
public long nowInMillis() {
return request.nowInMillis();
}
public Scroll scroll() {
return this.scroll;
}
public SearchContext scroll(Scroll scroll) {
this.scroll = scroll;
return this;
}
public SearchContextFacets facets() {
return facets;
}
public SearchContext facets(SearchContextFacets facets) {
this.facets = facets;
return this;
}
public SearchContextHighlight highlight() {
return highlight;
}
public void highlight(SearchContextHighlight highlight) {
this.highlight = highlight;
}
public SuggestionSearchContext suggest() {
return suggest;
}
public void suggest(SuggestionSearchContext suggest) {
this.suggest = suggest;
}
public RescoreSearchContext rescore() {
return this.rescore;
}
public void rescore(RescoreSearchContext rescore) {
this.rescore = rescore;
}
public boolean hasScriptFields() {
return scriptFields != null;
}
public ScriptFieldsContext scriptFields() {
if (scriptFields == null) {
scriptFields = new ScriptFieldsContext();
}
return this.scriptFields;
}
public boolean hasPartialFields() {
return partialFields != null;
}
public PartialFieldsContext partialFields() {
if (partialFields == null) {
partialFields = new PartialFieldsContext();
}
return this.partialFields;
}
/**
* A shortcut function to see whether there is a fetchSourceContext and it says the source is requested.
*
* @return
*/
public boolean sourceRequested() {
return fetchSourceContext != null && fetchSourceContext.fetchSource();
}
public boolean hasFetchSourceContext() {
return fetchSourceContext != null;
}
public FetchSourceContext fetchSourceContext() {
return this.fetchSourceContext;
}
public SearchContext fetchSourceContext(FetchSourceContext fetchSourceContext) {
this.fetchSourceContext = fetchSourceContext;
return this;
}
public ContextIndexSearcher searcher() {
return this.searcher;
}
public IndexShard indexShard() {
return this.indexShard;
}
public MapperService mapperService() {
return indexService.mapperService();
}
public AnalysisService analysisService() {
return indexService.analysisService();
}
public IndexQueryParserService queryParserService() {
return indexService.queryParserService();
}
public SimilarityService similarityService() {
return indexService.similarityService();
}
public ScriptService scriptService() {
return scriptService;
}
public CacheRecycler cacheRecycler() {
return cacheRecycler;
}
public FilterCache filterCache() {
return indexService.cache().filter();
}
public DocSetCache docSetCache() {
return indexService.cache().docSet();
}
public IndexFieldDataService fieldData() {
return indexService.fieldData();
}
public IdCache idCache() {
return indexService.cache().idCache();
}
public long timeoutInMillis() {
return timeoutInMillis;
}
public void timeoutInMillis(long timeoutInMillis) {
this.timeoutInMillis = timeoutInMillis;
}
public SearchContext minimumScore(float minimumScore) {
this.minimumScore = minimumScore;
return this;
}
public Float minimumScore() {
return this.minimumScore;
}
public SearchContext sort(Sort sort) {
this.sort = sort;
return this;
}
public Sort sort() {
return this.sort;
}
public SearchContext trackScores(boolean trackScores) {
this.trackScores = trackScores;
return this;
}
public boolean trackScores() {
return this.trackScores;
}
public SearchContext parsedFilter(ParsedFilter filter) {
this.filter = filter;
return this;
}
public ParsedFilter parsedFilter() {
return this.filter;
}
public Filter aliasFilter() {
return aliasFilter;
}
public SearchContext parsedQuery(ParsedQuery query) {
queryRewritten = false;
this.originalQuery = query;
this.query = query.query();
return this;
}
public ParsedQuery parsedQuery() {
return this.originalQuery;
}
/**
* The query to execute, might be rewritten.
*/
public Query query() {
return this.query;
}
/**
* Has the query been rewritten already?
*/
public boolean queryRewritten() {
return queryRewritten;
}
/**
* Rewrites the query and updates it. Only happens once.
*/
public SearchContext updateRewriteQuery(Query rewriteQuery) {
query = rewriteQuery;
queryRewritten = true;
return this;
}
public int from() {
return from;
}
public SearchContext from(int from) {
this.from = from;
return this;
}
public int size() {
return size;
}
public SearchContext size(int size) {
this.size = size;
return this;
}
public boolean hasFieldNames() {
return fieldNames != null;
}
public List<String> fieldNames() {
if (fieldNames == null) {
fieldNames = Lists.newArrayList();
}
return fieldNames;
}
public void emptyFieldNames() {
this.fieldNames = ImmutableList.of();
}
public boolean explain() {
return explain;
}
public void explain(boolean explain) {
this.explain = explain;
}
@Nullable
public List<String> groupStats() {
return this.groupStats;
}
public void groupStats(List<String> groupStats) {
this.groupStats = groupStats;
}
public boolean version() {
return version;
}
public void version(boolean version) {
this.version = version;
}
public int[] docIdsToLoad() {
return docIdsToLoad;
}
public int docIdsToLoadFrom() {
return docsIdsToLoadFrom;
}
public int docIdsToLoadSize() {
return docsIdsToLoadSize;
}
public SearchContext docIdsToLoad(int[] docIdsToLoad, int docsIdsToLoadFrom, int docsIdsToLoadSize) {
this.docIdsToLoad = docIdsToLoad;
this.docsIdsToLoadFrom = docsIdsToLoadFrom;
this.docsIdsToLoadSize = docsIdsToLoadSize;
return this;
}
public void accessed(long accessTime) {
this.lastAccessTime = accessTime;
}
public long lastAccessTime() {
return this.lastAccessTime;
}
public long keepAlive() {
return this.keepAlive;
}
public void keepAlive(long keepAlive) {
this.keepAlive = keepAlive;
}
public SearchLookup lookup() {
// TODO: The types should take into account the parsing context in QueryParserContext...
if (searchLookup == null) {
searchLookup = new SearchLookup(mapperService(), fieldData(), request.types());
}
return searchLookup;
}
public DfsSearchResult dfsResult() {
return dfsResult;
}
public QuerySearchResult queryResult() {
return queryResult;
}
public FetchSearchResult fetchResult() {
return fetchResult;
}
public void addRewrite(Rewrite rewrite) {
if (this.rewrites == null) {
this.rewrites = new ArrayList<Rewrite>();
}
this.rewrites.add(rewrite);
}
public List<Rewrite> rewrites() {
return this.rewrites;
}
public ScanContext scanContext() {
if (scanContext == null) {
scanContext = new ScanContext();
}
return scanContext;
}
public MapperService.SmartNameFieldMappers smartFieldMappers(String name) {
return mapperService().smartName(name, request.types());
}
public FieldMappers smartNameFieldMappers(String name) {
return mapperService().smartNameFieldMappers(name, request.types());
}
public FieldMapper smartNameFieldMapper(String name) {
return mapperService().smartNameFieldMapper(name, request.types());
}
public MapperService.SmartNameObjectMapper smartNameObjectMapper(String name) {
return mapperService().smartNameObjectMapper(name, request.types());
}
}

View File

@ -9,37 +9,26 @@
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing, software
* software distributed under the License is distributed on an * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* KIND, either express or implied. See the License for the * License for the specific language governing permissions and limitations under
* specific language governing permissions and limitations * the License.
* under the License.
*/ */
package org.elasticsearch.search.internal; package org.elasticsearch.search.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.apache.lucene.search.Filter; import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort; import org.apache.lucene.search.Sort;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.cache.recycler.CacheRecycler; import org.elasticsearch.cache.recycler.CacheRecycler;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.lease.Releasable;
import org.elasticsearch.common.lucene.search.AndFilter;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
import org.elasticsearch.common.lucene.search.XFilteredQuery;
import org.elasticsearch.common.lucene.search.function.BoostScoreFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.analysis.AnalysisService; import org.elasticsearch.index.analysis.AnalysisService;
import org.elasticsearch.index.cache.docset.DocSetCache; import org.elasticsearch.index.cache.docset.DocSetCache;
import org.elasticsearch.index.cache.filter.FilterCache; import org.elasticsearch.index.cache.filter.FilterCache;
import org.elasticsearch.index.cache.id.IdCache; import org.elasticsearch.index.cache.id.IdCache;
import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.index.fielddata.IndexFieldDataService; import org.elasticsearch.index.fielddata.IndexFieldDataService;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.FieldMappers; import org.elasticsearch.index.mapper.FieldMappers;
@ -48,7 +37,6 @@ import org.elasticsearch.index.query.IndexQueryParserService;
import org.elasticsearch.index.query.ParsedFilter; import org.elasticsearch.index.query.ParsedFilter;
import org.elasticsearch.index.query.ParsedQuery; import org.elasticsearch.index.query.ParsedQuery;
import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.shard.service.IndexShard; import org.elasticsearch.index.shard.service.IndexShard;
import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptService;
@ -67,13 +55,11 @@ import org.elasticsearch.search.rescore.RescoreSearchContext;
import org.elasticsearch.search.scan.ScanContext; import org.elasticsearch.search.scan.ScanContext;
import org.elasticsearch.search.suggest.SuggestionSearchContext; import org.elasticsearch.search.suggest.SuggestionSearchContext;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
*
*/ */
public class SearchContext implements Releasable { public abstract class SearchContext implements Releasable {
private static ThreadLocal<SearchContext> current = new ThreadLocal<SearchContext>(); private static ThreadLocal<SearchContext> current = new ThreadLocal<SearchContext>();
@ -100,576 +86,204 @@ public class SearchContext implements Releasable {
void contextClear(); void contextClear();
} }
private final long id;
private final ShardSearchRequest request;
private final SearchShardTarget shardTarget;
private SearchType searchType;
private final Engine.Searcher engineSearcher;
private final ScriptService scriptService;
private final CacheRecycler cacheRecycler;
private final IndexShard indexShard;
private final IndexService indexService;
private final ContextIndexSearcher searcher;
private final DfsSearchResult dfsResult;
private final QuerySearchResult queryResult;
private final FetchSearchResult fetchResult;
// lazy initialized only if needed
private ScanContext scanContext;
private float queryBoost = 1.0f;
// timeout in millis
private long timeoutInMillis = -1;
private List<String> groupStats;
private Scroll scroll;
private boolean explain;
private boolean version = false; // by default, we don't return versions
private List<String> fieldNames;
private ScriptFieldsContext scriptFields;
private PartialFieldsContext partialFields;
private FetchSourceContext fetchSourceContext;
private int from = -1;
private int size = -1;
private Sort sort;
private Float minimumScore;
private boolean trackScores = false; // when sorting, track scores as well...
private ParsedQuery originalQuery;
private Query query;
private ParsedFilter filter;
private Filter aliasFilter;
private int[] docIdsToLoad;
private int docsIdsToLoadFrom;
private int docsIdsToLoadSize;
private SearchContextFacets facets;
private SearchContextHighlight highlight;
private SuggestionSearchContext suggest;
private RescoreSearchContext rescore;
private SearchLookup searchLookup;
private boolean queryRewritten;
private volatile long keepAlive;
private volatile long lastAccessTime;
private List<Rewrite> rewrites = null;
public SearchContext(long id, ShardSearchRequest request, SearchShardTarget shardTarget,
Engine.Searcher engineSearcher, IndexService indexService, IndexShard indexShard,
ScriptService scriptService, CacheRecycler cacheRecycler) {
this.id = id;
this.request = request;
this.searchType = request.searchType();
this.shardTarget = shardTarget;
this.engineSearcher = engineSearcher;
this.scriptService = scriptService;
this.cacheRecycler = cacheRecycler;
this.dfsResult = new DfsSearchResult(id, shardTarget);
this.queryResult = new QuerySearchResult(id, shardTarget);
this.fetchResult = new FetchSearchResult(id, shardTarget);
this.indexShard = indexShard;
this.indexService = indexService;
this.searcher = new ContextIndexSearcher(this, engineSearcher);
// initialize the filtering alias based on the provided filters
aliasFilter = indexService.aliasesService().aliasFilter(request.filteringAliases());
}
@Override
public boolean release() throws ElasticSearchException {
if (scanContext != null) {
scanContext.clear();
}
// clear and scope phase we have
if (rewrites != null) {
for (Rewrite rewrite : rewrites) {
rewrite.contextClear();
}
}
searcher.release();
engineSearcher.release();
return true;
}
/** /**
* Should be called before executing the main query and after all other parameters have been set. * Should be called before executing the main query and after all other parameters have been set.
*/ */
public void preProcess() { public abstract void preProcess();
if (query() == null) {
parsedQuery(ParsedQuery.parsedMatchAllQuery());
}
if (queryBoost() != 1.0f) {
parsedQuery(new ParsedQuery(new FunctionScoreQuery(query(), new BoostScoreFunction(queryBoost)), parsedQuery()));
}
Filter searchFilter = searchFilter(types());
if (searchFilter != null) {
if (Queries.isConstantMatchAllQuery(query())) {
Query q = new XConstantScoreQuery(searchFilter);
q.setBoost(query().getBoost());
parsedQuery(new ParsedQuery(q, parsedQuery()));
} else {
parsedQuery(new ParsedQuery(new XFilteredQuery(query(), searchFilter), parsedQuery()));
}
}
}
public Filter searchFilter(String[] types) { public abstract Filter searchFilter(String[] types);
Filter filter = mapperService().searchFilter(types);
if (filter == null) {
return aliasFilter;
} else {
filter = filterCache().cache(filter);
if (aliasFilter != null) {
return new AndFilter(ImmutableList.of(filter, aliasFilter));
}
return filter;
}
}
public long id() { public abstract long id();
return this.id;
}
public ShardSearchRequest request() { public abstract ShardSearchRequest request();
return this.request;
}
public SearchType searchType() { public abstract SearchType searchType();
return this.searchType;
}
public SearchContext searchType(SearchType searchType) { public abstract SearchContext searchType(SearchType searchType);
this.searchType = searchType;
return this;
}
public SearchShardTarget shardTarget() { public abstract SearchShardTarget shardTarget();
return this.shardTarget;
}
public int numberOfShards() { public abstract int numberOfShards();
return request.numberOfShards();
}
public boolean hasTypes() { public abstract boolean hasTypes();
return request.types() != null && request.types().length > 0;
}
public String[] types() { public abstract String[] types();
return request.types();
}
public float queryBoost() { public abstract float queryBoost();
return queryBoost;
}
public SearchContext queryBoost(float queryBoost) { public abstract SearchContext queryBoost(float queryBoost);
this.queryBoost = queryBoost;
return this;
}
public long nowInMillis() { public abstract long nowInMillis();
return request.nowInMillis();
}
public Scroll scroll() { public abstract Scroll scroll();
return this.scroll;
}
public SearchContext scroll(Scroll scroll) { public abstract SearchContext scroll(Scroll scroll);
this.scroll = scroll;
return this;
}
public SearchContextFacets facets() { public abstract SearchContextFacets facets();
return facets;
}
public SearchContext facets(SearchContextFacets facets) { public abstract SearchContext facets(SearchContextFacets facets);
this.facets = facets;
return this;
}
public SearchContextHighlight highlight() { public abstract SearchContextHighlight highlight();
return highlight;
}
public void highlight(SearchContextHighlight highlight) { public abstract void highlight(SearchContextHighlight highlight);
this.highlight = highlight;
}
public SuggestionSearchContext suggest() { public abstract SuggestionSearchContext suggest();
return suggest;
}
public void suggest(SuggestionSearchContext suggest) { public abstract void suggest(SuggestionSearchContext suggest);
this.suggest = suggest;
}
public RescoreSearchContext rescore() { public abstract RescoreSearchContext rescore();
return this.rescore;
}
public void rescore(RescoreSearchContext rescore) { public abstract void rescore(RescoreSearchContext rescore);
this.rescore = rescore;
}
public boolean hasScriptFields() { public abstract boolean hasScriptFields();
return scriptFields != null;
}
public ScriptFieldsContext scriptFields() { public abstract ScriptFieldsContext scriptFields();
if (scriptFields == null) {
scriptFields = new ScriptFieldsContext();
}
return this.scriptFields;
}
public boolean hasPartialFields() { public abstract boolean hasPartialFields();
return partialFields != null;
}
public PartialFieldsContext partialFields() { public abstract PartialFieldsContext partialFields();
if (partialFields == null) {
partialFields = new PartialFieldsContext();
}
return this.partialFields;
}
/** /**
* A shortcut function to see whether there is a fetchSourceContext and it says the source is requested. * A shortcut function to see whether there is a fetchSourceContext and it says the source is requested.
* *
* @return * @return
*/ */
public boolean sourceRequested() { public abstract boolean sourceRequested();
return fetchSourceContext != null && fetchSourceContext.fetchSource();
}
public boolean hasFetchSourceContext() { public abstract boolean hasFetchSourceContext();
return fetchSourceContext != null;
}
public FetchSourceContext fetchSourceContext() { public abstract FetchSourceContext fetchSourceContext();
return this.fetchSourceContext;
}
public SearchContext fetchSourceContext(FetchSourceContext fetchSourceContext) { public abstract SearchContext fetchSourceContext(FetchSourceContext fetchSourceContext);
this.fetchSourceContext = fetchSourceContext;
return this;
}
public ContextIndexSearcher searcher() { public abstract ContextIndexSearcher searcher();
return this.searcher;
}
public IndexShard indexShard() { public abstract IndexShard indexShard();
return this.indexShard;
}
public MapperService mapperService() { public abstract MapperService mapperService();
return indexService.mapperService();
}
public AnalysisService analysisService() { public abstract AnalysisService analysisService();
return indexService.analysisService();
}
public IndexQueryParserService queryParserService() { public abstract IndexQueryParserService queryParserService();
return indexService.queryParserService();
}
public SimilarityService similarityService() { public abstract SimilarityService similarityService();
return indexService.similarityService();
}
public ScriptService scriptService() { public abstract ScriptService scriptService();
return scriptService;
}
public CacheRecycler cacheRecycler() { public abstract CacheRecycler cacheRecycler();
return cacheRecycler;
}
public FilterCache filterCache() { public abstract FilterCache filterCache();
return indexService.cache().filter();
}
public DocSetCache docSetCache() { public abstract DocSetCache docSetCache();
return indexService.cache().docSet();
}
public IndexFieldDataService fieldData() { public abstract IndexFieldDataService fieldData();
return indexService.fieldData();
}
public IdCache idCache() { public abstract IdCache idCache();
return indexService.cache().idCache();
}
public long timeoutInMillis() { public abstract long timeoutInMillis();
return timeoutInMillis;
}
public void timeoutInMillis(long timeoutInMillis) { public abstract void timeoutInMillis(long timeoutInMillis);
this.timeoutInMillis = timeoutInMillis;
}
public SearchContext minimumScore(float minimumScore) { public abstract SearchContext minimumScore(float minimumScore);
this.minimumScore = minimumScore;
return this;
}
public Float minimumScore() { public abstract Float minimumScore();
return this.minimumScore;
}
public SearchContext sort(Sort sort) { public abstract SearchContext sort(Sort sort);
this.sort = sort;
return this;
}
public Sort sort() { public abstract Sort sort();
return this.sort;
}
public SearchContext trackScores(boolean trackScores) { public abstract SearchContext trackScores(boolean trackScores);
this.trackScores = trackScores;
return this;
}
public boolean trackScores() { public abstract boolean trackScores();
return this.trackScores;
}
public SearchContext parsedFilter(ParsedFilter filter) { public abstract SearchContext parsedFilter(ParsedFilter filter);
this.filter = filter;
return this;
}
public ParsedFilter parsedFilter() { public abstract ParsedFilter parsedFilter();
return this.filter;
}
public Filter aliasFilter() { public abstract Filter aliasFilter();
return aliasFilter;
}
public SearchContext parsedQuery(ParsedQuery query) { public abstract SearchContext parsedQuery(ParsedQuery query);
queryRewritten = false;
this.originalQuery = query;
this.query = query.query();
return this;
}
public ParsedQuery parsedQuery() { public abstract ParsedQuery parsedQuery();
return this.originalQuery;
}
/** /**
* The query to execute, might be rewritten. * The query to execute, might be rewritten.
*/ */
public Query query() { public abstract Query query();
return this.query;
}
/** /**
* Has the query been rewritten already? * Has the query been rewritten already?
*/ */
public boolean queryRewritten() { public abstract boolean queryRewritten();
return queryRewritten;
}
/** /**
* Rewrites the query and updates it. Only happens once. * Rewrites the query and updates it. Only happens once.
*/ */
public SearchContext updateRewriteQuery(Query rewriteQuery) { public abstract SearchContext updateRewriteQuery(Query rewriteQuery);
query = rewriteQuery;
queryRewritten = true;
return this;
}
public int from() { public abstract int from();
return from;
}
public SearchContext from(int from) { public abstract SearchContext from(int from);
this.from = from;
return this;
}
public int size() { public abstract int size();
return size;
}
public SearchContext size(int size) { public abstract SearchContext size(int size);
this.size = size;
return this;
}
public boolean hasFieldNames() { public abstract boolean hasFieldNames();
return fieldNames != null;
}
public List<String> fieldNames() { public abstract List<String> fieldNames();
if (fieldNames == null) {
fieldNames = Lists.newArrayList();
}
return fieldNames;
}
public void emptyFieldNames() { public abstract void emptyFieldNames();
this.fieldNames = ImmutableList.of();
}
public boolean explain() { public abstract boolean explain();
return explain;
}
public void explain(boolean explain) { public abstract void explain(boolean explain);
this.explain = explain;
}
@Nullable @Nullable
public List<String> groupStats() { public abstract List<String> groupStats();
return this.groupStats;
}
public void groupStats(List<String> groupStats) { public abstract void groupStats(List<String> groupStats);
this.groupStats = groupStats;
}
public boolean version() { public abstract boolean version();
return version;
}
public void version(boolean version) { public abstract void version(boolean version);
this.version = version;
}
public int[] docIdsToLoad() { public abstract int[] docIdsToLoad();
return docIdsToLoad;
}
public int docIdsToLoadFrom() { public abstract int docIdsToLoadFrom();
return docsIdsToLoadFrom;
}
public int docIdsToLoadSize() { public abstract int docIdsToLoadSize();
return docsIdsToLoadSize;
}
public SearchContext docIdsToLoad(int[] docIdsToLoad, int docsIdsToLoadFrom, int docsIdsToLoadSize) { public abstract SearchContext docIdsToLoad(int[] docIdsToLoad, int docsIdsToLoadFrom, int docsIdsToLoadSize);
this.docIdsToLoad = docIdsToLoad;
this.docsIdsToLoadFrom = docsIdsToLoadFrom;
this.docsIdsToLoadSize = docsIdsToLoadSize;
return this;
}
public void accessed(long accessTime) { public abstract void accessed(long accessTime);
this.lastAccessTime = accessTime;
}
public long lastAccessTime() { public abstract long lastAccessTime();
return this.lastAccessTime;
}
public long keepAlive() { public abstract long keepAlive();
return this.keepAlive;
}
public void keepAlive(long keepAlive) { public abstract void keepAlive(long keepAlive);
this.keepAlive = keepAlive;
}
public SearchLookup lookup() { public abstract SearchLookup lookup();
// TODO: The types should take into account the parsing context in QueryParserContext...
if (searchLookup == null) {
searchLookup = new SearchLookup(mapperService(), fieldData(), request.types());
}
return searchLookup;
}
public DfsSearchResult dfsResult() { public abstract DfsSearchResult dfsResult();
return dfsResult;
}
public QuerySearchResult queryResult() { public abstract QuerySearchResult queryResult();
return queryResult;
}
public FetchSearchResult fetchResult() { public abstract FetchSearchResult fetchResult();
return fetchResult;
}
public void addRewrite(Rewrite rewrite) { public abstract void addRewrite(Rewrite rewrite);
if (this.rewrites == null) {
this.rewrites = new ArrayList<Rewrite>();
}
this.rewrites.add(rewrite);
}
public List<Rewrite> rewrites() { public abstract List<Rewrite> rewrites();
return this.rewrites;
}
public ScanContext scanContext() { public abstract ScanContext scanContext();
if (scanContext == null) {
scanContext = new ScanContext();
}
return scanContext;
}
public MapperService.SmartNameFieldMappers smartFieldMappers(String name) { public abstract MapperService.SmartNameFieldMappers smartFieldMappers(String name);
return mapperService().smartName(name, request.types());
}
public FieldMappers smartNameFieldMappers(String name) { public abstract FieldMappers smartNameFieldMappers(String name);
return mapperService().smartNameFieldMappers(name, request.types());
}
public FieldMapper smartNameFieldMapper(String name) { public abstract FieldMapper smartNameFieldMapper(String name);
return mapperService().smartNameFieldMapper(name, request.types());
} public abstract MapperService.SmartNameObjectMapper smartNameObjectMapper(String name);
public MapperService.SmartNameObjectMapper smartNameObjectMapper(String name) {
return mapperService().smartNameObjectMapper(name, request.types());
}
} }

View File

@ -262,7 +262,7 @@ public class RecoveryPercolatorTests extends AbstractNodesTests {
.setSource(jsonBuilder().startObject().startObject("doc").field("field1", 100).endObject().endObject()) .setSource(jsonBuilder().startObject().startObject("doc").field("field1", 100).endObject().endObject())
.execute().actionGet(); .execute().actionGet();
assertThat(response.getMatches(), arrayWithSize(1)); assertThat(response.getMatches(), arrayWithSize(1));
assertThat(response.getMatches()[0].id().string(), equalTo("100")); assertThat(response.getMatches()[0].getId().string(), equalTo("100"));
} }
@Test @Test

View File

@ -28,6 +28,7 @@ import org.elasticsearch.action.percolate.PercolateResponse;
import org.elasticsearch.action.percolate.PercolateSourceBuilder; import org.elasticsearch.action.percolate.PercolateSourceBuilder;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.IgnoreIndices; import org.elasticsearch.action.support.IgnoreIndices;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests; import org.elasticsearch.client.Requests;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.ImmutableSettings;
@ -39,6 +40,8 @@ import org.elasticsearch.index.engine.DocumentMissingException;
import org.elasticsearch.index.engine.VersionConflictEngineException; import org.elasticsearch.index.engine.VersionConflictEngineException;
import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.factor.FactorBuilder;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.test.integration.AbstractSharedClusterTest; import org.elasticsearch.test.integration.AbstractSharedClusterTest;
import org.junit.Test; import org.junit.Test;
@ -1240,10 +1243,10 @@ public class SimplePercolatorTests extends AbstractSharedClusterTest {
.execute().actionGet(); .execute().actionGet();
assertNoFailures(response); assertNoFailures(response);
assertThat(response.getCount(), equalTo(2l)); assertThat(response.getCount(), equalTo(2l));
assertThat(response.getMatches()[0].id().string(), equalTo("2")); assertThat(response.getMatches()[0].getId().string(), equalTo("2"));
assertThat(response.getMatches()[0].score(), equalTo(2f)); assertThat(response.getMatches()[0].getScore(), equalTo(2f));
assertThat(response.getMatches()[1].id().string(), equalTo("1")); assertThat(response.getMatches()[1].getId().string(), equalTo("1"));
assertThat(response.getMatches()[1].score(), equalTo(1f)); assertThat(response.getMatches()[1].getScore(), equalTo(1f));
response = client().preparePercolate().setIndices("my-index").setDocumentType("my-type") response = client().preparePercolate().setIndices("my-index").setDocumentType("my-type")
.setSort(true) .setSort(true)
@ -1295,6 +1298,163 @@ public class SimplePercolatorTests extends AbstractSharedClusterTest {
assertThat(response.getCount(), equalTo(0l)); assertThat(response.getCount(), equalTo(0l));
} }
@Test
public void testPercolatorWithHighlighting() throws Exception {
Client client = cluster().nodeClient();
client.admin().indices().prepareCreate("test")
.setSettings(ImmutableSettings.settingsBuilder().put("index.number_of_shards", 2))
.execute().actionGet();
ensureGreen();
if (randomBoolean()) {
client.admin().indices().preparePutMapping("test").setType("type")
.setSource(
jsonBuilder().startObject().startObject("type")
.startObject("properties")
.startObject("field1").field("type", "string").field("term_vector", "with_positions_offsets").endObject()
.endObject()
.endObject().endObject()
)
.execute().actionGet();
}
logger.info("--> register a queries");
client.prepareIndex("test", "_percolator", "1")
.setSource(jsonBuilder().startObject().field("query", matchQuery("field1", "brown fox")).endObject())
.execute().actionGet();
client.prepareIndex("test", "_percolator", "2")
.setSource(jsonBuilder().startObject().field("query", matchQuery("field1", "lazy dog")).endObject())
.execute().actionGet();
client.prepareIndex("test", "_percolator", "3")
.setSource(jsonBuilder().startObject().field("query", termQuery("field1", "jumps")).endObject())
.execute().actionGet();
client.prepareIndex("test", "_percolator", "4")
.setSource(jsonBuilder().startObject().field("query", termQuery("field1", "dog")).endObject())
.execute().actionGet();
client.prepareIndex("test", "_percolator", "5")
.setSource(jsonBuilder().startObject().field("query", termQuery("field1", "fox")).endObject())
.execute().actionGet();
logger.info("--> Percolate doc with field1=The quick brown fox jumps over the lazy dog");
PercolateResponse response = client.preparePercolate()
.setIndices("test").setDocumentType("type")
.setSize(5)
.setPercolateDoc(docBuilder().setDoc(jsonBuilder().startObject().field("field1", "The quick brown fox jumps over the lazy dog").endObject()))
.setHighlightBuilder(new HighlightBuilder().field("field1"))
.execute().actionGet();
assertNoFailures(response);
assertNoFailures(response);
assertThat(response.getMatches(), arrayWithSize(5));
assertThat(convertFromTextArray(response.getMatches(), "test"), arrayContainingInAnyOrder("1", "2", "3", "4", "5"));
PercolateResponse.Match[] matches = response.getMatches();
Arrays.sort(matches, new Comparator<PercolateResponse.Match>() {
@Override
public int compare(PercolateResponse.Match a, PercolateResponse.Match b) {
return a.getId().compareTo(b.getId());
}
});
assertThat(matches[0].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick <em>brown</em> <em>fox</em> jumps over the lazy dog"));
assertThat(matches[1].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox jumps over the <em>lazy</em> <em>dog</em>"));
assertThat(matches[2].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox <em>jumps</em> over the lazy dog"));
assertThat(matches[3].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox jumps over the lazy <em>dog</em>"));
assertThat(matches[4].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown <em>fox</em> jumps over the lazy dog"));
// Anything with percolate query isn't realtime
client.admin().indices().prepareRefresh("test").execute().actionGet();
logger.info("--> Query percolate doc with field1=The quick brown fox jumps over the lazy dog");
response = client.preparePercolate()
.setIndices("test").setDocumentType("type")
.setSize(5)
.setPercolateDoc(docBuilder().setDoc(jsonBuilder().startObject().field("field1", "The quick brown fox jumps over the lazy dog").endObject()))
.setHighlightBuilder(new HighlightBuilder().field("field1"))
.setPercolateQuery(matchAllQuery())
.execute().actionGet();
assertNoFailures(response);
assertThat(response.getMatches(), arrayWithSize(5));
assertThat(convertFromTextArray(response.getMatches(), "test"), arrayContainingInAnyOrder("1", "2", "3", "4", "5"));
matches = response.getMatches();
Arrays.sort(matches, new Comparator<PercolateResponse.Match>() {
@Override
public int compare(PercolateResponse.Match a, PercolateResponse.Match b) {
return a.getId().compareTo(b.getId());
}
});
assertThat(matches[0].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick <em>brown</em> <em>fox</em> jumps over the lazy dog"));
assertThat(matches[1].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox jumps over the <em>lazy</em> <em>dog</em>"));
assertThat(matches[2].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox <em>jumps</em> over the lazy dog"));
assertThat(matches[3].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox jumps over the lazy <em>dog</em>"));
assertThat(matches[4].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown <em>fox</em> jumps over the lazy dog"));
logger.info("--> Query percolate with score for doc with field1=The quick brown fox jumps over the lazy dog");
response = client.preparePercolate()
.setIndices("test").setDocumentType("type")
.setSize(5)
.setPercolateDoc(docBuilder().setDoc(jsonBuilder().startObject().field("field1", "The quick brown fox jumps over the lazy dog").endObject()))
.setHighlightBuilder(new HighlightBuilder().field("field1"))
.setPercolateQuery(functionScoreQuery(matchAllQuery()).add(new FactorBuilder().boostFactor(5.5f)))
.setScore(true)
.execute().actionGet();
assertNoFailures(response);
assertThat(response.getMatches(), arrayWithSize(5));
assertThat(convertFromTextArray(response.getMatches(), "test"), arrayContainingInAnyOrder("1", "2", "3", "4", "5"));
matches = response.getMatches();
Arrays.sort(matches, new Comparator<PercolateResponse.Match>() {
@Override
public int compare(PercolateResponse.Match a, PercolateResponse.Match b) {
return a.getId().compareTo(b.getId());
}
});
assertThat(matches[0].getScore(), equalTo(5.5f));
assertThat(matches[0].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick <em>brown</em> <em>fox</em> jumps over the lazy dog"));
assertThat(matches[1].getScore(), equalTo(5.5f));
assertThat(matches[1].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox jumps over the <em>lazy</em> <em>dog</em>"));
assertThat(matches[2].getScore(), equalTo(5.5f));
assertThat(matches[2].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox <em>jumps</em> over the lazy dog"));
assertThat(matches[3].getScore(), equalTo(5.5f));
assertThat(matches[3].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox jumps over the lazy <em>dog</em>"));
assertThat(matches[4].getScore(), equalTo(5.5f));
assertThat(matches[4].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown <em>fox</em> jumps over the lazy dog"));
logger.info("--> Top percolate for doc with field1=The quick brown fox jumps over the lazy dog");
response = client.preparePercolate()
.setIndices("test").setDocumentType("type")
.setSize(5)
.setPercolateDoc(docBuilder().setDoc(jsonBuilder().startObject().field("field1", "The quick brown fox jumps over the lazy dog").endObject()))
.setHighlightBuilder(new HighlightBuilder().field("field1"))
.setPercolateQuery(functionScoreQuery(matchAllQuery()).add(new FactorBuilder().boostFactor(5.5f)))
.setSort(true)
.execute().actionGet();
assertNoFailures(response);
assertThat(response.getMatches(), arrayWithSize(5));
assertThat(convertFromTextArray(response.getMatches(), "test"), arrayContainingInAnyOrder("1", "2", "3", "4", "5"));
matches = response.getMatches();
Arrays.sort(matches, new Comparator<PercolateResponse.Match>() {
@Override
public int compare(PercolateResponse.Match a, PercolateResponse.Match b) {
return a.getId().compareTo(b.getId());
}
});
assertThat(matches[0].getScore(), equalTo(5.5f));
assertThat(matches[0].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick <em>brown</em> <em>fox</em> jumps over the lazy dog"));
assertThat(matches[1].getScore(), equalTo(5.5f));
assertThat(matches[1].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox jumps over the <em>lazy</em> <em>dog</em>"));
assertThat(matches[2].getScore(), equalTo(5.5f));
assertThat(matches[2].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox <em>jumps</em> over the lazy dog"));
assertThat(matches[3].getScore(), equalTo(5.5f));
assertThat(matches[3].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown fox jumps over the lazy <em>dog</em>"));
assertThat(matches[4].getScore(), equalTo(5.5f));
assertThat(matches[4].getHighlightFields().get("field1").fragments()[0].string(), equalTo("The quick brown <em>fox</em> jumps over the lazy dog"));
}
public static String[] convertFromTextArray(PercolateResponse.Match[] matches, String index) { public static String[] convertFromTextArray(PercolateResponse.Match[] matches, String index) {
if (matches.length == 0) { if (matches.length == 0) {
return Strings.EMPTY_ARRAY; return Strings.EMPTY_ARRAY;
@ -1302,7 +1462,7 @@ public class SimplePercolatorTests extends AbstractSharedClusterTest {
String[] strings = new String[matches.length]; String[] strings = new String[matches.length];
for (int i = 0; i < matches.length; i++) { for (int i = 0; i < matches.length; i++) {
assert index.equals(matches[i].getIndex().string()); assert index.equals(matches[i].getIndex().string());
strings[i] = matches[i].id().string(); strings[i] = matches[i].getId().string();
} }
return strings; return strings;
} }