Difference between queries with grouped - mongodb

I'm new to mongodb, and I have 2 queries:
frist:
db.movies.aggregate([
{ "$match": {
$and : [
{ "imdb.rating": { $lt: 7 }},
{$and: [ {"genres": { "$ne": "Crime" } }, {"genres": { "$ne": "Horror" } }]},
{$and: [ {"languages": { "$eq": "English" } }, {"languages": { "$eq": "Japanese" } }]},
{$or: [ {"rated": { "$eq": "PG" } }, {"rated": { "$eq": "G" } }]}
]
}
}
]).itcount()
The result its: 23
And now with this:
db.movies.aggregate([
{ "$match": {
"imdb.rating": { $lt: 7 },
$and: [ {"genres": { "$ne": "Crime" } }, {"genres": { "$ne": "Horror" } }],
$or: [ {"rated": { "$eq": "PG" } }, {"rated": { "$eq": "G" } }],
$and: [ {"languages": { "$eq": "English" } }, {"languages": { "$eq": "Japanese" } }]
}
}
]).itcount()
The result its 25,
But now I can't understand what is the difference between two queries, can help with this?

There is no functional difference. $match implicitly is using $and when you provide more than one expression and a simple key:value expression is shorthand for key: {$eq: value} e.g.
$match: {a:3, b:"buzz"}
is shorthand for:
$match: {$and: [{a:{$eq:3}}, {b:{$eq:"buzz"}} ] }

Related

Mongodb $exists inside $expr in mongodb

I want to add multiple conditions on join. Join those docs (of the same collection) who met the following conditions:
Have opposite gender
Have age (IF EXISTS) between the primary doc age preference and primary doc have age (IF EXISTS) between the foreign doc preference (i.e two-way check)
My attempt is the following but has two issues:
$exists can't be used inside $expr idk why
Age query is one way right now
$lookup: {
"from": "appusers",
"let": { 'gen': "$gender",'pref': "$preference" },
"pipeline": [{
$match: {$expr: {
$and: [
{ $ne: ["$gender", "$$gen"]},
{ $or: [
{$exists: {"$age": false}},
{$and: [
{ $gte: ["$age", '$$pref.age_from' ] },
{ $lte: [ "$age", '$$pref.age_to' ] }
]}
]}
]
}}}],
"as": "matches"
}
Example:
Input Docs:
{
name: "person1",
age: 36,
gender: "Male",
preference: {
age_from: 25,
age_to: 35
}
}
{
name: "person2",
age: 18,
gender: "Female",
preference: {
age_from: 25,
age_to: 40
}
}
{
name: "person3",
age: 26,
gender: "Female",
preference: {
age_from: 30,
age_to: 35
}
}
{
name: "person4",
age: 26,
gender: "Female",
preference: {
age_from: 30,
age_to: 40
}
}
Output:
For person 1 the matches array will show only person 4 (and similarly person 4 match will show person 1) i.e.:
{
name: person1,
age: 36,
gender: "Male",
preference: {
age_from: 28,
age_to: 35
},
matches: [
{
name: person4,
...
}
]
}
I have viewed this and this but didn't help
$exists can't be used inside $expr idk why
$expr Allows the use of aggregation expressions within the query language, and $exists is not an aggregation operator,
You just need to correct the 2 things:
put $expr condition inside first $and condition
put $expr in last $and condition
db.appusers.aggregate([
{
$lookup: {
from: "appusers",
let: { gen: "$gender", pref: "$preference" },
pipeline: [
{
$match: {
$and: [
{ $expr: { $ne: ["$gender", "$$gen"] } },
{
$or: [
{ age: { $exists: false } },
{
$expr: {
$and: [
{ $gte: ["$age", "$$pref.age_from"] },
{ $lte: ["$age", "$$pref.age_to"] }
]
}
}
]
}
]
}
}
],
as: "matches"
}
}
])
Playground
For the $exists problem, you can wrap age with $ifNull and use $eq to check for the existence.
For the 2-way age matching, I think you just need to repeat your age matching criteria from person1 to person4 for person4 to person1. Although in your current given test case, no match will be found as person4's age is out of person1's preference.
db.appusers.aggregate([
{
"$match": {
name: "person1"
}
},
{
$lookup: {
"from": "appusers",
"let": {
"a": "$age",
"gen": "$gender",
"pref": "$preference"
},
"pipeline": [
{
$match: {
$expr: {
$and: [
{
$ne: [
"$$gen",
"$gender"
]
},
{
$and: [
{
$or: [
{
$eq: [
{
"$ifNull": [
"$age",
"age-not-exists"
]
},
"age-not-exists"
]
},
{
$and: [
{
$gte: [
"$age",
"$$pref.age_from"
]
},
{
$lte: [
"$age",
"$$pref.age_to"
]
}
]
}
]
},
{
$or: [
{
$eq: [
{
"$ifNull": [
"$$a",
"age-not-exists"
]
},
"age-not-exists"
]
},
{
$and: [
{
$gte: [
"$$a",
"$preference.age_from"
]
},
{
$lte: [
"$$a",
"$preference.age_to"
]
}
]
}
]
}
]
}
]
}
}
}
],
"as": "matches"
}
}
])
Here is the Mongo playground for your reference.
You can use $eq undefined for the field age instead of the $exists
{
"from": "appusers",
"let": { 'gen': "$gender",'pref': "$preference" },
"pipeline": [{
$match: {$expr: {
$and: [
{ $ne: ["$gender", "$$gen"]},
{ $or: [
{$eq: ["$age" , undefined]},
{$and: [
{ $gte: ["$age", '$$pref.age_from' ] },
{ $lte: [ "$age", '$$pref.age_to' ] }
]}
]}
]
}}}],
"as": "matches"
}

