More Like This: removal of the MLT API

Removes the More Like This API, users should now use the More Like This query.
The MLT API tests were converted to their query equivalent. Also some clean
ups in MLT tests.

Closes #10736
Closes #11003
This commit is contained in:
Alex Ksikes 2015-05-05 17:51:24 +02:00
parent 234716a1ef
commit ec4f12f9ef
27 changed files with 97 additions and 2067 deletions

View File

@ -19,7 +19,8 @@ See <<setup-upgrade>> for more info.
include::migrate_2_0.asciidoc[]
include::migrate_1_6.asciidoc[]
include::migrate_1_4.asciidoc[]
include::migrate_1_0.asciidoc[]

View File

@ -0,0 +1,10 @@
[[breaking-changes-1.6]]
== Breaking changes in 1.6
This section discusses the changes that you need to be aware of when migrating
your application from Elasticsearch 1.x to Elasticsearch 1.6.
[float]
=== More Like This API
The More Like This API query has been deprecated and will be removed in 2.0. Instead use the <<query-dsl-mlt-query, More Like This Query>>.

View File

@ -24,10 +24,10 @@ The following deprecated methods have been removed:
Partial fields were deprecated since 1.0.0beta1 in favor of <<search-request-source-filtering,source filtering>>.
=== More Like This Field
=== More Like This
The More Like This Field query has been removed in favor of the <<query-dsl-mlt-query, More Like This Query>>
restrained set to a specific `field`.
The More Like This API and the More Like This Field query have been removed in
favor of the <<query-dsl-mlt-query, More Like This Query>>.
=== Routing

View File

@ -101,7 +101,4 @@ include::search/explain.asciidoc[]
include::search/percolate.asciidoc[]
include::search/more-like-this.asciidoc[]
include::search/field-stats.asciidoc[]

View File

@ -1,32 +0,0 @@
[[search-more-like-this]]
== More Like This API
The more like this (mlt) API allows to get documents that are "like" a
specified document. Here is an example:
[source,js]
--------------------------------------------------
$ curl -XGET 'http://localhost:9200/twitter/tweet/1/_mlt?mlt_fields=tag,content&min_doc_freq=1'
--------------------------------------------------
The API simply results in executing a search request with
<<query-dsl-mlt-query,moreLikeThis>> query (http
parameters match the parameters to the `more_like_this` query). This
means that the body of the request can optionally include all the
request body options in the <<search-search,search
API>> (aggs, from/to and so on). Internally, the more like this
API is equivalent to performing a boolean query of `more_like_this_field`
queries, with one query per specified `mlt_fields`.
Rest parameters relating to search are also allowed, including
`search_type`, `search_indices`, `search_types`, `search_scroll`,
`search_size` and `search_from`.
When no `mlt_fields` are specified, all the fields of the document will
be used in the `more_like_this` query generated.
By default, the queried document is excluded from the response (`include`
set to false).
Note: In order to use the `mlt` feature a `mlt_field` needs to be either
be `stored`, store `term_vector` or `source` needs to be enabled.

View File

@ -1,104 +0,0 @@
{
"mlt": {
"documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/master/search-more-like-this.html",
"methods": ["GET", "POST"],
"url": {
"path": "/{index}/{type}/{id}/_mlt",
"paths": ["/{index}/{type}/{id}/_mlt"],
"parts": {
"id": {
"type" : "string",
"required" : true,
"description" : "The document ID"
},
"index": {
"type" : "string",
"required" : true,
"description" : "The name of the index"
},
"type": {
"type" : "string",
"required" : true,
"description" : "The type of the document (use `_all` to fetch the first document matching the ID across all types)"
}
},
"params": {
"boost_terms": {
"type" : "number",
"description" : "The boost factor"
},
"max_doc_freq": {
"type" : "number",
"description" : "The word occurrence frequency as count: words with higher occurrence in the corpus will be ignored"
},
"max_query_terms": {
"type" : "number",
"description" : "The maximum query terms to be included in the generated query"
},
"max_word_length": {
"type" : "number",
"description" : "The minimum length of the word: longer words will be ignored"
},
"min_doc_freq": {
"type" : "number",
"description" : "The word occurrence frequency as count: words with lower occurrence in the corpus will be ignored"
},
"min_term_freq": {
"type" : "number",
"description" : "The term frequency as percent: terms with lower occurrence in the source document will be ignored"
},
"min_word_length": {
"type" : "number",
"description" : "The minimum length of the word: shorter words will be ignored"
},
"mlt_fields": {
"type" : "list",
"description" : "Specific fields to perform the query against"
},
"percent_terms_to_match": {
"type" : "number",
"description" : "How many terms have to match in order to consider the document a match (default: 0.3)"
},
"routing": {
"type" : "string",
"description" : "Specific routing value"
},
"search_from": {
"type" : "number",
"description" : "The offset from which to return results"
},
"search_indices": {
"type" : "list",
"description" : "A comma-separated list of indices to perform the query against (default: the index containing the document)"
},
"search_scroll": {
"type" : "string",
"description" : "A scroll search request definition"
},
"search_size": {
"type" : "number",
"description" : "The number of documents to return (default: 10)"
},
"search_source": {
"type" : "string",
"description" : "A specific search request definition (instead of using the request body)"
},
"search_type": {
"type" : "string",
"description" : "Specific search type (eg. `dfs_then_fetch`, `scan`, etc)"
},
"search_types": {
"type" : "list",
"description" : "A comma-separated list of types to perform the query against (default: the same type as the document)"
},
"stop_words": {
"type" : "list",
"description" : "A list of stop words to be ignored"
}
}
},
"body": {
"description" : "A specific search request definition"
}
}
}

View File

@ -30,11 +30,16 @@
wait_for_status: green
- do:
mlt:
index: test_1
type: test
id: 1
mlt_fields: title
search:
index: test_1
type: test
body:
query:
more_like_this:
like:
-
_id: 1
fields: ["title"]
- match: {hits.total: 0}

View File

@ -139,8 +139,6 @@ import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptAction;
import org.elasticsearch.action.indexedscripts.get.TransportGetIndexedScriptAction;
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptAction;
import org.elasticsearch.action.indexedscripts.put.TransportPutIndexedScriptAction;
import org.elasticsearch.action.mlt.MoreLikeThisAction;
import org.elasticsearch.action.mlt.TransportMoreLikeThisAction;
import org.elasticsearch.action.percolate.*;
import org.elasticsearch.action.search.*;
import org.elasticsearch.action.search.type.*;
@ -293,7 +291,6 @@ public class ActionModule extends AbstractModule {
TransportSearchScrollQueryAndFetchAction.class
);
registerAction(MultiSearchAction.INSTANCE, TransportMultiSearchAction.class);
registerAction(MoreLikeThisAction.INSTANCE, TransportMoreLikeThisAction.class);
registerAction(PercolateAction.INSTANCE, TransportPercolateAction.class);
registerAction(MultiPercolateAction.INSTANCE, TransportMultiPercolateAction.class, TransportShardMultiPercolateAction.class);
registerAction(ExplainAction.INSTANCE, TransportExplainAction.class);

View File

@ -1,46 +0,0 @@
/*
* Licensed to Elasticsearch 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.action.mlt;
import org.elasticsearch.action.Action;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.ElasticsearchClient;
/**
*/
public class MoreLikeThisAction extends Action<MoreLikeThisRequest, SearchResponse, MoreLikeThisRequestBuilder> {
public static final MoreLikeThisAction INSTANCE = new MoreLikeThisAction();
public static final String NAME = "indices:data/read/mlt";
private MoreLikeThisAction() {
super(NAME);
}
@Override
public SearchResponse newResponse() {
return new SearchResponse();
}
@Override
public MoreLikeThisRequestBuilder newRequestBuilder(ElasticsearchClient client) {
return new MoreLikeThisRequestBuilder(client, this);
}
}

View File

