1、问题

在使用搜索引擎(Elasticsearch或Solr)作为应用的后台搜索平台的时候,会遇到停用词(stopwords)的问题。

在信息检索中,停用词是为节省存储空间和提高搜索效率,处理文本时自动过滤掉某些字或词,这些字或词即被称为Stop Words(停用词)。停用词大致分为两类。一类是语言中的功能词,这些词极其普遍而无实际含义,比如“the”、“is“、“which“、“on”等。另一类是词汇词,比如'want'等,这些词应用广泛,但搜索引擎无法保证能够给出真正相关的搜索结果,难以缩小搜索范围,还会降低搜索效率。实践中,通常把这些词从问题中过滤,从而节省索引的存储空间、提高搜索性能。

但是在实际语言环境中,停用词有时也有用的。比如,莎士比亚的名句:“To be or not to be.”所有的词都是停用词。特别当停用词和通配符(*)同时使用的时候,问题就来了:“the”、“is“、“on”还是停用词码?

2、解决方案

实际运用中,没有一个解决方案是100%完美的。很多时候需要我们根据实际用例作相应的调整和折中,来达到期望的结果。在这个时候,需要用80/20原则,把目标专著在提高用户体验上。

2.1、对不同的搜索对象区别对待

过滤停用词是为节省存储空间和提高搜索效率。实践中,不同的应用场景和对象对存储空间和搜索效率的需求不一样。比如,文章的标题,一般都很短,而且有大量的限定词区别词的定义,它对节省存储空间和效率的要求不高,但是常常需要停用词来限定名词的意义。我们可以考虑保留停用词。而对于文章体的全文本,存储空间和效率的要求很高,使用停用词过滤可以大大减少存储空间,提高搜索效率。

