Elasticsearch: constructing bool query with filters - search

I have following document structure:
Movie:
{
id: int,
title: string,
language: string,
genre: string,
description: string,
cast: array[string],
directors: array[string],
(...)
}
Now, in the web interface, the user can choose (checkbox) the language, genre, directors etc and type some query to the search box.
Let's say I want to search within all thrillers (genre), that are in French or English (language), directed by James Cameron or George Lucas (directors) and I'm typing to the search box "abc" that I would like to find within title or description.
What I want as a result:
- only movies only in French or English
- only movies directed by James Cameron or George Lucas
- only thrillers
- movies that corresponds to "abc"
I'm not sure how to do the OR in the filters, but I have started from something like:
curl -X -XGET 'localhost:9200/movies/_search?pretty' -H 'Content-Type: application/json' -d'
{
"query" : {
"constant_score" : {
"filter" : {
"bool" : {
"should" : [
{ "term" : {"language" : "french"}},
{ "term" : {"language" : "english"}},
{ "term" : {"directors" : "James Cameron"}},
{ "term" : {"directors" : "George Lucas"}}
],
"filter" : [
{ "term" : {"genre" : "thriller"}}
]
}
}
}
}
}
'
Could you please give me some hints?

As I understand, you need a query like this: (language is A or B) and (directors is C or D) and (genre is E) and (title or description is F). In this case you need the following query:
{
"query": {
"bool": {
"filter": [
{
"terms": {
"language": [
"french",
"english"
]
}
},
{
"bool": {
"should": [
{
"match": {
"directors": "James Cameron"
}
},
{
"match": {
"directors": "George Lucas"
}
}
]
}
},
{
"term": {
"genre": "thriller"
}
},
{
"multi_match": {
"query": "abc",
"fields": [
"title",
"description"
]
}
}
]
}
}
}
filter query will work as AND condition but will get you the same score for all matched documents. If you need to vary score depending on subqueries match, you'd better to use must instead of filter. terms query will match if specified field contains at least one term. multi_match query will match if at least one field contains specified query

Related

MongoDB - Update array data in array

What is the best way to update the array element inside the array in MongoDB? For example, the data looks like this:
{
"_id" : ObjectId("6201396b866ffbf1b84fb8f9"),
"title" : "ironman",
"comments" : [
{
"text" : "nihao",
"replies" : [
{
"text" : "hi"
},
{
"text" : "bonjour"
},
{
"text" : "push replies!!!"
}
]
},
{
"text" : "what??",
"replies" : [
{
"text" : "the"
},
{
"text" : "hey"
}
]
},
{
"text" : "push comments!!!"
}
]
}
I want to change
"comments.replies.text: 'hi'"
to
"comments.replies.text: 'hello'"
What would be the best way to write a query if you want to update the elements inside replies?
You need $[<identifier>] filtered positional operator and arrayFilters to update nested document(s) in the array.
db.collection.update({
title: "ironman"
},
{
$set: {
"comments.$[comment].replies.$[reply].text": "hello"
}
},
{
arrayFilters: [
{
"comment.replies": {
$exists: true
}
},
{
"reply.text": "hi"
}
]
})
Sample Demo on Mongo Playground

Bool and query on Elasticsearch 6.5.3

