ElasticSearch备忘文档

447 阅读5分钟

最近一段时间用户推荐开发,专门去调用去调研了elasticsearch的基本用法。

es中的text类型的数据最让人奇怪,需要特别注意一下。

另外在7.0版本之上,es简化了type的设计,默认使用_doc类型,后续版本可能会删除type的设计。

索引操作

1、查看索引

curl --location --request GET 'http://127.0.0.1:9200/member_minicity'

成功返回之后的结果类似下面内容,

aliases下面的member_minicity2member_minicity3都是别名,通过5、设置索引别名的方式产生。

mappingsproperties是当前的索引对应字段,settings是索引的基本描述。

{
    "member_minicity": {
        "aliases": { 
            "member_minicity2": {},
            "member_minicity3": {}
        },
        "mappings": {
            "properties": {
                "age": {
                    "type": "short"
                },
                "city": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 100
                        }
                    }
                },
                "member_id": {
                    "type": "long"
                },
                "member_status": {
                    "type": "short"
                },
                "province_id": {
                    "type": "short"
                },
                "sex": {
                    "type": "byte"
                },
                "unionid": {
                    "type": "keyword"
                }
            }
        },
        "settings": {
            "index": {
                "creation_date": "1624515549327",
                "number_of_shards": "1",
                "number_of_replicas": "1",
                "uuid": "o69OTNTbQvCyI9dHeYSVKg",
                "version": {
                    "created": "7050199"
                },
                "provided_name": "member_minicity"
            }
        }
    }
}

2、创建索引

curl --location --request PUT 'http://127.0.0.1:9200/member_minicity' \
--header 'Content-Type: application/json' \
--data-raw '{
    "mappings": {
        "properties": {
            "member_id": {
                "type": "long"
            }
        }
    }
}'

3、重建索引(同步数据)

curl --location --request POST 'http://127.0.0.1:9200/_reindex' \
--header 'Content-Type: application/json' \
--data-raw '{
  "source": {
    "index": "member_mini_city"
  },
  "dest": {
    "index": "member_minicity"
  }
}'

4、删除索引

curl --location --request DELETE 'http://127.0.0.1:9200/member_minicity'

5、设置索引别名

actions中的index为原始索引名称,alias为新的索引名称。

curl --location --request POST 'http://127.0.0.1:9200/_aliases' \
--header 'Content-Type: application/json' \
--data-raw '{
    "actions": [
        {
            "add": {
                "index": "member_minicity",
                "alias": "member_minicity2"
            }
        }
    ]
}'

查询说明

1、字符串类型

这里主要指的是textkeywordtextkeyword都可以用于表示字符串,text前者会分词,keyword完全匹配。

2、数字类型

image

使用时,尽量选择最小满足条件的类型。

3、对象类型

manager类型下面包括了agename字段,name下面包括了firstlast字段。

{
  "mappings": {
    "properties": { 
      "region": {
        "type": "keyword"
      },
      "manager": { 
        "properties": {
          "age":  { "type": "integer" },
          "name": { 
            "properties": {
              "first": { "type": "text" },
              "last":  { "type": "text" }
            }
          }
        }
      }
    }
  }
}

es中索引数据的方式,

{
    "manager.age": "",
    "manager.name.first": "",
    "manager.name.last": ""
}

mapping可以针对不同的字段进行不同的设置。

{
  "mappings": {
    "properties": { 
      "name": {
        "type": "keyword"
      },
      "manager": { 
        "properties": {
          "age":  { "type": "integer" },
          "name": { 
            "properties": {
              "first": { "type": "text" },
              "last":  { "type": "text" }
            }
          }
        }
      }
    }
  }
}

注意: term是查询时对关键字不分词,keyword是索引时不分词

如何让字段同时设置keywordtext属性?使用fields扩展字段属性。

下面例子中的title字段,使用fields扩展title的属性,使其同时具备keywordtext属性。

{
  "settings": {
    "index": {
        "number_of_shards" : "2",
        "number_of_replicas" : "0"
    }
  },
  "mappings": {
    "properties": {
      "itemId" : {
        "type": "keyword",
        "ignore_above": 64
      },
      "title" : {
        "type": "text",
        "fields": {
          "keyword" : {"ignore_above" : 256, "type" : "keyword"}
        }
      },
      "desc" : {"type": "text", "analyzer": "ik_max_word"},
      "num" : {"type": "integer"},
      "price" : {"type": "long"}
    }
  }
}

实战演示

假设上面的例子中,title初始化了值{"title": "苏泊尔煮饭SL3200"},因为使用了text的类型来定义数据类型,所以title会被分词处理,假设分成了{ "苏泊尔","煮饭", "sl3200", "sl","3200"}中的多个字段,es存储的方式也是存放的各个字段,而不是苏泊尔煮饭SL3200完整字符串。

1、使用match查询(精确查询)

如果要完整的匹配,只能使用fields增加keyword属性,搜素的时候需要显示的设置title.keyword来查询,否则无法匹配到。

{
  "query": {
    "bool": {
        "must": {
            "match": {"title.keyword": "苏泊尔煮饭SL3200"}
        }
    }
  }
}

2、使用match查询(分词查询)

模糊查询会去对比分词集合中是不是有给出的关键字,比如下面的例子苏泊尔,关键字在分词集合中存在,返回的结果中元字段_score有评分。

{
  "query": {
    "bool": {
      "must": {"match": {"title": "苏泊尔"}}
    }
  }
}

可以使用数组来替代,用于判断多个条件。

{
  "query": {
    "bool": {
      "must": [{"match": {"title": "苏泊尔"}]}
    }
  }
}

3、使用term查询(精确查询)

{
  "query": {
    "bool": {
      "must": {
        "term": {"title.keyword": "苏泊尔煮饭SL3200"}
        }
    }
  }
}

