Match multiple conditions in an aggregate under expressions - mongodb

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

Related

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

in mongo how to match a field that is not list of empty elements?

I have field x which can be [[], [], ...] or ["", "", ....] I want to filter them out and keeps the document at least have 1 non-empty list or 1 non-empty string. for example [[], [1,2], [], ...]
This is an aggregation query which filters out collection documents with the array field x, having elements with all empty strings or all empty arrays.
db.collection.aggregate([
{
$addFields: {
filtered: {
$filter: {
input: "$x",
as: "e",
cond: {
$or: [
{ $and: [
{ $eq: [ { "$type": "$$e" }, "array" ] },
{ $gt: [ { $size: "$$e" }, 0 ] }
] },
{ $and: [
{ $eq: [ { "$type": "$$e" }, "string" ] },
{ $gt: [ { $strLenCP: "$$e" }, 0 ] }
] }
]
}
}
}
}
},
{
$match: {
$expr: { $gt: [ { $size: "$filtered" }, 0 ] }
}
},
{
$project: { filtered: 0 }
}
])
Reference: Various aggregation operators ($size, $type, $strLenCP, etc.) used.

Difference between queries with grouped

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

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