Elasticsearch学习笔记(二)-高级应用

104 阅读10分钟

Elasticsearch

1. 映射高级

1.1 地理坐标

  • 经度:lon

  • 纬度:lat

  • 地理坐标点需要显式声明对应字段类型为 geo_point

1.1.1 新建索引

PUT /chongqing-locations
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "location": {
        "type": "geo_point"
      }
    }
  }
}

1.1.2 新增文档

# 字符串形式 "lat, lon"
PUT /chongqing-locations/_doc/1
{
  "name": "重庆大学",
  "location": "29.591288,106.305424"
}

# 数组形式[lon,lat]
PUT /chongqing-locations/_doc/2
{
  "name": "国际城",
  "location": [106.314335,29.593361]
}

# 对象形式 显式命名为 lat 和 lon
PUT /chongqing-locations/_doc/3
{
  "name": "科技学院",
  "location": {
    "lon": 106.330002,
    "lat": 29.59581
  }
}

1.1.3 地理坐标点相关的过滤器

1.1.3.1 geo_distance

找出与指定位置在给定距离内的点

GET /chongqing-locations/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": [
        {
          "geo_distance": {
          "distance": "1.5km",
          "location": {
            "lon": 106.325079, # 康田曼城
            "lat": 29.593549
          }
        }
        }
      ]
    }
  }
}

2. Query DSL

索引库中有2部手机,2台电视

PUT /product
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "price":{
        "type": "float"
      }
    }
  }
}

POST /product/_doc
{
  "name":"小米手机",
  "price":1999
}

POST /product/_doc
{
  "name":"华为手机",
  "price":5999
}

POST /product/_doc
{
  "name":"小米电视",
  "price":3999
}

POST /product/_doc
{
  "name":"华为电视",
  "price":8999
}

2.1 查询所有(match_all query)

GET /product/_search
{
  "query": {
    "match_all": {}
  }
}

2.2 全文搜索(full-text query)

2.2.1 匹配搜索(match query)

全文查询的标准查询,它可以对一个字段进行模糊、短语查询。

match queries 接收text/numerics/dates, 对它们进行分词分析, 再组织成一个boolean查询。

可通过operator 指定bool组合操作(or、and 默认是 or )。

2.2.1.1 or 关系(默认)

match 类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是or的关系

  • 查询:
GET /product/_search
{
  "query": {
    "match": {
      "name": "小米手机"
    }
  }
}
  • 结果:
{
  "took" : 8, # 查询花费时间,单位是毫秒
  "timed_out" : false, # 是否超时
  "_shards" : { # 分片信息
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : { # 搜索结果总览对象
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 1.3862942, # 所有结果中文档得分的最高分
    "hits" : [ # 搜索结果的文档对象数组
      {
        "_index" : "product", # 索引库
        "_type" : "_doc",# 文档类型
        "_id" : "_Hvay4AB8OPoaxhgjtGh", # 文档id
        "_score" : 1.3862942, # 文档得分
        "_source" : { # 文档的源数据
          "name" : "小米手机",
          "price" : 1999
        }
      },
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "_Xvay4AB8OPoaxhgnNEe",
        "_score" : 0.6931471,
        "_source" : {
          "name" : "华为手机",
          "price" : 5999
        }
      },
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "_nvay4AB8OPoaxhgpNFw",
        "_score" : 0.6931471,
        "_source" : {
          "name" : "小米电视",
          "price" : 3999
        }
      }
    ]
  }
}

2.2.1.2 and 关系
  • 查询:
GET /product/_search
{
  "query": {
    "match": {
      "name": {"query": "小米手机","operator": "and"}
    }
  }
}
  • 结果:
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.3862942,
    "hits" : [
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "_Hvay4AB8OPoaxhgjtGh",
        "_score" : 1.3862942,
        "_source" : {
          "name" : "小米手机",
          "price" : 1999
        }
      }
    ]
  }
}

只有同时包含小米和手机的词条才会被搜索到。

2.2.2 短语搜索(match phrase query)

POST /product/_doc
{
  "name":"三星电视4A",
  "price":"8888"
}
  • 查询:
