Filter out inner collection on mongodb/mongoose - mongodb

Can you tell me how to filter out on videoDetails where vimeo_id==null? According to the below query, it retrieves all the data. i.e. including the none Vimeo videos. Here I have used Mongoose.
Data:
{
"_id": ObjectId("5b10e8c475356969da5f91b2"),
"playlists": [{
"listId": "5b26a1040c4ebb107f0038e5",
"title": "Featured Stories (copy)",
"playlist_item_count": 1,
"sortOrder": 1,
"_id": ObjectId("5b37adf40fc31552f9280603"),
"isPrimary": true,
"videoDetails": [{
"short_description": "",
"vimeo_id": null,
"title": "Managing-Time-Effectively-Section-4-Lecture-1-Questions-and-Answers.mp4",
"duration": 493,
"videoId": "5a3c2f03e338f91564000130",
"_id": ObjectId("5b37adf40fc31552f9280604"),
"accessLevel": "public"
}]
},
{
"listId": "5b375560d80ed51238004998",
"title": "Atv-test",
"playlist_item_count": 1,
"sortOrder": 1,
"_id": ObjectId("5b37adf40fc31552f9280601"),
"isPrimary": false,
"videoDetails": [{
"short_description": "When Alice’s husband arrives home to try and make amends it leaves her navigating a tricky situation.",
"vimeo_id": "277029143",
"title": "Love Is Blind",
"duration": 385,
"videoId": "5b375add2ef7c612f1002304",
"presentedBy": "CEO TEAM",
"_id": ObjectId("5b37adf40fc31552f9280602"),
"accessLevel": "public"
}]
}
],
"__v": 90
}
get
get(req) {
mongoose.connect(config.mongoConnectionUrl, { useMongoClient: true })
return Playlist
.findOne({})
.exec()
.then((playlist) => {
mongoose.disconnect()
return Promise.resolve({ error: false, playlist })
})
.catch((e) => {
mongoose.disconnect()
const err = prepareApiError(e)
return new api.ApiResponse({ error: true, reason: err.reason }, { "Content-Type": "application/json" }, err.errorCode)
})
},

You can try below aggregation
db.collection.aggregate([
{ "$project": {
"playlists": {
"$map": {
"input": "$playlists",
"as": "play",
"in": {
"listId": "$$play.listId",
"title": "$$play.title",
"playlist_item_count": "$$play.playlist_item_count",
"sortOrder": "$$play.sortOrder",
"_id": "$$play._id",
"isPrimary": "$$play.isPrimary",
"videoDetails": {
"$filter": {
"input": "$$play.videoDetails",
"as": "video",
"cond": { "$ne": ["$$video.vimeo_id", null] }
}
}
}
}
}
}}
])

Related

MongoDB $pull failing in different ways intermittently

I tried everything. update, updateMany, findOndAndUpdate, findByIdAndUpdate. I want to delete the object that matches the notification id.
indifferent approach it returns me the object, sometimes returns "mongodb cannot apply $pull to a non-array value"
and sometimes "mongodb matchedCount: 1 but modify 0".
This is one of the examples I tried
const userNotifications = await UserModel.findOneAndUpdate({_id: teacherUpdate.userId}, {
$pull: { notifications: {_id: notificationId} }
})
My DB:
{
"_id": "46df7324d6760d",
"username": "teacher",
"image": null,
"firstname": "Teacher",
"lastname": "Killer",
"skills": null,
"teacher": {
"teacherId": "046e2734d6260dc6"
},
"student": null,
"email": "teacher#gmail.com",
"activeSession": false,
"__v": 1,
"notifications": [
{
"type": "Lesson request",
"content": {
"date": "8.25",
"time": "10",
"status": "Pending",
"studentId": "d03d3c6d0085",
"teacherId": "2732d6286dc6"
},
"_id": "f9659d128c297f85",
"__v": 0
},
{
"type": "Lesson request",
"content": {
"date": "8.26",
"time": "08",
"status": "Pending",
"studentId": "8b4d3d3f8f3485",
"teacherId": "6e273246286d9c6"
},
"_id": "9659d1128c8b",
"__v": 0
}
]
},
Try this.
UserModel.update(
{ '_id': teacherUpdate.userId },
{ $pull: { notifications: { _id: notificationId } } },
false, // Upsert
true, // Multi
);
Edit
this should work
let updatedUser = await UserModel.findOneAndUpdate({ '_id': teacherUpdate.userId }, { $pull: { notifications: { _id: notificationId } } }, {
new: true
});

