my collection:
groups : [{_id: 001, name: ABC, type: a}, {_id: 002, name: DEF, type: b}]
I'm doing the below coding to get result in mongodb:
.aggregate([
{
$lookup: {
from: 'groups',
localField: 'groupId',
foreignField: '_id',
as: 'groupName'
}
},
{
$unwind: {
path: '$groupName',
preserveNullAndEmptyArrays: true
}
},
{
$project:
groupName: {
name: 1
}
}
The result I get from the above coding is :
groupName: Object { name: "ABC" }
But I don't want the ABC to be as an object.
I want my result to be single element:
groupName: "ABC"
Any idea how to do it?
Using $project you can achieve your expected result.
.aggregate([
{
$lookup: {
from: 'groups',
localField: 'groupId',
foreignField: '_id',
as: 'groupName'
}
},
{
$unwind: {
path: '$groupName',
preserveNullAndEmptyArrays: true
}
},
{
$project:
{groupName: "$groupName.name"}
}
Related
I'm trying to add in a project pipeline where the values are greater than 100, the values are fields inside and object which are part of an array. I have something like this:
Database:
---Clients Collection---
client: {
_id: 1,
taxID: aldsfkjasdlñfk
// other stuff
}
---Invoices Collection---
invoice: {
_id: 1,
clientID: 1,
total: 50
},
invoice: {
_id: 2,
clientID: 1,
total: 150
},
invoice: {
_id: 3,
clientID: 1,
total: 200
}
AND THIS IS MY QUERY:
{
$lookup: {
from: 'invoices',
localField: '_id',
foreignField: 'client.id',
as: 'invoices'
}
},
{
$project: {
id: 1,
taxID: aldsfkjasdlñfk,
invoicesAmountGreaterThanOneHundred: {
$sum: {
$cond: { if: { $gte: ['$invoices.total', 100] }, then: '$invoices.total', else: 0 }
}
}
}
}
So the output should be:
{
_id: 1.
taxID: aldsfkjasdlñfk,
invoicesAmountGreaterThanOneHundred: 350
}
I'm using Mongo 3.6.3.
Also in the future I will add a "invoicesAmountLesserThanOneHundred", same method, but for lesser than 100 of course.
use $filter before $sum
db.client.aggregate([
{
$lookup: {
from: "invoices",
localField: "_id",
foreignField: "clientID",
as: "invoices"
}
},
{
$set: {
"invoices": {
"$filter": {
"input": "$invoices",
"as": "i",
"cond": { $gte: [ "$$i.total", 100 ] }
}
}
}
},
{
$project: {
id: 1,
taxID: 1,
invoicesAmountGreaterThanOneHundred: {
$sum: "$invoices.total"
}
}
}
])
mongoplayground
use $reduce
db.client.aggregate([
{
$lookup: {
from: "invoices",
localField: "_id",
foreignField: "clientID",
as: "invoices"
}
},
{
$set: {
"invoicesAmountGreaterThanOneHundred": {
$reduce: {
input: "$invoices",
initialValue: "",
in: {
$sum: [
"$$value",
{
$cond: {
if: { $gte: [ "$$this.total", 100 ] },
then: "$$this.total",
else: 0
}
}
]
}
}
}
}
}
])
mongoplayground
I have these 2 collections in MongoDb:
User
{
_id: ObjectId("xxxxxxxxx"),
roleId: ObjectId: ("xxxxxxxxx)
}
Courses
{
_id: ObjectId("xxxxxxxxx"),
name: "Course 1",
relationRoleCourses: [
{
userRoleId: "xxxxxxxxxxx"
},
{
userRoleId: "xxxxxxxxxxx"
}
]
}
I'm looking for a query using lookup to get all users with their corresponding courses where the course must include the role of the user.
Expected Result
{
_id: ObjectId("xxxxxxxxx"),
roleId: ObjectId: ("xxxxxxxxx),
courses: [
{
_id: ObjectId("xxxxxxxxx"),
name: "Course 1",
},
{
_id: ObjectId("xxxxxxxxx"),
name: "Course 2",
}
],
}
Considerations:
In the relationRoleCourses array the userRoleId is a string, not an ObjectId.
You can try this:
db.users.aggregate([
{
$lookup: {
from: "courses",
localField: "roleId",
foreignField: "relationRoleCourses.userRoleId",
as: "courses"
}
}
])
I have written a shell query which works perfectly on mongo shell but is not returning any value when run using mongoose in nodejs + typescript;
Mongo shell
db.userworks.aggregate([
{
$match: {
user: ObjectId("607dfc9fd1ae095014ab57a0"),
workspace: ObjectId("607dfca7d1ae095014ab57a1"),
},
},
{
$project: {
_id: 0,
},
},
{
$lookup: {
from: 'workspaces',
localField: 'workspace',
foreignField: '_id',
as: 'workspaces',
},
},
{
$unwind: '$workspaces',
},
{
$lookup: {
from: 'projects',
localField: 'workspaces.projects',
foreignField: '_id',
as: 'projects',
},
},
{
$unwind: '$projects',
},
{
$project: {
projects: 1,
},
},
{ $replaceRoot: { newRoot: '$projects' } },
{
$sort: {
'projects.createdAt': -1,
},
},
]).pretty()
But when I run the same query using mongoose in one of my routes.
Mongoose :
const projects = await UserWorks.aggregate([
{
$match: {
user: '607dfc9fd1ae095014ab57a0',
workspace: '607dfca7d1ae095014ab57a1',
},
},
{
$project: {
_id: 0,
},
},
{
$lookup: {
from: 'workspaces',
localField: 'workspace',
foreignField: '_id',
as: 'workspaces',
},
},
{
$unwind: '$workspaces',
},
{
$lookup: {
from: 'projects',
localField: 'workspaces.projects',
foreignField: '_id',
as: 'projects',
},
},
{
$unwind: '$projects',
},
{
$project: {
projects: 1,
},
},
{ $replaceRoot: { newRoot: '$projects' } },
{
$sort: {
'projects.createdAt': -1,
},
},
])
I would really appreciate if someone could help me with this.
Because it took me a while to make this query on shell.
You can't compare ObjectId and String.
you have to convert it ObjectId
mongoose.Types.ObjectId('607dfc9fd1ae095014ab57a0')
{
$match: {
user: mongoose.Types.ObjectId('607dfc9fd1ae095014ab57a0') ,
workspace: mongoose.Types.ObjectId('607dfca7d1ae095014ab57a1'),
},
},
I am trying to get the last message of every single conversation between User 1 and User N.
I've managed to compile the below together, however it is throwing the above, aforementioned error. How can I solve this, with ObjectIds, as that is what I have in the DB, not strings?
await MostRecentMessages.aggregate(
[
{
$match: {
$or: [
{ from: mongoose.Types.ObjectId(id) },
{ to: mongoose.Types.ObjectId(id) }
],
deletedBy: { $ne: id }
}
},
{ $sort: { date: -1 } },
{ $project: { _id: 1, from: 1, to: 1, conversation: 1, date: 1 } },
{
$group: {
_id: {
lastMessage: {
$cond: [
{
$gt: [
{ $substr: ["$to", 0, 1] },
{ $substr: ["$from", 0, 1] }
]
},
{ $concat: ["$to", " and ", "$from"] },
{ $concat: ["$from", " and ", "$to"] }
]
}
},
conversation: { $first: "$$ROOT" }
}
},
{
$lookup: {
from: "conversations",
localField: "conversation",
foreignField: "_id",
as: "conversation"
}
},
{ $unwind: { path: "$conversation" } },
{
$lookup: {
from: "users",
localField: "to",
foreignField: "_id",
as: "to"
}
},
{ $unwind: { path: "$to" } },
{
$lookup: {
from: "users",
localField: "from",
foreignField: "_id",
as: "from"
}
},
{ $unwind: { path: "$from" } }
],
function(err, docs) {
if (err) {
console.log(err);
} else {
console.log("MostRecentMessages", docs);
return res.json(docs);
}
}
);
My schema, if it matters:
const MostRecentMessageSchema = new Schema({
to: {
type: mongoose.Schema.Types.ObjectId,
ref: "user"
},
from: {
type: mongoose.Schema.Types.ObjectId,
ref: "user"
},
conversation: {
type: mongoose.Schema.Types.ObjectId,
ref: "conversation"
},
deletedBy: {
type: [String]
},
date: {
type: Date,
default: Date.now
}
});
Edit
Here are the 5 relevant documents:
// db.MostRecentMessages
_id:5dca7a61e95bd3341cad64b9
to:5dca58c21825d8269a32cb10
from:5dca58ce1825d8269a32cb11
conversation:5dca7aea51b626350fa865dd
date:2019-11-12T09:24:49.906+00:00
_id:5dca7ab0d3a44a34c98b7263
to:5dca58ce1825d8269a32cb11
from:5dca58c21825d8269a32cb10
conversation:5dca7ab0d3a44a34c98b7262
date:2019-11-12T09:26:08.125+00:00
// db.Conversation
_id:5dca7a61e95bd3341cad64b8
text:"Test1"
user:5dca58ce1825d8269a32cb11 // sender
recipient:5dca58c21825d8269a32cb10
createdAt:2019-11-12T09:24:49.827+00:00
_id:5dca7ab0d3a44a34c98b7262
text:"Test2"
user:5dca58c21825d8269a32cb10 // sender
recipient:5dca58ce1825d8269a32cb11
createdAt:2019-11-12T09:26:08.105+00:00
_id:5dca7aea51b626350fa865dd
text:"Test3"
user:5dca58ce1825d8269a32cb11 // sender
recipient:5dca58c21825d8269a32cb10
createdAt:2019-11-12T09:27:06.562+00:00
I copied and pasted the answer below, but it only returns the message with text Test2
Link in comment is for your own knowledge...
Now let's see what is working (it will probably sounds like 'deja vu' for you, but it works!). All (and your aforementioned error) take place in $group stage
db.lastMessage.aggregate([
{
$match: {
$or: [
{
from: ObjectId("5a934e000102030405000001")
},
{
to: ObjectId("5a934e000102030405000001")
}
],
deletedBy: {
$ne: ObjectId("5a934e000102030405000001")
}
}
},
{
$sort: {
date: -1
}
},
{
$project: {
_id: 1,
from: 1,
to: 1,
conversation: 1,
date: 1
}
},
{
$group: {
_id: {
userConcerned: {
$cond: {
if: {
$eq: [
"$to",
ObjectId("5a934e000102030405000001")
]
},
then: "$to",
else: "$from"
}
},
interlocutor: {
$cond: {
if: {
$eq: [
"$to",
ObjectId("5a934e000102030405000001")
]
},
then: "$from",
else: "$to"
}
}
},
from: {
$first: "$from"
},
to: {
$first: "$to"
},
date: {
$first: "$date"
},
conversation: {
$first: "$conversation"
}
}
},
{
$lookup: {
from: "conversations",
localField: "conversation",
foreignField: "_id",
as: "conversation"
}
},
{
$unwind: {
path: "$conversation"
}
},
{
$lookup: {
from: "users",
localField: "to",
foreignField: "_id",
as: "to"
}
},
{
$unwind: {
path: "$to"
}
},
{
$lookup: {
from: "users",
localField: "from",
foreignField: "_id",
as: "from"
}
},
{
$unwind: {
path: "$from"
}
}
])
You can verify here . I just modified your group stage with the one i already provided in your other question.
Pick specific field from $arrayElemAt inside $map.
I want to pick only the name field returned from the object at $arrayElemAt
const data = await this.aggregate([
{
$match: { provider_id: providerId },
},
{
$lookup: {
from: 'users',
localField: 'staff.user_id',
foreignField: '_id',
as: 'staffUsers',
},
},
{
$project: {
staff: {
$map: {
input: '$staff',
in: {
_id: '$$this._id',
email_login: '$$this.email_login',
full_calendar_view: '$$this.full_calendar_view',
verified: '$$this.verified',
user_id: '$$this.user_id',
description: '$$this.description',
name: {
$arrayElemAt: [
'$staffUsers',
{
$indexOfArray: ['$staffUsers._id', '$$this.user_id'],
},
],
},
},
},
},
},
},
]);
Simply use .dot with the $staffUsers
{ "name": {
"$arrayElemAt": [
"$staffUsers.name",
{ "$indexOfArray": ["$staffUsers._id", "$$this.user_id"] }
]
}}