@ -1,710 +0,0 @@
/*
* Licensed to Elasticsearch 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.action.mlt;
import com.google.common.collect.Lists;
import org.elasticsearch.ElasticsearchGenerationException;
import org.elasticsearch.action.*;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.Requests;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.search.Scroll.readScroll;
/**
* A more like this request allowing to search for documents that a "like" the provided document. The document
* to check against to fetched based on the index, type and id provided. Best created with {@link org.elasticsearch.client.Requests#moreLikeThisRequest(String)}.
* <p/>
* <p>Note, the {@link #index()}, {@link #type(String)} and {@link #id(String)} are required.
*
* @see org.elasticsearch.client.Client#moreLikeThis(MoreLikeThisRequest)
* @see org.elasticsearch.client.Requests#moreLikeThisRequest(String)
* @see org.elasticsearch.action.search.SearchResponse
*/
public class MoreLikeThisRequest extends ActionRequest<MoreLikeThisRequest> implements CompositeIndicesRequest {
private String index;
private String type;
private String id;
private String routing;
private String[] fields;
private String minimumShouldMatch = "0%";
private int minTermFreq = -1;
private int maxQueryTerms = -1;
private String[] stopWords = null;
private int minDocFreq = -1;
private int maxDocFreq = -1;
private int minWordLength = -1;
private int maxWordLength = -1;
private float boostTerms = -1;
private boolean include = false;
private SearchType searchType = SearchType.DEFAULT;
private int searchSize = 0;
private int searchFrom = 0;
private String[] searchIndices;
private String[] searchTypes;
private Scroll searchScroll;
private BytesReference searchSource;
MoreLikeThisRequest() {
}
/**
* Constructs a new more like this request for a document that will be fetch from the provided index.
* Use {@link #type(String)} and {@link #id(String)} to specify the document to load.
*/
public MoreLikeThisRequest(String index) {
this.index = index;
}
/**
* The index to load the document from which the "like" query will run with.
*/
public String index() {
return index;
}
/**
* The type of document to load from which the "like" query will run with.
*/
public String type() {
return type;
}
void index(String index) {
this.index = index;
}
public IndicesOptions indicesOptions() {
return IndicesOptions.strictSingleIndexNoExpandForbidClosed();
}
@Override
public List<? extends IndicesRequest> subRequests() {
//we create two fake indices subrequests as we don't have the actual ones yet
//since they get created later on in TransportMoreLikeThisAction
List<IndicesRequest> requests = Lists.newArrayList();
requests.add(new IndicesRequest() {
@Override
public String[] indices() {
return new String[]{index};
}
@Override
public IndicesOptions indicesOptions() {
return MoreLikeThisRequest.this.indicesOptions();
}
});
requests.add(new IndicesRequest.Replaceable() {
@Override
public String[] indices() {
if (searchIndices != null) {
return searchIndices;
}
return new String[]{index};
}
@Override
public IndicesRequest indices(String[] indices) {
searchIndices = indices;
return this;
}
@Override
public IndicesOptions indicesOptions() {
return SearchRequest.DEFAULT_INDICES_OPTIONS;
}
});
return requests;
}
/**
* The type of document to load from which the "like" query will execute with.
*/
public MoreLikeThisRequest type(String type) {
this.type = type;
return this;
}
/**
* The id of document to load from which the "like" query will execute with.
*/
public String id() {
return id;
}
/**
* The id of document to load from which the "like" query will execute with.
*/
public MoreLikeThisRequest id(String id) {
this.id = id;
return this;
}
/**
* @return The routing for this request. This used for the `get` part of the mlt request.
*/
public String routing() {
return routing;
}
public void routing(String routing) {
this.routing = routing;
}
/**
* The fields of the document to use in order to find documents "like" this one. Defaults to run
* against all the document fields.
*/
public String[] fields() {
return this.fields;
}
/**
* The fields of the document to use in order to find documents "like" this one. Defaults to run
* against all the document fields.
*/
public MoreLikeThisRequest fields(String... fields) {
this.fields = fields;
return this;
}
/**
* Number of terms that must match the generated query expressed in the
* common syntax for minimum should match. Defaults to <tt>30%</tt>.
*
* @see org.elasticsearch.common.lucene.search.Queries#calculateMinShouldMatch(int, String)
*/
public MoreLikeThisRequest minimumShouldMatch(String minimumShouldMatch) {
this.minimumShouldMatch = minimumShouldMatch;
return this;
}
/**
* Number of terms that must match the generated query expressed in the
* common syntax for minimum should match.
*
* @see org.elasticsearch.common.lucene.search.Queries#calculateMinShouldMatch(int, String)
*/
public String minimumShouldMatch() {
return this.minimumShouldMatch;
}
/**
* The percent of the terms to match for each field. Defaults to <tt>0.3f</tt>.
*/
@Deprecated
public MoreLikeThisRequest percentTermsToMatch(float percentTermsToMatch) {
return minimumShouldMatch(Math.round(percentTermsToMatch * 100) + "%");
}
/**
* The percent of the terms to match for each field. Defaults to <tt>0.3f</tt>.
*/
@Deprecated
public float percentTermsToMatch() {
if (minimumShouldMatch.endsWith("%")) {
return Float.parseFloat(minimumShouldMatch.substring(0, minimumShouldMatch.indexOf("%"))) / 100;
} else {
return -1;
}
}
/**
* The frequency below which terms will be ignored in the source doc. Defaults to <tt>2</tt>.
*/
public MoreLikeThisRequest minTermFreq(int minTermFreq) {
this.minTermFreq = minTermFreq;
return this;
}
/**
* The frequency below which terms will be ignored in the source doc. Defaults to <tt>2</tt>.
*/
public int minTermFreq() {
return this.minTermFreq;
}
/**
* The maximum number of query terms that will be included in any generated query. Defaults to <tt>25</tt>.
*/
public MoreLikeThisRequest maxQueryTerms(int maxQueryTerms) {
this.maxQueryTerms = maxQueryTerms;
return this;
}
/**
* The maximum number of query terms that will be included in any generated query. Defaults to <tt>25</tt>.
*/
public int maxQueryTerms() {
return this.maxQueryTerms;
}
/**
* Any word in this set is considered "uninteresting" and ignored.
* <p/>
* <p>Even if your Analyzer allows stopwords, you might want to tell the MoreLikeThis code to ignore them, as
* for the purposes of document similarity it seems reasonable to assume that "a stop word is never interesting".
* <p/>
* <p>Defaults to no stop words.
*/
public MoreLikeThisRequest stopWords(String... stopWords) {
this.stopWords = stopWords;
return this;
}
/**
* Any word in this set is considered "uninteresting" and ignored.
* <p/>
* <p>Even if your Analyzer allows stopwords, you might want to tell the MoreLikeThis code to ignore them, as
* for the purposes of document similarity it seems reasonable to assume that "a stop word is never interesting".
* <p/>
* <p>Defaults to no stop words.
*/
public String[] stopWords() {
return this.stopWords;
}
/**
* The frequency at which words will be ignored which do not occur in at least this
* many docs. Defaults to <tt>5</tt>.
*/
public MoreLikeThisRequest minDocFreq(int minDocFreq) {
this.minDocFreq = minDocFreq;
return this;
}
/**
* The frequency at which words will be ignored which do not occur in at least this
* many docs. Defaults to <tt>5</tt>.
*/
public int minDocFreq() {
return this.minDocFreq;
}
/**
* The maximum frequency in which words may still appear. Words that appear
* in more than this many docs will be ignored. Defaults to unbounded.
*/
public MoreLikeThisRequest maxDocFreq(int maxDocFreq) {
this.maxDocFreq = maxDocFreq;
return this;
}
/**
* The maximum frequency in which words may still appear. Words that appear
* in more than this many docs will be ignored. Defaults to unbounded.
*/
public int maxDocFreq() {
return this.maxDocFreq;
}
/**
* The minimum word length below which words will be ignored. Defaults to <tt>0</tt>.
*/
public MoreLikeThisRequest minWordLength(int minWordLength) {
this.minWordLength = minWordLength;
return this;
}
/**
* The minimum word length below which words will be ignored. Defaults to <tt>0</tt>.
*/
public int minWordLength() {
return this.minWordLength;
}
/**
* The maximum word length above which words will be ignored. Defaults to unbounded.
*/
public MoreLikeThisRequest maxWordLength(int maxWordLength) {
this.maxWordLength = maxWordLength;
return this;
}
/**
* The maximum word length above which words will be ignored. Defaults to unbounded.
*/
public int maxWordLength() {
return this.maxWordLength;
}
/**
* The boost factor to use when boosting terms. Defaults to <tt>1</tt>.
*/
public MoreLikeThisRequest boostTerms(float boostTerms) {
this.boostTerms = boostTerms;
return this;
}
/**
* The boost factor to use when boosting terms. Defaults to <tt>1</tt>.
*/
public float boostTerms() {
return this.boostTerms;
}
/**
* Whether to include the queried document. Defaults to <tt>false</tt>.
*/
public MoreLikeThisRequest include(boolean include) {
this.include = include;
return this;
}
/**
* Whether to include the queried document. Defaults to <tt>false</tt>.
*/
public boolean include() {
return this.include;
}
/**
* An optional search source request allowing to control the search request for the
* more like this documents.
*/
public MoreLikeThisRequest searchSource(SearchSourceBuilder sourceBuilder) {
this.searchSource = sourceBuilder.buildAsBytes(Requests.CONTENT_TYPE);
return this;
}
/**
* An optional search source request allowing to control the search request for the
* more like this documents.
*/
public MoreLikeThisRequest searchSource(String searchSource) {
this.searchSource = new BytesArray(searchSource);
return this;
}
public MoreLikeThisRequest searchSource(Map searchSource) {
try {
XContentBuilder builder = XContentFactory.contentBuilder(Requests.CONTENT_TYPE);
builder.map(searchSource);
return searchSource(builder);
} catch (IOException e) {
throw new ElasticsearchGenerationException("Failed to generate [" + searchSource + "]", e);
}
}
public MoreLikeThisRequest searchSource(XContentBuilder builder) {
this.searchSource = builder.bytes();
return this;
}
/**
* An optional search source request allowing to control the search request for the
* more like this documents.
*/
public MoreLikeThisRequest searchSource(byte[] searchSource) {
return searchSource(searchSource, 0, searchSource.length);
}
/**
* An optional search source request allowing to control the search request for the
* more like this documents.
*/
public MoreLikeThisRequest searchSource(byte[] searchSource, int offset, int length) {
return searchSource(new BytesArray(searchSource, offset, length));
}
/**
* An optional search source request allowing to control the search request for the
* more like this documents.
*/
public MoreLikeThisRequest searchSource(BytesReference searchSource) {
this.searchSource = searchSource;
return this;
}
/**
* An optional search source request allowing to control the search request for the
* more like this documents.
*/
public BytesReference searchSource() {
return this.searchSource;
}
/**
* The search type of the mlt search query.
*/
public MoreLikeThisRequest searchType(SearchType searchType) {
this.searchType = searchType;
return this;
}
/**
* The search type of the mlt search query.
*/
public MoreLikeThisRequest searchType(String searchType) {
return searchType(SearchType.fromString(searchType));
}
/**
* The search type of the mlt search query.
*/
public SearchType searchType() {
return this.searchType;
}
/**
* The indices the resulting mlt query will run against. If not set, will run
* against the index the document was fetched from.
*/
public MoreLikeThisRequest searchIndices(String... searchIndices) {
this.searchIndices = searchIndices;
return this;
}
/**
* The indices the resulting mlt query will run against. If not set, will run
* against the index the document was fetched from.
*/
public String[] searchIndices() {
return this.searchIndices;
}
/**
* The types the resulting mlt query will run against. If not set, will run
* against the type of the document fetched.
*/
public MoreLikeThisRequest searchTypes(String... searchTypes) {
this.searchTypes = searchTypes;
return this;
}
/**
* The types the resulting mlt query will run against. If not set, will run
* against the type of the document fetched.
*/
public String[] searchTypes() {
return this.searchTypes;
}
/**
* An optional search scroll request to be able to continue and scroll the search
* operation.
*/
public MoreLikeThisRequest searchScroll(Scroll searchScroll) {
this.searchScroll = searchScroll;
return this;
}
/**
* An optional search scroll request to be able to continue and scroll the search
* operation.
*/
public Scroll searchScroll() {
return this.searchScroll;
}
/**
* The number of documents to return, defaults to 10.
*/
public MoreLikeThisRequest searchSize(int size) {
this.searchSize = size;
return this;
}
public int searchSize() {
return this.searchSize;
}
/**
* From which search result set to return.
*/
public MoreLikeThisRequest searchFrom(int from) {
this.searchFrom = from;
return this;
}
public int searchFrom() {
return this.searchFrom;
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
if (index == null) {
validationException = ValidateActions.addValidationError("index is missing", validationException);
}
if (type == null) {
validationException = ValidateActions.addValidationError("type is missing", validationException);
}
if (id == null) {
validationException = ValidateActions.addValidationError("id is missing", validationException);
}
return validationException;
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
index = in.readString();
type = in.readString();
id = in.readString();
// no need to pass threading over the network, they are always false when coming throw a thread pool
int size = in.readVInt();
if (size == 0) {
fields = Strings.EMPTY_ARRAY;
} else {
fields = new String[size];
for (int i = 0; i < size; i++) {
fields[i] = in.readString();
}
}
minimumShouldMatch(in.readString());
minTermFreq = in.readVInt();
maxQueryTerms = in.readVInt();
size = in.readVInt();
if (size > 0) {
stopWords = new String[size];
for (int i = 0; i < size; i++) {
stopWords[i] = in.readString();
}
}
minDocFreq = in.readVInt();
maxDocFreq = in.readVInt();
minWordLength = in.readVInt();
maxWordLength = in.readVInt();
boostTerms = in.readFloat();
include = in.readBoolean();
searchType = SearchType.fromId(in.readByte());
size = in.readVInt();
if (size == 0) {
searchIndices = null;
} else if (size == 1) {
searchIndices = Strings.EMPTY_ARRAY;
} else {
searchIndices = new String[size - 1];
for (int i = 0; i < searchIndices.length; i++) {
searchIndices[i] = in.readString();
}
}
size = in.readVInt();
if (size == 0) {
searchTypes = null;
} else if (size == 1) {
searchTypes = Strings.EMPTY_ARRAY;
} else {
searchTypes = new String[size - 1];
for (int i = 0; i < searchTypes.length; i++) {
searchTypes[i] = in.readString();
}
}
if (in.readBoolean()) {
searchScroll = readScroll(in);
}
searchSource = in.readBytesReference();
searchSize = in.readVInt();
searchFrom = in.readVInt();
routing = in.readOptionalString();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(index);
out.writeString(type);
out.writeString(id);
if (fields == null) {
out.writeVInt(0);
} else {
out.writeVInt(fields.length);
for (String field : fields) {
out.writeString(field);
}
}
out.writeString(minimumShouldMatch);
out.writeVInt(minTermFreq);
out.writeVInt(maxQueryTerms);
if (stopWords == null) {
out.writeVInt(0);
} else {
out.writeVInt(stopWords.length);
for (String stopWord : stopWords) {
out.writeString(stopWord);
}
}
out.writeVInt(minDocFreq);
out.writeVInt(maxDocFreq);
out.writeVInt(minWordLength);
out.writeVInt(maxWordLength);
out.writeFloat(boostTerms);
out.writeBoolean(include);
out.writeByte(searchType.id());
if (searchIndices == null) {
out.writeVInt(0);
} else {
out.writeVInt(searchIndices.length + 1);
for (String index : searchIndices) {
out.writeString(index);
}
}
if (searchTypes == null) {
out.writeVInt(0);
} else {
out.writeVInt(searchTypes.length + 1);
for (String type : searchTypes) {
out.writeString(type);
}
}
if (searchScroll == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
searchScroll.writeTo(out);
}
out.writeBytesReference(searchSource);
out.writeVInt(searchSize);
out.writeVInt(searchFrom);
out.writeOptionalString(routing);
}
}