Mongodb aggregation nested lookup

I have 3 collections as follows:
GroupRoles Collection:
//1
{
"_id": ObjectId("62a384ee0c4dbafc64000fba"),
"name": "GroupRole template 1",
"groupRoles": [
{
"_id": ObjectId("6298503f8a31000024002107"),
"members": [
ObjectId("629e1bb117366c39bc7d78e1")
]
},
{
"_id": ObjectId("629850368a31000024002106"),
"members": [
ObjectId("629ee6d502877d0f93f5dabe"),
ObjectId("629ee6d002877d0f93f5dab8")
]
},
{
"_id": ObjectId("6298502f8a31000024002105"),
"members": [ ]
},
{
"_id": ObjectId("629850288a31000024002104"),
"members": [ ]
},
{
"_id": ObjectId("6298501f8a31000024002103"),
"members": [ ]
},
{
"_id": ObjectId("629850128a31000024002102"),
"members": [ ]
}
]
}
Role Collections:
// 1 {"_id": ObjectId("629850128a31000024002102"), "type": 1, "name": "Role 1",}
// 2 {"_id": ObjectId("6298501f8a31000024002103"), "type": 1, "name": "Role 2",}
// 3 {"_id": ObjectId("629850288a31000024002104"), "type": 1, "name": "Role 3",}
// 4 {"_id": ObjectId("6298502f8a31000024002105"), "type": 1, "name": "Role 4",}
// 5 {"_id": ObjectId("629850368a31000024002106"), "type": 1, "name": "Role 5",}
// 6 {"_id": ObjectId("6298503f8a31000024002107"), "type": 1, "name": "Role 6",}
and User Collection:
// 1 {"_id": ObjectId("629e1bb117366c39bc7d78e1"), "email": "abc1#gmail.com", "name": "user 01"}
// 2 {"_id": ObjectId("629ee6d502877d0f93f5dabe"), "email": "abc2#gmail.com", "name": "user 02"}
// 3 {"_id": ObjectId("629ee6d002877d0f93f5dab8"), "email": "abc3#gmail.com", "name": "user 03"}
How can select GroupRoles with output format like this:
{
"_id": ObjectId("62a384ee0c4dbafc64000fba"),
"name": "GroupRole template 1",
"groupRoles": [
{
"_id": ObjectId("6298503f8a31000024002107"),
"type": 1,
"name": "Role 6",
"members": [
{
"_id": ObjectId("629e1bb117366c39bc7d78e1"),
"email": "abc1#gmail.com",
"name": "user 01"
}
]
},
{
"_id": ObjectId("629850368a31000024002106"),
"type": 1,
"name": "Role 5",
"members": [
{
"_id": ObjectId("629ee6d502877d0f93f5dabe"),
"email": "abc2#gmail.com",
"name": "user 02"
},
{
"_id": ObjectId("629ee6d002877d0f93f5dab8"),
"email": "abc3#gmail.com",
"name": "user 03"
}
]
},
{
"_id": ObjectId("6298502f8a31000024002105"),
"type": 1,
"name": "Role 4",
"members": [ ]
},
{
"_id": ObjectId("629850288a31000024002104"),
"type": 1,
"name": "Role 3",
"members": [ ]
},
{
"_id": ObjectId("6298501f8a31000024002103"),
"type": 1,
"name": "Role 2",
"members": [ ]
},
{
"_id": ObjectId("629850128a31000024002102"),
"type": 1,
"name": "Role 1",
"members": [ ]
}
]
}
I've tried aggregation and lookup, but it didn't get the results I wanted.
here is my aggregation query
The list of members is not in the correct index of groupRoles
[
{
'$lookup': {
'from': 'roles',
'localField': 'groupRoles._id',
'foreignField': '_id',
'as': 'temp_roles'
}
}, {
'$lookup': {
'from': 'users',
'localField': 'groupRoles.members',
'foreignField': '_id',
'as': 'temp_users'
}
}, {
'$addFields': {
'groupRoles': {
'$map': {
'input': '$groupRoles.members',
'as': 'mems',
'in': {
'members': {
'$filter': {
'input': '$temp_users',
'as': 'temu',
'cond': {
'$in': [
'$$temu._id', '$$mems'
]
}
}
}
}
}
}
}
}, {
'$addFields': {
'groupRoles': {
'$map': {
'input': {
'$zip': {
'inputs': [
'$groupRoles', '$temp_roles'
]
}
},
'in': {
'$mergeObjects': '$$this'
}
}
}
}
}, {
'$unset': [
'temp_roles', 'temp_users'
]
}, {
'$facet': {
'metadata': [
{
'$group': {
'_id': null,
'total': {
'$sum': 1
}
}
}
],
'data': []
}
}, {
'$project': {
'data': {
'_id': 1,
'name': 1,
'groupRoles': {
'_id': 1,
'type': 1,
'name': 1,
'members': {
'_id': 1,
'name': 1,
'email': 1
}
}
},
'total': {
'$arrayElemAt': [
'$metadata.total', 0
]
}
}
}
]
please help me find the solution.
Revise your query and thanks for the hints.
The issue is on $zip, which I think this operator is not suitable. You can check the behavior of $zip.
Combine both $addFields stages into one.
Iterate each value in groupRoles and with $mergeObjects for
Current iterate value.
The first match document from the temp_roles array by matching _id.
The document with members array.
db.groupRoles.aggregate([
{
"$lookup": {
"from": "roles",
"localField": "groupRoles._id",
"foreignField": "_id",
"as": "temp_roles"
}
},
{
"$lookup": {
"from": "users",
"localField": "groupRoles.members",
"foreignField": "_id",
"as": "temp_users"
}
},
{
"$addFields": {
"groupRoles": {
"$map": {
"input": "$groupRoles",
"as": "gr",
"in": {
"$mergeObjects": [
"$$gr",
{
$first: {
"$filter": {
"input": "$temp_roles",
"as": "r",
"cond": {
$eq: [
"$$gr._id",
"$$r._id"
]
}
}
}
},
{
"members": {
"$filter": {
"input": "$temp_users",
"as": "mem",
"cond": {
"$in": [
"$$mem._id",
"$$gr.members"
]
}
}
}
}
]
}
}
}
}
},
{
"$unset": [
"temp_roles",
"temp_users"
]
},
])
Sample Mongo Playground

