大数据学习[19]--elasticesearch--Search

来源于:使用scroll实现Elasticsearch数据遍历和深度分页
http://lxwei.github.io/posts/%E4%BD%BF%E7%94%A8scroll%E5%AE%9E%E7%8E%B0Elasticsearch%E6%95%B0%E6%8D%AE%E9%81%8D%E5%8E%86%E5%92%8C%E6%B7%B1%E5%BA%A6%E5%88%86%E9%A1%B5.html
http://blog.csdn.net/ld326/article/details/79240036
在 Elasticsearch 中,搜索一般包括两个阶段,query 和 fetch 阶段,可以简单的理解,query 阶段确定要取哪些doc,fetch 阶段取出具体的 doc。
Query 阶段
这里写图片描述
如上图所示,描述了一次搜索请求的 query 阶段。
1. Client 发送一次搜索请求,node1 接收到请求,然后,node1 创建一个大小为 from + size 的优先级队列用来存结果,我们管 node1 叫 coordinating node。
2. coordinating node将请求广播到涉及到的 shards,每个 shard 在内部执行搜索请求,然后,将结果存到内部的大小同样为 from + size 的优先级队列里,可以把优先级队列理解为一个包含 top N 结果的列表。
3. 每个 shard 把暂存在自身优先级队列里的数据返回给 coordinating node,coordinating node 拿到各个 shards 返回的结果后对结果进行一次合并,产生一个全局的优先级队列,存到自身的优先级队列里。
在上面的例子中,coordinating node 拿到 (from + size) * 6 条数据,然后合并并排序后选择前面的 from + size 条数据存到优先级队列,以便 fetch 阶段使用。另外,各个分片返回给 coordinating node 的数据用于选出前 from + size 条数据,所以,只需要返回唯一标记 doc 的 _id 以及用于排序的 _score 即可,这样也可以保证返回的数据量足够小。
coordinating node 计算好自己的优先级队列后,query 阶段结束,进入 fetch 阶段。
Fetch 阶段
query 阶段知道了要取哪些数据,但是并没有取具体的数据,这就是 fetch 阶段要做的。
这里写图片描述
上图展示了 fetch 过程:
1. coordinating node 发送 GET 请求到相关shards。
2. shard 根据 doc 的 _id 取到数据详情,然后返回给 coordinating node。
3. coordinating node 返回数据给 Client。
coordinating node 的优先级队列里有 from + size 个 _doc _id,但是,在 fetch 阶段,并不需要取回所有数据,在上面的例子中,前100条数据是不需要取的,只需要取优先级队列里的第101到110条数据即可。
需要取的数据可能在不同分片,也可能在同一分片,coordinating node 使用 multi-get 来避免多次去同一分片取数据,从而提高性能。

3.1 Query

北京编码后:%E5%8C%97%E4%BA%AC

GET edu/_search?q=address:%E5%8C%97%E4%BA%AC

运行结果:

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1.3862944,
    "hits": [
      {
        "_index": "edu",
        "_type": "student",
        "_id": "2",
        "_score": 1.3862944,
        "_source": {
          "name": "张四",
          "age": 14,
          "pDate": "2017-12-13",
          "info": "他是中国人",
          "sex": 0,
          "address": "北京朝阳"
        }
      },
      {
        "_index": "edu",
        "_type": "student",
        "_id": "3",
        "_score": 0.5649868,
        "_source": {
          "name": "张五",
          "age": 14,
          "pDate": "2017-12-14",
          "info": "他是美国人",
          "sex": 1,
          "address": "北京中关村"
        }
      }
    ]
  }
}

可带参数:
https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-uri-request.html

3.3From / Size

分页时,采这个来查询。

GET /_search
{
    "from" : 0, 
    "size" : 2,
    "query" : {
        "term" : { "age" : 18 }
    }
}
3.4 Sort

https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-sort.html#_sort_mode_option
a. order参数取值
The order option can have the following values:
asc Sort in ascending order
desc Sort in descending order
b. mode参数取值
主要用来处理数组或一个域名中多个值的统计计算的选项,选择的参数为:
min Pick the lowest value.
max Pick the highest value.
sum Use the sum of all values as sort value. Only applicable for number based array fields.
avg Use the average of all values as sort value. Only applicable for number based array fields.
median Use the median of all values as sort value. Only applicable for number based array fields.