View File

@ -1,261 +0,0 @@
/*
* Licensed to Elasticsearch 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.action.mlt;
import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import java.util.Map;
/**
*/
public class MoreLikeThisRequestBuilder extends ActionRequestBuilder<MoreLikeThisRequest, SearchResponse, MoreLikeThisRequestBuilder> {
public MoreLikeThisRequestBuilder(ElasticsearchClient client, MoreLikeThisAction action) {
super(client, action, new MoreLikeThisRequest());
}
public MoreLikeThisRequestBuilder(ElasticsearchClient client, MoreLikeThisAction action, String index, String type, String id) {
super(client, action, new MoreLikeThisRequest(index).type(type).id(id));
}
/**
* The fields of the document to use in order to find documents "like" this one. Defaults to run
* against all the document fields.
*/
public MoreLikeThisRequestBuilder setField(String... fields) {
request.fields(fields);
return this;
}
/**
* Sets the routing. Required if routing isn't id based.
*/
public MoreLikeThisRequestBuilder setRouting(String routing) {
request.routing(routing);
return this;
}
/**
* Number of terms that must match the generated query expressed in the
* common syntax for minimum should match. Defaults to <tt>30%</tt>.
*
* @see org.elasticsearch.common.lucene.search.Queries#calculateMinShouldMatch(int, String)
*/
public MoreLikeThisRequestBuilder setMinimumShouldMatch(String minimumShouldMatch) {
request.minimumShouldMatch(minimumShouldMatch);
return this;
}
/**
* The percent of the terms to match for each field. Defaults to <tt>0.3f</tt>.
*/
public MoreLikeThisRequestBuilder setPercentTermsToMatch(float percentTermsToMatch) {
return setMinimumShouldMatch(Math.round(percentTermsToMatch * 100) + "%");
}
/**
* The frequency below which terms will be ignored in the source doc. Defaults to <tt>2</tt>.
*/
public MoreLikeThisRequestBuilder setMinTermFreq(int minTermFreq) {
request.minTermFreq(minTermFreq);
return this;
}
/**
* The maximum number of query terms that will be included in any generated query. Defaults to <tt>25</tt>.
*/
public MoreLikeThisRequestBuilder maxQueryTerms(int maxQueryTerms) {
request.maxQueryTerms(maxQueryTerms);
return this;
}
/**
* Any word in this set is considered "uninteresting" and ignored.
* <p/>
* <p>Even if your Analyzer allows stopwords, you might want to tell the MoreLikeThis code to ignore them, as
* for the purposes of document similarity it seems reasonable to assume that "a stop word is never interesting".
* <p/>
* <p>Defaults to no stop words.
*/
public MoreLikeThisRequestBuilder setStopWords(String... stopWords) {
request.stopWords(stopWords);
return this;
}
/**
* The frequency at which words will be ignored which do not occur in at least this
* many docs. Defaults to <tt>5</tt>.
*/
public MoreLikeThisRequestBuilder setMinDocFreq(int minDocFreq) {
request.minDocFreq(minDocFreq);
return this;
}
/**
* The maximum frequency in which words may still appear. Words that appear
* in more than this many docs will be ignored. Defaults to unbounded.
*/
public MoreLikeThisRequestBuilder setMaxDocFreq(int maxDocFreq) {
request.maxDocFreq(maxDocFreq);
return this;
}
/**
* The minimum word length below which words will be ignored. Defaults to <tt>0</tt>.
*/
public MoreLikeThisRequestBuilder setMinWordLen(int minWordLen) {
request.minWordLength(minWordLen);
return this;
}
/**
* The maximum word length above which words will be ignored. Defaults to unbounded.
*/
public MoreLikeThisRequestBuilder setMaxWordLen(int maxWordLen) {
request().maxWordLength(maxWordLen);
return this;
}
/**
* The boost factor to use when boosting terms. Defaults to <tt>1</tt>.
*/
public MoreLikeThisRequestBuilder setBoostTerms(float boostTerms) {
request.boostTerms(boostTerms);
return this;
}
/**
* Whether to include the queried document. Defaults to <tt>false</tt>.
*/
public MoreLikeThisRequestBuilder setInclude(boolean include) {
request.include(include);
return this;
}
/**
* An optional search source request allowing to control the search request for the
* more like this documents.
*/
public MoreLikeThisRequestBuilder setSearchSource(SearchSourceBuilder sourceBuilder) {
request.searchSource(sourceBuilder);
return this;
}
/**
* An optional search source request allowing to control the search request for the
* more like this documents.
*/
public MoreLikeThisRequestBuilder setSearchSource(String searchSource) {
request.searchSource(searchSource);
return this;
}
/**
* An optional search source request allowing to control the search request for the
* more like this documents.
*/
public MoreLikeThisRequestBuilder setSearchSource(Map searchSource) {
request.searchSource(searchSource);
return this;
}
/**
* An optional search source request allowing to control the search request for the
* more like this documents.
*/
public MoreLikeThisRequestBuilder setSearchSource(XContentBuilder builder) {
request.searchSource(builder);
return this;
}
/**
* An optional search source request allowing to control the search request for the
* more like this documents.
*/
public MoreLikeThisRequestBuilder setSearchSource(byte[] searchSource) {
request.searchSource(searchSource);
return this;
}
/**
* The search type of the mlt search query.
*/
public MoreLikeThisRequestBuilder setSearchType(SearchType searchType) {
request.searchType(searchType);
return this;
}
/**
* The search type of the mlt search query.
*/
public MoreLikeThisRequestBuilder setSearchType(String searchType) {
request.searchType(searchType);
return this;
}
/**
* The indices the resulting mlt query will run against. If not set, will run
* against the index the document was fetched from.
*/
public MoreLikeThisRequestBuilder setSearchIndices(String... searchIndices) {
request.searchIndices(searchIndices);
return this;
}
/**
* The types the resulting mlt query will run against. If not set, will run
* against the type of the document fetched.
*/
public MoreLikeThisRequestBuilder setSearchTypes(String... searchTypes) {
request.searchTypes(searchTypes);
return this;
}
/**
* An optional search scroll request to be able to continue and scroll the search
* operation.
*/
public MoreLikeThisRequestBuilder setSearchScroll(Scroll searchScroll) {
request.searchScroll(searchScroll);
return this;
}
/**
* The number of documents to return, defaults to 10.
*/
public MoreLikeThisRequestBuilder setSearchSize(int size) {
request.searchSize(size);
return this;
}
/**
* From which search result set to return.
*/
public MoreLikeThisRequestBuilder setSearchFrom(int from) {
request.searchFrom(from);
return this;
}
}

