Has parent query

The has_parent query returns child documents whose parent documents match a specific query. You can establish parent/child relationships between documents in the same index by using a join field type.

The has_parent query is slower than other queries because of the join operation it performs. Performance decreases as the number of matching parent documents increases. Each has_parent query in your search may significantly impact query performance. If you prioritize speed, avoid using this query or limit its usage as much as possible.

Example

Before you can run a has_parent query, your index must contain a join field in order to establish parent/child relationships. The index mapping request uses the following format:

  1. PUT /example_index
  2. {
  3. "mappings": {
  4. "properties": {
  5. "relationship_field": {
  6. "type": "join",
  7. "relations": {
  8. "parent_doc": "child_doc"
  9. }
  10. }
  11. }
  12. }
  13. }

copy

For this example, first configure an index that contains documents representing products and their brands as described in the has_child query example.

To search for the child of a parent, use a has_parent query. The following query returns child documents (products) made by the brand matching the query economy:

  1. GET testindex1/_search
  2. {
  3. "query" : {
  4. "has_parent": {
  5. "parent_type":"brand",
  6. "query": {
  7. "match" : {
  8. "name": "economy"
  9. }
  10. }
  11. }
  12. }
  13. }

copy

The response returns all products made by the brand:

  1. {
  2. "took": 11,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 2,
  13. "relation": "eq"
  14. },
  15. "max_score": 1,
  16. "hits": [
  17. {
  18. "_index": "testindex1",
  19. "_id": "4",
  20. "_score": 1,
  21. "_routing": "2",
  22. "_source": {
  23. "name": "Electronic watch",
  24. "sales_count": 300,
  25. "product_to_brand": {
  26. "name": "product",
  27. "parent": "2"
  28. }
  29. }
  30. },
  31. {
  32. "_index": "testindex1",
  33. "_id": "5",
  34. "_score": 1,
  35. "_routing": "2",
  36. "_source": {
  37. "name": "Digital watch",
  38. "sales_count": 100,
  39. "product_to_brand": {
  40. "name": "product",
  41. "parent": "2"
  42. }
  43. }
  44. }
  45. ]
  46. }
  47. }

Retrieving inner hits

To return parent documents that matched the query, provide the inner_hits parameter:

  1. GET testindex1/_search
  2. {
  3. "query" : {
  4. "has_parent": {
  5. "parent_type":"brand",
  6. "query": {
  7. "match" : {
  8. "name": "economy"
  9. }
  10. },
  11. "inner_hits": {}
  12. }
  13. }
  14. }

copy

The response contains parent documents in the inner_hits field:

  1. {
  2. "took": 11,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 2,
  13. "relation": "eq"
  14. },
  15. "max_score": 1,
  16. "hits": [
  17. {
  18. "_index": "testindex1",
  19. "_id": "4",
  20. "_score": 1,
  21. "_routing": "2",
  22. "_source": {
  23. "name": "Electronic watch",
  24. "sales_count": 300,
  25. "product_to_brand": {
  26. "name": "product",
  27. "parent": "2"
  28. }
  29. },
  30. "inner_hits": {
  31. "brand": {
  32. "hits": {
  33. "total": {
  34. "value": 1,
  35. "relation": "eq"
  36. },
  37. "max_score": 1.3862942,
  38. "hits": [
  39. {
  40. "_index": "testindex1",
  41. "_id": "2",
  42. "_score": 1.3862942,
  43. "_source": {
  44. "name": "Economy brand",
  45. "product_to_brand": "brand"
  46. }
  47. }
  48. ]
  49. }
  50. }
  51. }
  52. },
  53. {
  54. "_index": "testindex1",
  55. "_id": "5",
  56. "_score": 1,
  57. "_routing": "2",
  58. "_source": {
  59. "name": "Digital watch",
  60. "sales_count": 100,
  61. "product_to_brand": {
  62. "name": "product",
  63. "parent": "2"
  64. }
  65. },
  66. "inner_hits": {
  67. "brand": {
  68. "hits": {
  69. "total": {
  70. "value": 1,
  71. "relation": "eq"
  72. },
  73. "max_score": 1.3862942,
  74. "hits": [
  75. {
  76. "_index": "testindex1",
  77. "_id": "2",
  78. "_score": 1.3862942,
  79. "_source": {
  80. "name": "Economy brand",
  81. "product_to_brand": "brand"
  82. }
  83. }
  84. ]
  85. }
  86. }
  87. }
  88. }
  89. ]
  90. }
  91. }