Merge documents from 2 collections in MongoDB & preserve property of a field

I have two collections, 1. temporaryCollection, 2. permanentCollection, I would like to take data from temporaryCollection and update in permanentCollection. To see the expected result see updatedPermanentCollection below.
Fields that are taken from Temporary collection and updated in Permanent collection are:
emailAddresses
phoneNumbers
ContactName
ContactNumber
For your info, the fields that are changed in Temporary collection
contacts[0]['emailAddresses']
contacts[0]['ContactName']
contacts[0]["phoneNumbers"]
contacts[0]["ContactNumber"]
Field that are that should not be changed after updation in UpdatedPermanentCollection is
contacts._id
Note: contacts is an Array of objects, for simplicity I have shown just one object.
I am currently using the below query which updates the permanentCollection but also overrides the contacts._id field. I don't want the contacts._id field to be overridden.
Here is my MongoDB Query
db.temporaryCollection.aggregate([
{
$match: {
userID: ObjectId("61d1efea2c0fab00340f47c8"),
},
},
{
$merge: {
into: "permanentCollection",
on: "userID",
whenMatched: "merge",
whenNotMatched: "insert",
},
},
]);
1. temporaryCollection
{
"_id": { "$oid": "61d1f04266289f003452d705" },
"userID": { "$oid": "61d1efea2c0fab00340f47c8" },
"contacts": [
{
"emailAddresses": [
{ "id": "6884", "label": "email1", "email": "addedemail#gmail.com" }
],
"phoneNumbers": [
{
"label": "other",
"id": "4594",
"number": "+918984292930"
},
{
"label": "other",
"id": "4595",
"number": "+911234567890"
}
],
"_id": { "$oid": "61d1f04266289f003452d744" },
"ContactName": "Sample User 1 Name Changed",
"ContactNumber": "+918984292930",
"recordID": "833"
}
],
"userNumber": "+911234567890",
"__v": 7
}
2. permanentCollection
{
"_id": { "$oid": "61d1f04266289f003452d701" },
"userID": { "$oid": "61d1efea2c0fab00340f47c8" },
"contacts": [
{
"emailAddresses": [],
"phoneNumbers": [
{
"label": "other",
"id": "4594",
"number": "+918984292929"
},
{
"label": "other",
"id": "4595",
"number": "+911234567890"
}
],
"_id": { "$oid": "61d1f04266289f003452d722" },
"ContactName": "Sample User 1",
"ContactNumber": "+918984292929",
"recordID": "833"
}
],
"userNumber": "+911234567890",
"__v": 7
}
3. updatedPermanentCollection (Expected result)
{
"_id": { "$oid": "61d1f04266289f003452d701" },
"userID": { "$oid": "61d1efea2c0fab00340f47c8" },
"contacts": [
{
"emailAddresses": [
{ "id": "6884", "label": "email1", "email": "addedemail#gmail.com" }
],
"phoneNumbers": [
{
"label": "other",
"id": "4594",
"number": "+918984292930"
},
{
"label": "other",
"id": "4595",
"number": "+911234567890"
}
],
"_id": { "$oid": "61d1f04266289f003452d722" },
"ContactName": "Sample User 1 Name Changed",
"ContactNumber": "+918984292930",
"recordID": "833"
}
],
"userNumber": "+911234567890",
"__v": 7
}
Try with this aggregation query.
db.temporarCollection.aggreagate(
[
{
"$lookup": {
"from": "permanantCollection",
"let": {
"user_id": "$userID"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$$user_id", "$userID"
]
}
}
}
],
"as": "pcontacts"
}
}, {
"$unwind": {
"path": "$pcontacts",
"preserveNullAndEmptyArrays": true
}
}, {
"$project": {
"contacts": {
"$map": {
"input": "$contacts",
"as": "contact",
"in": {
"tcontact": "$$contact",
"pcontact": {
"$first": {
"$filter": {
"input": "$pcontacts.contacts",
"as": "pcontact",
"cond": {
"$eq": [
"$$pcontact.recordID", "$$contact.recordID"
]
}
}
}
}
}
}
},
"userNumber": 1,
"userID": 1,
"_id": 0
}
}, {
"$project": {
"contacts": {
"$map": {
"input": "$contacts",
"as": "contact",
"in": {
"emailAddresses": "$$contact.tcontact.emailAddresses",
"phoneNumbers": "$$contact.tcontact.phoneNumbers",
"ContactName": "$$contact.tcontact.ContactName",
"ContactNumber": "$$contact.tcontact.ContactNumber",
"recordID": {
"$let": {
"vars": {},
"in": {
"$cond": {
"if": "$$contact.pcontact.recordID",
"then": "$$contact.pcontact.recordID",
"else": "$$contact.tcontact.recordID"
}
}
}
},
"_id": {
"$let": {
"vars": {},
"in": {
"$cond": {
"if": "$$contact.pcontact._id",
"then": "$$contact.pcontact._id",
"else": "$$contact.tcontact._id"
}
}
}
}
}
}
},
"userNumber": 1,
"userID": 1
}
}, {
"$merge": {
"into": "pc",
"on": "userID",
"whenMatched": "replace",
"whenNotMatched": "insert"
}
}
])
It is not a fully optimized query but it works.
Try to add $unset to db query.
db.temporaryCollection.aggregate([
{
$unset: "_id"
},
{
$match: {
userID: ObjectId("61d1efea2c0fab00340f47c8"),
},
},
{
$merge: {
into: "permanentCollection",
on: "userID",
whenMatched: "merge",
whenNotMatched: "insert",
},
},
]);

