我们来学习一下elasticsearch customScoreQuery 的使用。customScoreQuery 可以自定义score,从而来影响排序结果。
建立mapping
public static XContentBuilder createMapping(String indexName){
XContentBuilder mapping = null;
try {
mapping = jsonBuilder()
.startObject()
// 索引库名(类似数据库中的表)
.startObject(indexName)
.startObject("properties")
// 新闻ID
.startObject("id")
.field("type", "long")
.field("store", "yes")
.field("index", "not_analyzed")
.field("include_in_all", "false")
.endObject()
// title
.startObject("titele")
.field("type", "string")
.field("store", "yes")
.field("term_vector","with_positions_offsets")
.field("indexAnalyzer", "ik")
.field("searchAnalyzer", "ik")
.field("include_in_all", "false")
.field("boost", 4.0) // 打分(默认1.0)
.endObject()
.startObject("updatetime")
.field("type", "string")
.field("store", "yes")
.field("include_in_all", "false")
.endObject()
.endObject()
.endObject()
.endObject();
} catch (IOException e) {
e.printStackTrace();
}
return mapping;
}
创建索引
public static void createIndex(String indexName, String indexType){
Client esClient = ESServiceSingleton2.getTransportClient();
// 创建Mapping
XContentBuilder mapping = createMapping(indexName);
// 创建一个空索引
esClient.admin().indices().prepareCreate(indexName).execute().actionGet();
PutMappingRequest mappingRequest = Requests.putMappingRequest(indexName).type(indexType).source(mapping);
esClient.admin().indices().putMapping(mappingRequest).actionGet();
}
添加索引数据:
public static Integer addIndex(String indexName, String indexType){
Client esClient = ESServiceSingleton2.getTransportClient();
BulkRequestBuilder bulkRequest = esClient.prepareBulk();
try{
bulkRequest.add(esClient.prepareIndex(indexName, indexType, ""+5)
.setSource(jsonBuilder()
.startObject()
.field("id", 1)
.field("title", "脱北者中国")
.field("updatetime","1370563200")
.endObject())
);
bulkRequest.add(esClient.prepareIndex(indexName, indexType, ""+2)
.setSource(jsonBuilder()
.startObject()
.field("id", 2)
.field("title", "脱北者中国")
.field("updatetime","1350327958")
.endObject())
);
}catch(Exception e){
e.printStackTrace();
}
bulkRequest.execute().actionGet();
return bulkRequest.numberOfActions();
}
创建查询:
public static void query(String query,String indexName,String indexType){
Client client = ESServiceSingleton2.getTransportClient();
SearchResponse response = client.prepareSearch(indexName)
.setTypes(indexType)
//.setQuery(getQueryBuilder(query))
.setQuery(new CustomScoreQueryBuilder(getQueryBuilder(query)).script(
"long x=Long.parseLong(doc['updatetime'].value);" +
"x=x/86400l;"+
"_score=_score+sqrt(yy-x)"
).param("yy", System.currentTimeMillis()/1000l/86400l))
.addFields("id","title","updatetime")
.setFrom(0).setSize(60).setExplain(true)
.addHighlightedField("title")
.setHighlighterEncoder("UTF-8")
.setHighlighterPreTags("<em>")
.setHighlighterPostTags("</em>")
.execute()
.actionGet();
Float usetime = response.getTookInMillis()/1000f;
// 命中记录数
Long hits = response.getHits().totalHits();
System.out.println("usetime:"+usetime+" hits:"+hits);
for (SearchHit hit : response.getHits()) {
Float score = hit.getScore();
// 文章id
Integer id = Integer.parseInt(hit.getFields().get("id").value().toString());
String title = hit.getHighlightFields().containsKey("title")?hit.getHighlightFields().get("title").getFragments()[0].toString():hit.getFields().get("title").value().toString();
Long updatetime = Long.parseLong(hit.getFields().get("updatetime").value().toString());
System.out.println("the record id:"+id+" title: "+title+" updatetime:"+updatetime+" score:"+score);
}
}
获取querybuilder
public static QueryBuilder getQueryBuilder(String query){
QueryStringQueryBuilder qsqb=new QueryStringQueryBuilder(query);
qsqb.analyzer("ik").field("title");
return qsqb;
}
主函数
public class IndexTest {
public static void main(String[] args){
Client client = ESServiceSingleton2.getTransportClient();
client.admin().indices().prepareDelete("test").execute().actionGet();
createIndex("test", "xq");
addIndex("test", "xq");
client.admin().indices().prepareRefresh().execute().actionGet();
query("北","test", "xq");
client.close();
}
}
当时操作出现了空指针错误。
[2013-06-08 16:44:00,730][DEBUG][action.search.type ] [Golem] [test][1], node[h9UqCv4wTECtb_VLWnkxcQ], [P], s[STARTED]: Failed to execute [org.elasticsearch.action.search.SearchRequest@175edcf]
org.elasticsearch.search.query.QueryPhaseExecutionException: [test][1]: query[filtered(custom score (title:北,function=script[long x=Long.parseLong(doc['updatetime'].value);x=x/86400l;long nows=yy/86400l;int nowDay=(int)nows;int day=(int)x;if (day < nowDay) {} else if (day > nowDay) {} else{}], params [{_source=org.elasticsearch.search.lookup.SourceLookup@1cfefc3, nows=15864, _fields=org.elasticsearch.search.lookup.FieldsLookup@86100a, _doc=org.elasticsearch.search.lookup.DocLookup@1871341, doc=org.elasticsearch.search.lookup.DocLookup@1871341, day=15910, _score=0.13424811, nowDay=15864, yy=1370681040, x=15910.719386574074}]))->cache(_type:xq)],from[0],size[60]: Query Failed [Failed to execute main query]
at org.elasticsearch.search.query.QueryPhase.execute(QueryPhase.java:138)
at org.elasticsearch.search.SearchService.executeQueryPhase(SearchService.java:239)
at org.elasticsearch.search.action.SearchServiceTransportAction.sendExecuteQuery(SearchServiceTransportAction.java:141)
at org.elasticsearch.action.search.type.TransportSearchQueryThenFetchAction$AsyncAction.sendExecuteFirstPhase(TransportSearchQueryThenFetchAction.java:80)
at org.elasticsearch.action.search.type.TransportSearchTypeAction$BaseAsyncAction.performFirstPhase(TransportSearchTypeAction.java:206)
at org.elasticsearch.action.search.type.TransportSearchTypeAction$BaseAsyncAction.performFirstPhase(TransportSearchTypeAction.java:193)
at org.elasticsearch.action.search.type.TransportSearchTypeAction$BaseAsyncAction$2.run(TransportSearchTypeAction.java:179)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.NullPointerException
at org.elasticsearch.script.mvel.MvelScriptEngineService$MvelSearchScript.runAsFloat(MvelScriptEngineService.java:197)
at org.elasticsearch.index.query.CustomScoreQueryParser$ScriptScoreFunction.score(CustomScoreQueryParser.java:135)
at org.elasticsearch.common.lucene.search.function.FunctionScoreQuery$CustomBoostFactorScorer.score(FunctionScoreQuery.java:155)
at org.apache.lucene.search.TopScoreDocCollector$OutOfOrderTopScoreDocCollector.collect(TopScoreDocCollector.java:140)
at org.apache.lucene.search.Scorer.score(Scorer.java:65)
at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:612)
at org.elasticsearch.search.internal.ContextIndexSearcher.search(ContextIndexSearcher.java:161)
at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:482)
at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:438)
at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:281)
at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:269)
at org.elasticsearch.search.query.QueryPhase.execute(QueryPhase.java:134)
... 9 more
代码为:
new CustomScoreQueryBuilder(getQueryBuilder(query)).script(
"long x=Long.parseLong(doc['updatetime'].value);" +
"x=x/86400l;"
).param("yy", System.currentTimeMillis()/1000l/86400l)
从上面代码看,script没有问题,最后查到原因,CustomScoreQueryBuilder 必须要对_score 赋值,不然回报空指针错误。
customScoreQuery 的script 采用的是mvel的语法。mvel采用了类似java的语法。更多详细资料可参考:
http://blog.csdn.net/fhm727/article/details/6543152
获取java client可参考:http://www.chepoo.com/elasticsearch-java-client-demo.html
customScoreQuery 的script的函数支持可参考:
http://www.elasticsearch.org/guide/reference/modules/scripting/
customScoreQuery 的语法可参考:
http://www.elasticsearch.org/guide/reference/query-dsl/custom-score-query/
http://www.elasticsearch.org/guide/reference/query-dsl/custom-boost-factor-query/