GET /product/_search
{
  "query": {
    "match_phrase": {
      "name": {"query": "三星 4A"}
    }
  }
}
  • 结果:
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}
  • 查询:
GET /product/_search
{
  "query": {
    "match_phrase": {
      "name": {"query": "三星 4A", "slop": 2}
    }
  }
}
  • 结果:
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 2.794362,
    "hits" : [
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "AHv-zIAB8OPoaxhgttIt",
        "_score" : 2.794362,
        "_source" : {
          "name" : "三星电视4A",
          "price" : "8888"
        }
      }
    ]
  }
}

phrase match:将多个term作为一个短语,一起去搜索,只有包含这个短语的doc才会作为结果返回。

match:是只在包含其中任何一个分词就返回

slop:我们搜索时,文档中必须包含小米 电视这两个文档,且他们之间的距离不能超过2,如果slop的值足够大,那么单词的顺序可以是任意的。

2.2.3 query_string 查询

  • Query String Query提供了无需指定某字段而对文档全文进行匹配查询的一个高级查询,同时可以指定在哪些字段上进行匹配。
# 默认文档全文进行匹配查询 和 指定字段
GET /product/_search
{
  "query": {
    "query_string": {
      "query": "3999"
    }
  }
}

# 指定字段
GET /product/_search
{
  "query": {
    "query_string": {
      "query": "3999",
      "default_field": "price"
    }
  }
}

# 逻辑查询
GET /product/_search
{
  "query": {
    "query_string": {
      "query": "手机 OR 小米",
      "default_field": "name"
    }
  }
}

GET /product/_search
{
  "query": {
    "query_string": {
      "query": "手机 AND 小米",
      "default_field": "name"
    }
  }
}

# 模糊查询 ~1代表可以模糊1个字
GET /product/_search
{
  "query": {
    "query_string": {
      "query": "大米~1",
      "default_field": "name"
    }
  }
}

# 多字段支持
GET /product/_search
{
  "query": {
    "query_string": {
      "fields": ["name","price"],
      "query": "5999"
    }
  }
}

2.2.4 多字段匹配搜索(multi match query)

  • 如果你需要在多个字段上进行文本搜索,可用multi_match 。multi_match在 match的基础上支持对多个字段进行文本查询。
GET /product/_search
{
  "query": {
    "multi_match": {
      "fields": ["name","price"],
      "query": "5999"
    }
  }
}
  • 使用*匹配多个字段
GET /product/_search
{
  "query": {
    "multi_match": {
      "fields": ["name","p*"],
      "query": "5999"
    }
  }
}

2.3 词条级搜索(term-level queries)

可以使用term-level queries根据结构化数据中的精确值查找文档。

结构化数据的值包括日期范围、IP地址、价格或产品ID。

与全文查询不同,term-level queries不分析搜索词。相反,词条与存储在字段级别中的术语完全匹配。

PUT /book
{
  "settings": {},
  "mappings": {
    "properties": {
      "description": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "name": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "price": {
        "type": "float"
      },
      "timestamp": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}

PUT /book/_doc/1
{
  "name": "lucene",
  "description": "Lucene Core is a Java library providing powerful indexing and search features, as well as spellchecking, hit highlighting and advanced analysis/tokenization capabilities. The PyLucene sub project provides Python bindings for Lucene Core. ",
  "price": 100.45,
  "timestamp": "2020-08-21 19:11:35"
}

PUT /book/_doc/3
{
    "name": "Hadoop",
    "description": "The Apache Hadoop software library is a framework that allows
    for the distributed processing of large data sets across clusters of computers
    using simple programming models.",
    "price":620.45,
    "timestamp":"2020-08-22 19:18:35"
}

PUT /book/_doc/4
{
  "name": "ElasticSearch",
  "description": "Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。",
  "price": 999.99,
  "timestamp": "2020-08-15 10:11:35"
}

2.3.1 词条搜索(term query)

term 用于查询指定字段包含某个词项的文档

GET  /book/_search
{
  "query": {
    "term": {
      "name": "solr"
    }
  }
}

2.3.2 词条集合搜索(terms query)

terms 查询用于查询指定字段包含某些词项的文档

GET  /book/_search
{
  "query": {
    "terms": {
      "name": [
        "solr",
        "elasticsearch"
      ]
    }
  }
}

2.3.3 范围搜索(range query)

  • gte:大于等于
  • gt:大于
  • lte:小于等于
  • lt:小于
  • boost:查询权重

查询价格10-200

GET  /book/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 10,
        "lte": 200
      }
    }
  }
}

