Elastic sorting by most matches of filtered array - sorting

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
}

Related

Elastic search combine must and must_not

I have a document that holds data for a product the mapping is as follow:
"mappings" : {
"properties" : {
"view_score" : {
"positive_score_impact" : true,
"type" : "rank_feature"
},
"recipients" : {
"dynamic" : false,
"type" : "nested",
"enabled" : true,
"properties" : {
"type" : {
"similarity" : "boolean",
"type" : "keyword"
},
"title" : {
"type" : "text",
"fields" : {
"key" : {
"type" : "keyword"
}
}
}
}
}
}
}
And I have 2 documents with the following data:
{
"view_score": 10,
"recipients": [{"type":"gender", "title":"male"}, {"type":"gender", "title":"female"}]
}
{
"view_score": 10,
"recipients": [{"type":"gender", "title":"female"}]
}
When a user searches for a product she can say "I prefer products for females" so The products which specifies gender as just female should come before products that specifies gender as male and female both.
I have the following query which gives more score to products with just female gender:
GET _search
{
"sort": [
"_score"
],
"query": {
"script_score": {
"query": {
"bool": {
"should": [
{
"nested": {
"path": "recipients",
"ignore_unmapped": true,
"query": {
"bool": {
"boost": 10,
"must": [
{
"term": {
"recipients.type": "gender"
}
},
{
"match": {
"recipients.title": "female"
}
}
],
"must_not": {
"bool": {
"filter": [
{
"term": {
"recipients.type": "gender"
}
},
{
"match": {
"recipients.title": "male"
}
}
]
}
}
}
}
}
}
]
}
},
"script": {
"source": "return _score;"
}
}
}
}
But if I add another query to should query it won't behave the same and gives the same score to products with one or two genders in their specifications.
here is my final query which wont work as expected:
GET _search
{
"sort": [
"_score"
],
"query": {
"script_score": {
"query": {
"bool": {
"should": [
{
"rank_feature": {
"field": "view_score",
"linear": {}
}
},
{
"nested": {
"path": "recipients",
"ignore_unmapped": true,
"query": {
"bool": {
"boost": 10,
"must": [
{
"term": {
"recipients.type": "gender"
}
},
{
"match": {
"recipients.title": "female"
}
}
],
"must_not": {
"bool": {
"filter": [
{
"term": {
"recipients.type": "gender"
}
},
{
"match": {
"recipients.title": "male"
}
}
]
}
}
}
}
}
}
]
}
},
"script": {
"source": "return _score;"
}
}
}
}
So my problem is how to combine these should clause together to give more weight to the products that specify only one gender.

How to pass array to ElasticSearch search template using mustache?

This is a part of my search query template:
"query": {
"bool": {
"must": [
{
"terms": {
"platform_id": [
"{{#platform_ids}}",
"{{.}}",
"{{/platform_ids}}"
]
}
}
],
This solution was taken from the elasticsearch official website.
I have this query:
GET _render/template
{
"id": "fddf",
"params": {
"query_string": "tinders",
"platform_ids": [1, 2]
}
}
And this results with this:
"terms" : {
"platform_id" : [
"",
"1",
"",
"2",
""
]
}
But I need this:
"terms" : {
"platform_id" : [
1, 2
]
}
Couldn't find a solution for this.
You need to modify your template as shown below
GET _render/template
{
"source": "{\"query\":{\"bool\":{\"must\":[{\"terms\":{\"platform_id\":[{{#platform_id}}{{value}}{{#comma}},{{/comma}}{{/platform_id}}]}}]}}}",
"params": {
"platform_id": [
{
"value": 1,
"comma": true
},
{
"value": 2
}
]
}
}
Result would be
{
"template_output": {
"query": {
"bool": {
"must": [
{
"terms": {
"platform_id": [
1,
2
]
}
}
]
}
}
}
}
Refer to this SO answer to get a detailed explanation
Request:
GET _render/template
{
"source": "{ \"query\": { \"terms\": {{#toJson}}bids{{/toJson}} }}",
"params": {
"bids":[1,2]
}
}
Response:
{
"template_output" : {
"query" : {
"terms" : [
1,
2
]
}
}
}
Try below:
"query": {
"bool": {
"must": [
{
"terms": {
"platform_id": {{#toJson}}platform_id{{/toJson}}
}
}
],

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>
}
}
}
}
]
}
}

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

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
}

Elasticsearch - OR in term conditions

I need little help with transfering mysql query to ES. The query looks like this
SELECT * FROM `xyz` WHERE visibility IN (1,2) AND (active=0 OR (active=1 AND finished=1)
It's easy, to make only AND conditions, but how to mix AND with OR in term?
"query" : {
"bool" : {
"must" : [{
"terms" : { "visibility" : ["1", "2"] }
}, {
"term" : { "active" : "1" }
}, {
"term" : { "active" : "0", "finished" : "1" } // OR
},]
}
}
Try like this by nesting a bool/should and bool/filter query inside the main bool/filter query:
{
"query": {
"bool": {
"filter": [
{
"terms": {
"visibility": [
"1",
"2"
]
}
},
{
"bool": {
"should": [
{
"term": {
"active": "0"
}
},
{
"bool": {
"filter": [
{
"term": {
"active": "1"
}
},
{
"term": {
"finished": "1"
}
}
]
}
}
]
}
}
]
}
}
}

Resources