elasticsearch实现like查询

问题

elasticsearch查询需要实现类似于mysql的like查询效果,例如值为hello中国233的记录,即可以通过中国查询出记录,也可以通过llo查询出记录。

但是elasticsearch的查询都是基于分词查询,hello中国233会默认分词为hello233。当使用hello查询时可以匹配到该记录,但是使用llo查询时,匹配不到该记录。

解决

由于记录内容分词的结果的粒度不够细,导致分词查询匹配不到记录,因此解决方案是将记录内容以每个字符进行分词。即把hello中国233分词为helo23

elasticsearch默认没有如上效果的分词器,可以通过自定义分词器实现该效果:通过字符过滤器,将字符串的每一个字符间添加一个空格,再使用空格分词器将字符串拆分成字符。

效果

默认分词
PUT /like_search
{
  "mappings": {
    "like_search_type": {
      "properties": {
        "name": {
          "type": "text"
        }
      }
    }
  }
}

PUT /like_search/like_search_type/1
{
  "name": "hello中国233"
}

分词效果

GET /like_search/_analyze
{
  "text": [
    "hello中国233"
    ]
}
{
  "tokens": [
    {
      "token": "hello",
      "start_offset": 0,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "中",
      "start_offset": 5,
      "end_offset": 6,
      "type": "<IDEOGRAPHIC>",
      "position": 1
    },
    {
      "token": "国",
      "start_offset": 6,
      "end_offset": 7,
      "type": "<IDEOGRAPHIC>",
      "position": 2
    },
    {
      "token": "233",
      "start_offset": 7,
      "end_offset": 10,
      "type": "<NUM>",
      "position": 3
    }
  ]
}

elasticsearch默认使用standard分词器,如下通过llo查询不到hello中国233的记录。

GET /like_search/_search
{
  "query": {
    "match_phrase": {
      "name": "llo"
    }
  }
}
自定义分词
PUT /like_search
{
  "settings": {
    "analysis": {
      "analyzer": {
        "char_analyzer": {
          "char_filter": [
            "split_by_whitespace_filter"
          ],
          "tokenizer": "whitespace"
        }
      },
      "char_filter": {
        "split_by_whitespace_filter": {
          "type": "pattern_replace",
          "pattern": "(.+?)",
          "replacement": "$1 "
        }
      }
    }
  },
  "mappings": {
    "like_search_type": {
      "properties": {
        "name": {
          "type": "text",
          "analyzer": "char_analyzer"
        }
      }
    }
  }
}

PUT /like_search/like_search_type/1
{
  "name": "hello中国233"
}

分词效果

GET /like_search/_analyze
{
  "analyzer": "char_analyzer", 
  "text": [
    "hello中国233"
    ]
}
{
  "tokens": [
    {
      "token": "h",
      "start_offset": 0,
      "end_offset": 0,
      "type": "word",
      "position": 0
    },
    {
      "token": "e",
      "start_offset": 1,
      "end_offset": 1,
      "type": "word",
      "position": 1
    },
    {
      "token": "l",
      "start_offset": 2,
      "end_offset": 2,
      "type": "word",
      "position": 2
    },
    {
      "token": "l",
      "start_offset": 3,
      "end_offset": 3,
      "type": "word",
      "position": 3
    },
    {
      "token": "o",
      "start_offset": 4,
      "end_offset": 4,
      "type": "word",
      "position": 4
    },
    {
      "token": "中",
      "start_offset": 5,
      "end_offset": 5,
      "type": "word",
      "position": 5
    },
    {
      "token": "国",
      "start_offset": 6,
      "end_offset": 6,
      "type": "word",
      "position": 6
    },
    {
      "token": "2",
      "start_offset": 7,
      "end_offset": 7,
      "type": "word",
      "position": 7
    },
    {
      "token": "3",
      "start_offset": 8,
      "end_offset": 8,
      "type": "word",
      "position": 8
    },
    {
      "token": "3",
      "start_offset": 9,
      "end_offset": 9,
      "type": "word",
      "position": 9
    }
  ]
}

使用自定义的分词器,如下通过llo可以查询到hello中国233的记录。

GET /like_search/_search
{
  "query": {
    "match_phrase": {
      "name": "llo"
    }
  }
}
### 如何在 Elasticsearch实现类似于 SQL 的模糊查询like)功能 #### 使用 `wildcard` 查询 为了实现Elasticsearch中类似SQL `%关键字%`的单纯模糊查询,可以采用`wildcard`查询。这种查询允许指定通配符模式来进行前缀、后缀以及中间部分的匹配[^1]。 ```json GET /_search { "query": { "wildcard": { "field_name": "*AAA*" } } } ``` 此方法适用于不需要分词器处理的情况,即希望保持搜索项作为一个整体进行匹配而不是拆分成单独字符或词语的情形下非常有用。 #### 利用 nGram 分析器 另一种方案是通过配置自定义分析链,在索引阶段应用nGram过滤器创建子字符串片段。当执行查询操作时,则能够高效定位含有特定连续字符序列的文档[^3]。 首先设置映射: ```json PUT my_index { "settings": { "analysis": { "analyzer": { "ngram_analyzer": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase", "ngram_filter"] } }, "filter": { "ngram_filter": { "type": "ngram", "min_gram": 2, "max_gram": 50 } } } }, "mappings": { "_doc": { "properties": { "content_field": { "type": "text", "analyzer": "ngram_analyzer", "search_analyzer": "standard" } } } } } ``` 接着就可以像平常一样使用match或者term查询了,因为底层已经由nGram负责构建必要的索引来支持灵活的部分匹配需求。 #### Phrase Matching 方案 对于某些场景而言,如果仅仅是想要确保几个单词按照顺序出现而不想启用复杂的全文本解析过程的话,那么phrase matching是一个不错的选择。它能保证词条间的相对位置不变从而达到近似于“精确短语”的效果。 ```json GET /my_index/_search { "query": { "match_phrase": { "content_field": "AAA" } } } ``` 以上三种方式都可以满足不同业务逻辑下的模糊查找需求,具体选用哪一种取决于实际应用场景的要求以及性能考量因素。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值