In Elasticsearch, how do I search string on multiple fields from multi-level nested objects - elasticsearch

In Elasticsearch 6, I have data with nested objects like this:
{
"brands" :
[
{
"brand_name" : "xyz",
"products" :
[
{
"title" : "test",
"mrp" : 100,
"sp" : 90,
"status" : 1
},
{
"title" : "test1",
"mrp" : 50,
"sp" : 45,
"status" : 1
}
]
},
{
"brand_name" : "aaa",
"products" :
[
{
"title" : "xyz",
"mrp" : 100,
"sp" : 90,
"status" : 1
},
{
"title" : "abc",
"mrp" : 50,
"sp" : 45,
"status" : 1
}
]
}
]
}
I want to search from either from the field brand_name or from the field title. And I want return all results in same inner_hits.
For example : If I input the search string as "xyz" it should return both brands object with correspondent product object.
If I input the search string as "test" it should return only first brand array with only first product object.
How can I achieve this. Any ideas?
I have tried with the nested path query like this:
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "brands",
"query": {
"bool": {
"should": [
{
"term": {
"brands.brand_name": "xyz"
}
},
{
"term": {
"brands.brand_name.keyword": "aaa"
}
},
{
"nested": {
"path": "brands.products",
"query": {
"bool": {
"should": [
{
"match": {
"brands.products.title": "xyz"
}
}
]
}
},
"inner_hits": {}
}
}
]
}
},
"inner_hits": {}
}
}
]
}
}
}
But this query returning with multiple inner_hits response with multiple array objects for each brands and for each products.
I want the response like all brand names which is matching with the string should list under one array and all the products should list under another array under same inner_hits.

Since you want the inner hits to be different based on where the match has happened i.e. brands.brand_name or brands.products.title, you can have two queries one for brand name and other for product title as independent nested queries. These queries then should be inside should clause of a bool query. Each of the nested query should have its own inner_hits as below:
{
"query": {
"bool": {
"should": [
{
"nested": {
"path": "brands",
"inner_hits": {},
"query": {
"term": {
"brands.brand_name.keyword": "test"
}
}
}
},
{
"nested": {
"path": "brands.products",
"inner_hits": {},
"query": {
"term": {
"brands.products.title": "test"
}
}
}
}
]
}
},
"_source": false
}

Related

Query documents that contains all values in nested array Elasticsearch

I'm trying to query documents where the nested array contains all of the elements passed in the query.
The index stores groups and each group has a list of members. I want to query all the groups that contains the given members.
Mapping:
"properties" : {
"members" : {
"type" : "nested",
"properties" : {
"name" : {
"type" : "keyword"
}
}
},
"name" : {
"type" : "text"
}
}
}
}
Example content:
[
{
"name" : "group 1",
"members" : [
{
"name" : "alice"
},
{
"name" : "bob"
}
]
},
{
"name" : "group 2",
"members" : [
{
"name" : "alice"
},
{
"name" : "foo"
},
{
"name" : "bob"
}
]
},
{
"name" : "group 3",
"members" : [
{
"name" : "foo"
},
{
"name" : "bar"
}
]
}
]
How can I find all groups that have both "alice" and "foo" as members?
I have tried the following query but it returns nothing:
GET /group/_search
{
"query": {
"nested": {
"path": "members",
"query": {
"bool": {
"must": [
{"match": {"members.name": "alice"}},
{"match": {"members.name": "foo"}}
]
}
}
}
}
}
I have also tried with term instead of match but it gives no results.
You can use the nested within a must clause. Like this:
GET /group/_search
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "members",
"query": {
"term": {
"members.name": {
"value": "alice"
}
}
}
}
},
{
"nested": {
"path": "members",
"query": {
"term": {
"members.name": {
"value": "foo"
}
}
}
}
}
]
}
}
}

AND query nested objects

Full disclaimer: elasticsearch noob here.
I'm using the nested field value_per_id for an array of objects. Each object has the properties: value and id.
E.g.
"value_per_id": [
{
"id": 2,
"value": "positive"
},
{
"id": 23,
"value": "positive"
},
{
"id": 65,
"value": "neutral"
}
]
I have a query that looks like this (edited for clarity):
{
"query" : {
"bool" : {
"filter" : [
{
"bool" : {
"must" : {
"nested" : {
"path" : "value_per_id",
"query" : [
{"terms" : {"value_per_id.value" : <MY_VALUES>}},
{"terms" : {"value_per_id.id" : <MY_IDS>}},
]
}
}
}
}
]
}
}
}
With this query, I get all the elements that have an object with value in MY_VALUES OR id in MY_IDS.
I want all the elements that have value in MY_VALUES for any id in MY_IDS.
(E.g. if MY_VAULES = ['positive', 'neutral'] and MY_IDS = [1, 2], I want those that have an object with 1 - positive, 1 - negative, 2 - positive or 2 - negative).
What's the syntax for this?
EDIT - Wrong format
You need to use the bool within the nested query like this:
{
"query": {
"bool": {
"filter": [{
"nested": {
"path": "value_per_id",
"query": {
"bool": {
"must": [
{"term": {"value_per_id.value": 1}},
{"term": {"value_per_id.id": 2}}
]
}
}
}
}]
}
}
}
Use must or filter in the bool query.
If you use must, the sub queries will contribute to the score, if you use filter they won't. Also, if you use filter elasticsearch will be able to cache the results.
{
"bool": {
"must": [
{
"nested": {
"path": "value_per_id",
"query": {
"terms": {
"value_per_id.value": <MY_VALUES>
}
}
}
},
{
"nested": {
"path": "value_per_id",
"query": {
"terms": {
"value_per_id.id": <MY_IDS>
}
}
}
}
]
}
}

