elasticsearch complex query on nested object - elasticsearch

i have a list of books, each book has nested tag:
"hits": [
{
"_index": "",
"_type": "",
"_id": "",
"_score": ,
"_source": {
"name": "book1",
"tags": [
{
"t": "tagA",
"w": 100
},
{
"t": "tagB",
"w": 0
},
],
"active": true,
}
},
{
"_index": "",
"_type": "",
"_id": "",
"_score": ,
"_source": {
"name": "book2",
"tags": [
{
"t": "tagA",
"w": 100
},
{
"t": "tagB",
"w": 0
},
],
"active": true,
}
},
{
"_index": "",
"_type": "",
"_id": "",
"_score": ,
"_source": {
"name": "book3",
"tags": [
{
"t": "tagC",
"w": 100
},
{
"t": "tagB",
"w": 0
},
],
"active": false,
}
}]
first, i tried to get all 'active' books with a specific tag, this can get by this query:
GET /index/type/_search
{
"query": {
"bool": {
"must_not": {"term" : { "active" : false}},
"must":
[
{
"nested": {
"path": "tags",
"query": {
"bool": {
"must": [
{
"match": {
"tags.t": "tagB"
}
}
]
}
}
}
}
]
}
}
}
for the above, book1 and book2 returned.
but what i am trying to get now is become more complicated.
i am trying to get 'active' books with a specific tag (tagB). but if 'tagC' is in book, then book can return also if it is not active.
so for this question, book1, book2, book3 will return.
how can i do this query in elasticsearch?

Try this, a should clause for both conditions
{
"query": {
"bool": {
"should": [
{
"nested": {
"path": "tags",
"query": {
"bool": {
"must": [
{
"match": {
"tags.t": "tagC"
}
}
]
}
}
}
},
{
"bool": {
"must": [
{
"term": {
"active": true
}
},
{
"nested": {
"path": "tags",
"query": {
"bool": {
"must": [
{
"match": {
"tags.t": "tagB"
}
}
]
}
}
}
}
]
}
}
]
}
}
}

Related

How to use multi where condition with elasticsearch

I'm using laravel + elasticsearch.
I have an array like this:
[
{
"title": "product_title",
"stocks": [
{
"country": "EN",
"stock": 0
},
{
"country": "IN",
"stock": 1
}
]
},
{
"title": "product_title_2",
"stocks": [
{
"country": "EN",
"stock": 1
},
{
"country": "IN",
"stock": 0
}
]
}
]
Now I want to find all objects has country equal EN and stock is greater than 1.
updated
my query:
{
"index": "products",
"body": {
"size": 15,
"from": 1,
"sort": [
{
"stock": {
"order": "desc"
}
}
],
"query": {
"bool": {
"must": [
{
"query_string": {
"query": "**",
"type": "best_fields",
"fields": [
"erp_id",
"title_en^2",
"translations.title^2",
"erp.title_en",
"erp.title",
"erp.options.title",
"erp.options.title_en"
],
"analyze_wildcard": true,
"allow_leading_wildcard": true
}
}
],
"filter": [
{
"term": {
"is_active": 1
}
},
{
"term": {
"shops.shop_id": 1
}
}
]
}
},
"aggs": {
"max_price": {
"filter": {
"term": {
"erp.price_lists.currency.abbr": "tmn"
}
},
"aggs": {
"result": {
"max": {
"field": "erp.price_lists.pivot.price_tt"
}
}
}
},
"min_price": {
"filter": {
"term": {
"erp.price_lists.currency.abbr": "tmn"
}
},
"aggs": {
"result": {
"min": {
"field": "erp.price_lists.pivot.price_tt"
}
}
}
}
}
}
}
You can use nested query along with inner_hits to get the object satisfying the requirements
Adding a working example
Index Mapping:
{
"mappings": {
"properties": {
"stocks": {
"type": "nested"
}
}
}
}
Index Data:
{
"title": "product_title_2",
"stocks": [
{
"country": "EN",
"stock": 1
},
{
"country": "IN",
"stock": 0
}
]
}
{
"title": "product_title",
"stocks": [
{
"country": "EN",
"stock": 0
},
{
"country": "IN",
"stock": 1
}
]
}
{
"title": "product_title_3",
"stocks": [
{
"country": "EN",
"stock": 2
},
{
"country": "IN",
"stock": 0
}
]
}
Search Query:
{
"query": {
"nested": {
"path": "stocks",
"query": {
"bool": {
"filter": [
{
"match": {
"stocks.country": "EN"
}
},
{
"range": {
"stocks.stock": {
"gt": 1
}
}
}
]
}
},
"inner_hits":{}
}
}
}
Search Result:
"hits": [
{
"_index": "67294405",
"_type": "_doc",
"_id": "3",
"_score": 0.0,
"_source": {
"title": "product_title_3",
"stocks": [
{
"country": "EN",
"stock": 2
},
{
"country": "IN",
"stock": 0
}
]
},
"inner_hits": {
"stocks": {
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 0.0,
"hits": [
{
"_index": "67294405",
"_type": "_doc",
"_id": "3",
"_nested": {
"field": "stocks",
"offset": 0
},
"_score": 0.0,
"_source": {
"country": "EN",
"stock": 2
}
}
]
}
}
}
}
]

