Elastic:如何查询特殊字符

简介: 某些业务场景下我们需要使用特殊符号来进行查询,但是es的默认分词器以及ik分词器等大多数分词器都会将特殊符号过滤掉,导致后续无法通过特殊符号查询到数据。那么我们如何来解决这个问题呢,下面列举出几种处理方案

0 引言

某些业务场景下我们需要使用特殊符号来进行查询,但是es的默认分词器以及ik分词器等大多数分词器都会将特殊符号过滤掉,导致后续无法通过特殊符号查询到数据。

那么我们如何来解决这个问题呢,下面列举出几种处理方案

1 问题复现

1、设置mapping

PUT test_special
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text", 
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

2、插入带有特殊符号的数据

PUT test_special/_doc/1
{"name":"55555@#!sina.com"}

3、通过‘@’去查询原数据

GET test_special/_search
{
  "query": {
    "match": {
      "name": "@#!"
    }
  }
}

4、结果为空

"hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }

2 解决方案

2.1 使用keyword查询

首先从业务层考虑,是否需要单个的特殊符号查询,还是说是带上特殊符号的查询词搜索,所以先从最简单的用keyword+term查询试试看能够满足业务需求

GET test_special/_search
{
  "query": {
    "term": {
      "name.keyword": "55555@#!sina.com"
    }
  }
}

2.2 mapping映射,防止过滤

这里的特殊字符的别名用两个点隔开,并且加上了xxx前缀,点的作用是为了让其被单独分词,也可以替换成逗号,前缀的目的是为了让特殊字符的别名唯一,后续插入的数据不会和这个重复。xxx可以是你公司的名称或者其他含义的前缀。

