Filter result after an unwind - mongodb

I've got an aggregation with an unwind :
{
$unwind: { "path":"$contributions", preserveNullAndEmptyArrays:true}
},
Output :
contributions" : {
"_id" : ObjectId("5b4e0636d3befc00155ffb8e"),
"creationDate" : ISODate("2018-07-17T15:07:34.172+0000"),
"validationDate" : ISODate("2018-07-17T15:07:42.008+0000"),
},
contributions" : {
"_id" : ObjectId("5b4e0636d3befc00155ffb8e"),
"rejectionDate" : ISODate("2018-07-17T15:07:34.172+0000"),
"validationDate" : ISODate("2018-07-17T15:07:42.008+0000"),
},
Now I want to filter this result to show only result that doesn't have a rejectionDate. So in this example, it should show only one result.
I try to add a filter :
$filter: {
input: `$contributions`, as: 'item',
cond: { $and: [
{ $not: '$$item.rejectionDate' },
...dateRangeFilterHelper('$$item.contributionDate', period)
] }
}
But it doesn't work like that.

instead of $not you can use $exists operator. you might get the desired output. Also if you can post the sample dataset and desired output then it will help to write the complete query.

The following query can get us the expected output:
db.collection.aggregate([
{
$addFields:{
"contributions":{
$filter:{
"input":"$contributions",
"as":"contribution",
"cond":{
$eq:["$$contribution.rejectionDate", undefined]
}
}
}
}
}
]).pretty()
Data set:
{
"_id" : ObjectId("5d7275c4eb1b7fdde7ea09e8"),
"contributions" : [
{
"_id" : ObjectId("5b4e0636d3befc00155ffb8e"),
"creationDate" : ISODate("2018-07-17T15:07:34.172Z"),
"validationDate" : ISODate("2018-07-17T15:07:42.008Z")
},
{
"_id" : ObjectId("5b4e0636d3befc00155ffb8e"),
"rejectionDate" : ISODate("2018-07-17T15:07:34.172Z"),
"validationDate" : ISODate("2018-07-17T15:07:42.008Z")
}
]
}
Output:
{
"_id" : ObjectId("5d7275c4eb1b7fdde7ea09e8"),
"contributions" : [
{
"_id" : ObjectId("5b4e0636d3befc00155ffb8e"),
"creationDate" : ISODate("2018-07-17T15:07:34.172Z"),
"validationDate" : ISODate("2018-07-17T15:07:42.008Z")
}
]
}

Related

Mongodb Query to get the nth document

I need to create a query in mongodb that needs to return the SECOND TO THE LAST document. I am planning to use $group for this query but i dont know what aggregation function to use. I only know $first and $last.
I have an example collection below and also include the expected output. Thank you!
"_id" : ObjectId("60dc27ac54b7c46bfa1b84b4"),
"auditlogs" : [
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84be"),
"userid" : ObjectId("5ffe702d59a9205db81fcb69"),
"action" : "ADDTRANSACTION"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84bd"),
"userid" : ObjectId("5ffe644f9493e05db9245192"),
"action" : "EDITPROFILE"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84bc"),
"userid" : ObjectId("5ffe64949493e05db9245197"),
"action" : "DELETETRANSACTION"
} ]
"_id" : ObjectId("60dc27ac54b7c46bfa1b75ge2"),
"auditlogs" : [
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84bb"),
"userid" : ObjectId("5ffe64b69493e05db924519b"),
"action" : "ADDTRANSACTION"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84ba"),
"userid" : ObjectId("5ffe65419493e05db92451d4"),
"action" : "ADDTRANSACTION"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84b9"),
"userid" : ObjectId("5ffe65689493e05db92451d9"),
"action" : "CHANGEACCESS"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84b8"),
"userid" : ObjectId("5ffe65819493e05db92451dd"),
"action" : "DELETETRANSACTION"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84b7"),
"userid" : ObjectId("5ffe65df9493e05db92451f3"),
"action" : "EDITPROFILE",
]
OUTPUT:
{"_id" : ObjectId("60dc27ac54b7c46bfa1b84b4"),"_id" : ObjectId("60dc27ac54b7c46bfa1b84bd"),"userid" : ObjectId("5ffe644f9493e05db9245192"),"action" : "EDITPROFILE"},
{"_id" : ObjectId("60dc27ac54b7c46bfa1b75ge2"),"_id" : ObjectId("60dc27ac54b7c46bfa1b84b8"),"userid" : ObjectId("5ffe65819493e05db92451dd"),"action" : "DELETETRANSACTION"}
You can't have two _id keys in one single object.
I've made the parent object's id to _parentId you can give it's a name anything you want except _id
Aggregation:
db.collection.aggregate([
{
$unwind: "$auditlogs"
},
{
"$project": {
"_parentId": "$_id",
"_id": "$auditlogs._id",
"action": "$auditlogs.action",
"userid": "$auditlogs.userid",
}
}
])
Playground
You can slice the array by -2 to get the last two item, then by 1 to get first one. Therefore, the array will be left the second to the last. Finally, unwind auditlogs so it can be changed from array to object which is structure that you want.
db.collection.aggregate([
{
$project: { auditlogs : { $slice: [ "$auditlogs", -2 ] } }
},
{
$project: { auditlogs : { $slice: [ "$auditlogs", 1 ] } }
},
{
$unwind: "$auditlogs"
}
])

