12 搜索之DSL--基础查询


1 DSL介绍

Elasticsearch提供了基于JSON的完整查询DSL(Domain Specific Language)来定义查询。
将查询DSL视为查询的AST(抽象语法树),它由两种子句组成:

  • 叶子查询子句(Leaf query clauses)
    在特定字段中查找特定值, 例如match,term或range查询

  • 复合查询子句(Compound query clauses)
    以逻辑方式组合多个叶子、复合查询为一个查询(例如bool或dis_max查询),

查询和过滤的区别

查询:用于检查内容与条件是否匹配,并且计算_score元字段表示匹配度。查询的结构中以query参数来开始执行内容的查询。

过滤:不计算匹配得分,只是简单的决定文档是否匹配。过滤往往会被elasticsearch自动缓存起来提高性能。

查询的子句中也可以传递filter参数。

2 数据准备

  1. 准备一个商品的索引
PUT goods
{
  "mappings": {
    "properties": {
      "id":{
        "type": "long"
      },
      "name":{
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "category":{
        "type": "keyword"
      },
      "price":{
        "type": "float"
      },
      "content":{
       "type": "text",
        "analyzer": "ik_max_word"
      },
      "create_at":{
        "type": "date",
        "format": ["yyyy-MM-dd hh:mm:ss"]
      }
    }
  }
}
  1. 插入数据
PUT goods/_doc/1
{
  "id":1,
  "name": "苹果手机12",
  "category":"手机",
  "price":8999.00,
  "content":"Apple iPhone 12 Pro Max (A2412) 256GB 海蓝色 支持移动联通电信5G 双卡双待手机",
  "create_at":"2020-12-12 12:00:00"
  
}
PUT goods/_doc/2
{
  "id":2,
  "name": "华为手机P40",
  "category":"手机",
  "price":7999.00,
  "content":"华为p40 5G手机 亮黑色 8+128G全网通",
  "create_at":"2020-12-11 12:00:00"
  
}

3 文本查询

基本语法

GET /索引库名/_search
{
    "query":{
        "查询类型":{
            "查询条件":"查询条件值"
        }
    }
}
  • 查询类型:
    • 例如:match_allmatchtermrange 等等
  • 查询条件:查询条件会根据类型的不同,写法也有差异,后面详细讲解

3.1 match 查询

3.1.1 查询所有(match_all)
GET goods/_search
{
  "query": {"match_all": {}}
}
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "id" : 1,
          "name" : "苹果手机12",
          "category" : "手机",
          "price" : 8999.0,
          "content" : "Apple iPhone 12 Pro Max (A2412) 256GB 海蓝色 支持移动联通电信5G 双卡双待手机",
          "create_at" : "2020-12-12 12:00:00"
        }
      },
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "id" : 2,
          "name" : "华为手机P40",
          "category" : "手机",
          "price" : 7999.0,
          "content" : "华为p40 5G手机 亮黑色 8+128G全网通",
          "create_at" : "2020-12-11 12:00:00"
        }
      }
    ]
  }
}

可以不指定索引库:查询所有的索引库

GET _search
{
  "query": {"match_all": {}}
}
3.1.2 match_none

这是match_all查询的逆函数,该查询不匹配任何文档

3.1.3 match query(匹配查询)

用于执行全文查询的标准查询,包括模糊匹配和短语或接近查询。
match类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是or的关系(默认)

or
GET goods/_search?
{
  "query": {
    "match": {
      "content": {
        "query": "华为手机",
        "operator": "or"   # 默认就是or
      }
    }
  }
}

查询结果: 两条记录都查出来了

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.96994376,
    "hits" : [
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.96994376,
        "_source" : {
          "id" : 2,
          "name" : "华为手机P40",
          "category" : "手机",
          "price" : 7999.0,
          "content" : "华为p40 5G手机 亮黑色 8+128G全网通",
          "create_at" : "2020-12-11 12:00:00"
        }
      },
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.16613919,
        "_source" : {
          "id" : 1,
          "name" : "苹果手机12",
          "category" : "手机",
          "price" : 8999.0,
          "content" : "Apple iPhone 12 Pro Max (A2412) 256GB 海蓝色 支持移动联通电信5G 双卡双待手机",
          "create_at" : "2020-12-12 12:00:00"
        }
      }
    ]
  }
}

  • 华为手机,经过分词分为:华为,手机
  • 搜索的时候,content字段中包含手机,或者华为这两个词就可以被匹配出来