View File

@ -1,326 +0,0 @@
/*
* Licensed to Elasticsearch 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.action.mlt;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.Term;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.TransportGetAction;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.TransportSearchAction;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.routing.MutableShardRouting;
import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.engine.DocumentMissingException;
import org.elasticsearch.index.get.GetField;
import org.elasticsearch.index.mapper.*;
import org.elasticsearch.index.mapper.internal.SourceFieldMapper;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportException;
import org.elasticsearch.transport.TransportResponseHandler;
import org.elasticsearch.transport.TransportService;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import static com.google.common.collect.Sets.newHashSet;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
/**
* The more like this action.
*/
public class TransportMoreLikeThisAction extends HandledTransportAction<MoreLikeThisRequest, SearchResponse> {
private final TransportSearchAction searchAction;
private final TransportGetAction getAction;
private final IndicesService indicesService;
private final ClusterService clusterService;
private final TransportService transportService;
@Inject
public TransportMoreLikeThisAction(Settings settings, ThreadPool threadPool, TransportSearchAction searchAction, TransportGetAction getAction,
ClusterService clusterService, IndicesService indicesService, TransportService transportService, ActionFilters actionFilters) {
super(settings, MoreLikeThisAction.NAME, threadPool, transportService, actionFilters, MoreLikeThisRequest.class);
this.searchAction = searchAction;
this.getAction = getAction;
this.indicesService = indicesService;
this.clusterService = clusterService;
this.transportService = transportService;
}
@Override
protected void doExecute(final MoreLikeThisRequest request, final ActionListener<SearchResponse> listener) {
// update to actual index name
ClusterState clusterState = clusterService.state();
// update to the concrete index
final String concreteIndex = clusterState.metaData().concreteSingleIndex(request.index(), request.indicesOptions());
Iterable<MutableShardRouting> routingNode = clusterState.getRoutingNodes().routingNodeIter(clusterService.localNode().getId());
if (routingNode == null) {
redirect(request, concreteIndex, listener, clusterState);
return;
}
boolean hasIndexLocally = false;
for (MutableShardRouting shardRouting : routingNode) {
if (concreteIndex.equals(shardRouting.index())) {
hasIndexLocally = true;
break;
}
}
if (!hasIndexLocally) {
redirect(request, concreteIndex, listener, clusterState);
return;
}
Set<String> getFields = newHashSet();
if (request.fields() != null) {
Collections.addAll(getFields, request.fields());
}
// add the source, in case we need to parse it to get fields
getFields.add(SourceFieldMapper.NAME);
GetRequest getRequest = new GetRequest(request, request.index())
.fields(getFields.toArray(new String[getFields.size()]))
.type(request.type())
.id(request.id())
.routing(request.routing())
.operationThreaded(true);
getAction.execute(getRequest, new ActionListener<GetResponse>() {
@Override
public void onResponse(GetResponse getResponse) {
if (!getResponse.isExists()) {
listener.onFailure(new DocumentMissingException(null, request.type(), request.id()));
return;
}
final BoolQueryBuilder boolBuilder = boolQuery();
try {
final DocumentMapper docMapper = indicesService.indexServiceSafe(concreteIndex).mapperService().documentMapper(request.type());
if (docMapper == null) {
throw new ElasticsearchException("No DocumentMapper found for type [" + request.type() + "]");
}
final Set<String> fields = newHashSet();
if (request.fields() != null) {
for (String field : request.fields()) {
FieldMapper fieldMapper = docMapper.mappers().smartNameFieldMapper(field);
if (fieldMapper != null) {
fields.add(fieldMapper.names().indexName());
} else {
fields.add(field);
}
}
}
if (!fields.isEmpty()) {
// if fields are not empty, see if we got them in the response
for (Iterator<String> it = fields.iterator(); it.hasNext(); ) {
String field = it.next();
GetField getField = getResponse.getField(field);
if (getField != null) {
for (Object value : getField.getValues()) {
addMoreLikeThis(request, boolBuilder, getField.getName(), value.toString(), true);
}
it.remove();
}
}
if (!fields.isEmpty()) {
// if we don't get all the fields in the get response, see if we can parse the source
parseSource(getResponse, boolBuilder, docMapper, fields, request);
}
} else {
// we did not ask for any fields, try and get it from the source
parseSource(getResponse, boolBuilder, docMapper, fields, request);
}
if (!boolBuilder.hasClauses()) {
// no field added, fail
listener.onFailure(new ElasticsearchException("No fields found to fetch the 'likeText' from"));
return;
}
// exclude myself
if (!request.include()) {
Term uidTerm = docMapper.uidMapper().term(request.type(), request.id());
boolBuilder.mustNot(termQuery(uidTerm.field(), uidTerm.text()));
boolBuilder.adjustPureNegative(false);
}
} catch (Throwable e) {
listener.onFailure(e);
return;
}
String[] searchIndices = request.searchIndices();
if (searchIndices == null) {
searchIndices = new String[]{request.index()};
}
String[] searchTypes = request.searchTypes();
if (searchTypes == null) {
searchTypes = new String[]{request.type()};
}
SearchRequest searchRequest = new SearchRequest(request).indices(searchIndices)
.types(searchTypes)
.searchType(request.searchType())
.scroll(request.searchScroll());
SearchSourceBuilder extraSource = searchSource().query(boolBuilder);
if (request.searchFrom() != 0) {
extraSource.from(request.searchFrom());
}
if (request.searchSize() != 0) {
extraSource.size(request.searchSize());
}
searchRequest.extraSource(extraSource);
if (request.searchSource() != null) {
searchRequest.source(request.searchSource());
}
searchAction.execute(searchRequest, new ActionListener<SearchResponse>() {
@Override
public void onResponse(SearchResponse response) {
listener.onResponse(response);
}
@Override
public void onFailure(Throwable e) {
listener.onFailure(e);
}
});
}
@Override
public void onFailure(Throwable e) {
listener.onFailure(e);
}
});
}
// Redirects the request to a data node, that has the index meta data locally available.
private void redirect(MoreLikeThisRequest request, String concreteIndex, final ActionListener<SearchResponse> listener, ClusterState clusterState) {
ShardIterator shardIterator = clusterService.operationRouting().getShards(clusterState, concreteIndex, request.type(), request.id(), request.routing(), null);
ShardRouting shardRouting = shardIterator.nextOrNull();
if (shardRouting == null) {
throw new ElasticsearchException("No shards for index " + request.index());
}
String nodeId = shardRouting.currentNodeId();
DiscoveryNode discoveryNode = clusterState.nodes().get(nodeId);
transportService.sendRequest(discoveryNode, MoreLikeThisAction.NAME, request, new TransportResponseHandler<SearchResponse>() {
@Override
public SearchResponse newInstance() {
return new SearchResponse();
}
@Override
public void handleResponse(SearchResponse response) {
listener.onResponse(response);
}
@Override
public void handleException(TransportException exp) {
listener.onFailure(exp);
}
@Override
public String executor() {
return ThreadPool.Names.SAME;
}
});
}
private void parseSource(GetResponse getResponse, final BoolQueryBuilder boolBuilder, DocumentMapper docMapper, final Set<String> fields, final MoreLikeThisRequest request) {
if (getResponse.isSourceEmpty()) {
return;
}
docMapper.parse(SourceToParse.source(getResponse.getSourceAsBytesRef()).type(request.type()).id(request.id()), new DocumentMapper.ParseListenerAdapter() {
@Override
public boolean beforeFieldAdded(FieldMapper fieldMapper, Field field, Object parseContext) {
if (field.fieldType().indexOptions() == IndexOptions.NONE) {
return false;
}
if (fieldMapper instanceof InternalMapper) {
return true;
}
String value = fieldMapper.value(convertField(field)).toString();
if (value == null) {
return false;
}
if (fields.isEmpty() || fields.contains(field.name())) {
addMoreLikeThis(request, boolBuilder, fieldMapper, field, !fields.isEmpty());
}
return false;
}
});
}
private Object convertField(Field field) {
if (field.stringValue() != null) {
return field.stringValue();
} else if (field.binaryValue() != null) {
return BytesRef.deepCopyOf(field.binaryValue()).bytes;
} else if (field.numericValue() != null) {
return field.numericValue();
} else {
throw new IllegalStateException("Field should have either a string, numeric or binary value");
}
}
private void addMoreLikeThis(MoreLikeThisRequest request, BoolQueryBuilder boolBuilder, FieldMapper fieldMapper, Field field, boolean failOnUnsupportedField) {
addMoreLikeThis(request, boolBuilder, field.name(), fieldMapper.value(convertField(field)).toString(), failOnUnsupportedField);
}
private void addMoreLikeThis(MoreLikeThisRequest request, BoolQueryBuilder boolBuilder, String fieldName, String likeText, boolean failOnUnsupportedField) {
MoreLikeThisQueryBuilder mlt = moreLikeThisQuery(fieldName)
.likeText(likeText)
.minimumShouldMatch(request.minimumShouldMatch())
.boostTerms(request.boostTerms())
.minDocFreq(request.minDocFreq())
.maxDocFreq(request.maxDocFreq())
.minWordLength(request.minWordLength())
.maxWordLength(request.maxWordLength())
.minTermFreq(request.minTermFreq())
.maxQueryTerms(request.maxQueryTerms())
.stopWords(request.stopWords())
.failOnUnsupportedField(failOnUnsupportedField);
boolBuilder.should(mlt);
}
}