aggregation $unwind ,$lookup is not working in mongodb

this is the query i have structured in mongo shell
db.vendormasters.aggregate([
{
'$match': {
status: 'active',
}
},
{ '$unwind': '$mappedToDealers'},
{
$lookup: {
from: "orders",
let: {
vendorId: "$_id",dealerId:'$mappedToDealers'
},
pipeline: [
{
$match: {
$and: [
{
$eq: [
"$vendorId",
"$$vendorId"
]
},
{
$eq: [
"$dealerId",
"$$dealerId"
]
}
]
}
}
], as: "orders"
}
},
{ '$unwind': '$orders' },
}]).pretty()
**the error message which i am getting in the shell is **
E QUERY [js] Error: command failed: {
"ok" : 0,
"errmsg" : "unknown top level operator: $eq",
"code" : 2,
"codeName" : "BadValue"
} : aggregate failed :
my collection structure is
//////collection 1 : vendorMasters///////////
{
"_id" : ObjectId("5e5642e32500b8273cbde3ac"),
"mappedToDealers" : [
ObjectId("5e1d82156a67173cb877f67d"),
ObjectId("5e5906dfc749dc4498033f7f")
],
"phoneNo" : 6#7159###,
"name" : "addedVendor8",
"address" : "Address6",
}
//////collection 2: orders///////////
{
"_id" : ObjectId("5e3a710af2657521e8c5668a"),
"date" : ISODate("2020-02-11T18:30:00Z"),
"order" : [
{
"_id" : ObjectId("5e3a710af2657521e8c5668c"),
"punchCount" : "###1",
"leavecCount" : 5,
},
{
"_id" : ObjectId("5e3a710af2657521e8c5668b"),
"punchCount" : "###1",
"leavecCount" : 5,
}
],
"vendorId" : ObjectId("5e5642e32500b8273cbde3ac"),
"dealerId" : ObjectId("5e1d82156a67173cb877f67d"),
}
{
"_id" : ObjectId("5e3a710af2657521e8c5668a"),
"date" : ISODate("2020-02-11T18:30:00Z"),
"order" : [
{
"_id" : ObjectId("5e3a710af2657521e8c5668c"),
"punchCount" : "###1",
"leavecCount" : 6,
},
{
"_id" : ObjectId("5e3a710af2657521e8c5668b"),
"punchCount" : "###1",
"leavecCount" : 2,
}
],
"vendorId" : ObjectId("5e5642e32500b8273cbde3ac"),
"dealerId" : ObjectId("5e5906dfc749dc4498033f7f"),
}
NOTE: there can be different vendorId's and different dealerId's in the documents if nothing matches then i should return an empty array , point out whats wrong in my query.my objective is find out all the orders from the orders collection which have matching vendorId & dealerId if it doesnot match than it shall return empty array
Your $match condition just contains a logical expression:
$match: { $and: [...] }
However, it must contain a query. Try this one:
$match: { $expr: { $and: [...] } }

