Skip to content

Commit 0ebb74d

Browse files
committed
Query DSL: moreLikeThis & moreLikeThisField, closes #43.
1 parent e70bec7 commit 0ebb74d

File tree

10 files changed

+821
-0
lines changed

10 files changed

+821
-0
lines changed

modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonQueryBuilders.java

+8
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ public static ConstantScoreQueryJsonQueryBuilder constantScoreQuery(JsonFilterBu
116116
return new ConstantScoreQueryJsonQueryBuilder(filterBuilder);
117117
}
118118

119+
public static MoreLikeThisJsonQueryBuilder moreLikeThis(String... fields) {
120+
return new MoreLikeThisJsonQueryBuilder(fields);
121+
}
122+
123+
public static MoreLikeThisFieldJsonQueryBuilder moreLikeThisField(String name) {
124+
return new MoreLikeThisFieldJsonQueryBuilder(name);
125+
}
126+
119127
private JsonQueryBuilders() {
120128

121129
}

modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonQueryParserRegistry.java

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ public JsonQueryParserRegistry(Index index,
6262
add(queryParsersMap, new SpanFirstJsonQueryParser(index, indexSettings));
6363
add(queryParsersMap, new SpanNearJsonQueryParser(index, indexSettings));
6464
add(queryParsersMap, new SpanOrJsonQueryParser(index, indexSettings));
65+
add(queryParsersMap, new MoreLikeThisJsonQueryParser(index, indexSettings));
66+
add(queryParsersMap, new MoreLikeThisFieldJsonQueryParser(index, indexSettings));
6567

6668
// now, copy over the ones provided
6769
if (queryParsers != null) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Licensed to Elastic Search and Shay Banon under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. Elastic Search licenses this
6+
* file to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.index.query.json;
21+
22+
import org.elasticsearch.index.query.QueryBuilderException;
23+
import org.elasticsearch.util.json.JsonBuilder;
24+
25+
import java.io.IOException;
26+
27+
/**
28+
* @author kimchy (Shay Banon)
29+
*/
30+
public class MoreLikeThisFieldJsonQueryBuilder extends BaseJsonQueryBuilder {
31+
32+
private final String name;
33+
34+
private String likeText;
35+
private float percentTermsToMatch = -1;
36+
private int minTermFrequency = -1;
37+
private int maxQueryTerms = -1;
38+
private String[] stopWords = null;
39+
private int minDocFreq = -1;
40+
private int maxDocFreq = -1;
41+
private int minWordLen = -1;
42+
private int maxWordLen = -1;
43+
private Boolean boostTerms = null;
44+
private float boostTermsFactor = -1;
45+
46+
public MoreLikeThisFieldJsonQueryBuilder(String name) {
47+
this.name = name;
48+
}
49+
50+
public MoreLikeThisFieldJsonQueryBuilder likeText(String likeText) {
51+
this.likeText = likeText;
52+
return this;
53+
}
54+
55+
public MoreLikeThisFieldJsonQueryBuilder percentTermsToMatch(float percentTermsToMatch) {
56+
this.percentTermsToMatch = percentTermsToMatch;
57+
return this;
58+
}
59+
60+
public MoreLikeThisFieldJsonQueryBuilder minTermFrequency(int minTermFrequency) {
61+
this.minTermFrequency = minTermFrequency;
62+
return this;
63+
}
64+
65+
public MoreLikeThisFieldJsonQueryBuilder maxQueryTerms(int maxQueryTerms) {
66+
this.maxQueryTerms = maxQueryTerms;
67+
return this;
68+
}
69+
70+
public MoreLikeThisFieldJsonQueryBuilder stopWords(String... stopWords) {
71+
this.stopWords = stopWords;
72+
return this;
73+
}
74+
75+
public MoreLikeThisFieldJsonQueryBuilder minDocFreq(int minDocFreq) {
76+
this.minDocFreq = minDocFreq;
77+
return this;
78+
}
79+
80+
public MoreLikeThisFieldJsonQueryBuilder maxDocFreq(int maxDocFreq) {
81+
this.maxDocFreq = maxDocFreq;
82+
return this;
83+
}
84+
85+
public MoreLikeThisFieldJsonQueryBuilder minWordLen(int minWordLen) {
86+
this.minWordLen = minWordLen;
87+
return this;
88+
}
89+
90+
public MoreLikeThisFieldJsonQueryBuilder maxWordLen(int maxWordLen) {
91+
this.maxWordLen = maxWordLen;
92+
return this;
93+
}
94+
95+
public MoreLikeThisFieldJsonQueryBuilder boostTerms(boolean boostTerms) {
96+
this.boostTerms = boostTerms;
97+
return this;
98+
}
99+
100+
public MoreLikeThisFieldJsonQueryBuilder boostTermsFactor(float boostTermsFactor) {
101+
this.boostTermsFactor = boostTermsFactor;
102+
return this;
103+
}
104+
105+
@Override protected void doJson(JsonBuilder builder, Params params) throws IOException {
106+
builder.startObject(MoreLikeThisFieldJsonQueryParser.NAME);
107+
builder.startObject(name);
108+
if (likeText == null) {
109+
throw new QueryBuilderException("moreLikeThisField requires 'likeText' to be provided");
110+
}
111+
builder.field("likeText", likeText);
112+
if (percentTermsToMatch != -1) {
113+
builder.field("percentTermsToMatch", percentTermsToMatch);
114+
}
115+
if (minTermFrequency != -1) {
116+
builder.field("minTermFrequency", minTermFrequency);
117+
}
118+
if (maxQueryTerms != -1) {
119+
builder.field("maxQueryTerms", maxQueryTerms);
120+
}
121+
if (stopWords != null && stopWords.length > 0) {
122+
builder.startArray("stopWords");
123+
for (String stopWord : stopWords) {
124+
builder.string(stopWord);
125+
}
126+
builder.endArray();
127+
}
128+
if (minDocFreq != -1) {
129+
builder.field("minDocFreq", minDocFreq);
130+
}
131+
if (maxDocFreq != -1) {
132+
builder.field("maxDocFreq", maxDocFreq);
133+
}
134+
if (minWordLen != -1) {
135+
builder.field("minWordLen", minWordLen);
136+
}
137+
if (maxWordLen != -1) {
138+
builder.field("maxWordLen", maxWordLen);
139+
}
140+
if (boostTerms != null) {
141+
builder.field("boostTerms", boostTerms);
142+
}
143+
if (boostTermsFactor != -1) {
144+
builder.field("boostTermsFactor", boostTermsFactor);
145+
}
146+
builder.endObject();
147+
builder.endObject();
148+
}
149+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Licensed to Elastic Search and Shay Banon under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. Elastic Search licenses this
6+
* file to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.index.query.json;
21+
22+
import com.google.common.collect.Sets;
23+
import org.apache.lucene.search.Query;
24+
import org.codehaus.jackson.JsonParser;
25+
import org.codehaus.jackson.JsonToken;
26+
import org.elasticsearch.index.AbstractIndexComponent;
27+
import org.elasticsearch.index.Index;
28+
import org.elasticsearch.index.mapper.MapperService;
29+
import org.elasticsearch.index.query.QueryParsingException;
30+
import org.elasticsearch.index.settings.IndexSettings;
31+
import org.elasticsearch.util.lucene.search.MoreLikeThisQuery;
32+
import org.elasticsearch.util.settings.Settings;
33+
34+
import java.io.IOException;
35+
import java.util.Set;
36+
37+
import static org.elasticsearch.index.query.support.QueryParsers.*;
38+
39+
/**
40+
* @author kimchy (shay.banon)
41+
*/
42+
public class MoreLikeThisFieldJsonQueryParser extends AbstractIndexComponent implements JsonQueryParser {
43+
44+
public static final String NAME = "moreLikeThisField";
45+
46+
public MoreLikeThisFieldJsonQueryParser(Index index, @IndexSettings Settings indexSettings) {
47+
super(index, indexSettings);
48+
}
49+
50+
@Override public String name() {
51+
return NAME;
52+
}
53+
54+
@Override public Query parse(JsonQueryParseContext parseContext) throws IOException, QueryParsingException {
55+
JsonParser jp = parseContext.jp();
56+
57+
JsonToken token = jp.nextToken();
58+
assert token == JsonToken.FIELD_NAME;
59+
String fieldName = jp.getCurrentName();
60+
61+
// now, we move after the field name, which starts the object
62+
token = jp.nextToken();
63+
assert token == JsonToken.START_OBJECT;
64+
65+
66+
MoreLikeThisQuery mltQuery = new MoreLikeThisQuery();
67+
68+
String currentFieldName = null;
69+
while ((token = jp.nextToken()) != JsonToken.END_OBJECT) {
70+
if (token == JsonToken.FIELD_NAME) {
71+
currentFieldName = jp.getCurrentName();
72+
} else if (token == JsonToken.VALUE_STRING) {
73+
if ("likeText".equals(currentFieldName)) {
74+
mltQuery.setLikeText(jp.getText());
75+
}
76+
} else if (token == JsonToken.VALUE_NUMBER_INT) {
77+
if ("minTermFrequency".equals(currentFieldName)) {
78+
mltQuery.setMinTermFrequency(jp.getIntValue());
79+
} else if ("maxQueryTerms".equals(currentFieldName)) {
80+
mltQuery.setMaxQueryTerms(jp.getIntValue());
81+
} else if ("minDocFreq".equals(currentFieldName)) {
82+
mltQuery.setMinDocFreq(jp.getIntValue());
83+
} else if ("maxDocFreq".equals(currentFieldName)) {
84+
mltQuery.setMaxDocFreq(jp.getIntValue());
85+
} else if ("minWordLen".equals(currentFieldName)) {
86+
mltQuery.setMinWordLen(jp.getIntValue());
87+
} else if ("maxWordLen".equals(currentFieldName)) {
88+
mltQuery.setMaxWordLen(jp.getIntValue());
89+
} else if ("boostTerms".equals(currentFieldName)) {
90+
mltQuery.setBoostTerms(jp.getIntValue() != 0);
91+
} else if ("boostTermsFactor".equals(currentFieldName)) {
92+
mltQuery.setBoostTermsFactor(jp.getIntValue());
93+
}
94+
} else if (token == JsonToken.VALUE_NUMBER_FLOAT) {
95+
if ("boostTermsFactor".equals(currentFieldName)) {
96+
mltQuery.setBoostTermsFactor(jp.getFloatValue());
97+
}
98+
} else if (token == JsonToken.START_ARRAY) {
99+
if ("stopWords".equals(currentFieldName)) {
100+
Set<String> stopWords = Sets.newHashSet();
101+
while ((token = jp.nextToken()) != JsonToken.END_ARRAY) {
102+
stopWords.add(jp.getText());
103+
}
104+
mltQuery.setStopWords(stopWords);
105+
}
106+
}
107+
}
108+
109+
if (mltQuery.getLikeText() == null) {
110+
throw new QueryParsingException(index, "moreLikeThisField requires 'likeText' to be specified");
111+
}
112+
113+
// move to the next end object, to close the field name
114+
token = jp.nextToken();
115+
assert token == JsonToken.END_OBJECT;
116+
117+
mltQuery.setAnalyzer(parseContext.mapperService().searchAnalyzer());
118+
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
119+
if (smartNameFieldMappers != null) {
120+
if (smartNameFieldMappers.hasMapper()) {
121+
fieldName = smartNameFieldMappers.mapper().names().indexName();
122+
mltQuery.setAnalyzer(smartNameFieldMappers.mapper().searchAnalyzer());
123+
}
124+
}
125+
mltQuery.setMoreLikeFields(new String[]{fieldName});
126+
return wrapSmartNameQuery(mltQuery, smartNameFieldMappers, parseContext.filterCache());
127+
}
128+
}

0 commit comments

Comments
 (0)