Looks like MongoDB supports $or operator only in the query root, and not per field query. So this doesn't work:
db.collection.find({
foo: {
$or: [ fooQuery1, fooQuery2 ]
},
bar: {
$or: [ barQuery1, barQuery2 ]
}
})
so we have to write it like:
db.collection.find({
$or: [
{ foo: fooQuery1, bar: barQuery1 },
{ foo: fooQuery1, bar: barQuery2 },
{ foo: fooQuery2, bar: barQuery1 },
{ foo: fooQuery1, bar: barQuery2 },
]
})
Basically write all possible combinations for foo and bar. But this is insane when we have to query by >2 fields with >2 OR-statements each.
It is quite easy to write a function which takes the fields and its OR statements from the first example and to generate all possible variations from the second example. But is there some MongoDB native approach, may be we are missing here something.
And may be you know the reason, why the first approach is not supported? So that we better understand mongodb internals.
Thank you.
There are a couple of ways this query could be written, but it depends on the actual operations contained in each query.
If the queries are checking equality or a regular expression, you could use the $in operator, like
{
foo: { $in: [ "fooValue1", /^fooPrefix/ ]},
bar: { $in: [ "barValue1", "barValue2" ]}
}
If the subqueries have other tests, like inequality or existence, you could combine each field's queries in a separate $or, with an $and operator to ensure a match from each one, like:
{$and: [
{$or: [{foo: fooQuery1}, {foo: fooQuery2}]},
{$or: [{bar: barQuery1}, {bar: barQuery2}]}
]}
Related
I'm trying to achieve a join on three String fields between two collections on MongoDB v. 4.4.3: one containing the original documents, the other the translations.
Both document types look like this:
{
"_id" : ObjectId("60644367b521563be8044f07"),
"dsId" : "2051918",
"lcId" : "data_euscreenXL_EUS_15541BBE705033639D4E06691D7A5D2E",
"pgId" : "1",
(...)
This MongoDB query does what I need, embedding the Translations in the result:
db.Original.aggregate([
{ $match: { query parameters } },
{ $lookup:
{
from: "Translation",
let: { "origDsId": "$dsId", origLcId: "$lcId", "origPgId": "$pgId" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$dsId", "$$origDsId" ] },
{ $eq: [ "$lcId", "$$origLcId" ] },
{ $eq: [ "$pgId", "$$origPgId" ] }
]
}
}
},
{ $project: { dsId: 0, _id: 0 } }
],
as: "translations"
}
}])
However, I can't figure out how to write the equivalent Morphia query. I updated to Morphia v.2.2, which adds the required features, but it's all very new and hasn't yet been documented on morphia.dev; I couldn't find much more on Javadoc either. This Morphia unit test on Github looked interesting and I tried copying that approach:
Aggregation<Original> query = datastore.aggregate(Original.class)
.match(eq("dsId", datasetId), eq("lcId", localId))
.lookup(Lookup.lookup(Translation.class)
.let("origDsId", value("$dsId"))
.let("origLcId", value("$lcId"))
.let("origPgId", value("$pgId"))
.pipeline(Match.match(expr(Expressions.of()
.field("$and",
array(Expressions
.of().field("$eq",
array(field("dsId"),
field("$origDsId"))),
Expressions
.of().field("$eq",
array(field("lcId"),
field("$origLcId"))),
Expressions
.of().field("$eq",
array(field("pgId"),
field("$origPgId"))))))))
.as("translations"));
...
This returns the Original documents, but fails to join the Translations.
The problem is that the syntax of the pipeline stage is rather puzzling. I wonder if anyone can shed some light on this?
the unit test example does not use (or need?) the double-$ form seen in "$$origDsId"? From the MongoDB documentation I understand that this form is used to refer to externally defined variables (eg in the "let" assignment before the "pipeline") but they don't work in the quoted example either;
what is the role of the static ArrayExpression "array"? It looks as if it's a kind of assignment container, where Expressions.of().field("$eq", array(field("dsId"), field("$origDsId"))) might mean something like "dsId" = "$origDsId" - which would be what I need (if it would work ;) )
I tried all sorts of combinations, using field("$origDsId"), value("$origDsId"), field("$$origDsId"), value("$$origDsId"), etcetera, but having no luck so far.
Thanks in advance!
I'm using $match of aggregation.
and I tried this.
$match : {
$not: {
'A': false,
'B': 'condition'
}
}
but not work like nand. it works like not A and not B.
How can I query with Not(A and B) ?
This should fetch you the results
{
$or: [
{'a': { $ne: 1} },
{'b': { $ne: 2} }
]
}
However, this expression did not yield the desired result in my case so I tried this-
{
$nor: [
{
$and: [
{a: 1},
{b: 2}
]
}
]
}
The $not operator does not invert a complex expression.
You need to use $and or $or for complex expressions.
Using logic rules, we know that the following are identical:
not ( A and B ) = not A or not B
Using the MongoDB query language, you would have:
db.collection.find({$or:[{"a":{"$ne":false}},{"b":{"$ne":"condition"}}]})
OR simplifying the double boolean:
db.collection.find({$or:[{"a":true},{"b":{"$ne":"condition"}}]})
Using the MongoDB aggregation framework, you would have:
db.collection.aggregate([{"$match":{$or:[{"a":{"$ne":false}},{"b":{"$ne":"condition"}}]}}])
OR again, simplified to:
db.collection.aggregate([{"$match":{$or:[{"a":true},{"b":{"$ne":"condition"}}]}}])
there is a very simple trick to create NAND with mongoDB:
$nor : { $and [..., ...] }
you can think other way in mongodb with available options.
db.Collection.find({'arrary.a':{$exists:false},'arrary.b':{$exists:false}})
hope this will help you...
I´m developing a filter system that you can filter a set of tv´s by price, brand and screen size. Now, I´m able to filter the tv´s by brand like follows:
db.tvs.find({brand: {$in: ['LG', 'Samsung']}})
And it works.
But right now, I´d like to do the same but with multiple filters (price and screensize), something like this:
db.tvs.find({brand: {$in: ['LG', 'Samsung']}}, {screensize: {$in: ['37', '42']}})
It is possible?
You didn't have to use $and... You could do it like this:
db.tvs.find({brand: {$in: ['LG', 'Samsung']}, screensize: {$in: ['37', '42']}})
The only difference with
with your version
db.tvs.find({brand: {$in: ['LG', 'Samsung']}}, {screensize: {$in: ['37', '42']}})
is that i did not close the first brackets {} after the brand and didn't open a new one before screensize. You need to have them all in the same bson object
A more simple case to understand is this:
db.tvs.find( {brand : "LG" , screensize : '37'})
I did it.
With the $and operator.
db.tvs.find({ $and: [ { brand: {$in: ["LG", "Samsung"]} },{ size: {$in: ["37"] }}, { price: { $gte: 0 } }, { price: { $lte: 499 } } ] })
You should look into what is happening with you code:
db.tvs.find( { brand: { $in: ['LG', 'Samsung']} } , { screensize: { $in: ['37', '42']}} )
The query you put is some thing like
db.collectionName.find({},{});
Check this docs, It clearly says db.collection.find({<criteria>}, {<projection>}). So your query should be constructed such that it should belong to the first parameter not the <projection>.
In your case as mentioned above, the second query you wanted to make is going as second parameter which is intrepreted as for projection.
The projection parameter specifies which fields to return.
Projection parameter should be constructed as follows:
{'FieldName':1}
Where, 1 for the field to be returned in result otherwise 0 to explicitly excluded.
So coming to the answer you are looking for is as below:
db.tvs.find({brand:{$in: ['LG', 'Samsung']}, screensize: {$in: ['37', '42']}})
Implementing an application that looks up a table for mail id presence from a list of around 10 email ids. Tried using $or and $in.
$in seems to give better performance but not significant. Is one more optimized than other?
MongoDB docs have the answer:
"When using $or with <expressions> that are equality checks for the value of the same field, choose the $in operator over the $or operator."
$or operator is logical operator where you can define your own login but $in operator is Comparison operator where you can compare you can not put your on logic.
Syntax of $in:
{ field: { $in: [<value1>, <value2>, ... <valueN> ] } }
Example:
db.account.find( { qty: { $in: [ 5, 15 ] } } )
Syntax of $or:
{ $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
Example:
db.account.find( { $or: [ { quantity: { $lt: 20 } }, { price: 10 } ] } )
Note: Account is your collection name
"While "$or"will always work, use "$in"whenever possible as the query optimizer
handles it more efficiently."
Moreover "$in" has more readability.
Ref: MongoDB: The Definitive Guide
Well that will insure no indecis to be ensured if you use $in, however i prefer to format it to $or as it will ensure index (readability won't concern me at is being handled in application logic in which i prefer to consume the memory of app rather than mongodb server)
I am using MongoDB to store information on coffees and I am having problems with a query.
My document structure is:
{ _id: "Thailand Bok", tags: ["Strong", "Smooth", "Dark"] }
I am trying to create query that allows me to search either on the name or tags.
So given that a query string may be "Thailand Bok" or ["Strong", "Smooth"] I want to search for requests that either contain the _id or each tag.
If I were thinking in SQL terms it may be something like this:
"WHERE `_id` like 'Not present%' OR ("Strong" IN `tags` AND "Smooth" IN `tags`)"
The query I have so far is:
{
$or: [
{ _id: { $regex: '^Not present', '$options': 'i' } },
{
$and: [
{ tags: 'Strong' },
{ tags: 'Smooth' }
]
}
]
}
edit: correct a mistake in the query and clarified that it should work either if the _id matches OR the tags match
$and is only supported in version MongoDB 2.0+ I was using 1.8.2
You query seems allright except for using curly braces instead of square braces for $or.
{$or: [
{_id: {$regex: '^hailand', $options: 'i'} },
{'$and': [
{tags: 'Strong'},
{tags: 'Smooth'}
]}
]}
Works just fine.