Group by array element in Mongodb

We have nested document and trying to group by array element. Our document structure looks like
/* 1 */
{
"_id" : ObjectId("5a690a4287e0e50010af1432"),
"slug" : [
"true-crime-the-10-most-infamous-american-murder-mysteries",
"10-most-infamous-american-murder-mysteries"
],
"tags" : [
{
"id" : "59244aa6b1be5055278e9b5b",
"name" : "true crime",
"_id" : "59244aa6b1be5055278e9b5b"
},
{
"id" : "5924524db1be5055278ebd6e",
"name" : "Occult Museum",
"_id" : "5924524db1be5055278ebd6e"
},
{
"id" : "5a690f0fc1a72100110c2656",
"_id" : "5a690f0fc1a72100110c2656",
"name" : "murder mysteries"
},
{
"id" : "59244d71b1be5055278ea654",
"name" : "unsolved murders",
"_id" : "59244d71b1be5055278ea654"
}
]
}
We want to find list of all slugs group by tag name. I am trying with following and it gets result but it isn't accurate. We have hundreds of records with each tag but i only get few with my query. I am not sure what i am doing wrong here.
Thanks in advance.
// Requires official MongoShell 3.6+
db.getCollection("test").aggregate(
[
{
"$match" : {
"item_type" : "Post",
"site_id" : NumberLong(2),
"status" : NumberLong(1)
}
},
{$unwind: "$tags" },
{
"$group" : {
"_id" : {
"tags᎐name" : "$tags.name",
"slug" : "$slug"
}
}
},
{
"$project" : {
"tags.name" : "$_id.tags᎐name",
"slug" : "$_id.slug",
"_id" : NumberInt(0)
}
}
],
{
"allowDiskUse" : true
}
);
Expected output is
TagName Slug
----------
true crime "true-crime-the-10-most-infamous-american-murder-mysteries",
"10-most-infamous-american-murder-mysteries"
"All records where tags true crime"
Instead of using slug as a part of _id you should use $push or $addToSet to accumulate them, try:
db.test.aggregate([
{
$unwind: "$tags"
},
{
$unwind: "$slug"
},
{
$group: {
_id: "$tags.name",
slugs: { $addToSet: "$slug" }
}
},
{
$project: {
_id: 1,
slugs: {
$reduce: {
input: "$slugs",
initialValue: "",
in: {
$concat: [ "$$value", ",", "$$this" ]
}
}
}
}
}
])
EDIT: to get comma separated string for slugs you can use $reduce with $concat
Output:
{ "_id" : "murder mysteries", "slugs" : ",10-most-infamous-american-murder-mysteries,true-crime-the-10-most-infamous-american-murder-mysteries" }
{ "_id" : "Occult Museum", "slugs" : ",10-most-infamous-american-murder-mysteries,true-crime-the-10-most-infamous-american-murder-mysteries" }
{ "_id" : "unsolved murders", "slugs" : ",10-most-infamous-american-murder-mysteries,true-crime-the-10-most-infamous-american-murder-mysteries" }
{ "_id" : "true crime", "slugs" : ",10-most-infamous-american-murder- mysteries,true-crime-the-10-most-infamous-american-murder-mysteries" }

Mongo $filter with date comparison