I'm working on ElasticSearch 6.5.3 and . Please find below my code:
from elasticsearch import Elasticsearch, helpers
def search(es_object, index_name, request):
toto = es_object.search(index=index_name, body=request)
return toto
fuzziness = 1
request1 = {
"query": {
"match" : {
"Family_name" : {
"query" : family_names,
"fuzziness": fuzziness,
}
}
},
"size" : size
}
request2 = {
"query": {
"match" : {
"First_name" : {
"query" : first_names,
"fuzziness": fuzziness,
}
}
},
"size" : size
}
result1 = search(es,ELASTICSEARCH_INDEX_NAME,request1)
result2 = search(es,ELASTICSEARCH_INDEX_NAME,request2)
I'd like to make a bool fuzzy query on firstname and family name. How can I do it please ?
I tried the following :
request = {
"bool": {
"must": [
{
"query": {
"match" : {
"First_name" : {
"query" : family_name,
"fuzziness": fuzziness,
}
}
},
"query": {
"match" : {
"Prénom" : {
"query" : firstname,
"fuzziness": fuzziness,
}
}
}
}
]
}
}
result = search(es,ELASTICSEARCH_INDEX_NAME,request)
I got the following error meaning that there is some problem in my query. It seems that I cannot combine two match queries having fuzziness simultaneously
RequestError: RequestError(400, 'parsing_exception', 'Unknown key
for a START_OBJECT in [bool].')
Your query is a bit wrong, you need to fix it like this:
{
"query": {
"bool": {
"must": [
{
"match": {
"First_name": {
"query": "family_name",
"fuzziness": fuzziness
}
}
},
{
"match": {
"Prénom": {
"query": "firstname",
"fuzziness": fuzziness
}
}
}
]
}
}
}
The reason is, that boolean query array already understand that it will be an object of query, so you don't need to specify it again

MongoDB - query field to include separate words [duplicate]

I want to use $or and $regex operators same time.
db.users.insert([{name: "Alice"}, {name: "Bob"}, {name: "Carol"}, {name: "Dan"}, {name: "Dave"}])
Using $regex works fine:
> db.users.find({name: {$regex: "^Da"}})
{ "_id" : ObjectId("53e33682b09f1ca437078b1d"), "name" : "Dan" }
{ "_id" : ObjectId("53e33682b09f1ca437078b1e"), "name" : "Dave" }
When introducing $or, the response is changed. I expected the same response:
> db.users.find({name: {$regex: {$or: ["^Da"]}}})
{ "_id" : ObjectId("53e33682b09f1ca437078b1a"), "name" : "Alice" }
{ "_id" : ObjectId("53e33682b09f1ca437078b1b"), "name" : "Bob" }
{ "_id" : ObjectId("53e33682b09f1ca437078b1c"), "name" : "Carol" }
{ "_id" : ObjectId("53e33682b09f1ca437078b1d"), "name" : "Dan" }
{ "_id" : ObjectId("53e33682b09f1ca437078b1e"), "name" : "Dave" }
I also tried to change the order of the operators:
> db.users.find({name: {$or: [{$regex: "^Da"}, {$regex: "^Ali"}]}})
error: { "$err" : "invalid operator: $or", "code" : 10068 }
However, it seems that following query works fine, but it's a little bit long (name is repeated):
> db.users.find({$or: [{name: {$regex: "^Da"}}, {name: {$regex: "^Ali"}}]})
{ "_id" : ObjectId("53e33682b09f1ca437078b1a"), "name" : "Alice" }
{ "_id" : ObjectId("53e33682b09f1ca437078b1d"), "name" : "Dan" }
{ "_id" : ObjectId("53e33682b09f1ca437078b1e"), "name" : "Dave" }
Is there any shorter way to use $regex and $or in queries like this?
The goal is to use $regex operator and not /.../ (real regular expressions).
The $or operator expects whole conditions so the correct form would be:
db.users.find({ "$or": [
{ "name": { "$regex": "^Da"} },
{ "name": { "$regex": "^Ali" }}
]})
Or of course using $in:
db.users.find({ "name": { "$in": [/^Da/,/^Ali/] } })
But it's a regex so you can do:
db.users.find({ "name": { "$regex": "^Da|^Ali" } })
It is been a while. However, I would add case insensitive to the regex query like the query below. So that, it doesn't matter if names were saved into the database with capital letters:
db.users.find({ "name": { "$regex": "^Da|^Ali", "$options": "i" } })
Hope it helps
It seems when you have $and or $or and multiple search based and used at least one $regex you have to use $regex for all conditions.
First from below works ok, second more like $or operator.
db.big_data.users.find(
{ $and: [
{ sex: { $regex: /^M.*/ } },
{ name: { $regex: /^J.*/ } }
] })
db.big_data.users.find({ $and: [ {sex: "M"}, { name: { $regex: /^J*/m } } ] })
you can use OR operator like
db.collName.find({ "name": { "$regex": "^Da|^Ali" ,"$options": "i" } })
and operator
db.collName.find({ "name": { "$regex": "Ali" ,"$options": "i" } })
for more info
source - https://www.cs.jhu.edu/~jason/405/lectures1-2/sld049.htm

query multiple terms in multiple fields using elasticsearch

I want to search for multiple terms in 2 different fields (title, and description), the operator should be OR. Meaning that if any records contains any of these terms (heart, cancer) then that record should be returned.
Here is my code:
curl -XGET 'localhost:9200/INDEXED REPOSITORY/_search?pretty' -H 'Content-
Type: application/json' -d'{"query" : {"constant_score" : {"filter" : {"terms"
: {"description","title" : ["heart","cancer"]}}}}}'
But, I get this error:
"error" : "SearchPhaseExecutionException[Failed to execute phase [query],
all shards failed; shardFailures {[6hWIW7xlSbSqKi4dNg_1bg][geo_021017cde]
[0]: SearchParseException[[geo_021017cde][0]: from[-1],size[-1]: Parse
Failure [Failed to parse source [{\"query\" : {\"constant_score\" :
{\"filter\" : {\"terms\" : {\"description\",\"title\" :
[\"heart\",\"cancer\"]}}}}
Am I missing anything?
I figured out how to resolve it:
{
"query": {
"constant_score": {
"filter": {
"bool": {
"should": [
{
"terms": {
"description": [
"heart",
"cancer"
]
}
},
{
"terms": {
"title": [
"heart",
"cancer"
]
}
}
]
}
}
}
}
}

Mongo query using filter not fetching exact result?

My mongo table contains contains collection 'Shops' and data like the below:
{
"ShopId" : 9999,
"products" : [
{
"productId" : "1234",
"productName" : "abcd",
},
{
"productId" : "5678",
"productName" : "abc",
},
{
"productId" : "2345",
"productName" : "def",
}
],
}
There will be several shops in the table having a list of products.
Requirement:
I want to fetch the records having shopId=9999 and products matches the string abc
My query
model.Shops.aggregate([{$match:{"ShopId":9999}},{$project:{products:{$filter:{input:'$products',cond: {'productName':/abc/ }}}}}])
Problem:
It is fetching the productname:defwith the other data that matches productname:abc.
You can't use regex search with the $filter operator. The only way to achieve this is to unwind products, filter document and then re-group them in an array
model.Shops.aggregate([
{
$match:{
"ShopId":9999
}
},
{
$unwind:"$products"
},
{
$match:{
"products.productName":/abc/
}
},
{
$group:{
_id:null,
products:{
$push:{
"productName":"$products.productName",
"productId":"$products.productId"
}
}
}
}
])
output:
{
"_id":null,
"products":[
{
"productName":"abcd",
"productId":"1234"
},
{
"productName":"abc",
"productId":"5678"
}
]
}
to use it with a variable, declare your regex like this:
var regex: /abc/;
and then use it directly in the query
$match:{
"products.productName": regex
}
Below code worked on mongoshell for me, and your code was giving error to me, that Missing 'as' parameter to $filter.
db.Shops.aggregate([
{$match:{"ShopId":9999}},
{$project:{
products:{$filter:{input:'$products',as:"product",cond: { $eq: [ "$$product.productName", "abc" ] }}}
}}
])

Resources