Nested Elasticsearch queries

I'm trying to create a nested query which would filter out some documents with specific terms. In this case I'm trying to filter out documents which have matching terms in user.first. Data example:
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Tim",
"last" : "White"
}
]
},
{
"group" : "fans",
"user" : [
{
"first" : "Jim",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "Black"
}
]
}
My query doesn't get the desired result as it returns me all records which are not filtered. I tried querying using:
"query": {
"bool": {
"must_not": [
{
"nested": {
"query": {
"terms": {
"user.first": [
"John",
"Thomas"
]
}
},
"path": "user"
}
}
]
}
}
I expect here to get documents which don't match with the filter. In this case it should return only the second document. What is the right way to do this?
After a lot of trial and error I've managed to find a solution for the problem. This is the query that filters out documents we don't need.
"query": {
"bool": {
"must_not": [
{
"bool": {
"filter": [
{
"nested": {
"query": {
"terms": {
"user.first": [
"John",
"Thomas"
]
}
},
"path": "user"
}
}
]
}
}
]
}
}

Elasticsearch Nested object Search with AND Condition

Here is my data in Elasticsearch - I kept Tags as nested object
PUT myblog/3
{
"id" : 10003,
"tags" : [
{
"tag" : 45647
},
{
"tag" : 45648
}
]
}
PUT myblog/4
{
"id" : 10004,
"tags" : [
{
"tag" : 45647
}
]
}
PUT myblog/5
{
"id" : 10005,
"tags" : [
{
"tag" : 45648
}
]
}
I want to retrieve documents with tag is 45647 & 45648. I tried it in this way
GET myblog/_search
{
"query": {
"nested": {
"path": "tags",
"query": {
"bool": {
"must": [
{ "match": { "tags.tag": 45648}},
{ "match": { "tags.tag": 45647}}
]
}
}
}
}
}
But it is not returning the expected result. What is missing in my query to get expected result?
You're almost there. You need two nested clauses since each nested element is a different document underneath. Try like this:
GET myblog/_search
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "tags",
"query": {
"match": {
"tags.tag": 45648
}
}
}
},
{
"nested": {
"path": "tags",
"query": {
"match": {
"tags.tag": 45647
}
}
}
}
]
}
}
}

Elastic sorting by most matches of filtered array

I have documents like:
{
id : 1,
title : One,
tags : {
{id : 1, title : One},
{id : 2, title : Two},
{id : 3, title : Three},
}
},
{
id : 2,
title : Two,
tags : {
{id : 1, title : One},
{id : 4, title : Four},
{id : 5, title : Five},
}
},
{
id : 3,
title : Three,
tags : {
{id : 1, title : One},
{id : 2, title : Two},
{id : 4, title : Four},
}
}
I'm filtering by first item's tags.id:
{
"query": {
"filtered": {
"filter": {
"bool": {
"must_not": {
"ids": {
"values": [1]
}
},
"should": [
{
"term": {
"tags.id": "1"
}
},
{
"term": {
"tags.id": "2"
}
},
{
"term": {
"tags.id": "3"
}
}
]
}
}
}
},
"track_scores": true,
"size": 20,
"sort": {
"_score": "desc"
}
}
Is there any way to get result ordered by most matching tags? In this case item Three (2 matches) should be first and then item Two (1 match).
It seems if I use filter without a query, then the score is 1 for all items.
What about this:
{
"query" : {
"bool": {
"must_not": {
"ids": {
"values": [1]
}
},
"should": [
{
"constant_score" : {
"filter" : {
"term": {
"tags.id": "1"
}
}
}
},
{
"constant_score" : {
"filter" : {
"term": {
"tags.id": "2"
}
}
}
},
{
"constant_score" : {
"filter" : {
"term": {
"tags.id": "3"
}
}
}
}
]
}
}
}
This query will ensure that the record with id = 1 is not on of the results and that the results are ordered such that the results with more matching tags come before the results with fewer matching tags.
Based on what you are looking for in the description you provided so far, I don't think the filtered query is necessary. The must_not clause will filter out the undesired result. The bool query with its defaults will handle the ordering you want.
Probably #eemp's answer would work too, but I prefer to leave filter where possible, so it wouldn't be taken in to account on score count. So I moved tgas filter to query.
{
"query": {
"filtered": {
"filter": {
"bool": {
"must_not": {
"ids": {
"values": [1]
}
}
}
},
"query": {
"bool": {
"should": [
{
"term": {
"tags.id": "1"
}
},
{
"term": {
"tags.id": "2"
}
},
{
"term": {
"tags.id": "3"
}
}
]
}
}
}
},
"size": 20
}

Resources