使用es构建全自动搜索系统

相信每一个使用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;
    }

如此,全自动搜索就差不多了。当然这并不能包打天下,必要的时候还是要自己写搜索代码。。。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值