ElasticSearch是一个基于Lucene的搜索服务器,提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。它使用Java开发,并作为Apache许可条款下的开源发布,是当前流行的企业级搜索引擎。
—— 摘自百度百科
对于非关系型数据库不熟悉的,可以了解一下:
【知乎好文】MongoDB 等 NoSQL 与关系型数据库相比,有什么优缺点及适用场景?https://www.zhihu.com/question/20059632
与关系型数据库中表结构不同,文档中可以嵌入数组和子文档,就像程序中的数组和成员变量一样。这是关系型数据库三范式不允许的。但实际中我们经常看到一对多和一对一的数据。比如,一篇博客文章的 Tag 列表作为文章的一部分非常直观,而把 Tag 与文章的从属关系单独放一张表里就不那么自然。再比如,一个订单下面的收货地址,包括省、市、区、街道和门牌,作为一个子文档,与订单的信用卡地址很容易区分开。更方便的是,嵌入的数组和子文档之上可以直接建立索引,比如我可以很快找到所有 Tag 包含 MongoDB 的文章。
其实,我挺喜欢关系型数据库那些理论的,SQL 语言可以很精确地形式化,赏心悦目。然而,三范式强调的「数据没有任何冗余」并不是今天程序员们最关心的问题。他们用着方便不方便才是更重要的问题。
手册上简单的对比图:
Relational DB > Database > Table > Row > Column
Elasticsearch > Index > Type > Document > Field
但其实ES与关系型数据库的设计理念不同,所以与关系型数据库不存在一一对应的关系,阮一峰老师的博客中对ES的基本概念解释的比较清晰:
Index(索引)里面的单条记录称为Document(文档),使用JSON格式表示。同一个Index里面的Document,不要求有相同的结构,但最好保持相同,有利于提高搜索效率。
Document可以分组(Type),比如weather这个Index里面,可以按照城市分组(背景和上海),也可以按气候分组(晴天和雨天)。这种分组就叫做Type,它是虚拟的逻辑分组,用来过滤Document。
⚠️不同的Type应该有相似的结构(schema),举例来说,id字段不能在这个组是字符串,在另一个组是数值。这是与关系型数据库的表的一个区别。性质完全不同的数据(比如products和logs)应该存成两个Index,而不是一个Index里面的两个Type(虽然可以做到)。
– 根据规划,Elastic 6.x版只允许每个Index包含一个Type,7.x版将会彻底移除Type。
https://www.elastic.co/cn/blog/index-type-parent-child-join-now-future-in-elasticsearch
Elasticsearch提供基于JSON的完整查询DSL(域特定语言)来定义查询。将Query DSL视为查询的AST(抽象语法树),由两种类型的子句组成:
叶子查询从句:match、term、range
复合查询从句:bool、dis_max
GET /_search
{
"query":{
"match": {
"message": "this that"
}
}
}
⚠️这种写法会查询所有message包含this或that的文档,查询辅助还可以添加operator(or/and)、analyzer、lenient(是否忽略错误data-type)
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html
当message的查询词是QUICK!时:
将查询的字符串 QUICK! 传入标准分析器中,输出的结果是单个项 quick 。因为只有一个单词项,所以 match 查询执行的是单个底层 term 查询。
用 term 查询计算每个文档相关度评分 _score ,这是种将 词频(term frequency,即词 quick 在相关文档的 title 字段中出现的频率)和反向文档频率(inverse document frequency,即词 quick 在所有文档的 title 字段中出现的频率),以及字段的长度(即字段越短相关度越高)相结合的计算方式。参见 相关性的介绍 。
https://www.elastic.co/guide/cn/elasticsearch/guide/current/match-query.html
GET /_search
{
"query":{
"term":{
"user":{
"value": "Kimchy",
"boost": 1.0
}
}
}
}
用于查询一个特定的value,比如价格、ID、用户名
⚠️⚠️避免在text字段使用term,默认情况下text字段会被Elasticsearch预先处理,如去掉符号、分词成tokens、变为小写等
具体可以看一个Full text的查询例子:
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-term-query.html
terms: 查询的字段为数组时使用。
对比match/term总结:前者用于text匹配,后者用于精确字段匹配。
GET _search
{
"query":{
"range":{
"timestamp":{
"time_zone": "+1:00",
"gte": "2015-01-01 00:00:00",
"lte": "now"
}
}
}
}
比大小:gt > ,gte >= ,lt > ,lte <=
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html
POST _search
{
"query":{
"bool":{
"must": {
"term": {"user": "kimchy"}
},
"filter": {
"term": {"tag":"tech"}
},
"must_not": {
"range": {
"age": {"gte": 10, "lte": 20}
}
},
"should": [
{"term": {"tag": "wow"}},
{"term": {"tag": "elasticsearch"}}
],
"minim_should_match": 1,
"boost": 1.0
}
}
}
??? 计算score没看懂:
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html
⚠️bool过滤和bool查询的区别: should的匹配规则
在过滤中,should相当于or,至少匹配一个;
在查询中,所有 must 语句必须匹配,所有 must_not 语句都必须不匹配,但有多少 should 语句应该匹配呢? 默认情况下,没有 should 语句是必须匹配的,只有一个例外:那就是当没有 must 语句的时候,至少有一个 should 语句必须匹配。
https://www.elastic.co/guide/cn/elasticsearch/guide/current/bool-query.html
两个基本概念:桶、指标(这就是全部)
类比SQL语句:SELECT COUNT(color) FROM table GROUP BY color
指标:COUNT(color)
桶:GROUP BY color
(以上为文档例子,个人感觉牵强)
详细说明:
桶 ——
1、张三属于男性雇员桶、西二旗属于北京桶、10月5号属于10月桶……满足特定条件的文档的集合。
2、桶可以嵌套,西二旗属于北京桶,北京属于中国桶。
3、Elasticsearch有很多中类型的桶,能让你通过很多中方式来划分文档(时间、最受欢迎的词、年龄区间、地理位置等等)
指标 ——
1、最小值、平均值、最大值、汇总……桶让我们划分到有意义的集合,但是最终我们需要的是对这些桶内的文档进行一些指标的计算。
2、指标能让你计算像平均工资、最高售出价格、95%的查询延迟等。
组合:https://www.elastic.co/guide/cn/elasticsearch/guide/current/_combining_the_two.html
哪个汽车的销量最好:
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_aggregation_test_drive.html
GET /_search
{
"aggs":
{
"popular_colors" : {
"terms" : {
"field": "color"
}
}
}
}
在本例中,我们定义了一个单terms桶,这个terms桶会为每个碰到的唯一词项动态创建新的桶。因为我们告诉它使用color字段,所以terms桶会为每个颜色动态创建新桶。
结果读取为:res = aggs.popular_colors.buckets
nested结构是NoSQL的一种高级特性,当我们搜索时,嵌套结构会扁平化处理,失去嵌套对象的对应关系,为此使用nested类型构建嵌套对象映射
{
"query" : {
"bool" : {
"must" : {
{ "match" : { "title" : "eggs" }},
{
"nested" : {
"path" : "comments",
"query" : {
"bool" : {
"must" : [
{ "match" : { "comments.name" : "john"}},
{ "match" : { "comments.age" : 28 }}
]
}
}
}
}
}
}
}
}
https://blog.csdn.net/u012332735/article/details/62222953/
精确值查找:
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_finding_exact_values.html
组合过滤器:
https://www.elastic.co/guide/cn/elasticsearch/guide/current/combining-filters.html
查找多个term(terms:[]) / 精准查找:
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_finding_multiple_exact_values.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_dealing_with_null_values.html
{
"query": {
"constant_score": {
"filter": {
"exists" : {
"field" : "poiGrade"
}
}
}
}
}
在 Elasticsearch 的较早版本中,默认的行为是缓存一切可以缓存的对象。这也通常意味着系统缓存 bitsets 太富侵略性,从而因为清理缓存带来性能压力。不仅如此,尽管很多过滤器都很容易被评价,但本质上是慢于缓存的(以及从缓存中复用)。缓存这些过滤器的意义不大,因为可以简单地再次执行过滤器。
检查一个倒排是非常快的,然后绝大多数查询组件却很少使用它。例如 term 过滤字段 “user_id” :如果有上百万的用户,每个具体的用户 ID 出现的概率都很小。那么为这个过滤器缓存 bitsets 就不是很合算,因为缓存的结果很可能在重用之前就被剔除了。
这种缓存的扰动对性能有着严重的影响。更严重的是,它让开发者难以区分有良好表现的缓存以及无用缓存。
为了解决问题,Elasticsearch 会基于使用频次自动缓存查询。如果一个非评分查询在最近的 256 次查询中被使用过(次数取决于查询类型),那么这个查询就会作为缓存的候选。但是,并不是所有的片段都能保证缓存 bitset 。只有那些文档数量超过 10,000 (或超过总文档数量的 3% )才会缓存 bitset 。因为小的片段可以很快的进行搜索和合并,这里缓存的意义不大。
一旦缓存了,非评分计算的 bitset 会一直驻留在缓存中直到它被剔除。剔除规则是基于 LRU 的:一旦缓存满了,最近最少使用的过滤器会被剔除。