PUT test_special
{
  "settings": {
    "analysis": {
      "analyzer": {
        "special_analyzer": {
          "tokenizer": "standard",
          "char_filter": [
            "special_mapping"
          ]
        }
      },
      "char_filter": {
        "special_mapping":{
          "type": "mapping",
          "mappings": [
            "@=>.xxxAt.",
            "#=>.xxxJin.",
            "!=>.xxxSigh.",
            "*=>.xxxXing."
          ]
        }
      }
    }
  }, 
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "special_analyzer", 
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

如上方法的原理是将特殊字符转换为能识别的普通字符组合,只是这个字符组合具备唯一性,我们单独查看下分词结果就能一目了然

GET test_special/_analyze
{
  "analyzer": "special_analyzer", 
  "text": [
    "55555@#*sina.com"
  ]
} 

分词结果

"tokens" : [
    {
      "token" : "55555",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "<NUM>",
      "position" : 0
    },
    {
      "token" : "xxxAt",
      "start_offset" : 5,
      "end_offset" : 5,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "xxxJin",
      "start_offset" : 6,
      "end_offset" : 6,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "xxxXing.sina.com",
      "start_offset" : 7,
      "end_offset" : 16,
      "type" : "<ALPHANUM>",
      "position" : 3
    }
  ]

查询测试

GET test_special/_search
{
  "query": {
    "match": {
      "name": "@#*"
    }
  }
}

结果中可以将数据查询出来,查询词在查询时因为使用的match查询,因此也会被分词,并且调用的分词器就是查询字段的分词器,因此特殊符号就被替换且分词为了:xxxAt,xxxJin,xxxXing,通过这些分词是可以查询到结果的

"hits" : [
      {
        "_index" : "test_special",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.5098253,
        "_source" : {
          "name" : "55555@#!sina.com"
        }
      }
    ]

后续如果有新的特殊字符如何更新呢?
官方文档中有介绍,mapping char filter中除了直接写mappings外,还可以通过mappings_path,将mappings内容写到文本文件中,然后在mappings_path中配置这个文件的路径,就可以直接从这个文件中读取,下次更新时就更新文件,然后重启集群即可。这里需要注意的是该文本必须是UTF-8编码的,同时mappings_path路径要么是绝对路径要么是基于es config路径的相对路径

2.2 ngram分词器,设置min_gram为1

es中还提供了一个ngram分词器,可以将字段按照指定的长度进行分词

我们先通过一个实例来体会它的作用,比如字符串“12345”,设置了如下的分词器,那么分词结果就是1,12,2,23,3,34,4,45,5

也就是说它会将其按照最小长度为1,最大长度为2的范围进行分词。同时如何字符串中包含特殊字符或者空格也会保留,将其按照一个字符处理

"tokenizer": {
        "ngram_tokenizer": {
          "type": "ngram",
          "min_gram": 1,
          "max_gram": 2 
        }
      }

因此,我们创建索引,这里的min_gram必须设置为1,max_gram可以根据业务情况设置

PUT test_special
{
  "settings": {
    "analysis": {
      "analyzer": {
        "special_analyzer": {
          "tokenizer": "ngram_tokenizer"
        }
      },
      "tokenizer": {
        "ngram_tokenizer": {
          "type": "ngram",
          "min_gram": 1,
          "max_gram": 2 
        }
      }
    }
  },
 "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "special_analyzer", 
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

插入数据,并查询测试

PUT test_special/_doc/1
{"name":"55555@#!sina.com"}
 
GET test_special/_search
{
  "query": {
    "match": {
      "name": "@#!"
    }
  }
}

结果

"hits" : [
      {
        "_index" : "test_special",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.4384104,
        "_source" : {
          "name" : "55555@#!sina.com"
        }
      }
    ]

优缺点:

这种方式分隔的粒度很小,能够很准确的查询到我们要的数据,但是同时也因为其粒度很小,所以会占用很多空间,因此使用时需要综合考虑,是否真的需要这种空间换取时间的做法

2.3 其他解决方案

1、另外还可以通过修改lucene源码的方式来跳过特殊字符过滤
2、也可以用一些第三方插件来解决,比如elasticsearch-analysis-hanlp
3、同时考虑到java中有转义符的存在,推测es中应该也有类似的处理,但是尝试使用转义符插入数据会报错,可能用法不对

如果有知道其他方式的小伙伴也可以留言告诉我


更多文章关注:Elasticsearch之家

目录
相关文章
|
2月前
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用问题之在holo中创建好小写的表,如何把大写映射成小写呢
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
11月前
|
自然语言处理 API 索引
Elasticsearch汉字补全和拼写纠错
Elasticsearch汉字补全和拼写纠错
270 0
|
自然语言处理 安全 Java
Elastic:IK分词器分词、停用词热更新如何配置(二)基于数据库
上一期,我们说明了基于API形式的热更新,但是API形式的热更新存在词库的管理不方便,要直接操作磁盘文件,检索页很麻烦;文件的读写没有专门的优化,性能不好;多一次接口调用和网络传输等缺点,因此这期我们来说明直连数据库的方式来实现热更新
369 0
Elastic:IK分词器分词、停用词热更新如何配置(二)基于数据库
|
缓存 自然语言处理 前端开发
Elastic:IK分词器分词、停用词热更新如何配置(一)基于API
热更新是IK新版本中才支持的功能,其API需要满足两个要求: 1、http请求中需要返回两个header,一个是Last-Modified,一个ETag。两个header都是字符串类型的。他们之中只要有一个发生变化,就会读取详情的数据并且更新词库,如果没有变化则不会更新词库。这个条件如果做前端的同学应该会比较熟悉,前端判断缓存是否更改时也是通过这两个条件。
396 0
Elastic:IK分词器分词、停用词热更新如何配置(一)基于API
|
存储 自然语言处理 关系型数据库
Kibana查询语言(KQL)AND、OR匹配,模糊匹配
Kibana查询语言(KQL)AND、OR匹配,模糊匹配
|
自然语言处理
拼音分词扩展elasticsearch-analysis-pinyin安装
拼音分词扩展elasticsearch-analysis-pinyin安装
214 0
拼音分词扩展elasticsearch-analysis-pinyin安装
|
自然语言处理 索引
Elasticsearch添加拼音搜索支持
Elasticsearch添加拼音搜索支持
219 0
|
SQL C语言 关系型数据库
sql like 通配符 模糊查询技巧及特殊字符
最近碰到like模糊匹配的问题,找到一些答案接触迷惑,觉得有知识是自己忽略的,现在整理出来,既强化记忆,又是一次记录,以下转自一篇Blog,关于sql server like的通配符和字符带通配符的处理办法。
5741 0
|
自然语言处理 索引
Elasticsearch 如何实现查询/聚合不区分大小写?
1、实战问题 最近社区里有多个关于区分大小写的问题: 问题1:ES查询和聚合怎么设置不区分大小写呢? 问题2:ES7.6 如何实现模糊查询不区分大小写? 主要是如何进行分词和mapping的一些设置来实现这个效果, 自己也尝试过对setting 和 mapping字段进行设置,都是报错比较着急, 类似的问题,既然有很多同学问到,那么咱们就有必要梳理出完整的思路和方案。 这或许是铭毅天下公众号的使命所在。 这个问题不复杂,所以本文会言简意赅,直击要害!
825 0
Elasticsearch 如何实现查询/聚合不区分大小写?
|
XML SQL 前端开发
postgresql模糊查询过滤首尾空格
此处不讨论模糊查询的方法(like、bind等),只针对如何过滤前后字符串
177 0