and
GET goods/_search?
{
  "query": {
    "match": {
      "content": {
        "query": "华为手机",
        "operator": "and"
      }
    }
  }
}

根据上面,这里应该只会查出华为手机

{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.96994376,
    "hits" : [
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.96994376,
        "_source" : {
          "id" : 2,
          "name" : "华为手机P40",
          "category" : "手机",
          "price" : 7999.0,
          "content" : "华为p40 5G手机 亮黑色 8+128G全网通",
          "create_at" : "2020-12-11 12:00:00"
        }
      }
    ]
  }
}
minimum_should_match 最小匹配参数

在 or 与 and 间二选一有点过于非黑即白。 如果用户给定的条件分词后有 5 个查询词项,想查找只包含其中 4 个词的文档,该如何处理?将 operator 操作符参数设置成 and 只会将此文档排除,如果是or就会导致搜出的结果和用户的期望值差距较大。所以就引入了minimum_should_match最小匹配数的参数:

这让我们可以指定必须匹配的词项数用来表示一个文档是否相关。我们可以将其设置为某个具体数字,更常用的做法是将其设置为一个百分数,因为我们无法控制用户搜索时输入的单词数量

比如:下面这个查询我们指定了最小匹配数为2,所以只会查出华为手机

GET goods/_search?
{
  "query": {
    "match": {
      "content": {
        "query": "华为手机",
        "operator": "or",
        "minimum_should_match": 2
      }
    }
  }
}
可以使用通配符: *
GET goods/_search
{
  "query": {
    "match": {
      "name": {
        "query": "p*",
        "operator": "and"
      }
    }
  }
}
{
  "took" : 148,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.27779984,
    "hits" : [
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.27779984,
        "_source" : {
          "id" : 2,
          "name" : "华为手机P40",
          "category" : "手机",
          "price" : 7999.0,
          "content" : "华为p40 5G手机 亮黑色 8+128G全网通",
          "create_at" : "2020-12-11 12:00:00"
        }
      }
    ]
  }
}

其他参数
  • lenient: 可以设置为true来忽略数据类型匹配出错造成的异常,不常用
  • analyzer: 指定搜索时候使用的分词

eg: 使用标准分词器搜索华为手机

GET goods/_search?
{
  "query": {
    "match": {
      "content": {
        "query": "华为手机",
        "operator": "or",
        "analyzer": "standard", 
        "minimum_should_match": 2
      }
    }
  }
}

eg: price 是 float 类型的,如果查询 price=2499x ,就会导致无法转换而报错: search_phase_execution_exception

GET goods/_search?
{
  "query": {
    "match": {
      "price": {
        "query": "8999xx"
      }
    }
  }
}

就可以指定lenient为true来忽略这个错误

GET goods/_search?
{
  "query": {
    "match": {
      "price": {
        "query": "8999xx",
        "lenient": "true"
      }
    }
  }
}

3.2 Match Phrase Query(短语查询)

match_phrase 查询针对的是一个语句,比如 “like football”, 分析时也会将整个语句作为整体

词条位置

当一个字符串被分析时,分析器不仅只返回一个词条列表,它同时也返回原始字符串的每个词条的位置、或者顺序信息:

POST _analyze
{
  "analyzer": "ik_max_word",
  "text": "华为手机"
}
{
  "tokens" : [
    {
      "token" : "华为",
      "start_offset" : 0,  # 偏移量
      "end_offset" : 2, # 偏移量
      "type" : "CN_WORD",
      "position" : 0 # 位置
    },
    {
      "token" : "手机",
      "start_offset" : 2, # 偏移量
      "end_offset" : 4, # 偏移量
      "type" : "CN_WORD",
      "position" : 1 # 位置
    }
  ]
}

像match_phrase这样位置感知(Position-aware)的查询能够使用位置信息来匹配那些含有正确单词出现顺序的文档,且在这些单词之间没有插入别的单词