For more information about retrieving inner hits, see Inner hits.

Parameters

The following table lists all top-level parameters supported by has_parent queries.

ParameterRequired/OptionalDescription
parent_typeRequiredSpecifies the name of the parent relationship as defined in the join field mapping.
queryRequiredThe query to run on parent documents. If a parent document matches the query, the child document is returned.
ignore_unmappedOptionalIndicates whether to ignore unmapped parent_type fields and not return documents instead of throwing an error. You can provide this parameter when querying multiple indexes, some of which may not contain the parent_type field. Default is false.
scoreOptionalIndicates whether the relevance score of a matching parent document is aggregated into its child documents. If false, then the relevance score of the parent document is ignored, and each child document is assigned a relevance score equal to the query’s boost, which defaults to 1. If true, then the relevance score of the matching parent document is aggregated into the relevance scores of its child documents. Default is false.
inner_hitsOptionalIf provided, returns the underlying hits (parent documents) that matched the query.

Sorting limitations

The has_parent query does not support sorting results using standard sorting options. If you need to sort child documents by fields in their parent documents, you can use a function_score query and sort by the child document’s score.

For the preceding example, first add a customer_satisfaction field by which you’ll sort the child documents belonging to the parent (brand) documents:

  1. PUT testindex1/_doc/1
  2. {
  3. "name": "Luxury watch brand",
  4. "product_to_brand" : "brand",
  5. "customer_satisfaction": 4.5
  6. }

copy

  1. PUT testindex1/_doc/2
  2. {
  3. "name": "Economy watch brand",
  4. "product_to_brand" : "brand",
  5. "customer_satisfaction": 3.9
  6. }

copy

Now you can sort child documents (products) based on the customer_satisfaction field of their parent brands. This query multiplies the score by the customer_satisfaction field of the parent documents:

  1. GET testindex1/_search
  2. {
  3. "query": {
  4. "has_parent": {
  5. "parent_type": "brand",
  6. "score": true,
  7. "query": {
  8. "function_score": {
  9. "script_score": {
  10. "script": "_score * doc['customer_satisfaction'].value"
  11. }
  12. }
  13. }
  14. }
  15. }
  16. }

copy

The response contains the products, sorted by the highest parent customer_satisfaction:

  1. {
  2. "took": 11,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 3,
  13. "relation": "eq"
  14. },
  15. "max_score": 4.5,
  16. "hits": [
  17. {
  18. "_index": "testindex1",
  19. "_id": "3",
  20. "_score": 4.5,
  21. "_routing": "1",
  22. "_source": {
  23. "name": "Mechanical watch",
  24. "sales_count": 150,
  25. "product_to_brand": {
  26. "name": "product",
  27. "parent": "1"
  28. }
  29. }
  30. },
  31. {
  32. "_index": "testindex1",
  33. "_id": "4",
  34. "_score": 3.9,
  35. "_routing": "2",
  36. "_source": {
  37. "name": "Electronic watch",
  38. "sales_count": 300,
  39. "product_to_brand": {
  40. "name": "product",
  41. "parent": "2"
  42. }
  43. }
  44. },
  45. {
  46. "_index": "testindex1",
  47. "_id": "5",
  48. "_score": 3.9,
  49. "_routing": "2",
  50. "_source": {
  51. "name": "Digital watch",
  52. "sales_count": 100,
  53. "product_to_brand": {
  54. "name": "product",
  55. "parent": "2"
  56. }
  57. }
  58. }
  59. ]
  60. }
  61. }

Next steps