创新实训(42)——ElasticSearch检索的优化

前言

上个星期,使用ElasticSearch的Java API实现了ElasticSearch的连接已经简单的检索,但是那个检索有着很大的问题,就是比如准确匹配到单词或句子(单词或句子必须在搜索结果中连续的出现才行),如果搜索的内容很长或者用标点符号分割的话,就无法给出搜索结果了。
在这里插入图片描述

QueryBuilder的选择

参考文章:https://www.jianshu.com/p/3afae4105797
https://www.cnblogs.com/wenbronk/p/6432990.html

下面尝试了几种匹配的方法,看看效果怎么样

不同的QueryBuilder可以给出不同的检索结果,需要进行选择。
(1)match_phrase
比如"宝马多少马力"会被分词为"宝马 多少 马力", 所有有关"宝马 多少 马力", 那么所有包含这三个词中的一个或多个的文档就会被搜索出来
(2)match
一个文档"我的保时捷马力不错"也会被搜索出来,那么想要精确匹配所有同时包含"宝马 多少 马力"的文档就要用match_phrase
(3)term
代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇
(4)fuzzy
近似度查询。minimumSimilarity表示是最小相似度,可以通过指定一个相似度来决定模糊匹配的严格程度。默认为0.5,当这个值越小,通过模糊查找出的文档的匹配程度就越低,文档的数量也就越多;当这个值越大,说明要匹配程度更大,匹配的文档数也就越少,当相似度设置为1,那么就退化为TermQuery查询,所以当这个值>=1或<0会抛出IllegalArgumentException异常

使用结果:
(1)match
会分词处理

QueryBuilder titleQuery = QueryBuilders.matchQuery("title", content);
 QueryBuilder contentQuery = QueryBuilders.matchQuery("content", content);

在这里插入图片描述
在这里插入图片描述
(2)match_phrase

QueryBuilder titleQuery = QueryBuilders.matchPhraseQuery("title", content);
QueryBuilder contentQuery = QueryBuilders.matchPhraseQuery("content", content);

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
(3)term

QueryBuilder titleQuery = QueryBuilders.termQuery("title", content);
QueryBuilder contentQuery = QueryBuilders.termQuery("content", content);

效果很差 什么都查不出来
(4)fuzzy

  QueryBuilder titleQuery = QueryBuilders.fuzzyQuery("title", content).boost(0.8f);
  QueryBuilder contentQuery = QueryBuilders.termQuery("content", content).boost(0.8f);

效果很差 什么都查不出来
对比:(1)和(2)
(1)的优点是会进行分词检索,会查询出很多的结果。但缺点就是查询出得大部分都不是我们想要的
(2)的优点是,如果能查询出结果,那就是我们想要啥。但缺点是有的查询输入进去返回为空白,什么结果都不会有。

需要在两者之间进行选择。

此外还试了其他的QueryBuilder的

(5) 包裹查询, 高于设定分数, 不计算相关性

QueryBuilders.constantScoreQuery(QueryBuilders.matchQuery("title", content).fuzziness(Fuzziness.AUTO).prefixLength(3).maxExpansions(10)).boost(2.0f);
QueryBuilder contentQuery = QueryBuilders.constantScoreQuery(QueryBuilders.matchQuery("content", content).fuzziness(Fuzziness.AUTO).prefixLength(3).maxExpansions(10)).boost(2.0f);

(6)设定了相关参数的模糊查询

   QueryBuilder titleQuery = QueryBuilders.matchQuery("title", content).fuzziness(Fuzziness.AUTO).prefixLength(3).maxExpansions(10);
QueryBuilder contentQuery = QueryBuilders.matchQuery("content", content).fuzziness(Fuzziness.AUTO).prefixLength(3).maxExpansions(10);
            QueryBuilder titleQuery = 

尝试之后发现,结果都不是很乐观。有的根本不会携带我们的关键词,查询的结果根本对不上,不是我们想要的结果。