对于查询华为手机,必须满足:

  • 字段中必须含有这两个分词
  • 手机的位置必须比华为的位置大于1
演示

请求:查询content字段中时候有移动联通电信

GET goods/_search
{
  "query": {
    "match_phrase": {
      "content": {
        "query": "移动联通电信",
        "analyzer": "ik_max_word"
      }
    }
  }
}

响应:会查出苹果手机这一条

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 2.526501,
    "hits" : [
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 2.526501,
        "_source" : {
          "id" : 1,
          "name" : "苹果手机12",
          "category" : "手机",
          "price" : 8999.0,
          "content" : "Apple iPhone 12 Pro Max (A2412) 256GB 海蓝色 支持移动联通电信5G 双卡双待手机",
          "create_at" : "2020-12-12 12:00:00"
        }
      }
    ]
  }
}

如果查询的是移动电信联通呢,根据前面说的必须要保证联通在电信前面,是不会查出数据的

GET goods/_search
{
  "query": {
    "match_phrase": {
      "content": {
        "query": "移动电信联通",
        "analyzer": "ik_max_word"
      }
    }
  }
}
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

默认使用 match_phrase 时会精确匹配查询的短语,需要全部单词和顺序要完全一样,标点符号除外。,所以改了顺序之后就不到了

slop 参数

默认是0

slop 参数告诉 match_phrase 查询词条相隔多远时仍然能将文档视为匹配
什么是相隔多远? 意思是说为了让查询和文档匹配你需要移动词条多少次?

比如刚刚的移动电信联通没有搜索出来,就可以通过这个slop去调整,能够搜索出来

GET goods/_search
{
  "query": {
    "match_phrase": {
      "content": {
        "query": "移动电信联通",
        "analyzer": "ik_max_word",
        "slop": 3
      }
    }
  }
}
{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.6871974,
    "hits" : [
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.6871974,
        "_source" : {
          "id" : 1,
          "name" : "苹果手机12",
          "category" : "手机",
          "price" : 8999.0,
          "content" : "Apple iPhone 12 Pro Max (A2412) 256GB 海蓝色 支持移动联通电信5G 双卡双待手机",
          "create_at" : "2020-12-12 12:00:00"
        }
      }
    ]
  }
}

3.3 match_phrase_prefix

原理跟match_phrase类似,唯一的区别,就是把最后一个term作为前缀去搜索。

比如输入 hello w 进行搜索

  1. hello就是去进行match,搜索对应的doc
  2. w,会作为前缀,去扫描整个倒排索引,找到所有w开头的doc
  3. 然后找到所有doc中,即包含hello,又包含w开头的字符的doc
  4. 根据你的slop去计算,看在slop范围内,能不能让hello w,正好跟doc中的hello和w开头的单词的position相匹配
  5. max_expansions:指定prefix最多匹配多少个term,超过这个数量就不继续匹配了,限定性能
    默认情况下,前缀要扫描所有的倒排索引中的term,去查找w打头的单词,但是这样性能太差。可以用max_expansions限定,w前缀最多匹配多少个term,就不再继续搜索倒排索引了。
  • 尽量不要用,因为,最后一个前缀始终要去扫描大量的索引,性能可能会很差
  • max_expansions:指定prefix最多匹配多少个term,超过这个数量就不继续匹配了

可以通过这个去做那种联想搜索。

3.4 multi_match(多字段查询)

multi_match查询以match查询为基础,以允许多字段查询:

基本使用
GET 索引库/_search
{
  "query": {
    "multi_match": {
      "query": "查询关键词",
      "fields": ["字段1","字段2"],
      "operator": "and" # 默认是or 查询关键词分词之后,是or还是and
    }
  }
}

查询关键词:手机12

GET goods/_search
{
  "query": {
    "multi_match": {
      "query": "手机12",
      "fields": ["content","name"],
      "operator": "and"
    }
  }
}

只会查询苹果手机12:因为是and连接,手机12会被分词为苹果和手机

{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.9752057,
    "hits" : [
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.9752057,
        "_source" : {
          "id" : 1,
          "name" : "苹果手机12",
          "category" : "手机",
          "price" : 8999.0,
          "content" : "Apple iPhone 12 Pro Max (A2412) 256GB 海蓝色 支持移动联通电信5G 双卡双待手机",
          "create_at" : "2020-12-12 12:00:00"
        }
      }
    ]
  }
}