对Elasticsearch,下面是我们用到的索引定义:消息标题是text类型,没有使用停用词,而消息文本是standard_text类型,这个类型在设置里定义了使用英语标准的停用词过滤。

  1. {
  2. "demo": {
  3. "settings": {
  4. "index": {
  5. "number_of_shards": "",
  6. "number_of_replicas": "",
  7. "analysis": {
  8. "analyzer": {
  9. "standard_text": {
  10. "type": "standard",
  11. "stopwords": "_english_"
  12. }
  13. }
  14. }
  15. },
  16. "mappings": {
  17. "msg": {
  18. "_routing": { "required": true },
  19. "properties": {
  20. "title": {
  21. "type": "text"
  22. },
  23. "body": {
  24. "type": "text",
  25. "analyzer": "standard_text"
  26. }
  27. }
  28. }
  29. }
  30. }
  31. }

2.2、match查询

考虑一个例子:“and”。作为停用词,在“and”会在索引创建的时候被过滤掉:POST store/_analyze {  "field": "body", "text": ["and"] }

得到的分析结果是:{  "tokens": []  }

但是,如果我们用title字段来分析的时候,结果会得到保存:POST store/_analyze {  "field": "title", "text": ["and"] }

  1. { "tokens": [{
  2. "token": "and",
  3. "start_offset": ,
  4. "end_offset": ,
  5. "type": "<ALPHANUM>",
  6. "position":
  7. }
  8. ]
  9. }

但是当我们需要搜索文本的时候,会出现很多不如意的地方。比如,如果我们需要查消息体内chris && and && john这三个词的时候,因为and被过滤了,而查询条件又是与操作,导致没有任何信息符合。有人说,能不能把and从查询条件中去除啊?可以,虽然有点麻烦,总是可以做。但是,有几个新问题需要解决:

  • 你需要拿到所有语言的停用词才能做这个预处理。
  • 万一这些语言的停用词变了呢?我们还需要及时更正。

幸运的是Elasticsearch的match查询提供了一个功能解决这个问题,同时我们不需要在应用程序中预处理停用词:zero_terms_query和cutoff_frequency。

  • zero_terms_query

如果使用的分析器删除查询中的所有标记(如停用词),默认行为完全不匹配任何文档(none)。 可以使用zero_terms_query选项改变默认,none(默认),或all对应于match_all查询。

当查询使用"operator" : "and"的时候,需要把zero_terms_query设置为all。如果"operator" : "or",默认选项是我们需要的:

  1. GET demo/msg/_search
  2. {
  3. "query": {
  4. "match" : {
  5. "body" : {
  6. "query" : "chris and john",
  7. "operator" : "and",
  8. "zero_terms_query": "all"
  9. }
  10. }
  11. }
  12. }
  • cutoff_frequency

match查询支持cutoff_frequency,允许指定绝对或相对的文档频率:

    • OR:高频单词被放入“或许有”的类别,仅在至少有一个低频(低于截断)单词满足条件时才积分;
    • AND:高频单词被放入“或许有”的类别,仅在所有低频(低于截断)单词满足条件时才积分。

该查询允许在运行时动态地处理停用词,相对领域独立,并且不需要停用词文件。它防止评分/迭代高频词,只在更重要(更低频率)的词与文档匹配时才考虑。但是,如果所有查询条件都高于给定的cutoff_frequency,查询会自动转换为纯联合(和)查询以确保快速执行。

cutoff_frequency可以是相对于文档的总数的小数[0..1),也可以是绝对值[1, +∞)。

  1. GET demo/msg/_search
  2. {
  3. "query": {
  4. "match" : {
  5. "body" : {
  6. "query" : "chris and john",
  7. "cutoff_frequency" : 0.001
  8. }
  9. }
  10. }
  11. }

2.3、common 查询

大致说,common查询会分析查询文本,确定哪些单词“重要”,并使用这些单词进行搜索。 只有在文件与重要文字相匹配后才考虑“不重要”的字眼。“common查询”背后的动机是充分利用停用词清除的功能(更快的搜索),而不会完全消除停用词(因为它们有时可能有助于得分)。

执行此查询时会分几步:

  1. 查询会被发送到索引的每个shard;
  2. 在每个shard,Elasticsearch都会查看每个术语的文档频率
  3. 如果一个词的文档频率低于0.1%(0.001),那么它被认为是“低频”。 否则,它将被移到次要的“高频”列表中
  4. “低频”列表被重写为(逻辑AND)。 在这个例子中,它会包含“bonsai”,“cool”
  5. 然后将任何高频的文档分到剩余的高频列表中(“this”,“is”)

看看下面例子:

  1. {
  2. "common": {
  3. "body": {
  4. "query": "this is bonsai cool",
  5. "cutoff_frequency": 0.001
  6. }
  7. }
  8. }

在系统内,它被重写为:

  1. {
  2. "bool": {
  3. "must": [
  4. { "term": { "body": "bonsai"}},
  5. { "term": { "body": "cool"}}
  6. ],
  7. "should": [
  8. { "term": { "body": "this"}}
  9. { "term": { "body": "is"}}
  10. ]
  11. }
  12. }

3、通配符

我们还有一个问题,match查询不支持通配符。Elasticsearch对通配符支持包括两个情况:

  • keyword:wildcard,prefix
  • text:wildcard,prefix,match_phrase_prefix

第一种情况下,keyword的字段不会对索引和查询时文本做预处理。因此,在对该字段索引和查询的时候,应用程序必须做简单的一致性处理 ,比如把单词字母都变成小写。

第二种情况,text在索引时是通过分析器处理和过滤的,比如每个词都会正规化。而查询时,wildcard,match_prefix,match_phrase_prefix每个方法对查询文本的处理都不一样,需要分开对待:

  • wildcard:查询时Elasticsearch不会通过分析器处理,因此,应用程序必须对查询文本做简单的一致性处理。
  • prefix,match_phrase_prefix:查询时Elasticsearch会通过分析器处理。这时候停用词会被过滤掉。

参考文献

[1] https://www.elastic.co/blog/stop-stopping-stop-words-a-look-at-common-terms-query

Elasticsearch的停用词(stopwords)的更多相关文章

  1. Elasticsearch之停用词

    前提 什么是倒排索引? Elasticsearch之分词器的作用 Elasticsearch之分词器的工作流程 Elasticsearch的停用词 1.有些词在文本中出现的频率非常高,但是对文本所携带 ...

  2. ElasticSearch 2 (24) - 语言处理系列之停用词:性能与精度

    ElasticSearch 2 (24) - 语言处理系列之停用词:性能与精度 摘要 在信息检索早期,磁盘和内存相较我们今天的使用只是很小的一部分.将索引空间保持在一个较小的水平是至关重要的,节省每个 ...

  3. Elasticsearch的索引模块(正排索引、倒排索引、索引分析模块Analyzer、索引和搜索、停用词、中文分词器)

    正向索引的结构如下: “文档1”的ID > 单词1:出现次数,出现位置列表:单词2:出现次数,出现位置列表:…………. “文档2”的ID > 此文档出现的关键词列表. 一般是通过key,去 ...

  4. elasticsearch对无意义的词进行屏蔽——停用词

    介绍 在使用elasticsearch进行搜索业务的时候,发现一篇和搜索关键字完全不匹配的文章排在最前面.打开它发现原来是这篇文章含有非常多的"的"这个无意义的词.而我的搜索关键字 ...

  5. ES 实现实时从Mysql数据库中读取热词,停用词

    IK分词器虽然自带词库 但是在实际开发应用中对于词库的灵活度的要求是远远不够的,IK分词器虽然配置文件中能添加扩展词库,但是需要重启ES 这章就当写一篇扩展了 其实IK本身是支持热更新词库的,但是需要 ...

  6. IKAnalyzer进行中文分词和去停用词

    最近学习主题模型pLSA.LDA,就想拿来试试中文.首先就是找文本进行切词.去停用词等预处理,这里我找了开源工具IKAnalyzer2012,下载地址:(:(注意:这里尽量下载最新版本,我这里用的IK ...

  7. 【Lucene3.6.2入门系列】第05节_自定义停用词分词器和同义词分词器

    首先是用于显示分词信息的HelloCustomAnalyzer.java package com.jadyer.lucene; import java.io.IOException; import j ...

  8. 三、spark入门:文本中发现5个最常用的word,排除常用停用词

    package com.yl.wordcount import java.io.File import org.apache.spark.{SparkConf, SparkContext} impor ...

  9. R系列:分词、去停用词、画词云(词云形状可自定义)

    附注:不要问我为什么写这么快,是16年写的. R的优点:免费.界面友好(个人认为没有matlab友好,matlab在我心中就是统计软件中极简主义的代表).小(压缩包就几十M,MATLAB.R2009b ...

随机推荐

  1. java基础集合类——ArrayList 源码略读

    ArrayList是java的动态数组,底层是基于数组实现. 1. 成员变量 public class ArrayList<E> extends AbstractList<E> ...

  2. bootstrap 多选款样式:bootstrap-switch

    有时候,为了美化checkbox后者radio的时候,让用户体验起来更好,jquery里有icheck. bootstrap里有bootstrap-switch,就简单介绍下bootstrap-swi ...

  3. PAT乙级1004

    1004 成绩排名 (20 分)   读入 n(>0)名学生的姓名.学号.成绩,分别输出成绩最高和成绩最低学生的姓名和学号. 输入格式: 每个测试输入包含 1 个测试用例,格式为 第 1 行:正 ...

  4. P1719 最大加权矩形

    题目描述 为了更好的备战NOIP2013,电脑组的几个女孩子LYQ,ZSC,ZHQ认为,我们不光需要机房,我们还需要运动,于是就决定找校长申请一块电脑组的课余运动场地,听说她们都是电脑组的高手,校长没 ...

  5. 倒计数锁存器(CountDown Latch)和 CyclicBarrier(同步屏障)

    倒计数锁存器(CountDown Latch)是异常性障碍,允许一个或多个线程等待一个或者多个其他线程来做某些事情. public static long time(Executor executor ...

  6. javascript初学者注意事项

    注:以下属于个人学习中的理解不能保证全部正确,如果有错误以后修正. 1.javascript和c#语言一样严格区分大小写,有没有类的概念. 2.所有的变量声明都使用var,虽然能打出蓝色int,但却不 ...

  7. win7-x64上MySql的初次安装

    1.官网:https://dev.mysql.com/downloads/mysql/下载对应的zip包 2.将包解压缩到本地,如:F:\mysql\mysql-8.0.15-winx64 3.配置环 ...

  8. Delphi的idhttp报508 Loop Detected错误的原因

    一般是访问https时才出现“508 Loop Detected”,idhttp+IdSSLIOHandlerSocketOpenSSL,这个在上篇文章中讲过了. 由于该问题网上资料极少,连外文资料也 ...

  9. pandas对excel处理过程中的总结

    在处理excel数据时需要将一组具有相同标签值的数据给按标签抽取出来,同样的标签值对应着同一个类别,这项操作让我对pandas的聚合功能有了更深刻的认识. 所谓聚合groupby,实际上是指将向量或者 ...

  10. 早上出现的zabbix启动错误

    之前根据教程安装好zabbix,MySQL的版本是5.1. 昨天无聊想升级成5.6.不过升级比较麻烦.我就直接把5.1删了再装5.6. 安装中途zabbix挂了一次.把5.6装上启动后就好了. 早上z ...