还是(1)与(2)的结果我们可以接受

所以需要在(1)与(2)的结果之间抉择

实现句子的查询

(1)查询输入之后,先根据标点符号,将句子分开。

 /**
     * 将文章分割为句子  分割依据为标点符号
     * @param document
     * @return
     */
    static List<String> spiltSentence(String document) throws IOException {
        List<String> sentences = new ArrayList<String>();
        if (document == null) {
            return sentences;
        }
        String regex1 = "[\r\n]";
        String regex2 = "[,,。::“”??!!;;]";
        for (String line : document.split(regex1)) {
            line = line.trim();
            if (line.length() == 0) {continue;}

            for (String sent : line.split(regex2))
            {
                sent = sent.trim();
                if (sent.length() == 0) {
                    continue;
                }
                sent = TokenUtil.removalOfStopWords(sent);
                sentences.add(sent);
            }
        }

        return sentences;
    }

(2)将每一句话分词之后,去除句子中的停用词
分词使用的还是HanLP,并且找到了停用词表


        /**
         * 去除停用词
         * @param oldString:原中文文本
         * @return 去除停用词之后的中文文本
         * @throws IOException
         */
        public static String removalOfStopWords(String oldString) throws IOException {
            String newString = oldString;

            // 分词
            List<Term> termList = HanLP.segment(newString);
            System.out.println(termList);


            // 中文 停用词 .txt 文件路径
            String filePath = "src/main/resources/stop_words.txt";
            File file = new File(filePath);

            BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
            List<String> stopWords = new ArrayList<>();
            String temp = null;
            while ((temp = bufferedReader.readLine()) != null) {
                //System.out.println("*" + temp+ "*");
                stopWords.add(temp.trim());
            }

            List<String> termStringList = new ArrayList<>();
            for(Term term:termList) {
                termStringList.add(term.word);
                //System.out.println("*" + term.word + "*");
            }

            termStringList.removeAll(stopWords);

            newString = "";
            for (String string:termStringList) {
                newString += string;
            }

            return newString;
        }


(3)将每一句话分别进行查询

    /**
     * 将句子分词之后,然后实现搜索
     * @param content
     * @return
     */
    public List<Article> searchSentence(String content) throws IOException {
        List<String> sentence = spiltSentence(content);
        List<Article> articles = new ArrayList<>();
        for(String str :sentence)
        {
            List<Term> termList = HanLP.segment(str);
            //是否应当将这个term纳入计算,词性属于名词、动词、副词、形容词
            for (Term term : termList) {`在这里插入代码片`
                if (term.nature.toString().contains("n")) {
                    String word =term.word;
                    List<Article> temp = querySearchType(word);
                    for(Article article:temp)
                    {
                        articles.add(article);
                    }
                }
            }

        }
     return articles;
}

(4)将每一句查询的article进行排序,综合来看哪一个article中出现的关键词较多,就将其排序到最前列

    /**
     * 筛选中命中最多的句子
     * @return
     */
    public List<Integer> getTopK(List<Article> articles)
    {
            //查看命中关键词最多的句子
            Map<Integer,Integer> score = new HashMap<>();
            for(Article article :articles)
            {
                if(!score.containsKey(article.getId()))
                {
                    score.put(article.getId(),0);
                }
                score.put(article.getId(),score.get(article.getId()) + getCount(article.getVerbalContent()));
            }

        List<Map.Entry<Integer, Integer>> list = new ArrayList<>(score.entrySet());
        list.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
        int index = 0;
        List<Integer> result = new ArrayList<>();
        for (Map.Entry<Integer, Integer> t : list) {
            result.add(t.getKey());
            if (++index >= 15) {
                break;
            }
        }
        return result;
    }

最后经过相应的计算将结果返回

结束

最后实现的效果为
在这里插入图片描述
问题:在ElasticSearch自动分词的时候,还是有很多停用词的出现,需要给ElasticSearch更换分词器试试,看能不能让查询的结果更加的优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值