前言
上个星期,使用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更换分词器试试,看能不能让查询的结果更加的优化。