$switch inside a $match MONGODB

Hi i am trying to use MONGODB query inside TIBCO jasperstudio to create a report
What I am trying to do is filter the data using two parameters #orderitemuid and #ordercatuid. My case is if I put a parameter using #orderitemuid, it will disregard the parameter for #ordercatuid. Vise versa, if I put a parameter using #ordercatuid, it will disregard the parameter for #orderitemuid. But there is also an option when using bot parameters in the query. I used a $switch inside the $match but I am getting an error. Below is the $match I am using
{
$match: {
$switch: {
branches: [
{
case: { $eq: [{ $IfNull: [$P{orderitemuid}, 0] }, 0] },
then: { 'ordcat._id': { '$eq': { '$oid': $P{ordercatuid} } } },
},
{
case: { $eq: [{ $IfNull: [$P{ordercatuid}, 0] }, 0] },
then: { '_id': { '$eq': { '$oid': $P{orderitemuid} } } },
},
],
default: {
$expr: {
$and: [
{ $eq: ['_id', { '$oid': $P{orderitemuid} }] },
{ $eq: ['ordcat_id', { '$oid': $P{ordercatuid} }] },
],
},
},
},
},
}
Thank you in advance
As mentioned in the $match docs
$match takes a document that specifies the query conditions. The query syntax is identical to the read operation query syntax; i.e. $match does not accept raw aggregation expressions. ...
And $switch is an aggregation expressions. this means it cannot be used in a $match stage without being wrapped with $expr.
You can however wrap it with $expr, this will also require you to restructure the return values a little bit, like so:
db.collection.aggregate([
{
$match: {
$expr: {
$switch: {
branches: [
{
case: {
$eq: [
{
$ifNull: [
$P{orderitemuid},
0
]
},
0
]
},
then: {
$eq: [
"$ordcat._id",
{"$oid":$P{ordercatuid}}
]
}
},
{
case: {
$eq: [
{
"$ifNull": [
$P{ordercatuid},
0
]
},
0
]
},
then: {
$eq: [
"$_id",
{"$oid":$P{orderitemuid}}
]
}
}
],
default: {
$and: [
{
$eq: [
"$_id",
{"$oid": $P{orderitemuid} }
]
},
{
$eq: [
"$ordcat_id",
{"$oid": $P{ordercatuid}}
]
}
]
}
}
}
}
}
])
Mongo Playground

MongoDb Create Aggregate Create query

