相信每一个使用lucene或者solr的程序员最深恶痛觉的就是为每一个文档(document)创建索引,特别是lucene,一些复杂一点的索引需求会写半个屏幕的查询语法。但是有了es,这些问题都迎刃而解了。通过es的特性,只需要做一个后台管理页面,让业务人员录入索引规则就可以自动索引和搜索啦,再也不用我手动写索引程序啦!全自动,不需要管理!
下面我来讲讲具体实现。
首先是建表,创建一个属性(property)表,定义type里每个字段的属性。
property_id 索引属性id
custom_type_id 自定义索引id
property_name 索引属性名称
property_mapping 属性映射
property_type 索引属性类型
property_index 索引方式
property_analyzer 分词器
property_highlight 高亮字段,00是,01否
property_category 是否是类别字段,00是,01否
property_boost 权重
property_should 索引匹配百分比,minimumShouldMatch,用于去长尾
然后是创建type表,对应mapping里的每个type
type_id type的id
index_id index的id
type_name type的名称
index_mapping 索引的映射,用来在es的mappings中创建type
is_highlight 是否开启高亮
is_search_log 是否创建搜索日志,用于后续搜索分析
最后是创建index了
custom_index_id 自定义索引id
custom_index_name 自定义索引名称
custom_index_mapping 自定义索引映射,用于在es中创建索引
数据库的表建好以后就可以在程序中创建具体的索引了。只需要把程序中把属性表集合遍历一遍,很容易就能创建出映射。
public boolean createIndex(Client client, String indexStore, String index,Object obj) {
CustomIndex customIndex = (CustomIndex)obj;
List<Property> properties = customIndex.getProperties();
XContentBuilder xContentBuilder = null;
try {
xContentBuilder = XContentFactory.jsonBuilder();
xContentBuilder.startObject();
xContentBuilder.startObject(index);
xContentBuilder.startObject("properties");
for (Property property : properties) {
xContentBuilder.startObject(property.getPropertyMapping());
xContentBuilder.field("type",property.getPropertyType());
if(!"index".equals(property.getPropertyIndex())){
xContentBuilder.field("index",property.getPropertyIndex());
}
if(!StringUtils.isEmpty(property.getPropertyAnalyzer())){
xContentBuilder.field("analyzer", property.getPropertyAnalyzer());
}
xContentBuilder.endObject();
}
xContentBuilder.endObject();
xContentBuilder.endObject();
xContentBuilder.endObject();
} catch (IOException e) {
e.printStackTrace();
logger.error("创建索引文档失败",e);
return false;
}
//查看es是否存在该type
if(ESClient.testTypeExist(client, indexStore, index)){
return false;
}
PutMappingResponse response = client.admin().indices().preparePutMapping(indexStore).setType(index).setSource(xContentBuilder).execute().actionGet();
return response.isAcknowledged();
}
接下来是重头戏搜索了,搜索稍微麻烦一点,要根据创建的自定义索引id查询出整个索引对象,然后拼凑出搜索需要的数据。
public ReturnMessage<String> Search(Client client, Searcher searcher) {
//获取自定义索引对象
CustomIndex customIndex = searcher.getCustomIndex();
//封装分页
SplitPage splitPage = new SplitPage();
splitPage.setPageNum(searcher.getPageNum());
splitPage.setPageSize(searcher.getPageSize());
// 创建搜索器
BoolQueryBuilder boolQuery = boolQuery();
// 创建搜索请求器
SearchRequestBuilder responsebuilder = client.prepareSearch(searcher.getIndex()).setTypes(searcher.getType());
// 设置搜索模式,高精度
responsebuilder.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);
//遍历自定义属性
List<Property> properties = customIndex.getProperties();
//获取分类id
String[] categoryIds = searcher.getCategoryIds();
//高亮字段
List<String> highlightedFields = new ArrayList<>();
for (Property property : properties) {
//判断需要分词索引的属性
if("index".equals(property.getPropertyIndex())){
MatchQueryBuilder matchQuery = matchQuery(property.getPropertyMapping(),searcher.getKeywords());
//添加权重
if(property.getPropertyBoost() != null && property.getPropertyBoost() != 0f){
matchQuery.boost(property.getPropertyBoost());
}
//添加匹配百分比
if(!StringUtils.isEmpty(property.getPropertyShould())){
matchQuery.minimumShouldMatch(property.getPropertyShould());
}
boolQuery.should(matchQuery);
//判断是否是高亮字段
if("00".equals(property.getPropertyHighlight())){
responsebuilder.addHighlightedField(property.getPropertyMapping());
highlightedFields.add(property.getPropertyMapping());
}
//判断是否是分类字段
if("00".equals(property.getPropertyCategory())){
if(categoryIds != null){
for (String categoryId : categoryIds) {
responsebuilder.setPostFilter(termQuery("categoryId", categoryId));
}
}
}
}
}
// 添加高亮标签
responsebuilder.setHighlighterPreTags(searcher.getHighlightStart());
responsebuilder.setHighlighterPostTags(searcher.getHighlightend());
//添加查询和分页
responsebuilder.setQuery(boolQuery).setFrom(splitPage.getStartRow()).setSize(splitPage.getPageSize());
//执行查询
SearchResponse actionGet = responsebuilder.execute().actionGet();
SearchHits hits = actionGet.getHits();
int totalHits = (int) hits.totalHits();// 拿到总条数
List<Map<String, Object>> sources = new ArrayList<>();
//迭代结果集
Iterator<SearchHit> iterator = hits.iterator();
while (iterator.hasNext()) {
SearchHit searchHit = iterator.next();
Map<String, Object> source = searchHit.getSource();
Map<String, HighlightField> highlightFields = searchHit.highlightFields();
Set<Entry<String, HighlightField>> entrySet = highlightFields.entrySet();
for (Entry<String, HighlightField> entry : entrySet) {
HighlightField highlightField = entry.getValue();
//获取文本域
Text[] texts = highlightField.fragments();
//高亮上下文
StringBuilder highlightContext = new StringBuilder();
for (Text text : texts) {
highlightContext.append( text.string());
}
//替换为高亮文本
source.put(entry.getKey(), highlightContext.toString());
}
sources.add(source);
}
splitPage.setRowCount(totalHits);
String json = JSON.toJSONString(sources);
ReturnMessage<String> returnMessage = new ReturnMessage<String>("100", "搜索成功", json);
returnMessage.setSplitPage(splitPage);
return returnMessage;
}
如此,全自动搜索就差不多了。当然这并不能包打天下,必要的时候还是要自己写搜索代码。。。