View File

@ -1,23 +0,0 @@
/*
* Licensed to Elasticsearch 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.
*/
/**
* More Like This action.
*/
package org.elasticsearch.action.mlt;

View File

@ -19,7 +19,8 @@
package org.elasticsearch.client;
import org.elasticsearch.action.*;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
@ -51,8 +52,6 @@ import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptResponse;
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptRequest;
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptRequestBuilder;
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptResponse;
import org.elasticsearch.action.mlt.MoreLikeThisRequest;
import org.elasticsearch.action.mlt.MoreLikeThisRequestBuilder;
import org.elasticsearch.action.percolate.*;
import org.elasticsearch.action.search.*;
import org.elasticsearch.action.suggest.SuggestRequest;
@ -467,32 +466,7 @@ public interface Client extends ElasticsearchClient, Releasable {
* Performs multiple search requests.
*/
MultiSearchRequestBuilder prepareMultiSearch();
/**
* A more like this action to search for documents that are "like" a specific document.
*
* @param request The more like this request
* @return The response future
*/
ActionFuture<SearchResponse> moreLikeThis(MoreLikeThisRequest request);
/**
* A more like this action to search for documents that are "like" a specific document.
*
* @param request The more like this request
* @param listener A listener to be notified of the result
*/
void moreLikeThis(MoreLikeThisRequest request, ActionListener<SearchResponse> listener);
/**
* A more like this action to search for documents that are "like" a specific document.
*
* @param index The index to load the document from
* @param type The type of the document
* @param id The id of the document
*/
MoreLikeThisRequestBuilder prepareMoreLikeThis(String index, String type, String id);
/**
* An action that returns the term vectors for a specific document.
*

View File

@ -55,7 +55,6 @@ import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.exists.ExistsRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.mlt.MoreLikeThisRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.suggest.SuggestRequest;
@ -156,19 +155,7 @@ public class Requests {
public static SuggestRequest suggestRequest(String... indices) {
return new SuggestRequest(indices);
}
/**
* More like this request represents a request to search for documents that are "like" the provided (fetched)
* document.
*
* @param index The index to load the document from
* @return The more like this request
* @see org.elasticsearch.client.Client#moreLikeThis(org.elasticsearch.action.mlt.MoreLikeThisRequest)
*/
public static MoreLikeThisRequest moreLikeThisRequest(String index) {
return new MoreLikeThisRequest(index);
}
/**
* Creates a search request against one or more indices. Note, the search source must be set either using the
* actual JSON search source, or the {@link org.elasticsearch.search.builder.SearchSourceBuilder}.

View File

@ -249,9 +249,6 @@ import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptAction;
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptRequest;
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptRequestBuilder;
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptResponse;
import org.elasticsearch.action.mlt.MoreLikeThisAction;
import org.elasticsearch.action.mlt.MoreLikeThisRequest;
import org.elasticsearch.action.mlt.MoreLikeThisRequestBuilder;
import org.elasticsearch.action.percolate.*;
import org.elasticsearch.action.search.*;
import org.elasticsearch.action.suggest.SuggestAction;
@ -636,21 +633,6 @@ public abstract class AbstractClient extends AbstractComponent implements Client
return new SuggestRequestBuilder(this, SuggestAction.INSTANCE).setIndices(indices);
}
@Override
public ActionFuture<SearchResponse> moreLikeThis(final MoreLikeThisRequest request) {
return execute(MoreLikeThisAction.INSTANCE, request);
}
@Override
public void moreLikeThis(final MoreLikeThisRequest request, final ActionListener<SearchResponse> listener) {
execute(MoreLikeThisAction.INSTANCE, request, listener);
}
@Override
public MoreLikeThisRequestBuilder prepareMoreLikeThis(String index, String type, String id) {
return new MoreLikeThisRequestBuilder(this, MoreLikeThisAction.INSTANCE, index, type, id);
}
@Override
public ActionFuture<TermVectorsResponse> termVectors(final TermVectorsRequest request) {
return execute(TermVectorsAction.INSTANCE, request);

View File

@ -23,8 +23,6 @@ import com.google.common.collect.Lists;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.multibindings.Multibinder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.action.admin.indices.upgrade.RestUpgradeAction;
import org.elasticsearch.rest.action.admin.cluster.repositories.verify.RestVerifyRepositoryAction;
import org.elasticsearch.rest.action.admin.cluster.health.RestClusterHealthAction;
import org.elasticsearch.rest.action.admin.cluster.node.hotthreads.RestNodesHotThreadsAction;
import org.elasticsearch.rest.action.admin.cluster.node.info.RestNodesInfoAction;
@ -32,6 +30,7 @@ import org.elasticsearch.rest.action.admin.cluster.node.stats.RestNodesStatsActi
import org.elasticsearch.rest.action.admin.cluster.repositories.delete.RestDeleteRepositoryAction;
import org.elasticsearch.rest.action.admin.cluster.repositories.get.RestGetRepositoriesAction;
import org.elasticsearch.rest.action.admin.cluster.repositories.put.RestPutRepositoryAction;
import org.elasticsearch.rest.action.admin.cluster.repositories.verify.RestVerifyRepositoryAction;
import org.elasticsearch.rest.action.admin.cluster.reroute.RestClusterRerouteAction;
import org.elasticsearch.rest.action.admin.cluster.settings.RestClusterGetSettingsAction;
import org.elasticsearch.rest.action.admin.cluster.settings.RestClusterUpdateSettingsAction;
@ -64,6 +63,7 @@ import org.elasticsearch.rest.action.admin.indices.mapping.get.RestGetMappingAct
import org.elasticsearch.rest.action.admin.indices.mapping.put.RestPutMappingAction;
import org.elasticsearch.rest.action.admin.indices.open.RestOpenIndexAction;
import org.elasticsearch.rest.action.admin.indices.optimize.RestOptimizeAction;
import org.elasticsearch.rest.action.admin.indices.recovery.RestRecoveryAction;
import org.elasticsearch.rest.action.admin.indices.refresh.RestRefreshAction;
import org.elasticsearch.rest.action.admin.indices.segments.RestIndicesSegmentsAction;
import org.elasticsearch.rest.action.admin.indices.settings.RestGetSettingsAction;
@ -73,11 +73,11 @@ import org.elasticsearch.rest.action.admin.indices.template.delete.RestDeleteInd
import org.elasticsearch.rest.action.admin.indices.template.get.RestGetIndexTemplateAction;
import org.elasticsearch.rest.action.admin.indices.template.head.RestHeadIndexTemplateAction;
import org.elasticsearch.rest.action.admin.indices.template.put.RestPutIndexTemplateAction;
import org.elasticsearch.rest.action.admin.indices.upgrade.RestUpgradeAction;
import org.elasticsearch.rest.action.admin.indices.validate.query.RestValidateQueryAction;
import org.elasticsearch.rest.action.admin.indices.warmer.delete.RestDeleteWarmerAction;
import org.elasticsearch.rest.action.admin.indices.warmer.get.RestGetWarmerAction;
import org.elasticsearch.rest.action.admin.indices.warmer.put.RestPutWarmerAction;
import org.elasticsearch.rest.action.admin.indices.recovery.RestRecoveryAction;
import org.elasticsearch.rest.action.bulk.RestBulkAction;
import org.elasticsearch.rest.action.cat.*;
import org.elasticsearch.rest.action.delete.RestDeleteAction;
@ -89,7 +89,6 @@ import org.elasticsearch.rest.action.get.RestHeadAction;
import org.elasticsearch.rest.action.get.RestMultiGetAction;
import org.elasticsearch.rest.action.index.RestIndexAction;
import org.elasticsearch.rest.action.main.RestMainAction;
import org.elasticsearch.rest.action.mlt.RestMoreLikeThisAction;
import org.elasticsearch.rest.action.percolate.RestMultiPercolateAction;
import org.elasticsearch.rest.action.percolate.RestPercolateAction;
import org.elasticsearch.rest.action.script.RestDeleteIndexedScriptAction;
@ -209,8 +208,6 @@ public class RestActionModule extends AbstractModule {
bind(RestValidateQueryAction.class).asEagerSingleton();
bind(RestMoreLikeThisAction.class).asEagerSingleton();
bind(RestExplainAction.class).asEagerSingleton();
bind(RestRecoveryAction.class).asEagerSingleton();

View File

@ -1,88 +0,0 @@
/*
* Licensed to Elasticsearch 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.rest.action.mlt;
import org.elasticsearch.action.mlt.MoreLikeThisRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.*;
import org.elasticsearch.rest.action.support.RestToXContentListener;
import org.elasticsearch.search.Scroll;
import static org.elasticsearch.client.Requests.moreLikeThisRequest;
import static org.elasticsearch.common.unit.TimeValue.parseTimeValue;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;
/**
*
*/
public class RestMoreLikeThisAction extends BaseRestHandler {
@Inject
public RestMoreLikeThisAction(Settings settings, RestController controller, Client client) {
super(settings, controller, client);
controller.registerHandler(GET, "/{index}/{type}/{id}/_mlt", this);
controller.registerHandler(POST, "/{index}/{type}/{id}/_mlt", this);
}
@Override
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) {
MoreLikeThisRequest mltRequest = moreLikeThisRequest(request.param("index")).type(request.param("type")).id(request.param("id"));
mltRequest.routing(request.param("routing"));
//TODO the ParseField class that encapsulates the supported names used for an attribute
//needs some work if it is to be used in a REST context like this too
// See the MoreLikeThisQueryParser constants that hold the valid syntax
mltRequest.fields(request.paramAsStringArray("mlt_fields", null));
mltRequest.minimumShouldMatch(request.param("minimum_should_match", "0"));
mltRequest.minTermFreq(request.paramAsInt("min_term_freq", -1));
mltRequest.maxQueryTerms(request.paramAsInt("max_query_terms", -1));
mltRequest.stopWords(request.paramAsStringArray("stop_words", null));
mltRequest.minDocFreq(request.paramAsInt("min_doc_freq", -1));
mltRequest.maxDocFreq(request.paramAsInt("max_doc_freq", -1));
mltRequest.minWordLength(request.paramAsInt("min_word_len", request.paramAsInt("min_word_length", -1)));
mltRequest.maxWordLength(request.paramAsInt("max_word_len", request.paramAsInt("max_word_length", -1)));
mltRequest.boostTerms(request.paramAsFloat("boost_terms", -1));
mltRequest.include(request.paramAsBoolean("include", false));
mltRequest.searchType(SearchType.fromString(request.param("search_type")));
mltRequest.searchIndices(request.paramAsStringArray("search_indices", null));
mltRequest.searchTypes(request.paramAsStringArray("search_types", null));
mltRequest.searchSize(request.paramAsInt("search_size", mltRequest.searchSize()));
mltRequest.searchFrom(request.paramAsInt("search_from", mltRequest.searchFrom()));
String searchScroll = request.param("search_scroll");
if (searchScroll != null) {
mltRequest.searchScroll(new Scroll(parseTimeValue(searchScroll, null)));
}
if (request.hasContent()) {
mltRequest.searchSource(request.content());
} else {
String searchSource = request.param("search_source");
if (searchSource != null) {
mltRequest.searchSource(searchSource);
}
}
client.moreLikeThis(mltRequest, new RestToXContentListener<SearchResponse>(channel));
}
}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.action;
import org.apache.lucene.util.LuceneTestCase.Slow;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeAction;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest;
@ -52,7 +53,6 @@ import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsAction;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryAction;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
import org.elasticsearch.action.bulk.BulkAction;
@ -71,7 +71,6 @@ import org.elasticsearch.action.get.MultiGetAction;
import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.index.IndexAction;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.mlt.MoreLikeThisRequest;
import org.elasticsearch.action.percolate.MultiPercolateAction;
import org.elasticsearch.action.percolate.MultiPercolateRequest;
import org.elasticsearch.action.percolate.PercolateAction;
@ -89,20 +88,15 @@ import org.elasticsearch.action.termvectors.TermVectorsRequest;
import org.elasticsearch.action.update.UpdateAction;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.cluster.settings.ClusterDynamicSettings;
import org.elasticsearch.cluster.settings.DynamicSettings;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.node.settings.NodeSettingsService;
import org.elasticsearch.search.action.SearchServiceTransportAction;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.*;
import org.apache.lucene.util.LuceneTestCase.Slow;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -730,34 +724,6 @@ public class IndicesRequestTests extends ElasticsearchIntegrationTest {
assertSameIndices(searchRequest, SearchServiceTransportAction.SCAN_ACTION_NAME);
}
@Test
public void testMoreLikeThis() {
interceptTransportActions(GetAction.NAME + "[s]", SearchServiceTransportAction.QUERY_ACTION_NAME,
SearchServiceTransportAction.FETCH_ID_ACTION_NAME, SearchServiceTransportAction.FREE_CONTEXT_ACTION_NAME);
String[] randomIndicesOrAliases = randomIndicesOrAliases();
for (int i = 0; i < randomIndicesOrAliases.length; i++) {
client().prepareIndex(randomIndicesOrAliases[i], "type", "id-" + i).setSource("field", "value").get();
}
refresh();
assertAcked(prepareCreate("test-get").addAlias(new Alias("alias-get")));
client().prepareIndex("test-get", "type", "1").setSource("field","value").get();
String indexGet = randomBoolean() ? "test-get" : "alias-get";
MoreLikeThisRequest moreLikeThisRequest = new MoreLikeThisRequest(indexGet).type("type").id("1")
.searchIndices(randomIndicesOrAliases());
internalCluster().clientNodeClient().moreLikeThis(moreLikeThisRequest).actionGet();
clearInterceptedActions();
//get might end up being executed locally, only optionally over the transport
assertSameIndicesOptionalRequests(new String[]{indexGet}, GetAction.NAME + "[s]");
//query might end up being executed locally as well, only optionally over the transport
assertSameIndicesOptionalRequests(moreLikeThisRequest.searchIndices(), SearchServiceTransportAction.QUERY_ACTION_NAME);
//free context messages are not necessarily sent through the transport, but if they are, check their indices
assertSameIndicesOptionalRequests(moreLikeThisRequest.searchIndices(), SearchServiceTransportAction.FETCH_ID_ACTION_NAME, SearchServiceTransportAction.FREE_CONTEXT_ACTION_NAME);
}
private static void assertSameIndices(IndicesRequest originalRequest, String... actions) {
assertSameIndices(originalRequest, false, actions);
}

