May I search among some fields, but use another field's matching score for sorting? - elasticsearch

I have some documents like this
{"id":1,"city":"London","content":"soccer","continent":"Europe"},
{"id":2,"city":"New York","content":"basketball","continent":"North America"},
{"id":3,"city":"Tokyo","content":"baseball","continent":"Asia"},
...
I need to search keywords among some fields(excluding city field), e.g. a query like
{
"query": {
"bool": {
"should": [ //SHOULD_CLAUSE
"match": {
"continent": "America"
},
"term": {
"content": "soccer"
}
]
}
}
}
To make the results more "personalized", I want to make matched documents whose city field is the same as the visiting user's city property.
However, if I make city as a query field(something like "match":{"city":"Tokyo"}) in should boolean clause, it may return some documents that only match the city field, which mismatch the fields I need to search. When using boost to make city field more "important" for sorting things goes worse.
How can I achieve my goal?
It seems that a possible way write the SHOULD_CLAUSE part twice and make one of it combined with city clause using and
{
"query": {
"bool": {
"should": [{
"bool": {
"must": [{
"bool": {
SHOULD_CLAUSE
}
}, {
"match": {
"city": {
"query": "Tokyo",
"boost": 4.0
}
}
}]
}
}, {
"bool": {
SHOULD_CLAUSE
}
}]
}
}
}
But under the real circumstance the SHOULD_CLAUSE part may be more complicated and the whole query seems too long to write. I wonder if there is a better way.

If you want to have only result matching your user city, you should wrap your should query into a must query, something like :
{
"query": {
"bool": {
"must": [{
"bool": {
"should": [{
SHOULD_CLAUSE_1
}, {
SHOULD_CLAUSE_2
}]
}
}, {
"match": {
"city": "Tokyo"
}
}]
}
}
}

Related

Elasticsearch conditional query for nested array

Using the following document, I'm trying to perform an Elasticsearch keyword query, conditionally excluding field data from the scope of the search. Is this possible?
{
"Name":"doc1",
"UserData":[
{
"EnteredBy":"Eric",
"Description":"Desc entered by Eric, abc"
},
{
"EnteredBy":"Alex",
"Description":"Desc entered by Alex, def"
}
]
}
The Elasticsearch query I need will allow me to search across the whole document, except it should exclude from the search UserData items where EnteredBy does not match the specified user.
The following queries would return results:
User:Eric doc1
User:Eric abc
User:Alex doc1
User:Fred doc1
The following queries would not return results:
User:Eric def
User:Fred def
Everything I've tried thus far, ends up filtering content based on the presence of UserData nodes which apply to the specified user. I can't think of a way to specify that a field should be searched, only if the EnteredBy field matches.
I could restructure the document, if that would solve the problem.
Edit 1
The index..
PUT index1
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 0
},
"mappings": {
"properties" : {
"UserData" : {
"type":"nested"
},
"Name": {
"type":"text"
}
}
}
}
Edit 2
The query below is providing the results that I need, except for the child entity, I have to search in a specific field. If I change the second condition of the nested search into a query_string search, then it no longer uses the EnteredBy condition.
GET index1/_search
{
"query": {
"bool": {
"should": [
{
"nested":
{
"path": "UserData",
"query": {
"bool": {
"must": [{
"match": {
"UserData.EnteredBy": "Eric"
}},
{
"match": {
"UserData.Description": "def"
}
}]
}
}
}
},
{
"query_string":
{
"query": "doc1x"
}
}
]
}
}
}
This query appears to be working. I think I answered my own question.
GET index1/_search
{
"query": {
"bool": {
"should": [
{
"nested":
{
"path": "UserData",
"query": {
"bool": {
"must": [{
"match": {
"UserData.EnteredBy": "Eric"
}},
{
"query_string": {
"query": "def"
}
}]
}
}
}
},
{
"query_string":
{
"query": "doc1"
}
}
]
}
}
}

How to get documents that contain certain word in some fields with filtered query?

I want to find all books with "Agriculture" category. the books should contain word "paddy" in the title OR abstract field.
Here is my query :
GET /books/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"abstract": "paddy"
}
},
{
"match": {
"title": "paddy"
}
}
],
"filter": {
"term": {
"category": "Agriculture"
}
}
}
}
}
those query return all books with "Agriculture" category, even it's contain word "paddy" or not.
What did I do wrong?
Let me first explain to you how, Your query was fetching all the records, even it contains word "paddy" or not. This is b/c you are using the should clause which simply means that The clause (query) should appear in the matching document. but it means it is not forcing that it must appear, hence it's fetching all the documents in your index and then just filtering it on basis of category.
What you need is an upper level must clause, which means it must appear but then I used nested should as it can be either in title or abstract field, but at least one of these fields must contain the value, so that upper level must clause returns true.
The right query is below, which I tried locally and working fine, according to your use case:
{
"query": {
"bool": {
"must": {
"bool": {
"should": [
{
"match": {
"abstract": "paddy"
}
},
{
"match": {
"title": "paddy"
}
}
]
}
},
"filter": {
"term": {
"category": "agriculture"
}
}
}
}
}
Let me know if it's clear to you and solves your issue.