如果改成or:

GET goods/_search
{
  "query": {
    "multi_match": {
      "query": "手机12",
      "fields": ["content","name"]
    }
  }
}
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.9752057,
    "hits" : [
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.9752057,
        "_source" : {
          "id" : 1,
          "name" : "苹果手机12",
          "category" : "手机",
          "price" : 8999.0,
          "content" : "Apple iPhone 12 Pro Max (A2412) 256GB 海蓝色 支持移动联通电信5G 双卡双待手机",
          "create_at" : "2020-12-12 12:00:00"
        }
      },
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.20199655,
        "_source" : {
          "id" : 2,
          "name" : "华为手机P40",
          "category" : "手机",
          "price" : 7999.0,
          "content" : "华为p40 5G手机 亮黑色 8+128G全网通",
          "create_at" : "2020-12-11 12:00:00"
        }
      }
    ]
  }
}
加权:可以使用(^)表示法增强各个字段:
GET /_search
{
  "query": {
    "multi_match" : {
      "query" : "华为手机",
      "fields" : [ "content^3", "name" ] 
    }
  }
}

content字段是name字段重要三倍

如果未提供任何字段,则multi_match查询默认为index.query.default_field索引设置,而默认设置为提取映射中所有符合词条查询条件的字段,并过滤元数据字段。 然后将所有提取的字段组合起来以构建查询。

查询类型(TODO)

多匹配查询内部执行方式取决于:type参数,他的取值有:

  • best_fields(默认):查找匹配任何字段的文档,但是使用最佳匹配的_score
  • most_fields:查找匹配任何字段的文档,结合每个字段的_score
  • cross_fields: 用相同的分词器处理字段,把这些字段当做一个大字段。查找任何字段的每个单词
  • phrase:每个字段上进行短语匹配查询(match_phrase),结合每个字段的_score
  • phrase_prefix:每个字段上进行短语前缀匹配查询(match_phrase_prefix),结合每个字段的_score
  1. best_fields: 在同一个字段中,搜索多个单词的时候,此参数最有用,比如一个字段包含华为手机,比包含手机或者包含华为更有意义,best_fields会对每个字段生成一个匹配查询,并且封装成dis_max查询,来找到最佳匹配字段,例如:
GET goods/_search
{
  "query": {
    "multi_match": {
      "query": "华为手机",
      "fields": ["name","content"],
      "type": "best_fields"
    }
  }
}

回执行为:

GET goods/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "content": "华为手机"
          }
        },
        {
          "match": {
            "name": "华为手机"
          }
        }
      ]
    }
  }
}

4 字段查询

前面的查询都会先分析查询字符串(进行分词),而字段查询只针对存储在反向索引中的精确索引词。查询关键字不会先进行分词

这些精确值可能是数字、时间、布尔或者那些未分词的字符串

4.1 单字段查询

查询指定字段中包含的指定内容进行查找。

eg: 查询name字段包含苹果手机的

GET goods/_search
{
  "query": {
    "term": {
      "name": "苹果手机"
    }
  }
}
  1. 苹果手机不会被分词,作为一个整体去匹配,name字段中是否包含的苹果手机

这里的查询结果是空!!!!

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

name为苹果手机12的那条文档,在倒序索引中,会被ik分词器分词为苹果手机12,显然这三个索引词都和我们指定的苹果手机不匹配。说到底就是我们的查询关键字不会先进行分词

4.2 多词条精确匹配(terms)