View File

@ -1,160 +0,0 @@
/*
* Licensed to Elasticsearch 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.action.mlt;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.BytesStreamInput;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
import java.io.IOException;
import static org.elasticsearch.test.VersionUtils.randomVersion;
import static org.hamcrest.CoreMatchers.*;
public class MoreLikeThisRequestTests extends ElasticsearchTestCase {
@Test
public void testSerialization() throws IOException {
MoreLikeThisRequest mltRequest = new MoreLikeThisRequest(randomAsciiOfLength(randomIntBetween(1, 20)))
.id(randomAsciiOfLength(randomIntBetween(1, 20))).type(randomAsciiOfLength(randomIntBetween(1, 20)));
if (randomBoolean()) {
mltRequest.boostTerms(randomFloat());
}
if (randomBoolean()) {
mltRequest.maxDocFreq(randomInt());
}
if (randomBoolean()) {
mltRequest.minDocFreq(randomInt());
}
if (randomBoolean()) {
mltRequest.maxQueryTerms(randomInt());
}
if (randomBoolean()) {
mltRequest.minWordLength(randomInt());
}
if (randomBoolean()) {
mltRequest.maxWordLength(randomInt());
}
if (randomBoolean()) {
mltRequest.percentTermsToMatch(randomFloat());
}
if (randomBoolean()) {
mltRequest.searchTypes(randomStrings(5));
}
if (randomBoolean()) {
mltRequest.searchType(randomFrom(SearchType.values()));
}
if (randomBoolean()) {
mltRequest.searchIndices(randomStrings(5));
}
if (randomBoolean()) {
mltRequest.routing(randomAsciiOfLength(randomIntBetween(1, 20)));
}
if (randomBoolean()) {
mltRequest.searchFrom(randomInt());
}
if (randomBoolean()) {
mltRequest.searchSize(randomInt());
}
if (randomBoolean()) {
mltRequest.searchScroll(new Scroll(TimeValue.timeValueNanos(randomLong())));
}
if (randomBoolean()) {
mltRequest.searchSource(SearchSourceBuilder.searchSource().query(QueryBuilders.termQuery("term", "value")));
}
if(randomBoolean()) {
mltRequest.include(randomBoolean());
}
if (randomBoolean()) {
mltRequest.stopWords(randomStrings(10));
}
if (randomBoolean()) {
mltRequest.fields(randomStrings(5));
}
BytesStreamOutput out = new BytesStreamOutput();
out.setVersion(randomVersion(random()));
mltRequest.writeTo(out);
BytesStreamInput in = new BytesStreamInput(out.bytes());
in.setVersion(out.getVersion());
MoreLikeThisRequest mltRequest2 = new MoreLikeThisRequest();
mltRequest2.readFrom(in);
assertThat(mltRequest2.index(), equalTo(mltRequest.index()));
assertThat(mltRequest2.type(), equalTo(mltRequest.type()));
assertThat(mltRequest2.id(), equalTo(mltRequest.id()));
assertThat(mltRequest2.boostTerms(), equalTo(mltRequest.boostTerms()));
assertThat(mltRequest2.maxDocFreq(), equalTo(mltRequest.maxDocFreq()));
assertThat(mltRequest2.minDocFreq(), equalTo(mltRequest.minDocFreq()));
assertThat(mltRequest2.maxQueryTerms(), equalTo(mltRequest.maxQueryTerms()));
assertThat(mltRequest2.minWordLength(), equalTo(mltRequest.minWordLength()));
assertThat(mltRequest2.maxWordLength(), equalTo(mltRequest.maxWordLength()));
assertThat(mltRequest2.percentTermsToMatch(), equalTo(mltRequest.percentTermsToMatch()));
assertThat(mltRequest2.searchTypes(), equalTo(mltRequest.searchTypes()));
assertThat(mltRequest2.searchType(), equalTo(mltRequest.searchType()));
assertThat(mltRequest2.searchIndices(), equalTo(mltRequest.searchIndices()));
assertThat(mltRequest2.routing(), equalTo(mltRequest.routing()));
assertThat(mltRequest2.searchFrom(), equalTo(mltRequest.searchFrom()));
assertThat(mltRequest2.searchSize(), equalTo(mltRequest.searchSize()));
if (mltRequest.searchScroll() == null) {
assertThat(mltRequest2.searchScroll(), nullValue());
} else {
assertThat(mltRequest2.searchFrom(), notNullValue());
assertThat(mltRequest2.searchScroll().keepAlive(), equalTo(mltRequest.searchScroll().keepAlive()));
}
if (mltRequest.searchSource() == null) {
assertThat(mltRequest2.searchSource().length(), equalTo(0));
} else {
assertThat(mltRequest2.searchSource().length(), equalTo(mltRequest.searchSource().length()));
}
if (mltRequest.stopWords() != null && mltRequest.stopWords().length > 0) {
assertThat(mltRequest2.stopWords(), equalTo(mltRequest.stopWords()));
} else {
assertThat(mltRequest2.stopWords(), nullValue());
}
if (mltRequest.fields() == null) {
assertThat(mltRequest2.fields(), equalTo(Strings.EMPTY_ARRAY));
} else {
assertThat(mltRequest2.fields(), equalTo(mltRequest.fields()));
}
assertThat(mltRequest2.include(), equalTo(mltRequest.include()));
}
private static String[] randomStrings(int max) {
int count = randomIntBetween(0, max);
String[] strings = new String[count];
for (int i = 0; i < strings.length; i++) {
strings[i] = randomAsciiOfLength(randomIntBetween(1, 20));
}
return strings;
}
}

View File

@ -17,7 +17,7 @@
* under the License.
*/
package org.elasticsearch.common.lucene.search;
package org.elasticsearch.common.lucene.search.morelikethis;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
@ -30,10 +30,10 @@ import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.lucene.search.MoreLikeThisQuery;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
/**

View File

@ -17,7 +17,7 @@
* under the License.
*/
package org.elasticsearch.mlt;
package org.elasticsearch.common.lucene.search.morelikethis;
import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.analysis.MockTokenizer;