例子:

GET /edu/_search
{
    "from":0,
    "size":5,
    "sort":[
           "age",
           {"sex":"desc"},
           {"_score": {"order" : "desc"}},
           {"orders":{"mode":"sum","order":"desc"}}
           ],
    "query" : {
         "match" : { "info" :"日本"  }
    }
}

对年龄/性别/相似得分进行排序.

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 8,
    "max_score": null,
    "hits": [
      {
        "_index": "edu",
        "_type": "student",
        "_id": "4",
        "_score": 0.92305315,
        "_source": {
          "name": "张六",
          "age": 15,
          "pDate": "2017-12-15",
          "info": "他是日本人",
          "sex": 0,
          "address": "辽宁大连"
        },
        "sort": [
          15,
          0,
          0.92305315,
          -9223372036854776000
        ]
      },
      {
        "_index": "edu",
        "_type": "student",
        "_id": "11",
        "_score": 1.3612909,
        "_source": {
          "name": "张三",
          "age": 18,
          "pDate": "2017-12-12",
          "info": "他是日本人",
          "sex": 1,
          "address": "广东广州",
          "orders": [
            1,
            2,
            3,
            6
          ]
        },
        "sort": [
          18,
          1,
          1.3612909,
          12
        ]
      },
      {
        "_index": "edu",
        "_type": "student",
        "_id": "7",
        "_score": 1.3612909,
        "_source": {
          "name": "张八",
          "age": 18,
          "pDate": "2017-12-14",
          "info": "他是日本人",
          "sex": 1,
          "address": "广东广州"
        },
        "sort": [
          18,
          1,
          1.3612909,
          -9223372036854776000
        ]
      },
      {
        "_index": "edu",
        "_type": "student",
        "_id": "10",
        "_score": 0.20692043,
        "_source": {
          "name": "张三",
          "age": 18,
          "pDate": "2017-12-12",
          "info": "他是日本人",
          "sex": 1,
          "address": "广东广州",
          "orders": [
            1,
            2,
            3,
            5
          ]
        },
        "sort": [
          18,
          1,
          0.20692043,
          11
        ]
      },
      {
        "_index": "edu",
        "_type": "student",
        "_id": "9",
        "_score": 0.20692043,
        "_source": {
          "name": "张三",
          "age": 18,
          "pDate": "2017-12-12",
          "info": "他是日本人",
          "sex": 1,
          "address": "广东广州",
          "orders": [
            1,
            2,
            3,
            4
          ]
        },
        "sort": [
          18,
          1,
          0.20692043,
          10
        ]
      }
    ]
  }
}
3.5 Source filtering

对source存的内容进行过滤。默认时,会把source内容都显示出来。如果设置的_source为false时,_source内容就不会在查询时被返回了。
把_source设置为false:

GET /_search
{
    "_source": false,
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

_source也接收一个或多个通配符匹配去控制_source返回。

GET /_search
{
    "_source": [ "obj1.*", "obj2.*" ],
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

GET /_search
{
    "_source": {
        "includes": [ "obj1.*", "obj2.*" ],
        "excludes": [ "*.description" ]
    },
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}
3.6 stored fields

在mapping映射中明确指的stored列的查询。

GET /_search
{
    "stored_fields" : ["user", "postDate"],
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

使用stored filelds失效
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-stored-fields.html

GET /_search
{
    "stored_fields": "_none_",
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}
3.7 Script Fields

https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-script-fields.html
查询时,把一段脚本加入到列的查询。
一句话:就是用脚本去自定字段数据。脚本语言一般用Painless.这个脚本看官方文档5.5之后才有。
这个属于ES中Modules—>Scripting模块的内容。
关于painless脚本可查看这个教程:
《Getting Started with Painless》
https://www.elastic.co/guide/en/elasticsearch/painless/5.6/painless-getting-started.html
https://www.elastic.co/guide/en/elasticsearch/painless/5.6/painless-api-reference.html
《elasticsearch painless最强教程》
http://blog.csdn.net/u013613428/article/details/78134170

GET /_search
{
    "query" : {
        "match_all": {}
    },
    "script_fields" : {
        "test1" : {
            "script" : {
                "lang": "painless",
                "source": "doc['my_field_name'].value * 2"
            }
        },
        "test2" : {
            "script" : {
                "lang": "painless",
                "source": "doc['my_field_name'].value * factor",
                "params" : {
                    "factor"  : 2.0
                }
            }
        }
    }
}
3.8 Doc value Fields

这个要了解一下DocValues这个概念,
引入这个概念是通过牺牲一定的磁盘空间带来的好处主要有两个:
(1)节省内存;
(2)对排序,分组和一些聚合操作时能够大大提升性能;
DocValues其实是Lucene在构建索引时,会额外建立一个有序的基于document => field value的映射列表;
DocValue的引入是解决倒排在排序、分组、聚合所引起的内存问题[溢出、缓慢]提出的解决方案。在构建索引时会对开启docvalues的字段,额外构建一个已经排好序的文档到字段级别的一个列式存储映射,它减轻了在排序和分组时,对内存的依赖,而且大大提升了这个过程的性能,当然它也会耗费的一定的磁盘空间。
应该场景:
1,需要聚合的字段,包括sort,agg,group,facet等 ;
2,需要提供函数查询的字段;
3,需要高亮的字段,这个确实能加速;
4,需要参与自定义评分的字段;

GET /_search
{
    "query" : {
        "match_all": {}
    },
    "docvalue_fields" : ["test1", "test2"]
}
3.9 Post filter

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-post-filter.html
Post filter叫后置过滤器。
Post过滤器应用来过滤搜索结果,不过滤聚合。
post_filter元素是一个顶层元素,只会对搜索结果进行过滤。

GET /shirts/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": { "brand": "gucci" } 
      }
    }
  },
  "aggs": {
    "colors": {
      "terms": { "field": "color" } 
    },
    "color_red": {
      "filter": {
        "term": { "color": "red" } 
      },
      "aggs": {
        "models": {
          "terms": { "field": "model" } 
        }
      }
    }
  },
  "post_filter": { 
    "term": { "color": "red" }
  }
}
3.10 Highlighting

对搜索出来的内容进行高亮。

3.11 Rescoring

https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-rescore.html#_query_rescorer
分值重计算。
The query rescorer executes a second query only on the Top-K results returned by the queryand post_filter phases.
[Elasticsearch] 邻近匹配 (三) - 性能,关联单词查询以及Shingles
http://blog.csdn.net/dm_vincent/article/details/41978363 引用:
一个查询可能会匹配百万计的结果,但是我们的用户很可能只对前面几页结果有兴趣。
一个简单的match查询已经通过排序将含有所有搜索词条的文档放在结果列表的前面了。而我们只想对这些前面的结果进行重新排序来给予那些同时匹配了短语查询的文档额外的相关度。
也是一个查询之后的过滤问题:从结果进行重新过滤,并重新设置分值。例如:

POST /_search
{
   "query" : {
      "match" : {
         "message" : {
            "operator" : "or",
            "query" : "the quick brown"
         }
      }
   },
   "rescore" : {
      "window_size" : 50,
      "query" : {
         "rescore_query" : {
            "match_phrase" : {
               "message" : {
                  "query" : "the quick brown",
                  "slop" : 2
               }
            }
         },
         "query_weight" : 0.7,
         "rescore_query_weight" : 1.2
      }
   }
}
3.12 Search Type

https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-search-type.html

3.13 Scroll

主要用来滚页的。
像传统数据库中使用游标操作功能。
也要把一批数据读到内存,像游标一样操作,所以,数据不可永久放在内存,在使用scroll时是要指定一个时间的。
scroll 并不适合用来做实时搜索,而更适用于后台批处理任务,比如群发。
Table 1. 时间单位对照表

Time Units
y Year
M Month
w Week
d Day
h Hour
m Minute
s Second

它可能不是响应实现的请求,可是它处理大数据量。例如,它可以实现,由旧的索引重新去索引一个新的索引的操作。
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html
python api 中也提供这个操作:
http://elasticsearch-py.readthedocs.io/en/master/helpers.html