查询时间在两天前

GET /book/_search
{
  "query": {
    "range": {
      "timestamp": {
        "gte": "now-2d/d",
        "lte": "now/d"
      }
    }
  }
}

指定时间格式查询

GET /book/_search
{
  "query": {
    "range": {
      "timestamp": {
        "gte": "18/08/2020",
        "lte": "2022",
        "format": "dd/MM/yyyy||yyyy"
      }
    }
  }
}

2.3.4 不为空搜索(exists query)

查询指定字段值不为空的文档。相当 SQL 中的 column is not null

# 注意field关键词
GET /book/_search
{
  "query": {
    "exists": {"field": "price"}
  }
}

2.3.5 词项前缀搜索(prefix query)

查询名字前缀是so

GET /book/_search
{
  "query": {
    "prefix": {
      "name": {
        "value": "so"
      }
    }
  }
}

2.3.6 通配符搜索(wildcard query)

使用*通配符

GET /book/_search
{
  "query": {
    "wildcard": {
      "name": {
        "value": "lu*"
      }
    }
  }
}

2.3.7 正则搜索(regexp query)

regexp允许使用正则表达式进行term查询。

注意regexp如果使用不正确,会给服务器带来很严重的性能压力。

比如.*开头的查询,将会匹配所有的倒排索引中的关键字,这几乎相当于全表扫描,会很慢。

因此如果可以的话,最好在使用正则前,加上匹配的前缀。

GET /book/_search
{
  "query": {
    "regexp": {
      "name": "s.*"
    }
  }
}

2.3.8 模糊搜索(fuzzy query)

GET /book/_search
{
  "query": {
    "fuzzy": {
      "name": {
        "value": "so",
        "fuzziness": 2
      }
    }
  }
}

2.3.9 ids搜索(id集合查询)

GET /book/_search
{
  "query": {
    "ids": {
      "values": ["1", "3"]
    }
  }
}

2.4 复合搜索(compound query)

2.4.1 constant_score query

用来包装另一个查询,将查询匹配的文档的评分设为一个常值

# "max_score" : 0.85840076
GET /book/_search
{
  "query": {
    "term": {
      "description": {
        "value": "solr"
      }
    }
  }
}

# "max_score" : 1.2
GET /book/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "description": {
            "value": "solr"
          }
        }
      },
      "boost": 1.2
    }
  }
}

2.4.2 布尔搜索(bool query)

bool 查询用bool操作来组合多个查询字句为一个查询。

可用的关键字:

  • must:必须满足
  • filter:必须满足,但执行的是filter上下文,不参与、不影响评分
  • should:或
  • must_not:必须不满足,在filter上下文中执行,不参与、不影响评分
GET /book/_search 
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "description": "java"
        }}
      ]
    }
  }
}

GET /book/_search 
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "description": "java"
        }}
      ],
      "filter": [
        {"term": {
          "name": "solr"
        }}
      ],
      "must_not": [
        {"range": {
          "price": {
            "gte": 200,
            "lte": 300
          }
        }}
      ]
    }
  }
}

GET /book/_search 
{
  "query": {
    "bool": {
      "should": [
        {"match": {
          "description": "java"
        }}
      ],
      "filter": [
        {"term": {
          "name": "solr"
        }}
      ],
      "must_not": [
        {"range": {
          "price": {
            "gte": 200,
            "lte": 300
          }
        }}
      ],
      "minimum_should_match": 1
    }
  }
}

minimum_should_match代表了最小匹配精度,如果设置minimum_should_match=1,那么should语句中至少需要有一个条件满足。

2.5 排序

默认是按照_score降序

  • 升序排序