I have the following aggregation which has a date comparison where I only want to grab documents that have the $$item.date less than the current time.
[
{ $match : { "_id" : "57b4c5f0291ebb13110b888e" } },
{ $project : {
"fulfillments" : {
$filter : {
"input" : "$fulfillments",
"as" : "item",
"cond" : { "$gte" : ["$$item.date","new Date()"]}
}
}
}
},
...
...
]
The important part that I have a question about above is the following:
"cond" : { "$gte" : ["$$item.date","new Date()"]}
This doesn't seem to be working as I can change the new Date() to 1 or 0 or pretty much any value and it still returns all documents. I need it to only return documents that have the date greater than or equal to the current date.
For example, given the following document
{
"_id": "57b4c5f0291ebb13110b888e",
"fulfillments": [
{
"_id": "582deb33bb117300010a2ae5",
"date": new Date("2016-11-23T17:00:00-0700"),
},
{
"_id": "582deb33bdf17300010a2ae5",
"date": new Date("2017-11-23T17:00:00-0700"),
}
}
Only the following fulfillment should be returned because it is 2017-11-23
{
"_id": "582deb33bdf17300010a2ae5",
"date": new Date("2017-11-23T17:00:00-0700"),
}
Update
There is question if I am giving an accurate document strucutre, so I included a screenshot below to validate this.
If you just want the current time on the machine just remove the " from the query, see below for my example
> db.test.find().pretty()
{
"_id" : "57b4c5f0291ebb13110b888e",
"fulfillments" : [
{
"_id" : "582deb33bb117300010a2ae5",
"date" : ISODate("2016-11-24T00:00:00Z")
},
{
"_id" : "582deb33bdf17300010a2ae5",
"date" : ISODate("2017-11-24T00:00:00Z")
}
]
}
>
> db.test.aggregate([
... { $match : { "_id" : "57b4c5f0291ebb13110b888e" } },
... { $project : {
... "fulfillments" : {
... $filter : {
... "input" : "$fulfillments",
... "as" : "item",
... "cond" : { "$gte" : ["$$item.date",new Date()]}
... }
... }
... }
... }
... ]).pretty()
{
"_id" : "57b4c5f0291ebb13110b888e",
"fulfillments" : [
{
"_id" : "582deb33bdf17300010a2ae5",
"date" : ISODate("2017-11-24T00:00:00Z")
}
]
}
>

mongodb aggregation find min value and other fields in nested array

Is it possible to find in a nested array the max date and show its price then show the parent field like the actual price.
The result I want it to show like this :
{
"_id" : ObjectId("5547e45c97d8b2c816c994c8"),
"actualPrice":19500,
"lastModifDate" :ISODate("2015-05-04T22:53:50.583Z"),
"price":"16000"
}
The data :
db.adds.findOne()
{
"_id" : ObjectId("5547e45c97d8b2c816c994c8"),
"addTitle" : "Clio pack luxe",
"actualPrice" : 19500,
"fistModificationDate" : ISODate("2015-05-03T22:00:00Z"),
"addID" : "1746540",
"history" : [
{
"price" : 18000,
"modifDate" : ISODate("2015-05-04T22:01:47.272Z"),
"_id" : ObjectId("5547ec4bfeb20b0414e8e51b")
},
{
"price" : 16000,
"modifDate" : ISODate("2015-05-04T22:53:50.583Z"),
"_id" : ObjectId("5547f87e83a1dae00bc033fa")
},
{
"price" : 19000,
"modifDate" : ISODate("2015-04-04T22:53:50.583Z"),
"_id" : ObjectId("5547f87e83a1dae00bc033fe")
}
],
"__v" : 1
}
my query
db.adds.aggregate(
[
{ $match:{addID:"1746540"}},
{ $unwind:"$history"},
{ $group:{
_id:0,
lastModifDate:{$max:"$historique.modifDate"}
}
}
])
I dont know how to include other fields I used $project but I get errors
thanks for helping
You could try the following aggregation pipeline which does not need to make use of the $group operator stage as the $project operator takes care of the fields projection:
db.adds.aggregate([
{
"$match": {"addID": "1746540"}
},
{
"$unwind": "$history"
},
{
"$project": {
"actualPrice": 1,
"lastModifDate": "$history.modifDate",
"price": "$history.price"
}
},
{
"$sort": { "lastModifDate": -1 }
},
{
"$limit": 1
}
])
Output
/* 1 */
{
"result" : [
{
"_id" : ObjectId("5547e45c97d8b2c816c994c8"),
"actualPrice" : 19500,
"lastModifDate" : ISODate("2015-05-04T22:53:50.583Z"),
"price" : 16000
}
],
"ok" : 1
}