I have 3 table users,shifts,temporaryShifts,
shifts:[{_id:ObjectId(2222),name:"Morning"},{_id:ObjectId(454),name:"Night"}]
users:[{_id:ObjectId(123),name:"Albert",shift_id:ObjectId(2222)}]
temporaryShifts:[
{_id:2,userId:ObjectId(123),shiftId:ObjectId(454),type:"temporary",date:"2020-02-01"},
{_id:987,userId:ObjectId(123),shiftId:ObjectId(454),type:"temporary",date:"2020-02-03"},
{_id:945,userId:ObjectId(123),shiftId:ObjectId(454),type:"temporary",date:"2020-02-08"},
{_id:23,userId:ObjectId(123),shiftId:ObjectId(454),date:"2020-02-09"}]
i want to make a mongoose aggregate query then give me result :
get result between two dates for example :2020-02-01 2020-02-05,
resullts is :
[
{_id:ObjectId(123),name:"Albert",shift:[
{_id:2,shiftId:ObjectId(454),type:"temporary",date:"2020-02-01"},
{_id:2,shiftId:ObjectId(2222),type:"permanent",date:"2020-02-02"},
{_id:2,shiftId:ObjectId(454),type:"temporary",date:"2020-02-03"},
{_id:2,shiftId:ObjectId(2222),type:"permanent",date:"2020-02-04"},
{_id:2,shiftId:ObjectId(2222),type:"permanent",date:"2020-02-05"},
]}
]
in result type temporary mean selected date in table temporaryShift document available else type permanent
MongoPlayGround You Can edit
You can first project a date range array using $range, in your example it will be like [2020-02-01, 2020-02-02, 2020-02-03, 2020-02-04, 2020-02-05], then you can use the array to perform $lookup
db.users.aggregate([
{
$limit: 1
},
{
"$addFields": {
"startDate": ISODate("2020-02-01"),
"endDate": ISODate("2020-02-05")
}
},
{
"$addFields": {
"dateRange": {
"$range": [
0,
{
$add: [
{
$divide: [
{
$subtract: [
"$endDate",
"$startDate"
]
},
86400000
]
},
1
]
}
]
}
}
},
{
"$addFields": {
"dateRange": {
$map: {
input: "$dateRange",
as: "increment",
in: {
"$add": [
"$startDate",
{
"$multiply": [
"$$increment",
86400000
]
}
]
}
}
}
}
},
{
"$unwind": "$dateRange"
},
{
"$project": {
"name": 1,
"shiftId": 1,
"dateCursor": "$dateRange"
}
},
{
"$lookup": {
"from": "temporaryShifts",
"let": {
dateCursor: "$dateCursor",
shiftId: "$shiftId"
},
"pipeline": [
{
"$addFields": {
"parsedDate": {
"$dateFromString": {
"dateString": "$date",
"format": "%Y-%m-%d"
}
}
}
},
{
$match: {
$expr: {
$and: [
{
$eq: [
"$$dateCursor",
"$parsedDate"
]
}
]
}
}
}
],
"as": "temporaryShiftsLookup"
}
},
{
"$unwind": {
path: "$temporaryShiftsLookup",
preserveNullAndEmptyArrays: true
}
},
{
$project: {
shiftId: 1,
type: {
"$ifNull": [
"$temporaryShiftsLookup.type",
"permanent"
]
},
date: "$dateCursor"
}
}
])
Here is the Mongo Playground for your reference.

Match multiple conditions in an aggregate under expressions

sample format of document:
{
"_id": {
"$oid": "5e158e2de6facf7181cc368f"
},
"word": "as luck would have it",
}
I am trying to match multiple conditions in an expression as:
query = {
"$match": {
"$expr": {"$eq": [{"$strLenCP": "$word"}, 6],
'$lt': [
{
'$size': {
'$split': [
"$word",
" "
]
}
},
2
]
}
}
}
And pipe line as follows:
pipeline = [query]
cursor_objects = db['test'].aggregate(pipeline)
In the above query I am trying to achieve word length must be 6 and it doesn't contain any spaces
When I did this I am getting and error :
pymongo.errors.OperationFailure: An object representing an expression must have exactly one field: { $eq: [ { $strLenCP: "$word" }, 6 ], $lt: [ { $size: { $split: [ "$word", " " ]
May I know how could I achieve this ?
any help is appreciated ,...TIA
To use multiple conditions in $expr you have to use $and operator
query = {
$match: {
$expr: {
$and: [
{
$lt: [
{
$size: {
$split: ["$word", " "]
}
},
2
]
},
{ $eq: [{ $strLenCP: "$word" }, 6] }
]
}
}
};
Try this:
query = {
"$match": {
"$expr": {
$and: [{
"$eq": [{ "$strLenCP": "$word" }, 6]
}, {
'$lt': [{
'$size': {
'$split': [
"$word",
" "
]
}
},
2
]
}]
}
}
}

How to group query with multiple $cond?

I want to query like below, but this contains only one $cond.
How to query with two $cond?
collection.aggregate(
{
$match : {
'_id' : {$in:ids}
}
},
{
$group: {
_id: '$someField',
...
count: {$sum: { $cond: [ { $eq: [ "$otherField", false] } , 1, 0 ] }}
}
},
function(err, result){
...
}
);
You want to use a compound expression inside {$cond:[]} - something like:
collection.aggregate(
{
$match : {
'_id' : {$in:ids}
}
},
{
$group: {
_id: '$someField',
...
count: {$sum: { $cond: [ {$and : [ { $eq: [ "$otherField", false] },
{ $eq: [ "$anotherField","value"] }
] },
1,
0 ] }}
}
},
function(err, result){
...
}
);
The $and operator is documented here: http://docs.mongodb.org/manual/reference/operator/aggregation/#boolean-operators
you can add multiple $cond and multiple criterias inside $cond like this
`
collection.aggregate(
[
{
"$match": {
//matching criteria
}
},
{
"$project": {
"service": {
"$cond": {
"if": {
"$eq": [
"$foo",
"bar"
]
},
"then": "return string1",
"else": {
"$cond": {
"if": {
"$eq": [
"$foo",
"bar"
]
},
"then": "return string2",
"else": {
"$cond": {
"if": {
"$or": [
{
"$eq": [
"$foo",
"bar1"
]
},
{
"$eq": [
"$foo",
"bar2"
]
}
]
},
"then": "return string3",
"else": "$foo"
}
}
}
}
}
},
"_id": 0
}
}
]
)
`