nested类型是一种特殊的对象object数据类型,允许对象数组彼此独立的进行索引和查询
_

对象数组如何扁平化

内部对象object字段的数组不能像我们所期望的那样工作。Lucene内部没有对象的概念,所以ES将对象层次结构扁平化为一个字段名称和值的简单列表:

  1. PUT /my_index/_doc/1?pretty
  2. {
  3. "group": "fans",
  4. "user": [
  5. {"first":"John", "last":"Smith"},
  6. {"first":"Alice", "last":"White"}
  7. ]
  8. }

user字段被动态的添加为object类型的字段,在其内部转化为一个看起来像下面这样的文档:

  1. {
  2. "group":"fans",
  3. "user.first":["John","Alice"],
  4. "user.last":["Smith","White"]
  5. }

user.first和user.last字段被扁平化为多值字段,并且alice和white质检的关联已经丢失。

以下文本文档将错误的匹配user.first和user.last为smith查询:

  1. GET /my_indx/_doc
  2. {
  3. "query":{
  4. "bool":{
  5. "must":[
  6. {"match":{"user.first":"Alice"}},
  7. {"match":{"user.last":"Smith"}}
  8. ]
  9. }
  10. }
  11. }
  12. //响应
  13. {
  14. "took": 2,
  15. "timed_out": false,
  16. "_shards": {
  17. "total": 1,
  18. "successful": 1,
  19. "skipped": 0,
  20. "failed": 0
  21. },
  22. "hits": {
  23. "total": {
  24. "value": 1,
  25. "relation": "eq"
  26. },
  27. "max_score": 0.5753642,
  28. "hits": [
  29. {
  30. "_index": "my_index",
  31. "_type": "_doc",
  32. "_id": "1",
  33. "_score": 0.5753642,
  34. "_source": {
  35. "group": "fans",
  36. "user": [
  37. {
  38. "first": "john",
  39. "last": "smith"
  40. },
  41. {
  42. "first": "alice",
  43. "last": "white"
  44. }
  45. ]
  46. }
  47. }
  48. ]
  49. }
  50. }

解释:可以看到,我们原本是想要查询用户名为Alice Smith的用户,实际文档中并不存在此用户,但是搜索还是错误的命中了此文档。

对对象数组使用嵌套字段

上面的例子描述了我们日常搜索的一些错误场景,但是我们要如何解决上面例子描述的问题呢?
如果需要索引对象数组并维护每个对象的独立性,则应对字段使用nested类型而不是使用object类型。
在内部,嵌套对象将数组中的每个对象作为单独的隐藏文档进行索引,这意味着每个嵌套对象都可以使用嵌套查询nested query独立于其他对象进行查询。

要实现netsted查询,我们首先要为文档设置正确的映射(mapping):

  1. DELETE /my_index
  2. PUT /my_index
  3. {
  4. "mappings": {
  5. "properties": {
  6. "user": {
  7. "type":"nested"
  8. }
  9. }
  10. }
  11. }
  12. PUT /my_index/_doc/1?pretty
  13. {
  14. "group": "fans",
  15. "user": [
  16. {"first":"John", "last":"Smith"},
  17. {"first":"Alice", "last":"White"}
  18. ]
  19. }

说明:此时user字段映射为nested类型,而不是默认的object类型

此时需要对bool查询进行一定的更改,再使用nested查询:

  1. GET /my_index/_serach?pretty
  2. {
  3. "query": {
  4. "nested": {
  5. "path":"user",
  6. "query": {
  7. "bool": {
  8. "must": [
  9. {"match":{"user.first":"Alice"}},
  10. {"match":{"user.last":"White"}},
  11. ]
  12. }
  13. }
  14. }
  15. }
  16. }
  17. //响应
  18. {
  19. "took": 2,
  20. "timed_out": false,
  21. "_shards": {
  22. "total": 1,
  23. "successful": 1,
  24. "skipped": 0,
  25. "failed": 0
  26. },
  27. "hits": {
  28. "total": {
  29. "value": 1,
  30. "relation": "eq"
  31. },
  32. "max_score": 1.3862942,
  33. "hits": [
  34. {
  35. "_index": "my_index",
  36. "_type": "_doc",
  37. "_id": "1",
  38. "_score": 1.3862942,
  39. "_source": {
  40. "group": "fans",
  41. "user": [
  42. {
  43. "first": "John",
  44. "last": "Smith"
  45. },
  46. {
  47. "first": "Alice",
  48. "last": "White"
  49. }
  50. ]
  51. }
  52. }
  53. ]
  54. }
  55. }

可以发现。此时Alice White用户文档则被正确的命中。