elasticsearch.helpers.scan(client, query=None, scroll=u'5m', raise_on_error=True,preserve_order=False, size=1000, request_timeout=None, clear_scroll=True, scroll_kwargs=None,**kwargs)

例子:
初始化操作:

POST /info/articles/_search?scroll=1m
{
    "size": 100,
    "query": {
        "match" : {
            "title" : "蛀牙 牙痛"
        }
    }
}

运行结果:

{
  "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAATnxFld1MXlzUmt3VHFTb19GT1dBcDNVT3cAAAAAAAE59RZXdTF5c1Jrd1RxU29fRk9XQXAzVU93AAAAAAABOfIWV3UxeXNSa3dUcVNvX0ZPV0FwM1VPdwAAAAAAATnzFld1MXlzUmt3VHFTb19GT1dBcDNVT3cAAAAAAAE59BZXdTF5c1Jrd1RxU29fRk9XQXAzVU93",
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
......
......
......

遍历操作:

POST  /_search/scroll
{
   "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAATnxFld1MXlzUmt3VHFTb19GT1dBcDNVT3cAAAAAAAE59RZXdTF5c1Jrd1RxU29fRk9XQXAzVU93AAAAAAABOfIWV3UxeXNSa3dUcVNvX0ZPV0FwM1VPdwAAAAAAATnzFld1MXlzUmt3VHFTb19GT1dBcDNVT3cAAAAAAAE59BZXdTF5c1Jrd1RxU29fRk9XQXAzVU93"
}
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 10.695598,
    "hits": []
  }
}

多次查询,那个id会变的,因为时间到了而失效。
分两步处理:初始化和遍历;
初始化后会返回一个id,id用来下次取数据用。
把读出来的数据,请求一次读取一次。

3.14 explain
GET info/_search
{
  "_source" : false,
  "size": 2,
  "query": {
    "match": {
      "title": "公安机关严打食品药品犯罪"
    }
  }
}

运行结果(拿了一个数据分支出来):

 {
        "_index": "kad_info",
        "_type": "articles",
        "_id": "14997",
        "_score": 19.89322
      },

