Jay Shah
Jay Shah

Reputation: 3771

ElasticSearch - Get only matching nested objects with All Top level fields in search response

let say I have following Document:

{
    id: 1,
    name: "xyz",
    users: [
        {
            name: 'abc',
            surname: 'def'
        },
        {
            name: 'xyz',
            surname: 'wef'
        },
        {
            name: 'defg',
            surname: 'pqr'
        }
    ]
}

I want to Get only matching nested objects with All Top level fields in search response. I mean If I search/filter for users with name 'abc', I want below response

{
    id: 1,
    name: "xyz",
    users: [
        {
            name: 'abc',
            surname: 'def'
        }
    ]
}

How can I do that?

Reference : select matching objects from array in elasticsearch

Upvotes: 31

Views: 17012

Answers (6)

Oleg Dmitrochenko
Oleg Dmitrochenko

Reputation: 579

I use the following body to get that result (I have set the full path to the values):

   {
  "_source": {
    "includes": [ "*" ],
    "excludes": [ "users" ]
  },
  "query": {
    "nested": {
      "path": "users",
      "inner_hits": {        
        "_source": [
          "users.name", "users.surname"
        ]
      },
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "users.name": "abc"
              }
            }
          ]
        }
      }
    }
  }
}

Also another way exists:


   {
  "_source": {
    "includes": [ "*" ],
    "excludes": [ "users" ]
  },
  "query": {
    "nested": {
      "path": "users",
      "inner_hits": {      
        "_source": false,
        "docvalue_fields": [
          "users.name", "users.surname"
        ]
      },
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "users.name": "abc"
              }
            }
          ]
        }
      }
    }
  }
}

See results in inner_hits of the result hits.

https://www.elastic.co/guide/en/elasticsearch/reference/7.15/inner-hits.html#nested-inner-hits-source

Upvotes: 0

Brijesh Wani
Brijesh Wani

Reputation: 61

In one of my projects, My expectation was to retrieve unique conversation messages text(inner fields like messages.text) having specific tags. So instead of using inner_hits, I used aggregation like below,

final NestedAggregationBuilder aggregation = AggregationBuilders.nested("parentPath", "messages").subAggregation(AggregationBuilders.terms("innerPath").field("messages.tag"));
        final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .addAggregation(aggregation).build();
        final Aggregations aggregations = elasticsearchOperations.search(searchQuery, Conversation.class).getAggregations();
        final ParsedNested parentAgg = (ParsedNested) aggregations.asMap().get("parentPath");
        final Aggregations childAgg = parentAgg.getAggregations();
        final ParsedStringTerms childParsedNested = (ParsedStringTerms) childAgg.asMap().get("innerPath");
        // Here you will get unique expected inner fields in key part.
        Map<String, Long> agg = childParsedNested.getBuckets().stream().collect(Collectors.toMap(Bucket::getKeyAsString, Bucket::getDocCount));

Upvotes: 0

vl4deee11
vl4deee11

Reputation: 81

You can make such a request, but the response will have internal fields starting with _

{
  "_source": {
    "includes": [ "*" ],
    "excludes": [ "users" ]
  },
  "query": {
    "nested": {
      "path": "users",
      "inner_hits": {},
      "query": {
        "bool": {
          "must": [
            { "match": { "users.name":  "abc" }}
          ]
        }
      }
    }
  }
}

Upvotes: 0

user8393839
user8393839

Reputation: 1

https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-inner-hits.html#nested-inner-hits-source

  "inner_hits": {
    "_source" : false,
    "stored_fields" : ["name", "surname"]
  }

but you may need to change mapping to set those fields as "stored_fields" , otherwise you can use "inner_hits": {} to get a result that not that perfect.

Upvotes: 0

Mathieu Giboulet
Mathieu Giboulet

Reputation: 61

Maybe late, I use nested sorting to limit element on my nested relation, here a example :

"sort": {
    "ouverture.periodesOuvertures.dateDebut": {
      "order": "asc",
      "mode": "min",
      "nested_filter": {
        "range": {
          "ouverture.periodesOuvertures.dateFin": {
            "gte": "2017-08-29",
            "format": "yyyy-MM-dd"
          }
        }
      },
      "nested_path": "ouverture.periodesOuvertures"
    }
  },

Since 5.5 ES (I think) you can use filter on nested query. Here a example of nested query filter I use:

{
            "nested": {
              "path": "ouverture.periodesOuvertures",
              "query": {
                "bool": {
                  "must": [
                    {
                      "range": {
                        "ouverture.periodesOuvertures.dateFin": {
                          "gte": "2017-08-29",
                          "format": "yyyy-MM-dd"
                        }
                      }
                    },
                    {
                      "range": {
                        "ouverture.periodesOuvertures.dateFin": {
                          "lte": "2017-09-30",
                          "format": "yyyy-MM-dd"
                        }
                      }
                    }
                  ],
                  "filter": [
                    {
                      "range": {
                        "ouverture.periodesOuvertures.dateFin": {
                          "gte": "2017-08-29",
                          "format": "yyyy-MM-dd"
                        }
                      }
                    },
                    {
                      "range": {
                        "ouverture.periodesOuvertures.dateFin": {
                          "lte": "2017-09-30",
                          "format": "yyyy-MM-dd"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }

Hope this can help ;)

Plus if you ES is not in the last version (5.5) inner_hits could slow your query Including inner hits drastically slows down query results

Upvotes: 1

Val
Val

Reputation: 217304

If you're ok with having all root fields except the nested one and then only the matching inner hits in the nested field, then we can re-use the previous answer like this by specifying a slightly more involved source filtering parameter:

{
  "_source": {
    "includes": [ "*" ],
    "excludes": [ "users" ]
  },
  "query": {
    "nested": {
      "path": "users",
      "inner_hits": {        <---- this is where the magic happens
        "_source": [
          "name", "surname"
        ]
      },
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "users.name": "abc"
              }
            }
          ]
        }
      }
    }
  }
}

Upvotes: 49

Related Questions