terms` 查询和 term 查询一样,但它允许你指定多值进行匹配。

GET goods/_search
{
  "query": {
    "terms": {
      "name": [
        "手机",
        "苹果"
      ]
    }
  }
}

4.3 范围查询 (range)

根据指定字段包含的值(日期,数字或字符串等)范围查找文档

入门

查询价格大等于8000的商品

GET goods/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 8000
      }
    }
  }
}
接受的参数
  • gte:大于等于
  • gt:大于
  • lte:小于等于
  • lt:小于
  • boots:设置查询的加权值,默认是1.0
日期匹配
入门
GET goods/_search
{
  "query": {
    "range": {
      "create_at": {
        "gte": "now-d/d"  # 当前时间的上一天, 四舍五入到最近的一天
        "lt": "now-1d/d" # 当前时间, 四舍五入到最近的一天
        
      }
    }
  }
}
时间的数学表达

Elasticsearch中时间可以表示为now, 也就是系统当前时间, 也可以是以||结尾的日期字符串表示.

在日期之后, 可以选择一个或多个数学表达式:

表示式含义
y
M
w
d
H
m
s

eg:

  • +1h —— 加1小时;

  • -1d —— 减1天;

  • /d —— 四舍五入到最近的一天.

  • /M —— 四舍五入到最近的一天.
    说明: 假设系统当前时间now = 2018-10-01 12:00:00 :

  • now+1h: now的毫秒值 + 1小时, 结果是: 2018-10-01 13:00:00.

  • now-1h: now的毫秒值 - 1小时, 结果是: 2018-10-01 11:00:00.

  • now-1h/d: now的毫秒值 - 1小时, 然后四舍五入到最近的一天的起始, 结果是: 2018-10-01 00:00:00.

  • 2018.10.01||+1M/d: 2018-10-01的毫秒值 + 1月, 再四舍五入到最近一天的起始, 结果是: 2018-11-01 00:00:00

关于时间的四舍五入

向上舍入: 移动到舍入范围的最后一毫秒;

向下舍入: 一定到舍入范围的第一毫秒.

  • “gt”: “2018-12-18||/M” —— 大于日期, 需要向上舍入, 结果是2018-12-31T23:59:59.999, 也就是不包含整个12月.
  • “gte”: “2018-12-18||/M” —— 大于或等于日期, 需要向下舍入, 结果是 2018-12-01, 也就是包含整个12月.
  • “lt”: “2018-12-18||/M” —— 小于日期, 需要向上舍入, 结果是2018-12-01, 也就是不包含整个12月.
  • “lte”: “2018-12-18||/M” —— 小于或等于日期, 需要向下舍入, 结果是2018-12-31T23:59:59.999, 也就是包含整个12月.
日期格式化
GET goods/_search
{
  "query": {
    "range": {
      "create_at": {
        "lt": "2021-01-01",
        "format": "yyyy-MM-dd"
      }
    }
  }
}
指定时区
GET goods/_search
{
  "query": {
    "range": {
      "create_at": {
        "time_zone": "+01:00",        
        "gte": "2020-01-01 00:00:00",
        "format": "yyyy-MM-dd hh:mm:ss"
      }
    }
  }
}

5 其他查询

5.1 是否存在查询

查询指定字段包含任何非空值的文档

查询name字段不为空的商品

GET goods/_search
{
  "query": {
    "exists": {
      "field": "name"
    }
  }
}

name为空的情况:

  • null
  • [] (空数组)
  • [null]
  • 没有name字段的文档
查询指定字段包含为空值的文档

上一个的逆操作

GET goods/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "exists": {
            "field": "name"
          }
        }
      ]
    }
  }
}

5.2 前缀查询(prefix)

查询字段包含指定前缀的索引词(不分词)。
插叙name字段前缀为华为的商品

GET goods/_search
{
  "query": {
    "prefix": {
      "name": {
        "value": "华为"
      }
    }
  }
}

5.3 通配符查询(wildcard)

注意的是这个查询会比较慢,需要在多个索引词上面重复执行。为了避免极端缓慢的查询,应该避免查询的时使用通配符开头

  • *:匹配任意字符(包含空字符)

  • ?:匹配任意单个字符

GET goods/_search
{
  "query": {
    "wildcard": {
      "name": "华为?"  
    }
  }
}

5.4 模糊查询(Fuzziness)

fuzzniess 参数可以使查询的字段具有模糊搜索的特性。来先了解下什么是模糊搜索。

什么是模糊搜索?

模糊搜索是指系统允许被搜索信息和搜索提问之间存在一定的差异,这种差异就是“模糊”在搜索中的含义。例如,查找名字Smith时,就会找出与之相似的Smithe, Smythe, Smyth, Smitt等。(百度百科)

通过模糊搜索可以查询出存在一定相似度的单词,那么怎么计算两个单词是否有相似度以及相似度的大小呢?这就要了解下另外一个概念:Levenshtein Edit Distance

Levenshtein Edit Distance 叫做莱文斯坦距离,是编辑距离的一种。指两个字串之间,由一个转成另一个所需的最少编辑操作次数。允许的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。

例如,单词 “god” 只需要插入一个 ‘o’ 字符就可以变为 “good”,因此它们之间的编辑距离为 1。

入门
  1. 查询ipone
GET goods/_search
{
  "query": {
    "match": {
      "content": {
        "query": "ipone"
      }
    }
  }
}

没有查到结果

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

  1. 执行下面的查询,便可模糊查出结果
GET goods/_search
{
  "query": {
    "match": {
      "content": {
        "query": "ipone",
        "fuzziness": 1
      }
    }
  }
}
{
  "took" : 39,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.5053002,
    "hits" : [
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.5053002,
        "_source" : {
          "id" : 1,
          "name" : "苹果手机12",
          "category" : "手机",
          "price" : 8999.0,
          "content" : "Apple iPhone 12 Pro Max (A2412) 256GB 海蓝色 支持移动联通电信5G 双卡双待手机",
          "create_at" : "2020-12-12 12:00:00"
        }
      }
    ]
  }
}

或者

GET goods/_search
{
  "query": {
    "fuzzy": {
      "content": {
        "value": "ipone",
        "fuzziness": 1
      }
    }
  }
}
{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.5053002,
    "hits" : [
      {
        "_index" : "goods",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.5053002,
        "_source" : {
          "id" : 1,
          "name" : "苹果手机12",
          "category" : "手机",
          "price" : 8999.0,
          "content" : "Apple iPhone 12 Pro Max (A2412) 256GB 海蓝色 支持移动联通电信5G 双卡双待手机",
          "create_at" : "2020-12-12 12:00:00"
        }
      }
    ]
  }
}

fuzziness 参数取值规则

在查询 text 或者 keyword 类型的字段时, fuzziness 可以看做是莱文斯坦距离。

fuzziness 参数的取值如下

  • 0,1,2: 表示最大可允许的莱文斯坦距离
  • auto:
    会根据词项的长度来产生可编辑距离,它还有两个可选参数,形式为AUTO:[low],[high], 分别表示短距离参数和长距离参数;如果没有指定,默认值是 AUTO:3,6 表示的意义如下
    • 0…2 单词长度为 0 到 2 之间时必须要精确匹配,这其实很好理解,单词长度太短是没有相似度可言的,例如 ‘a’ 和 ‘b’。
    • 3…5: 单词长度 3 到 5 个字母时,最大编辑距离为 1
    • 5: 单词长度大于 5 个字母时,最大编辑距离为 2
      如果不设置 fuziness 参数,查询是精确匹配的。
      fuzziness 在绝大多数场合都应该设置成 AUTO(来自官网)
      对在中文环境下Fuzziness这个模糊查询的没有什么意义,

prefix_length 参数

prefix_length 表示不能没模糊化的初始字符数。由于大部分的拼写错误发生在词的结尾,而不是词的开始,使用 prefix_length 就可以完成优化。注意 prefix_length 必须结合 fuzziness 参数使用。默认0

prefix_length设置3,前三个字母就不能模糊化,所以这里ihpone不会匹配到数据

GET goods/_search
{
  "query": {
    "fuzzy": {
      "content": {
        "value": "ihpone",
        "fuzziness": 1,
        "prefix_length": 3
      }
    }
  }
}

或者

GET goods/_search
{
  "query": {
    "match": {
      "content": {
        "query": "ihpone",
        "fuzziness": 1,
        "prefix_length": 3
      }
    }
  }
}
max_expansions参数

模糊查询将要扩展的索引词最大数量,默认50

ID查询
GET goods/_search
{
  "query": {
    "ids": {
      "type": "_doc", 
      "values": [1,2]
    }
  }
}

在老版本,es还没有取消的type的时候,请求是上面这样的,后来取消type了 (Deprecation: [types removal] Types are deprecated in [ids] queries.)

GET goods/_search
{
  "query": {
    "ids": {
      "values": [1,2]
    }
  }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值