View File

@ -1,8 +1,8 @@
{
more_like_this:{
fields:["name.first", "name.last"],
like_text:"something",
min_term_freq:1,
max_query_terms:12
"more_like_this" : {
"fields" : ["name.first", "name.last"],
"like_text" : "something",
"min_term_freq" : 1,
"max_query_terms" : 12
}
}

View File

@ -1,9 +0,0 @@
{
more_like_this_field:{
"name.first":{
like_text:"something",
min_term_freq:1,
max_query_terms:12
}
}
}

View File

@ -17,7 +17,7 @@
* under the License.
*/
package org.elasticsearch.index.query;
package org.elasticsearch.search.morelikethis;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import org.elasticsearch.action.get.MultiGetRequest;
@ -27,6 +27,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder.Item;
import org.elasticsearch.search.fetch.source.FetchSourceContext;
import org.elasticsearch.test.ElasticsearchTestCase;
@ -134,7 +135,7 @@ public class ItemSerializationTests extends ElasticsearchTestCase {
public void testSimpleItemSerializationFromFile() throws Exception {
// test items from JSON
List<MultiGetRequest.Item> itemsFromJSON = testItemsFromJSON(
copyToStringFromClasspath("/org/elasticsearch/index/query/items.json"));
copyToStringFromClasspath("/org/elasticsearch/search/morelikethis/items.json"));
// create builder from items
XContentBuilder builder = XContentFactory.jsonBuilder();

View File

@ -17,16 +17,13 @@
* under the License.
*/
package org.elasticsearch.mlt;
package org.elasticsearch.search.morelikethis;
import org.apache.lucene.util.ArrayUtil;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.mlt.MoreLikeThisRequest;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -34,14 +31,11 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder.Item;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutionException;
@ -58,7 +52,7 @@ import static org.hamcrest.Matchers.notNullValue;
/**
*
*/
public class MoreLikeThisActionTests extends ElasticsearchIntegrationTest {
public class MoreLikeThisTests extends ElasticsearchIntegrationTest {
@Test
public void testSimpleMoreLikeThis() throws Exception {
@ -77,8 +71,9 @@ public class MoreLikeThisActionTests extends ElasticsearchIntegrationTest {
client().admin().indices().refresh(refreshRequest()).actionGet();
logger.info("Running moreLikeThis");
SearchResponse mltResponse = client().moreLikeThis(moreLikeThisRequest("test").type("type1").id("1").minTermFreq(1).minDocFreq(1)).actionGet();
assertHitCount(mltResponse, 1l);
SearchResponse response = client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("test", "type1", "1")).minTermFreq(1).minDocFreq(1)).get();
assertHitCount(response, 1l);
}
@ -98,8 +93,9 @@ public class MoreLikeThisActionTests extends ElasticsearchIntegrationTest {
client().admin().indices().refresh(refreshRequest()).actionGet();
logger.info("Running moreLikeThis");
SearchResponse mltResponse = client().moreLikeThis(moreLikeThisRequest("test").type("type1").id("1").minTermFreq(1).minDocFreq(1)).actionGet();
assertHitCount(mltResponse, 0l);
SearchResponse response = client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("test", "type1", "1")).minTermFreq(1).minDocFreq(1)).get();
assertHitCount(response, 0l);
}
@ -125,23 +121,27 @@ public class MoreLikeThisActionTests extends ElasticsearchIntegrationTest {
client().admin().indices().refresh(refreshRequest()).actionGet();
logger.info("Running moreLikeThis on index");
SearchResponse mltResponse = client().moreLikeThis(moreLikeThisRequest("test").type("type1").id("1").minTermFreq(1).minDocFreq(1)).actionGet();
assertHitCount(mltResponse, 2l);
SearchResponse response = client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("test", "type1", "1")).minTermFreq(1).minDocFreq(1)).get();
assertHitCount(response, 2l);
logger.info("Running moreLikeThis on beta shard");
mltResponse = client().moreLikeThis(moreLikeThisRequest("beta").type("type1").id("1").minTermFreq(1).minDocFreq(1)).actionGet();
assertHitCount(mltResponse, 1l);
assertThat(mltResponse.getHits().getAt(0).id(), equalTo("3"));
response = client().prepareSearch("beta").setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("test", "type1", "1")).minTermFreq(1).minDocFreq(1)).get();
assertHitCount(response, 1l);
assertThat(response.getHits().getAt(0).id(), equalTo("3"));
logger.info("Running moreLikeThis on release shard");
mltResponse = client().moreLikeThis(moreLikeThisRequest("test").type("type1").id("1").minTermFreq(1).minDocFreq(1).searchIndices("release")).actionGet();
assertHitCount(mltResponse, 1l);
assertThat(mltResponse.getHits().getAt(0).id(), equalTo("2"));
response = client().prepareSearch("release").setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("test", "type1", "1")).minTermFreq(1).minDocFreq(1)).get();
assertHitCount(response, 1l);
assertThat(response.getHits().getAt(0).id(), equalTo("2"));
logger.info("Running moreLikeThis on alias with node client");
mltResponse = internalCluster().clientNodeClient().moreLikeThis(moreLikeThisRequest("beta").type("type1").id("1").minTermFreq(1).minDocFreq(1)).actionGet();
assertHitCount(mltResponse, 1l);
assertThat(mltResponse.getHits().getAt(0).id(), equalTo("3"));
response = internalCluster().clientNodeClient().prepareSearch("beta").setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("test", "type1", "1")).minTermFreq(1).minDocFreq(1)).get();
assertHitCount(response, 1l);
assertThat(response.getHits().getAt(0).id(), equalTo("3"));
}
@ -159,12 +159,14 @@ public class MoreLikeThisActionTests extends ElasticsearchIntegrationTest {
client().admin().indices().prepareRefresh("foo").execute().actionGet();
assertThat(ensureGreen(), equalTo(ClusterHealthStatus.GREEN));
SearchResponse searchResponse = client().prepareMoreLikeThis("foo", "bar", "1").execute().actionGet();
assertNoFailures(searchResponse);
assertThat(searchResponse, notNullValue());
searchResponse = client.prepareMoreLikeThis("foo", "bar", "1").execute().actionGet();
assertNoFailures(searchResponse);
assertThat(searchResponse, notNullValue());
SearchResponse response = client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("foo", "bar", "1"))).get();
assertNoFailures(response);
assertThat(response, notNullValue());
response = client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("foo", "bar", "1"))).get();
assertNoFailures(response);
assertThat(response, notNullValue());
}
@Test
@ -183,9 +185,10 @@ public class MoreLikeThisActionTests extends ElasticsearchIntegrationTest {
.execute().actionGet();
client().admin().indices().prepareRefresh("foo").execute().actionGet();
SearchResponse searchResponse = client().prepareMoreLikeThis("foo", "bar", "1").setRouting("2").execute().actionGet();
assertNoFailures(searchResponse);
assertThat(searchResponse, notNullValue());
SearchResponse response = client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder().addItem((Item) new Item("foo", "bar", "1").routing("2"))).get();
assertNoFailures(response);
assertThat(response, notNullValue());
}
@Test
@ -205,9 +208,10 @@ public class MoreLikeThisActionTests extends ElasticsearchIntegrationTest {
.setRouting("4000")
.execute().actionGet();
client().admin().indices().prepareRefresh("foo").execute().actionGet();
SearchResponse searchResponse = client().prepareMoreLikeThis("foo", "bar", "1").setRouting("4000").execute().actionGet();
assertNoFailures(searchResponse);
assertThat(searchResponse, notNullValue());
SearchResponse response = client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder().addItem((Item) new Item("foo", "bar", "1").routing("4000"))).get();
assertNoFailures(response);
assertThat(response, notNullValue());
}
@Test
@ -232,11 +236,13 @@ public class MoreLikeThisActionTests extends ElasticsearchIntegrationTest {
refresh();
// Implicit list of fields -> ignore numeric fields
SearchResponse searchResponse = client().prepareMoreLikeThis("test", "type", "1").setMinDocFreq(1).setMinTermFreq(1).execute().actionGet();
SearchResponse searchResponse = client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("test", "type", "1")).minTermFreq(1).minDocFreq(1)).get();
assertHitCount(searchResponse, 1l);
// Explicit list of fields including numeric fields -> fail
assertThrows(client().prepareMoreLikeThis("test", "type", "1").setField("string_value", "int_value"), SearchPhaseExecutionException.class);
assertThrows(client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder("string_value", "int_value").addItem(new Item("test", "type", "1")).minTermFreq(1).minDocFreq(1)), SearchPhaseExecutionException.class);
// mlt query with no field -> OK
searchResponse = client().prepareSearch().setQuery(moreLikeThisQuery().likeText("index").minTermFreq(1).minDocFreq(1)).execute().actionGet();
@ -292,65 +298,18 @@ public class MoreLikeThisActionTests extends ElasticsearchIntegrationTest {
client().admin().indices().refresh(refreshRequest()).actionGet();
logger.info("Running More Like This with include true");
SearchResponse mltResponse = client().moreLikeThis(
moreLikeThisRequest("test").type("type1").id("1").minTermFreq(1).minDocFreq(1).include(true).percentTermsToMatch(0))
.actionGet();
assertOrderedSearchHits(mltResponse, "1", "2");
SearchResponse response = client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("test", "type1", "1")).minTermFreq(1).minDocFreq(1).include(true).percentTermsToMatch(0)).get();
assertOrderedSearchHits(response, "1", "2");
mltResponse = client().moreLikeThis(
moreLikeThisRequest("test").type("type1").id("2").minTermFreq(1).minDocFreq(1).include(true).percentTermsToMatch(0))
.actionGet();
assertOrderedSearchHits(mltResponse, "2", "1");
response = client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("test", "type1", "2")).minTermFreq(1).minDocFreq(1).include(true).percentTermsToMatch(0)).get();
assertOrderedSearchHits(response, "2", "1");
logger.info("Running More Like This with include false");
mltResponse = client().moreLikeThis(
moreLikeThisRequest("test").type("type1").id("1").minTermFreq(1).minDocFreq(1).percentTermsToMatch(0))
.actionGet();
assertSearchHits(mltResponse, "2");
}
@Test
public void testMoreLikeThisBodyFromSize() throws Exception {
logger.info("Creating index test");
assertAcked(prepareCreate("test").addMapping("type1",
jsonBuilder().startObject().startObject("type1").startObject("properties")
.startObject("text").field("type", "string").endObject()
.endObject().endObject().endObject()));
logger.info("Running Cluster Health");
assertThat(ensureGreen(), equalTo(ClusterHealthStatus.GREEN));
logger.info("Indexing...");
List<IndexRequestBuilder> builders = new ArrayList<>(10);
for (int i = 1; i <= 10; i++) {
builders.add(client().prepareIndex("test", "type1").setSource("text", "lucene").setId(String.valueOf(i)));
}
indexRandom(true, builders);
logger.info("'size' set but 'search_from' and 'search_size' kept to defaults");
SearchResponse mltResponse = client().moreLikeThis(
moreLikeThisRequest("test").type("type1").id("1").minTermFreq(1).minDocFreq(1).include(true)
.searchSource(SearchSourceBuilder.searchSource().size(5)))
.actionGet();
assertSearchResponse(mltResponse);
assertEquals(mltResponse.getHits().hits().length, 5);
logger.info("'from' set but 'search_from' and 'search_size' kept to defaults");
mltResponse = client().moreLikeThis(
moreLikeThisRequest("test").type("type1").id("1").minTermFreq(1).minDocFreq(1).include(true)
.searchSource(SearchSourceBuilder.searchSource().from(5)))
.actionGet();
assertSearchResponse(mltResponse);
assertEquals(mltResponse.getHits().hits().length, 5);
logger.info("When set, 'search_from' and 'search_size' should override 'from' and 'size'");
mltResponse = client().moreLikeThis(
moreLikeThisRequest("test").type("type1").id("1").minTermFreq(1).minDocFreq(1).include(true)
.searchSize(10).searchFrom(2)
.searchSource(SearchSourceBuilder.searchSource().size(1).from(1)))
.actionGet();
assertSearchResponse(mltResponse);
assertEquals(mltResponse.getHits().hits().length, 8);
response = client().prepareSearch().setQuery(
new MoreLikeThisQueryBuilder().addItem(new Item("test", "type1", "1")).minTermFreq(1).minDocFreq(1).percentTermsToMatch(0)).get();
assertSearchHits(response, "2");
}
public void testSimpleMoreLikeThisIds() throws Exception {
@ -376,85 +335,6 @@ public class MoreLikeThisActionTests extends ElasticsearchIntegrationTest {
assertHitCount(mltResponse, 3l);
}
@Test
public void testCompareMoreLikeThisDSLWithAPI() throws Exception {
logger.info("Creating index test");
assertAcked(prepareCreate("test").addMapping("type1",
jsonBuilder().startObject().startObject("type1").startObject("properties")
.startObject("text").field("type", "string").endObject()
.endObject().endObject().endObject()));
logger.info("Running Cluster Health");
assertThat(ensureGreen(), equalTo(ClusterHealthStatus.GREEN));
logger.info("Indexing...");
String[] texts = new String[] {
"Apache Lucene",
"free and open source",
"information retrieval",
"software library",
"programmed in Java",
"Doug Cutting",
"Apache Software Foundation",
"Apache Software License",
"Lucene programming languages",
"Delphi, Perl, C#, C++, Python, Ruby, and PHP"
};
List<IndexRequestBuilder> builders = new ArrayList<>(10);
for (int i = 0; i < texts.length; i++) {
builders.add(client().prepareIndex("test", "type1").setSource("text", texts[i]).setId(String.valueOf(i)));
}
indexRandom(true, false, builders);
int iters = between(10, 20);
for (int j = 0; j < iters; j++) {
logger.info("Running MoreLikeThis DSL with IDs");
String id = String.valueOf(getRandom().nextInt(texts.length));
Client client = client();
MoreLikeThisQueryBuilder queryBuilder = QueryBuilders.moreLikeThisQuery("text").ids(id).minTermFreq(1).minDocFreq(1)
.minimumShouldMatch("0%");
SearchResponse mltResponseDSL = client.prepareSearch()
.setSearchType(SearchType.QUERY_THEN_FETCH)
.setTypes("type1")
.setQuery(queryBuilder)
.setSize(texts.length)
.execute().actionGet();
assertSearchResponse(mltResponseDSL);
logger.info("Running MoreLikeThis API");
MoreLikeThisRequest mltRequest = moreLikeThisRequest("test").type("type1").searchSize(texts.length).id(id).minTermFreq(1).minDocFreq(1)
.minimumShouldMatch("0%");
SearchResponse mltResponseAPI = client.moreLikeThis(mltRequest).actionGet();
assertSearchResponse(mltResponseAPI);
logger.info("Ensure the documents and scores returned are the same.");
SearchHit[] hitsDSL = mltResponseDSL.getHits().hits();
SearchHit[] hitsAPI = mltResponseAPI.getHits().hits();
// we have to resort since the results might come from
// different shards and docIDs that are used for tie-breaking might not be the same on the shards
Comparator<SearchHit> cmp = new Comparator<SearchHit>() {
@Override
public int compare(SearchHit o1, SearchHit o2) {
if (Float.compare(o1.getScore(), o2.getScore()) == 0) {
return o1.getId().compareTo(o2.getId());
}
return Float.compare(o1.getScore(), o2.getScore());
}
};
ArrayUtil.timSort(hitsDSL, cmp);
ArrayUtil.timSort(hitsAPI, cmp);
assertThat("Not the same number of results.", hitsAPI.length, equalTo(hitsDSL.length));
for (int i = 0; i < hitsDSL.length; i++) {
assertThat("Expected id: " + hitsDSL[i].getId() + " at position " + i + " but wasn't.",
hitsAPI[i].getId(), equalTo(hitsDSL[i].getId()));
assertThat("Expected score: " + hitsDSL[i].getScore() + " at position " + i + " but wasn't.",
hitsAPI[i].getScore(), equalTo(hitsDSL[i].getScore()));
}
}
}
@Test
public void testSimpleMoreLikeThisIdsMultipleTypes() throws Exception {
logger.info("Creating index test");
@ -519,14 +399,6 @@ public class MoreLikeThisActionTests extends ElasticsearchIntegrationTest {
.setQuery(mltQuery).execute().actionGet();
assertSearchResponse(response);
assertHitCount(response, max_query_terms);
logger.info("Running More Like This API with with max_query_terms = %s returns all docs!", max_query_terms);
response = client().moreLikeThis(moreLikeThisRequest("test").type("type1")
.id("0").fields("text").minTermFreq(1).minDocFreq(1)
.maxQueryTerms(max_query_terms).percentTermsToMatch(0))
.actionGet();
assertSearchResponse(response);
assertHitCount(response, values.length);
}
}