Set of records with non existing nested field or conditiontional existance of a nested field

I'm trying to get a set of product documents where there is no "product_text.language_id": 1 record in product_texts object OR product_texts with an empty product_texts.description field with "product_text.language_id": 1. product_texts is the sub-document of products index.
I tried this below query but it returns some products with existing product_texts.description.
"bool": {
"must": [
{
"nested": {
"path": "product_texts",
"query": {
"bool": {
"should": [
{
"bool": {
"must": [
{
"term": {
"product_texts.language_id": 1
}
}
],
"must_not": [
{
"exists": {
"field": "product_texts.description"
}
}
]
}
},
{
"bool": {
"must_not": [
{
"bool": {
"must": [
{
"term": {
"product_texts.language_id": 1
}
}
]
}
}
]
}
}
]
}
}
}
}
]
}
Here is the product mapping;
properties: {
id: {
type: 'long',
},
...
product_texts: {
type: 'nested',
properties: {
description: {
type: 'text',
fields: {
keyword: {
type: 'keyword',
ignore_above: 256,
},
},
},
id: {
type: 'long',
},
language_id: {
type: 'long',
},
name: {
type: 'text',
fields: {
keyword: {
type: 'keyword',
ignore_above: 256,
},
},
},
},
},
Example complate set:
{
"hits": [
{
"_index": "products",
"_type": "_doc",
"_id": "131483",
"_score": null,
"_source": {
"product_texts": [
{
"description": "Some description",
"language_id": 1
},
{
"description": "Some description",
"language_id": 2
},
{
"description": "Some description",
"language_id": 3
}
]
}
},
{
"_index": "products",
"_type": "_doc",
"_id": "131484",
"_score": null,
"_source": {
"product_texts": [
{
"description": "Some description",
"language_id": 2
},
{
"description": "Some description",
"language_id": 3
}
]
}
}, {
"_index": "products",
"_type": "_doc",
"_id": "131485",
"_score": null,
"_source": {
"product_texts": [
{
"description": null,
"language_id": 1
},
{
"description": "Some description",
"language_id": 2
},
{
"description": "Some description",
"language_id": 3
}
]
}
}
]
}
Expected query result _ids 131484 and 131485, NOT 131483 because it has a record in product_texts with a not-null description and language_id 1.
This one does the job
{
"query": {
"bool": {
"should": [ /** OR condition **/
{
"nested": {
"path": "product_texts",
"query": {
"bool": { /** No change in the first condition **/
"must": [
{
"bool": {
"must": [
{
"term": {
"product_texts.language_id": 1
}
}
],
"must_not": [
{
"exists": {
"field": "product_texts.description"
}
}
]
}
}
]
}
}
}
},
{
"bool": { /** Negated the existence query with value - None of the nested array matches - that's where the real problem**/
"must_not": { /**Negating the logic **/
"nested": {
"path": "product_texts",
"query": {
"bool": {
"must": {
"term": {/**lang id 1 is present **/
"product_texts.language_id": 1
}
}
}
}
}
}
}
}
]
}
}
}
Please find the comments.
So your second condition: key with value should not be present.

elasticsearch terms on bool field not working

I have this query that returns always null :
{
"query": {
"bool": {
"should": {
"nested": {
"query": {
"bool": {
"must": [
{
"term": {
"old": false
}
}
]
}
},
"path": "jobOffers"
}
}
}
}
}
Here's what match all returns :
{
"hits": [{
"_index": "dev",
"_type": "recruitment",
"_id": "202837r",
"_score": 1,
"_routing": "202837",
"_parent": "202837",
"_source": {
"score": 1,
"jobOffers": [{
"jobId": "jksncdjkqsnhcjkqs",
"jobCompany": "company 1",
"jobTitle": "Comptable",
"old": false
}],
"totalCount": 1
}
},
{
"_index": "dev",
"_type": "recruitment",
"_id": "202838r",
"_score": 1,
"_routing": "202838",
"_parent": "202838",
"_source": {
"score": 1,
"jobOffers": [{
"jobId": "wxjkckjwxhcmlazdkklqjkcn",
"jobCompany": "company 2",
"jobTitle": "Commercial",
"old": false
},
{
"jobId": "lxjkckazdwxctrzadjkoo",
"jobCompany": "company 2",
"jobTitle": "Chargé de développement commercial",
"old": false
}
],
"totalCount": 2
},
...
}
I made sure I'am querying the right index and the right type. Is this behavior normal? How can I make it return the expected result?
In your query, you need to write jobOffers.old instead of just old
{
"query": {
"bool": {
"should": {
"nested": {
"query": {
"bool": {
"must": [
{
"term": {
"jobOffers.old": false <--- modify this
}
}
]
}
},
"path": "jobOffers"
}
}
}
}
}

How to narrow down the current aggregation context to a specific scope within set of documents returned from Filter Aggregation?

I have a nested object mapping, the sample data:
{
"_index": "simpleindex",
"_type": "games",
"_id": "AU_eC-Uzt6KxlUliF68N",
"_score": 1,
"_source": {
"continents": [
{
"name": "Asia",
"countries": [
{
"name": "India",
"states": [
{
"name": "TN",
"game": "soccor",
"wins": 1
}
]
},
{
"name": "India",
"states": [
{
"name": "KA",
"game": "soccor",
"wins": 1
}
]
}
]
}
]
}
},
{
"_index": "simpleindex",
"_type": "games",
"_id": "AU_eCf5dt6KxlUliF637",
"_score": 1,
"_source": {
"continents": [
{
"name": "Asia",
"countries": [
{
"name": "India",
"states": [
{
"name": "TN",
"game": "soccor",
"wins": 1
}
]
}
]
}
]
}
},
{
"_index": "simpleindex",
"_type": "games",
"_id": "AU_eDIdXt6KxlUliF69i",
"_score": 1,
"_source": {
"continents": [
{
"name": "Asia",
"countries": [
{
"name": "India",
"states": [
{
"name": "TN",
"game": "soccor",
"wins": 1
}
]
},
{
"name": "India",
"states": [
{
"name": "KA",
"game": "soccor",
"wins": 1
}
]
},
{
"name": "Pak",
"states": [
{
"name": "NA",
"game": "soccor",
"wins": 1
}
]
}
]
}
]
}
}
Here is my Filtered Aggregation that returns documents that matches the filter criteria (i.e. continent should be 'Asia' AND country should be 'India'):
{
"aggs": {
"DocumentSet": {
"filter": {
"and": {
"filters": [
{
"nested": {
"path": "continents",
"query": {
"match": {
"continents.name": "asia"
}
}
}
},
{
"nested": {
"path": "continents.countries",
"query": {
"match": {
"continents.countries.name": "india"
}
}
}
}
]
}
},
"aggs": {
"continents": {
"nested": {
"path": "continents"
},
"aggs": {
"countries": {
"nested": {
"path": "continents.countries"
},
"aggs": {
"states": {
"nested": {
"path": "continents.countries.states"
},
"aggs": {
"count": {
"value_count": {
"field": "continents.countries.states.wins"
}
}
}
}
}
}
}
}
}
}}}
And here is the result (copy pasted only the aggregation here):
"aggregations": {
"DocumentSet": {
"doc_count": 3,
"continents": {
"doc_count": 3,
"countries": {
"doc_count": 6,
"states": {
"doc_count": 6,
"count": {
"value": 6
}
}
}
}
}
}
My intention is to get "wins" only from continents.name=asia AND countries.name=india. The filter works as expected but I need to narrow down the aggregation scope only to countries.name=india; essentially another level of scope on the docs returned by Filter aggregation so that leaf aggregation count is 5 instead of 6.
Try this aggregation:
{
"aggs": {
"continents": {
"nested": {
"path": "continents"
},
"aggs": {
"asia_continent": {
"filter": {
"query": {
"match": {
"continents.name": "asia"
}
}
},
"aggs": {
"countries": {
"nested": {
"path": "continents.countries"
},
"aggs": {
"india_country": {
"filter": {
"query": {
"match": {
"continents.countries.name": "india"
}
}
},
"aggs": {
"states": {
"nested": {
"path": "continents.countries.states"
},
"aggs": {
"count": {
"value_count": {
"field": "continents.countries.states.wins"
}
}
}
}
}
}
}
}
}
}
}
}
}
}

Filter an array of dictionaries that all must contain all of specified values

Say I had this document:
{
"_index": "food",
"_type": "recipes",
"_id": "AU2LjsMLOuShTUj_LBrT",
"_score": 1,
"_source": {
"name": "granola bars",
"ingredients": [
{
"name": "butter",
"quantity": 4
},
{
"name": "granola",
"quantity": 6
}
]
}
}
Using the following filter matches this document fine:
POST /food/recipes/_search
{
"query": {
"filtered": {
"query": {
"match_all": { }
},
"filter": {
"nested": {
"path": "ingredients",
"filter": {
"bool": {
"must": [
{
"terms": {
"ingredients.name": [
"butter",
"granola"
]
}
}
]
}
}
}
}
}
}
}
However it will also match documents that have additional ingredients.
How can I query so that it will only match documents that only have the ingredients butter and granola?
You need a "double negative", so to speak. You want to match parent documents that have nested docs that match your query, and no nested documents that don't match your query.
To test I set up the following index:
PUT /test_index
{
"settings": {
"number_of_shards": 1
},
"mappings": {
"doc": {
"properties": {
"ingredients": {
"type": "nested",
"properties": {
"name": {
"type": "string"
},
"quantity": {
"type": "long"
}
}
},
"name": {
"type": "string"
}
}
}
}
}
And added these two documents:
PUT /test_index/doc/1
{
"name": "granola bars",
"ingredients": [
{
"name": "butter",
"quantity": 4
},
{
"name": "granola",
"quantity": 6
}
]
}
PUT /test_index/doc/2
{
"name": "granola cookies",
"ingredients": [
{
"name": "butter",
"quantity": 5
},
{
"name": "granola",
"quantity": 7
},
{
"name": "milk",
"quantity": 2
},
{
"name": "sugar",
"quantity": 7
}
]
}
Your query returns both the documents. For the purposes of this question, to make it easier to understand, I first simplified your query a little:
POST /test_index/doc/_search
{
"query": {
"filtered": {
"query": {
"match_all": {}
},
"filter": {
"nested": {
"path": "ingredients",
"filter": {
"terms": {
"ingredients.name": [
"butter",
"granola"
]
}
}
}
}
}
}
}
Then I added an outer "bool" with two "nested" filters. One is the filter you originally had inside a "must", and the second is the opposite of the filter you had (so it will match nested documents that do NOT contain those terms), inside a "must_not":
POST /test_index/doc/_search
{
"query": {
"filtered": {
"query": {
"match_all": {}
},
"filter": {
"bool": {
"must": [
{
"nested": {
"path": "ingredients",
"filter": {
"terms": {
"ingredients.name": [
"butter",
"granola"
]
}
}
}
}
],
"must_not": [
{
"nested": {
"path": "ingredients",
"filter": {
"not": {
"filter": {
"terms": {
"ingredients.name": [
"butter",
"granola"
]
}
}
}
}
}
}
]
}
}
}
}
}
This returns only the one doc:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "test_index",
"_type": "doc",
"_id": "1",
"_score": 1,
"_source": {
"name": "granola bars",
"ingredients": [
{
"name": "butter",
"quantity": 4
},
{
"name": "granola",
"quantity": 6
}
]
}
}
]
}
}
Here is all the code I used for testing it:
http://sense.qbox.io/gist/e5fd0c35070fb329d40ad342b3198695e6f52d3a

Resources