Elasticsearch filter with multi_match

I'm trying to write a query in ElasticSearch where I combine multi_match with filter for an id or a number og ids.
This is what i have so far:
{
"query": {
"bool": {
"must": {
"multi_match": {
"query": "Kasper",
"fields": ["name", "first_name", "last_name"]
}
},
"filter": {
"term": {
"user_id": "ea7528f0-1b8a-11e8-a492-13e39bbd17cb"
}
}
}
}
}
The "must" part of the query works perfectly, and when I run it alone, I get two results.
When I pick out the "user_id" from one of the two results and adds the "filter" part of the query with that id, I get nothing.
What I really want to do is have something like in SQL where user_id in ('id1', 'id2'), so the filtering would be something like:
...,
"filter": {
"terms": {
"user_id": ["ea7528f0-1b8a-11e8-a492-13e39bbd17cb"]
}
}
Did I misunderstand something here?
I'm guessing that this is because user_id field is treated as a text and is analyzed. You should use keyword type in this situation (you need just change the mapping of user_id field.
Another way (if you are on Elasticsearch 5+) you can search in keyword subfield. Just try use below query:
{
"query": {
"bool": {
"must": {
"multi_match": {
"query": "Kasper",
"fields": ["name", "first_name", "last_name"]
}
},
"filter": {
"term": {
"user_id.keyword": "ea7528f0-1b8a-11e8-a492-13e39bbd17cb"
}
}
}
}
}
I only changed "user_id" to "user_id.keyword" in your query.

ElasticSearch multi_match if field exists apply filter otherwise dont worry about it?

So we got an elasticsearch instance, but a job is requiring a "combo search" (A single search field, with checkboxes for types across a specific index)
This is fine, I simply apply this kind of search to my index (for brevity: /posts):
{
"query": {
"multi_match": {
"query": querystring,
"type":"cross_fields",
"fields":["title","name"]
}
}
}
}
As you may guess from the need for the multi_match here, the schemas to each of these types differs in one way or another. And that's my challenge right now.
In one of the types, just one, there is a field that doesnt exist in the other types, it's called active and it's a basic boolean 0 or 1.
We want to index inactive items in the type for administration search purposes, but we don't want inactive items in this type to be exposed to the public when searching.
To my knowledge and understanding, I want to use a filter. But when I supply a filter asking for active to be 1, I only ever now get results from that type and nothing else. Because now it's explicitly looking for items with that field and equal to one.
How can I do a conditional "if field exists, make sure it equals 1, otherwise ignore this condition"? Can this even be achieved?
if field exists, make sure it equals 1, otherwise ignore this condition
I think it can be implemented like this:
{
"query": {
"filtered": {
"filter": {
"bool": {
"should": [
{
"bool": {
"must": [
{
"exists": {
"field": "active"
}
},
{
"term": {
"active": 1
}
}
]
}
},
{
"missing": {
"field": "active"
}
}
]
}
}
}
}
}
and the complete query:
{
"query": {
"filtered": {
"query": {
"multi_match": {
"query": "whatever",
"type": "cross_fields",
"fields": [
"title",
"name"
]
}
},
"filter": {
"bool": {
"should": [
{
"bool": {
"must": [
{
"exists": {
"field": "active"
}
},
{
"term": {
"active": 1
}
}
]
}
},
{
"missing": {
"field": "active"
}
}
]
}
}
}
}
}

Using term query with Or operator

I am trying to use the term query the following way!!
{
"query": {
"bool": {
"must": [
{
"term": {
"technology": "Space"
}
},
{
"term": {
"Person": "Steve Simon"
}
}
]
}
}
}
Which returns me a response of feeds which has both fields present in single feed like an intersection operation. Can I use the term query to get UNION result for the above query like, I want all feeds which has space, Steve Simon present individually with feeds which has both present.
Use should instead of must. Also you have to set minimum_should_match to 1 which means that only one should clause is needed for matching a document.
{
"query": {
"bool": {
"should": [
{
"term": {
"technology": "Space"
}
},
{
"term": {
"Person": "Steve Simon"
}
}
],
"minimum_should_match": 1
}
}
}

Resources