当前位置:   article > 正文

Elasticsearch 嵌套类型nested

elasticsearch 嵌套类型

1.背景介绍

        我们在使用Elasticsearch做搜索引擎的时候有可能会遇到跨domain查询的场景,比如做一个学生课程管理系统,搜一个学生的名字,像知道该学生的选课情况。

        当然解决问题的方法有很多,我可以搜学生,然后去db查找学生关联的选课,就可以查到所有的课程,有时候数据量不是很大,并且我的索引只有一个课程维度的时候,就需要使用嵌套类型来解决这类问题。本文使用es和kibina来操作实例,因为基于中文的实例,还使用到了ik分词器,具体可以参考:

Elasticsearch安装和使用

Elasticsearch中IK分词器的使用

2.对象类型

        Elasticsearch支持对象类型的存储,我们可以把一个对象数组存到某个document的字段内,比如一个课程作为一个document,那么这个课程可以建立一个students字段,存储该课程下的学生object数组。

       在Elasticsearch中,新建一个如下的class_test索引,其中student作为一个object数组类型。

  1. PUT /class_test
  2. {
  3. "mappings":{
  4. "class_test": {
  5. "properties": {
  6. "id": {
  7. "type": "keyword"
  8. },
  9. "name": {
  10. "analyzer": "ik_max_word",
  11. "type": "text"
  12. },
  13. "type":{
  14. "type":"keyword"
  15. },
  16. "student":{
  17. "properties": {
  18. "name":{
  19. "analyzer": "ik_max_word",
  20. "type": "text"
  21. },
  22. "id":{
  23. "type":"keyword"
  24. }
  25. }
  26. }
  27. }
  28. }
  29. },
  30. "settings":{
  31. "index": {
  32. "refresh_interval": "1s",
  33. "number_of_shards": 5,
  34. "max_result_window": "10000000",
  35. "mapper": {
  36. "dynamic": "false"
  37. },
  38. "number_of_replicas": 0
  39. }
  40. }
  41. }复制代码

        往class_test放入一下数据,现在索引里面一共有两条数据

  1. {
  2. "took" : 1,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 5,
  6. "successful" : 5,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : 2,
  12. "max_score" : 1.0,
  13. "hits" : [
  14. {
  15. "_index" : "class_test",
  16. "_type" : "class_test",
  17. "_id" : "ijfJ5GoBJeNZPNCWykLR",
  18. "_score" : 1.0,
  19. "_source" : {
  20. "id" : "1",
  21. "name" : "数学课",
  22. "student" : [
  23. {
  24. "id" : "1",
  25. "name" : "张三"
  26. },
  27. {
  28. "id" : "2",
  29. "name" : "李四"
  30. }
  31. ]
  32. }
  33. },
  34. {
  35. "_index" : "class_test",
  36. "_type" : "class_test",
  37. "_id" : "Q9NxGGsBa-TqHCWqAaM4",
  38. "_score" : 1.0,
  39. "_source" : {
  40. "id" : "2",
  41. "name" : "语文",
  42. "student" : [
  43. {
  44. "id" : "3",
  45. "name" : "杰克"
  46. },
  47. {
  48. "id" : "4",
  49. "name" : "玛丽"
  50. }
  51. ]
  52. }
  53. }
  54. ]
  55. }
  56. }复制代码

        接下来,我们可以使用查询语句对索引进行查询。当我们查询id为1的学生参见的课程的时候,可以查到数学课。

  1. GET /class_test/class_test/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "student.id": "1"
  9. }
  10. }
  11. ]
  12. }
  13. }
  14. }复制代码

  1. {
  2. "took" : 0,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 5,
  6. "successful" : 5,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : 1,
  12. "max_score" : 0.2876821,
  13. "hits" : [
  14. {
  15. "_index" : "class_test",
  16. "_type" : "class_test",
  17. "_id" : "ijfJ5GoBJeNZPNCWykLR",
  18. "_score" : 0.2876821,
  19. "_source" : {
  20. "id" : "1",
  21. "name" : "数学课",
  22. "student" : [
  23. {
  24. "id" : "1",
  25. "name" : "张三"
  26. },
  27. {
  28. "id" : "2",
  29. "name" : "李四"
  30. }
  31. ]
  32. }
  33. }
  34. ]
  35. }
  36. }
  37. 复制代码

    当我们查名字叫张三的学生参加的课程的时候,也能查到数学课。

  1. GET /class_test/class_test/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "student.name": "张三"
  9. }
  10. }
  11. ]
  12. }
  13. }
  14. }复制代码

  1. {
  2. "took" : 4,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 5,
  6. "successful" : 5,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : 1,
  12. "max_score" : 0.5753642,
  13. "hits" : [
  14. {
  15. "_index" : "class_test",
  16. "_type" : "class_test",
  17. "_id" : "ijfJ5GoBJeNZPNCWykLR",
  18. "_score" : 0.5753642,
  19. "_source" : {
  20. "id" : "1",
  21. "name" : "数学课",
  22. "student" : [
  23. {
  24. "id" : "1",
  25. "name" : "张三"
  26. },
  27. {
  28. "id" : "2",
  29. "name" : "李四"
  30. }
  31. ]
  32. }
  33. }
  34. ]
  35. }
  36. }复制代码

        但是当我们查询id为1并且名字叫李四的学生参加的课程时

  1. GET /class_test/class_test/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "student.name": "李四"
  9. }
  10. },
  11. {
  12. "match": {
  13. "student.id": "1"
  14. }
  15. }
  16. ]
  17. }
  18. }
  19. }复制代码

  1. {
  2. "took" : 6,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 5,
  6. "successful" : 5,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : 1,
  12. "max_score" : 0.8630463,
  13. "hits" : [
  14. {
  15. "_index" : "class_test",
  16. "_type" : "class_test",
  17. "_id" : "ijfJ5GoBJeNZPNCWykLR",
  18. "_score" : 0.8630463,
  19. "_source" : {
  20. "id" : "1",
  21. "name" : "数学课",
  22. "student" : [
  23. {
  24. "id" : "1",
  25. "name" : "张三"
  26. },
  27. {
  28. "id" : "2",
  29. "name" : "李四"
  30. }
  31. ]
  32. }
  33. }
  34. ]
  35. }
  36. }
  37. 复制代码

        我们发现,出来的结果也是数学课,这就有点奇怪,因为并没有一个id为1并且名字是李四的学生,那就不应该有这么课。这是怎么回事?原来在es内部,object数组类型会被打平,简单来说我们输入的数组,实际存储的类型是:

  1. "student.id":[1,2],
  2. "student.name":[张三,李四]复制代码

        所以倒排索引的建立,也是按照这种打平的逻辑。这个时候我们可以借助Elasticsearch内的嵌套类型来解决问题。