MongoDB inner join with specific condition from both collections

I have got two collections, chapters and courses with one-to-one association,
db={
"chapters": [
{
"_id": 10,
"course_id": 1,
"author": "John"
},
{
"_id": 20,
"course_id": 2,
"author": "John"
},
{
"_id": 30,
"course_id": 3,
"author": "Timmy"
},
{
"_id": 40,
"course_id": 4,
"author": "John"
},
],
"courses": [
{
"_id": 1,
"published": true,
"name": "course 1"
},
{
"_id": 2,
"published": false,
"name": "course 2"
},
{
"_id": 3,
"published": true,
"name": "course 3"
},
{
"_id": 4,
"published": true,
"name": "course 4"
}
]
}
How do I query all chapters with the published course (published=true) and the author is "John"?
You can use $match, $lookup then $group by author then simply $filter it based on published
db.chapters.aggregate([
{
$match: {
author: "John"
}
},
{
"$lookup": {
"from": "courses",
"localField": "course_id",
"foreignField": "_id",
"as": "course"
}
},
{
$project: {
_id: 1,
course: {
$first: "$course"
},
author: 1
}
},
{
$group: {
_id: "$author",
courseChapters: {
$push: "$$ROOT"
}
}
},
{
$project: {
courseChapters: {
$filter: {
input: "$courseChapters",
as: "cc",
cond: {
$eq: [
"$$cc.course.published",
true
]
}
}
}
}
}
])
Output
[
{
"_id": "John",
"courseChapters": [
{
"_id": 10,
"author": "John",
"course": {
"_id": 1,
"name": "course 1",
"published": true
}
},
{
"_id": 40,
"author": "John",
"course": {
"_id": 4,
"name": "course 4",
"published": true
}
}
]
}
]
Mongoplayground: https://mongoplayground.net/p/x-XghXzZUk4