terms能够实现sql中的in的效果。

格式为:

{
  "query": {
    "terms": {
      "{FIELD}": [
        "{VALUE1}",
        "{VALUE2}"
      ]
    }
  }
}

{FIELD} - 就是我们需要匹配的字段名 {VALUE1}, {VALUE2} .... {VALUE N} - 就是我们需要匹配的内容,除了TEXT类型字段以外的任意类型。

{
  "query": {
    "bool": {
      "must": {
        "terms": { "shop_id": [123,100,300]}
        }
    }
  }
}

4、范围查询

查询范围参数说明,

gt - 大于 ( > )
gte - 大于且等于 ( >= )
lt - 小于 ( < )
lte - 小于且等于 ( <= )

基本格式,

{
  "query": {
    "range": {
      "{FIELD}": {
        "gte": 10, 
        "lte": 20
      }
    }
  }
}

5、bool组合查询

基本语法格式如下,上面的说的matchtermrange都可以放到mustmust_notshould中。

{
  "query": {
    "bool": { // bool查询
      "must": [], // must条件,类似SQL中的and, 代表必须匹配条件
      "must_not": [], // must_not条件,跟must相反,必须不匹配条件
      "should": [] // should条件,类似SQL中or, 代表匹配其中一个条件
    }
  }
}

示例:

{
  "query": {
    "bool": {
        "must": [
            {
                "term": {
                "order_no":  "202003131209120999"
                }
            },
            {
                "term": {
                    "shop_id":  123
                }
            }]
        },
        "must_not": [
          {
            "term": {
              "shop_id": 1
            }
          },
          {
            "term": {
              "shop_id":  2
            }
          }
        ],
        "should": [
            {
                "match": {
                    "order_no": "202003131209120999"
                }
            },
            {
                "match": {
                    "order_no": "22222222222222222"
                }
            }
        ]
    }
  }
}

boolshouldmust之间可以互相嵌套,如下例子,

GET /order_v2/_search

{
  "query": {
    "bool": {
      "should": [
        {
          "bool": {
            "must": [
              {
                "term": {
                  "order_no": "2020031312091209991"
                }
              },
              {
                "range": {
                  "shop_id": {
                    "gte": 10,
                    "lte": 200
                  }
                }
              }
            ]
          }
        },
        {
          "terms": {
            "tag": [
              1,
              2,
              3,
              4,
              5,
              12
            ]
          }
        }
      ]
    }
  }
}

切换成sql可以是,

select * from order_v2 where (order_no='202003131209120999' and (shop_id>=10 and shop_id<=200)) or tag in (1,2,3,4,5)

总结一下,

1、mustmust_notshould都使用数组的方式,确保能够灵活增加多个查询条件。

2、mustmust_notshould接收的查询条件可以通过termtermsmatchrange等来表示。

3、对于分词查询的字段即设置成text属性的字段,可以通过fields来设置keyword以便能够通过term条件来精确匹配。


补充查询

1、获取所有

GET /ad/phone/_search
{
  "query": {
    "match_all": {}
  }
}

2、分页

GET /ad/phone/_search
{
  "query": {
    "match_all": {}
  },
  "from": 1,
  "size": 2
}

这种分页方式如果进行深度分页,比如到100页,每页十条数据,它会从每个分片都查询出100*10条数据,假设有五个分片,就是5000条数据,然后在内存中进行排序,然后返回拍过序之后的集合中的第1000-1010条数据

3、指定查询出来的数据返回的字段

GET /ad/phone/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["name","price"]
}

4、排序

GET /ad/phone/_search
{
  "query": {
    "match": {
      "ad": "white"
    }
  }, 
  "sort": [
    {
      "price": {
        "order": "asc"
      }
    }
  ]
}

5、使用filter不会计算评分

返回的结果中元字段_score字段等于0,没评分,说明使用filter不会计算评分。

GET /ad/phone/_search
{
  "query": {
    "bool": {
      "filter": {
        "range": {
          "price": {
            "gt": 5000
          }
        }
      }
    }
  }
}

6、分词匹配逻辑控制

使用operator来选择and或者or

match 查询还可以接受 operator 操作符作为输入参数,默认情况下该操作符是or,只要匹配其中的一个分词就可以搜索到。我们可以将它修改成 and 让所有指定词项都必须匹配。

GET /ad/phone/_search
{
  "query": {
    "match": {
      "ad": {
        "query": "a red",
        "operator": "and"
      }
    }
  }
}

match 查询支持 minimum_should_match 最小匹配参数, 可以指定必须匹配的词项数用来表示一个文档是否相关。 我们可以将其设置为某个具体数字(指需要匹配倒排索引的词的数量),更常用的做法是将其设置为一个百分数,因为我们无法控制用户搜索时输入的单词数量。 只会返回匹配上a和red两个词的文档返回,如果minimum_should_match是1,则只要匹配上其中一个词,文档就会返回。

GET /ad/phone/_search
{
  "query": {
    "match": {
      "ad": {
        "query": "a red",
        "minimum_should_match": "2"
      }
    }
  }
}

7、multi_match查询

多字段查询,比如查询colorad字段包含单词red的文档。

GET /ad/phone/_search
{
  "query": {
    "multi_match": {
      "query": "red",
      "fields": ["color","ad"]
    }
  }
}

8、有值或者无值查询,exists 查询和 missing 查询

指定name字段有值:

GET /ad/phone/_search
{
  "query": {
    "bool": {
      "filter": {
        "exists": {
          "field": "name"
        }
      }
    }
  }
}

指定name字段无值:

GET /ad/phone/_search
{
  "query": {
    "bool": {
      "filter": {
        "missing": {
          "field": "name"
        }
      }
    }
  }
}

参考文档