3.Nested类型

        和2中类似的,我们需要建一个测试索引,名字为class,不同的是student有了type字段,为 "type":"nested"。

  1. PUT /class
  2. {
  3. "mappings":{
  4. "class": {
  5. "properties": {
  6. "id": {
  7. "type": "keyword"
  8. },
  9. "name": {
  10. "analyzer": "ik_max_word",
  11. "type": "text"
  12. },
  13. "type":{
  14. "type":"keyword"
  15. },
  16. "student":{
  17. "type":"nested",
  18. "properties": {
  19. "name":{
  20. "analyzer": "ik_max_word",
  21. "type": "text"
  22. },
  23. "id":{
  24. "type":"keyword"
  25. }
  26. }
  27. }
  28. }
  29. }
  30. },
  31. "settings":{
  32. "index": {
  33. "refresh_interval": "1s",
  34. "number_of_shards": 5,
  35. "max_result_window": "10000000",
  36. "mapper": {
  37. "dynamic": "false"
  38. },
  39. "number_of_replicas": 0
  40. }
  41. }
  42. }复制代码

        我们导入相同的数据,然后用搜索id为1并且名字为李四的学生的课程,这个时候我们看到搜索结果为空:

  1. GET /class/class/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "nested": {
  8. "path": "student",
  9. "query": {
  10. "bool": {"must": [
  11. {
  12. "match": {
  13. "student.name": "李四"
  14. }
  15. },
  16. {
  17. "match": {
  18. "student.id": "1"
  19. }
  20. }
  21. ]}
  22. }
  23. }
  24. }
  25. ]
  26. }
  27. }
  28. }复制代码

  1. {
  2. "took" : 3,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 5,
  6. "successful" : 5,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : 0,
  12. "max_score" : null,
  13. "hits" : [ ]
  14. }
  15. }
  16. 复制代码

4.其他方式

        其实解决这种跨domain的搜索还有一些其他方式,对于嵌套类型,其实是非常消耗Elasticsearch的性能的,我们可以选择将需要搜索字段的值打平存一个字段,或者对学生单独建立一个索引,然后去学生-班级映射关系表查询班级。这一块后面有机会再做介绍。


转载于:https://juejin.im/post/5cf3cd576fb9a07ec56e6184

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/456806
推荐阅读
相关标签
  

闽ICP备14008679号