Mongo Aggregation using $Max

I have a collection that stores history, i.e. a new document is created every time a change is made to the data, I need to extract fields based on the max value of a date field, however my query keeps returning either all of the dates or requires me to push the fields into an array which make the data hard to analyze for an end-user.
Expected output as CSV:
MAX(DATE), docID, url, type
1579719200216, 12371, www.foodnetwork.com, food
1579719200216, 12371, www.cnn.com, news,
1579719200216, 12371, www.wikipedia.com, info
Sample Doc:
{
"document": {
"revenueGroup": "fn",
"metaDescription": "",
"metaData": {
"audit": {
"lastModified": 1312414124,
"clientId": ""
},
"entities": [],
"docId": 1313943,
"url": ""
},
"rootUrl": "",
"taggedImages": {
"totalSize": 1,
"list": [
{
"image": {
"objectId": "woman-reaching-for-basket",
"caption": "",
"url": "",
"height": 3840,
"width": 5760,
"owner": "Facebook",
"alt": "Woman reaching for basket"
},
"tags": {
"totalSize": 4,
"list": []
}
}
]
},
"title": "The 8 Best Food Items of 2020",
"socialTitle": "The 8 Best Food Items of 2020",
"primaryImage": {
"objectId": "woman-reaching-for-basket.jpg",
"caption": "",
"url": "",
"height": 3840,
"width": 5760,
"owner": "Hero Images / Getty Images",
"alt": "Woman reaching for basket in laundry room"
},
"subheading": "Reduce your footprint with these top-performing diets",
"citations": {
"list": []
},
"docId": 1313943,
"revisionId": "1313943_1579719200216",
"templateType": "LIST",
"documentState": {
"activeDate": 579719200166,
"state": "ACTIVE"
}
},
"url": "",
"items": {
"totalSize": "",
"list": [
{
"type": "recipe",
"data": {
"comInfo": {
"list": [
{
"type": "food",
"id": "https://www.foodnetwork.com"
}
]
},
"type": ""
},
"id": 4,
"uuid": "1313ida-qdad3-42c3-b41d-223q2eq2j"
},
{
"type": "recipe",
"data": {
"comInfo": {
"list": [
{
"type": "news",
"id": "https://www.cnn.com"
},
{
"type": "info",
"id": "https://www.wikipedia.com"
}
]
},
"type": "PRODUCT"
},
"id": 11,
"uuid": "318231jc-da12-4475-8994-283u130d32"
}
]
},
"vertical": "food"
}
Below query:
db.collection.aggregate([
{
$match: {
vertical: "food",
"document.documentState.state": "ACTIVE",
"document.templateType": "LIST"
}
},
{
$unwind: "$document.items"
},
{
$unwind: "$document.items.list"
},
{
$unwind: "$document.items.list.contents"
},
{
$unwind: "$document.items.list.contents.list"
},
{
$match: {
"document.items.list.contents.list.type": "recipe",
"document.revenueGroup": "fn"
}
},
{
$sort: {
"document.revisionId": -1
}
},
{
$group: {
_id: {
_id: {
docId: "$document.docId",
date: {$max: "$document.revisionId"}
},
url: "$document.items.list.contents.list.data.comInfo.list.id",
type: "$document.items.list.contents.list.data.comInfo.list.type"
}
}
},
{
$project: {
_id: 1
}
},
{
$sort: {
"document.items.list.contents.list.id": 1, "document.revisionId": -1
}
}
], {
allowDiskUse: true
})
First of all, you need to go through the documentation of the $group aggregation here.
you should be doing this instead:
{
$group: {
"_id": "$document.docId"
"date": {
$max: "$document.revisionId"
},
"url": {
$first: "$document.items.list.contents.list.data.comInfo.list.id"
},
"type": {
$first:"$document.items.list.contents.list.data.comInfo.list.type"
}
}
}
This will give you the required output.