对上面查询增加了”explain”:”true”(同样是一个数据分支):

       "_shard": "[kad_info][0]",
        "_node": "Wu1ysRkwTqSo_FOWAp3UOw",
        "_index": "kad_info",
        "_type": "articles",
        "_id": "14997",
        "_score": 19.89322,
        "_explanation": {
          "value": 19.89322,
          "description": "sum of:",
          "details": [
            {
              "value": 5.108964,
              "description": "weight(title:公安机关 in 10) [PerFieldSimilarity], result of:",
              "details": [
                {
                  "value": 5.108964,
                  "description": "score(doc=10,freq=1.0 = termFreq=1.0\n), product of:",
                  "details": [
                    {
                      "value": 4.8094707,
                      "description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
                      "details": [
                        {
                          "value": 1,
                          "description": "docFreq",
                          "details": []
                        },
                        {
                          "value": 183,
                          "description": "docCount",
                          "details": []
                        }
                      ]
                    },
                    {
                      "value": 1.0622716,
                      "description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:",
                      "details": [
                        {
                          "value": 1,
                          "description": "termFreq=1.0",
                          "details": []
                        },
                        {
                          "value": 1.2,
                          "description": "parameter k1",
                          "details": []
                        },
                        {
                          "value": 0.75,
                          "description": "parameter b",
                          "details": []
                        },
                        {
                          "value": 8.300547,
                          "description": "avgFieldLength",
                          "details": []
                        },
                        {
                          "value": 7.111111,
                          "description": "fieldLength",
                          "details": []
                        }
                      ]
                    }
                  ]
                }
              ]
            },
3.15 version

增加version,默认时version不显示出来的。

GET kad_info/_search
{
  "_source" : false,
  "version": true, 
  "query": {
    "match": {
      "title": "公安机关严打食品药品犯罪"
    }
  }
}

运行结果:

{
        "_index": "kad_info",
        "_type": "articles",
        "_id": "14997",
        "_version": 1,
        "_score": 19.89322
      },
3.16 min_score

设置一个最小分值。

GET kad_info/_search
{
  "_source" : false,
  "min_score":10,
  "query": {
    "match": {
      "title": "公安机关严打食品药品犯罪"
    }
  }
}

查询出来只有一条记录。

3.17 Index Boost
GET /_search
{
    "indices_boost" : [
        { "alias1" : 1.4 },
        { "index*" : 1.3 }
    ]
}

如果多个匹配上,第一个会被使用。如果索引在这两个索引中都找到,1.4的值的索引被使用。

3.18 Named Queries

Each filter and query can accept a _name in its top level definition.

GET /_search
{
    "query": {
        "bool" : {
            "should" : [
                {"match" : { "name.first" : {"query" : "shay", "_name" : "first"} }},
                {"match" : { "name.last" : {"query" : "banon", "_name" : "last"} }}
            ],
            "filter" : {
                "terms" : {
                    "name.last" : ["banon", "kimchy"],
                    "_name" : "test"
                }
            }
        }
    }
}
3.19 Inner hits

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-inner-hits.html
使用 Inner_hits 查询Parent-Child(父子)文档,可以把父子文档同时返回——既返回父文档,也返回匹配has-child条件的子文档,相当于在父子之间join了。
同时,也是查询Nested数据类型数据。
可参考:
《ElasticSearch 使用 Inner_hits 查询Parent-Child(父子)文档 - 1.50新特性http://blog.csdn.net/hereiskxm/article/details/44937455

3.20 Field Collapsing

字段折叠:可以理解就是按特定字段进行合并去重,比如我们有一个菜谱搜索,我希望按菜谱的“菜系”字段进行折叠,即返回结果每个菜系都返回一个结果,也就是按菜系去重,我搜索关键字“鱼”,要去返回的结果里面各种菜系都有,有湘菜,有粤菜,有中餐,有西餐,别全是湘菜,就是这个意思,通过按特定字段折叠之后,来丰富搜索结果的多样性。
而新的的字段折叠的方式是怎么实现的的呢,有这些要点:
1.折叠+取 inner_hits 分两阶段执行(组合聚合的方式只有一个阶段),所以 top hits 永远是精确的。
2.字段折叠只在 top hits 层执行,不需要每次都在完整的结果集上对为每个折叠主键计算实际的 doc values 值,只对 top hits 这小部分数据操作就可以,和 term agg 相比要节省很多内存。
3.因为只在 top hits 上进行折叠,所以相比组合聚合的方式,速度要快很多。
4.折叠 top docs 不需要使用全局序列(global ordinals)来转换 string,相比 agg 这也节省了很多内存。
5.分页成为可能,和常规搜索一样,具有相同的局限,先获取 from+size 的内容,再合并。
6.search_after 和 scroll 暂未实现,不过具备可行性。
7.折叠只影响搜索结果,不影响聚合,搜索结果的 total 是所有的命中纪录数,去重的结果数未知(无法计算)。
《Elasticsearch 5.x 字段折叠的使用》https://elasticsearch.cn/article/132

3.21 Search After

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-search-after.html

3.22 Search Template

这个采用mustache语法来定义模板,mustache一般是应用在前端上,如果深入学习,有一个mustache.js包可以学习。
Mustache 的模板语法:
http://mustache.github.io/mustache.5.html
{{data}}:{{}}就是 Mustache 的标示符,花括号里的 data 表示键名,这句的作用是直接输出与键名匹配的键值;
{{#data}} {{/data}}:以#开始、以/结束表示区块,它会根据当前上下文中的键值来对区块进行一次或多次渲染,例如改写下 Demo 中的 tpl;
{{^data}} {{/data}}:该语法与{{#data}} {{/data}}类似,不同在于它是当 data值为 null, undefined, false 时才渲染输出该区块内容。
{{.}}:表示枚举,可以循环输出整个数组
{{

参考

浅谈Lucene中的DocValues http://blog.csdn.net/u010454030/article/details/51365893

作者:happyprince, http://blog.csdn.net/ld326/article/details/79259407

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值