My first collection, called Scenes looks like this:
{
sceneId: "id1",
contentId: "contentId1",
comments: [{
comment: "abc",
userId: "test1"
},
{
comment: "def",
userId: "test2"
}]
},
{
sceneId: "id2",
contentId: "contentId2",
comments: [{
comment: "abc",
userId: "test1"
},
{
comment: "def",
userId: "test2"
}]
}
Any my second collection, called Userdatas, looks like this:
{
userId: "test1",
profilPicture: "def.jpg"
},
{
userId: "test2",
profilPicture: "abc.jpg"
}
And I want to join them in a way, that I get the following:
{
sceneId: "id1",
contentId: "contentId1",
comments: [{
comment: "abc",
userId: "test1",
profilPicture: "def.jpg"
},
{
comment: "def",
userId: "test2",
profilPicture: "abc.jpg"
}]
},
{
sceneId: "id2",
contentId: "contentId2",
comments: [{
comment: "abc",
userId: "test1",
profilPicture: "def.jpg"
},
{
comment: "def",
userId: "test2",
profilPicture: "abc.jpg"
}]
}
and I have no idea how to do this, all my previous attempts failed. Help is appreciated! The key problem is, that the $group operator will not show the contentId, when grouping via the sceneId
You can use this aggregation query:
First $unwind comments array to destructure and join by comments.userId.
Then $lookup that is like a JOIN, to merge userIds between collections generating a object called data.
Destructure $data to get values.
Add profilPicture to the object comments using $set.
$group by sceneId adding all comments into an array.
And use $projection to show only fields you want.
db.Scenes.aggregate([
{
"$unwind": "$comments"
},
{
"$lookup": {
"from": "Userdatas",
"localField": "comments.userId",
"foreignField": "userId",
"as": "data"
}
},
{
"$unwind": "$data"
},
{
"$set": {
"comments.profilPicture": "$data.profilPicture"
}
},
{
"$group": {
"_id": "$sceneId",
"comments": {
"$push": "$comments"
}
}
},
{
"$project": {
"_id": 0,
"sceneId": "$_id",
"comments": 1
}
}
])
Example here
Related
I am using MongoDB 4.2.9 and have the following requirements:
Collection 'A' has multiple documents with a string field 'status' that I need to filter on
Collection 'B' has multiple documents
Collection A
{ _id: "1",
status: "Report",
type: "Academy",
rating: "Excellent",
ReportNo: "A1"
},
{ _id: "2",
status: "Open",
type: "Academy",
rating: "",
ReportNo: ""
},
{ _id: "3",
status: "Draft",
type: "Academy",
rating: "",
ReportNo: ""
},
{ _id: "4",
status: "Report",
type: "Academy",
rating: "Great",
ReportNo: "A4"
}
Collection B
{ _id: "98",
status: "Archived",
type: "Academy",
rating: "So So",
ReportNo: "X2"
},
{ _id: "99",
status: "Archived",
type: "Academy",
rating: "Great",
ReportNo: "X1"
}
Resulting View
{ _id: "1",
status: "Report",
type: "Academy",
rating: "Excellent",
ReportNo: "A1"
},
{ _id: "4",
status: "Report",
type: "Academy",
rating: "Great",
ReportNo: "A4"
},
{ _id: "98",
status: "Archived",
type: "Academy",
rating: "So So",
ReportNo: "X2"
},
{ _id: "99",
status: "Archived",
type: "Academy",
rating: "Great",
ReportNo: "X1"
}
My goal is to create an aggregation view so that I can filter on a status value in Collection 'A' and then merge those results with Collection 'B' and show in the view ?
I can filter on Collection 'A' using the match call, just can't see how to merge resulting documents into Collection 'B'
From my understandings, your "merge" behaviour is actually a union view of filtered view of collection A and collection B.
With MongoDB v4.2, you can use $facet to handle collection A and collection B separately.
simply perform filtering on A
perform uncorrelated $lookup on B
wrangle the result and merge them together to get the union view that you are looking for.
db.createCollection(
"unionView",
{
"viewOn" : "A",
"pipeline" : [
{
"$facet": {
"A": [
{
"$match": {
status: "Report"
}
}
],
"B": [
{
$limit: 1
},
{
"$lookup": {
"from": "B",
"pipeline": [],
"as": "B"
}
},
{
$unwind: "$B"
},
{
"$replaceRoot": {
"newRoot": "$B"
}
}
]
}
},
{
$project: {
all: {
"$setUnion": [
"$A",
"$B"
]
}
}
},
{
$unwind: "$all"
},
{
"$replaceRoot": {
"newRoot": "$all"
}
}
]
}
)
Here is the Mongo Playground for your reference.
With MongoDB v4.4+, you can create a view with $unionWith
db.createCollection(
"unionView",
{
"viewOn" : "A",
"pipeline" : [
{
"$match": {
status: "Report"
}
},
{
"$unionWith": {
"coll": "B"
}
}
]
}
)
Here is the Mongo playground for your reference.
I saw similar questions here, but can't really figure this problem out. I've got the following orders collection:
{
orders: [
{
userId: "abc",
orderId: "123",
balance: 2,
},
{
userId: "abc",
orderId: "123",
balance: 5,
},
{
userId: "def",
orderId: "456",
balance: 1,
},
{
userId: "abc",
orderId: "456",
balance: 3,
},
];
}
I need an aggregation query that would return the sum of balances for the given userId AND orderId. For this following example, given userId = "abc" and orderId = "123", the result of that query would be 7. So far, I have tried $map and $sum, but can't really put together the structure of the query.
How can I get the sum of the balances given the userId AND orderId ?
I managed to work it out with this:
db.collection.aggregate([
{
$match: {
"userId": "abc",
"orderId": "123"
}
},
{
$group: {
_id: "$userId",
total: {
$sum: "$amount"
}
}
},
{
$sort: {
total: -1
}
}
])
orders.aggregate([
{
"$group": {
"_id": {
"user": "$userId",
"order": "$orderId"
},
"amount": {
"$sum": "$balance"
}
}
}
])
Let's say I have a document like this:
{
_id: ObjectId("1234567890"),
author: "ABC1",
text:"this is a Post",
details: {
time: "14/05/2015",
Edit: "none"
},
comments: [
{
comment_text: "Hello",
user: "alan",
time:"20/05/2014 20:44"
},
{
comment_text: "Hi Every One",
user: "bob",
time:"20/05/2014 20:44"
},
{
comment_text: "Good morning , Alan",
user: "Alan",
time:"20/05/2014 20:44"
},
{
comment_text: "I'm bob",
user: "bob",
time:"20/05/2014 20:44"
},
],
category: "IT"
}
I want to build a query that returns this object but with only Bob's comments in the comments array.
{
author: "ABC1",
text:"this is a Post",
details: {
time: "14/05/2015",
Edit: "none"
},
comments: [
{
comment_text: "Hi Every One",
user: "bob",
time:"20/05/2014 20:44"
},
{
comment_text: "I'm bob",
user: "bob",
time:"20/05/2014 20:44"
},
],
category: "IT"
}
How can this be done?
Using $unwind (and $match) in an aggregation pipeline will get me the correct subdocuments, but not as an array of objects within the original document.
Using $elemMatch within a projection (with find() or findOne()) only returns the first matching comment (subdocument).
You can use the $filter operator in the $project stage of an aggregation pipeline:
db.collection.aggregate([
{
$project: {
_id: 0,
author: 1,
text: 1,
details: 1,
category: 1,
comments: {
$filter: {
input: "$comments",
as: "comment",
cond: {
$eq: [
"$$comment.user",
"bob"
]
}
}
}
}
}
])
Example mongoplayground
I have a document like this
{
"_id": {
"$oid": "5c7369826023661073802f63"
},
"participants": [
{
"id": "ABC",
"nickname": "USER1",
},
{
"id": "DEF",
"nickname": "USER2",
}
]},... etc, et
I want to find the record that has the two ids that you provide
I try with this.
moodel.aggregate([
{
$match:{'participants.id':idOne}
},{
$project:{
list:{
$filter:{
input:'$list',
as:'item',
cond: {$eq: ['$$item.participants.id', idTwo]}
}
},
}
}
])
but the result is:
[ { _id: 5c7369826023661073802f63, list: null }]
I want it to return only the record that match the two ids.
use $elematch and $in
https://docs.mongodb.com/manual/reference/operator/query/elemMatch/
https://docs.mongodb.com/manual/reference/operator/query/in/
db.moodel.find({"participants": {$elemMatch: {id: {$in: [idOne, idTwo]}}}})
This is a document in the collection BlogPosts:
{
_id: ObjectId("..."),
post_title: "Hello World!",
post_body: "",
comments: [
{ user_id: ObjectId("123"), body: "nice post!" },
{ user_id: ObjectId("456"), body: "awesome!" },
]
}
I would like to display comments with the user's first name, which is found in the referenced document in the Users collection:
{
_id: ObjectId("123"),
first_name: "Marion",
last_name: "Smith",
email_address: "marion#example.com",
password: "..."
}
Is there a way to retrieve the BlogPosts document while including first_name from this referenced data?
For example, I'm looking for an output like this (each comment has a first name):
{
_id: ObjectId("..."),
post_title: "Hello World!",
post_body: "",
comments: [
{ user_id: ObjectId("..."), first_name: "Marion", body: "nice post!" },
{ user_id: ObjectId("..."), first_name: "Margaret", body: "awesome!" },
]
}
I'm using Mongoose.
You can use below aggregation
db.collection.aggregate([
{ "$unwind": "$comments" },
{ "$lookup": {
"from": "users",
"let": { "userId": "$comments.user_id" },
"pipeline": [{ "$match": { "$expr": { "$eq": ["$$userId", "$_id"] } } }],
"as": "user"
}},
{ "$addFields": {
"comments.first_name": { "$arrayElemAt": ["$user.first_name", 0] }
}},
{ "$group": {
"_id": "$_id",
"comments": { "$push": "$comments" },
"post_title": { "$first": "$post_title" },
"post_body": { "$first": "$post_body" }
}}
])
I've since found a more straightforward approach, using just Populate.
BlogPosts
.findOne({_id: req.params.id})
.populate('comments.user_id', ['first_name', 'last_name'])
.then(post => console.log(post))
In the schema for BlogPosts, a ref should be defined for the comments.user_id field:
const User = require('./User.model.js');
const blogPostSchema = new Schema({
post_title: { type: String },
post_body: { type: String },
comments: [{
user_id: {
type: Schema.ObjectId,
ref: 'User' <-------- here
}
}]
});
const BlogPost = mongoose.model('BlogPost', blogPostSchema);
module.exports = BlogPost;