mongodb how to query with nand operator? - mongodb

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...

Related

Is it possible to write a Mongo query that validates a condition in each element of an array?

Consider the following documents:
{
_id: 1,
a: [{b:true},{b:true}]
},
{
_id: 2,
a: [{b:false},{b:true}]
},
{
_id: 3,
a: [{b:true}]
}
I'd like to write a query that will return all of the top level documents that have an array ("a") that contain only elements matching {b : true}. In this example, I'm expecting the first and third document to be returned, but not the second.
When writing the query like this, all 3 documents are returned..
{a : {b : true}}
Is there an operator that I'm missing? I've reviewed quite a few of them ($all) and I'm not sure which would match best for this use case
Thanks so much
Simply use $allElementsTrue on the array a.b.
db.collection.find({
$expr: {
"$allElementsTrue": "$a.b"
}
})
Here is the Mongo Playground for your reference.

MongoDB $or operator for multiple fields separately

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

How to add order in $all operator in MongoDB

How can I get documents from mongo with an array containing some elements but IN THE SAME ORDER?
I know that $all do the job but ignoring the order of elements. The order in my case is important and I can't sort my arrays since it's describing a path that I want to keep the order.
111,222,333 is not the same as 222,111,333
Is there a way to do it using $all or maybe another operator in mongo aggregation framework?
You can avoid the first "intersect" field, is just to give you back as debug what MongoDB make with this command. You should create the $and operator dynamically.
db.Test6.aggregate([
{
$project: {
_id:1,
pages:1,
intersect: {$setIntersection: [[111,666], "$pages"]},
theCondition: {$let: {
vars: {
intersect: {$setIntersection: [[111,666], "$pages"]}
},
in: {
$cond:[ {$and:[
{$eq:[{$arrayElemAt:["$$intersect", 0]}, 111]},
{$eq:[{$arrayElemAt:["$$intersect", 1]}, 666]}
]} , true, false]
}
}
}
}
}
]);

Optimized way of Querying in MongoDB using $in vs $or

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)

Mongodb query with fields in the same documents

I have the following json:
{
"a1": {"a": "b"},
"a2": {"a": "c"}
}
How can I request all documents where a1 and a2 are not equal in the same document?
You could use $where:
db.myCollection.find( { $where: "this.a1.a != this.a2.a" } )
However, be aware that this won't be very fast, because it will have to spin up the java script engine and iterate each and every document and check the condition for each.
If you need to do this query for large collections, or very often, it's best to introduce a denormalized flag, like areEqual. Still, such low-selectivity fields don't yield good index performance, because he candidate set is still large.
update
using the new $expr operator available as of mongo 3.6 you can use aggregate expressions in find query like this:
db.myCollection.find({$expr: {$ne: ["$a1.a", "$a2.a"] } });
Although this comment solves the problem, I think a better match for this use case would be to use $addFields operator available as of version 3.4 instead of $project.
db.myCollection.aggregate([
{"$match":{"a1":{"$exists":true},"a2":{"$exists":true}}},
{"$addFields": {
"aEq": {"$eq":["$a1.a","$a2.a"]}
}
},
{"$match":{"aEq": false}}
]);
To avoid JavaScript use the aggregation framework:
db.myCollection.aggregate([
{"$match":{"a1":{"$exists":true},"a2":{"$exists":true}}},
{"$project": {
"a1":1,
"a2":1,
"aCmp": {"$cmp":["$a1.a","$a2.a"]}
}
},
{"$match":{"aCmp":0}}
])
On our development server the equivalent JavaScript query takes 7x longer to complete.
Update (10 May 2017)
I just realized my answer didn't answer the question, which wanted values that are not equal (sometimes I'm really slow). This will work for that:
db.myCollection.aggregate([
{"$match":{"a1":{"$exists":true},"a2":{"$exists":true}}},
{"$project": {
"a1":1,
"a2":1,
"aEq": {"$eq":["$a1.a","$a2.a"]}
}
},
{"$match":{"aEq": false}}
])
$ne could be used in place of $eq if the match condition was changed to true but I find using $eq with false to be more intuitive.
MongoDB uses Javascript in the background, so
{"a": "b"} == {"a": "b"}
would be false.
So to compare each you would have to a1.a == a2.a
To do this in MongoDB you would use the $where operator
db.myCollection.find({$where: "this.a1.a != this.a2.a"});
This assumes that each embedded document will have a property "a". If that isn't the case things get more complicated.
Starting in Mongo 4.4, for those that want to compare sub-documents and not only primitive values (since {"a": "b"} == {"a": "b"} is false), we can use the new $function aggregation operator that allows applying a custom javascript function:
// { "a1" : { "x" : 1, "y" : 2 }, "a2" : { "x" : 1, "y" : 2 } }
// { "a1" : { "x" : 1, "y" : 2 }, "a2" : { "x" : 3, "y" : 2 } }
db.collection.aggregate(
{ $match:
{ $expr:
{ $function: {
body: function(a1, a2) { return JSON.stringify(a1) != JSON.stringify(a2); },
args: ["$a1", "$a2"],
lang: "js"
}}
}
}
)
// { "a1" : { "x" : 1, "y" : 2 }, "a2" : { "x" : 3, "y" : 2 } }
$function takes 3 parameters:
body, which is the function to apply, whose parameter are the two fields to compare.
args, which contains the fields from the record that the body function takes as parameter. In our case, both "$a1" and "$a2".
lang, which is the language in which the body function is written. Only js is currently available.
Thanks all for solving my problem -- concerning the answers that use aggregate(), one thing that confused me at first is that $eq (or $in, or lots of other operators) has different meaning depending on where it is used. In a find(), or the $match phase of aggregation, $eq takes a single value, and selects matching documents:
db.items.aggregate([{$match: {_id: {$eq: ObjectId("5be5feb45da16064c88e23d4")}}}])
However, in the $project phase of aggregation, $eq takes an Array of 2 expressions, and makes a new field with value true or false:
db.items.aggregate([{$project: {new_field: {$eq: ["$_id", "$foreignID"]}}}])
In passing, here's the query I used in my project to find all items whose list of linked items (due to a bug) linked to themselves:
db.items.aggregate([{$project: {idIn: {$in: ["$_id","$header.links"]}, "header.links": 1}}, {$match: {idIn: true}}])