GET /book/_search
{
  "query": {
    "match": {
      "description": "solr"
    }
  },
  "sort": [
    {
      "_score": {
        "order": "asc"
      }
    }
  ]
}
  • 字段值排序
GET /book/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ]
}
  • 多级排序
GET /book/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    },
    {
      "timestamp": {
        "order": "desc"
      }
    }
  ]
}

2.6 分页

size:每页显示多少条

from:当前页起始索引, int start = (pageNum - 1) * size

GET /book/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ],
  "size": 2,
  "from": 0
}

2.7 高亮

  • pre_tags:前置标签
  • post_tags:后置标签
  • fields:需要高亮的字段
  • name:这里声明title字段需要高亮,后面可以为这个字段设置特有配置,也可以空
GET /book/_search
{
  "query": {
    "query_string": {
      "query": "solr"
    }
  },
  "highlight": {
    "pre_tags": "<font color='yellow'>",
    "post_tags": "</font>",
    "fields": [{"name":{}},{"description":{}}]
  }
}

2.8 文档批量操作(bulk 和 mget)

2.8.1 mget 批量查询

单条查询 GET /book/_doc/1,如果查询多个id的文档一条一条查询,网络开销太大。

GET /_mget
{
  "docs": [
    {
      "_index": "book",
      "_id": 1
    },
    {
      "_index": "book",
      "_id": 2
    }
  ]
}

# 同一索引下批量查询
GET /book/_mget
{
  "docs": [
    {
      "_id": 1
    },
    {
      "_id": 2
    }
  ]
}

# 搜索简化写法
GET /book/_search
{
 "query": {
   "ids": {
     "values": ["1", "2"]
   }
 }
}

2.8.2 批量增删改

Bulk 操作解释将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数。

语法:

POST /_bulk
{"action": {"metadata"}}
{"data"}
  • delete:删除一个文档,只要1个json串就可以了 删除的批量操作不需要请求体
  • create:相当于强制创建 PUT /index/type/id/_create
  • index:普通的put操作,可以是创建文档,也可以是全量替换文档
  • update:执行的是局部更新partial update操作
POST /_bulk
{ "delete": { "_index": "book", "_id": "1" }}
{ "create": { "_index": "book", "_id": "5" }}
{ "name": "test14","price":100.99 }
{ "update": { "_index": "book", "_id": "2"} }
{ "doc" : {"name" : "test"} }

实际用法:

bulk请求一次不要太大,否则一下积压到内存中,性能会下降。

所以,一次请求几千个操作、大小在几M正好。

bulk会将要处理的数据载入内存中,所以数据量是有限的,最佳的数据量不是一个确定的数据,它取决于你的硬件,你的文档大小以及复杂性,你的索引以及搜索的负载。

一般建议是1000-5000个文档,大小建议是5-15MB,默认不能超过100M,可以在es的配置文件(ES的 config下的elasticsearch.yml)中配置:http.max_content_length: 10mb

2.9 Filter DSL

Elasticsearch中的所有的查询都会触发相关度得分的计算。对于那些我们不需要相关度得分的场景下,Elasticsearch以过滤器的形式提供了另一种查询功能,过滤器在概念上类似于查询,但是它们有非常快的执行速度,执行速度快主要有以下两个原因:

  • 过滤器不会计算相关度的得分,所以它们在计算上更快一些。
  • 过滤器可以被缓存到内存中,这使得在重复的搜索查询上,其要比相应的查询快出许多。
POST /book/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": [
        {
          "range": {
            "price": {
              "gte": 200,
              "lte": 300
            }
          } 
        }
      ]
    }
  }
}

分解上面的例子,被过滤的查询包含一个match_all查询(查询部分)和一个过滤器(filter部分)。我们可以在查询部分中放入其他查询,在filter部分放入其它过滤器。

在上面的应用场景中,由于所有的在这个范围之内的文档都是平等的(或者说相关度都是一样的),没有一个文档比另一个文档更相关,所以这个时候使用范围过滤器就非常合适了。通常情况下,要决定是使用过滤器还是使用查询,你就需要问自己是否需要相关度得分。如果相关度是不重要的,使用过滤器,否则使用查询。查询和过滤器在概念上类似于SELECT WHERE语句。