How to get object in deeply nested array in mongoose using nodes - arrays

In my collection of users I have the following
{
_id: ObjectId('whatever user id'),
movies: [
{
_id: ObjectId('whatever id of this movie'),
name: 'name of this movie',
actors: [
{
_id: ObjectId('whatever id of this actor'),
name: 'name of this actor'
}
]
}
]
}
So in my users collection I want to be able to query for a actor by the user.id, pet.id, and the actor.id
I want to return the actor somewhat like this...
actor: {
fields...
}
I tried the following...
const actor = await User.findById(req.user.id, {
movies: {
$elemMatch: {
_id: req.params.movie_id,
actors: {
$elemMatch: {
_id: req.params.actor_id,
},
},
},
},
});
I have tried other things but can't seem to get it to work. I saw that you can maybe use aggregate but I am not sure how to query that while using the ids I have at my disposal.

I was able to figure it out by using aggregate. I was using this before but it seems that I needed to cast my ids with mongoose.Types.ObjectId so a simple req.user.id would not work.
In order to get my answer I did...
const user = await User.aggregate([
{ $match: { _id: mongoose.Types.ObjectId(req.user.id) } },
{ $unwind: '$movies' },
{ $match: { 'movies._id': mongoose.Types.ObjectId(req.params.movie_id) } },
{ $unwind: '$movies.actors' },
{
$match: {
'movies.actors._id': mongoose.Types.ObjectId(req.params.actor_id),
},
},
]);
This did not return data in the following format...
actor: {
fields...
}
but returns it instead like this...
user: {
movies: {
actor: {
fields...
}
},
otherFields...
}
then sending the response back...
res.status(200).json({
status: 'success',
data: {
actor
}
})
gives that format I wanted. However, I would still want to know how to just get the data actor without getting the full document

Related

Mongoose how to sort by date which is in array of objects of a field

i want to grab data sorted by its date but the date is not in a field's value, its in the field's value's object's array(if i said it correctly).
here is an example of the data i have:
{
role: "User",
fullName: "Verna Pagac",
username: "dwightkoss95",
email: "shawn.ryan#yahoo.com",
orders: [{
buyerUsername: 'admin',
boughtAt: 2022-09-20T20:14:59.304Z
},
{
buyerUsername: 'admin',
boughtAt: 2022-10-30T22:35:35.546Z
}]
}
after i extracted the orders by the following command, i want to sort them by the boughtAt but how?
const usersWithOrders = await users
.find({
orders: { $exists: true, $ne: [] }
})
const orders = []
usersWithOrders.map((user) => {
for (i=0 ; i<user.orders.length ; i++) {
orders.push(user.orders[i])
}
})
i want the newer order to be top.
console.log(orders)
// now it shows the following out put:
/*
[{
buyerUsername: 'admin',
boughtAt: 2022-09-20T20:14:59.304Z
},
{
buyerUsername: 'admin',
boughtAt: 2022-10-30T22:35:35.546Z
}]
*/
If you want you can sort directly from db using the aggregation framework
db.users.aggregate([
{ $unwind: '$orders' },
{ $sort: { 'orders.boughtAt': -1 } },
{
$group: {
_id: '$_id',
user: { $first: '$$ROOT' },
orders: { $push: '$orders' },
},
},
])
or if you prefer you can sort in JS
orders.sort((a, b) => new Date(b.boughtAt) - new Date(a.boughtAt))

Exclude field from a document based on another field value mongodb mongoose

I have a users schema in which there is a companies field which I only want to select if role field value is admin else it should not be returned in find query.
user Schema:
const userInfoSchema = new mongoose.Schema({
...,
companies: [
{
type: mongoose.Schema.ObjectId,
ref: 'companyinfos',
},
],
role: {
type: String,
enum: ['user', 'admin', 'employee'],
default: 'user',
},
});
I have tried to solve this by using pre find hook but was unable to exclude the companies field.
userInfoSchema.post(/^find/, function (doc, next) {
if (doc.role !== 'admin') {
this.find({}).select('-companies');
}
next();
});
Or is there any way to conditionally set select in the companies field in the userInfoSchema based on the role value?
Please help.
use aggregate
db.collection.aggregate([
{
"$project": {
companies: {
"$cond": {
"if": {
$eq: [
"$role",
"admin"
]
},
"then": "$companies",
"else": 0
}
}
}
},
{
$match: {
companies: {
$ne: 0
}
}
}
])
https://mongoplayground.net/p/I8HGvz6h7MP

Match $and based on array of filter items in MongoDB

I have a Collection in MongoDB of CatalogItems. Every CatalogItem contains a product that has an array of metafields.
In these metafields there are 2 fields that are Brand_ID and Article_No
Example CatalogItem document:
CatalogItem = {
product: {
metafields: [
{
key: "Brand_ID",
value: "317"
},
{
key: "Article_No",
value: "48630"
}
]
}
}
I have an array of filters that is used to match the CatalogItems documents based on these metafields
filter array
filter = [
{ brandId: '317', articleId: '48630' },
{ brandId: '257', articleId: 'ZSA04036' }
]
I want to return all CatalogItems that match any of the exact combinations in filter.
For example to return the stated CatalogItem I currently use this query
// Checks for { brandId: '317', articleId: '48630' }
query = {
$and: [
{ "product.metafields": { $elemMatch: { key: "Brand_ID", value: filter[0].brandId } } },
{ "product.metafields": { $elemMatch: { key: "Article_No", value: filter[0].articleId } } },
]
}
The issue that I have is that in order for me to look trough all the filter items I have to increment the filter index and rerunning the query.
For example looking for the second filter index I would have to change
filter[0].brandId to filter[1].brandId
Is there a way in Mongo to query using a predefined array of objects instead of rerunning the query multiple times?
I figured out a way to set variables to the query using $or and .forEach()
let query = {
$or: []
};
filter = [
{ brandId: '317', articleId: '48630' },
{ brandId: '257', articleId: 'ZSA04036' }
]
filter.forEach(filterItem => {
query.$or.push(
{
$and: [
{ "product.metafields": { $elemMatch: { key: "Brand_ID", value: filterItem.brandId } } },
{ "product.metafields": { $elemMatch: { key: "Article_No", value: filterItem.articleId } } },
]
}
)
})

How to return id with Mongoose Aggregate Lookup with Apollo GraphQL

I have an aggregate function that returns people in a collection:
const getById = ({ id }) => {
return Project.aggregate([
{ $match: { _id: Types.ObjectId(id) } },
{
$lookup: {
from: "members",
localField: "_id",
foreignField: "project_id",
as: "members"
}
},
])
.then(data => {
const [project] = data;
console.log(project) // see below
return {
id: project._id,
...project
};
})
.catch(err => console.log(err));
}
If I return the data from this I get the following:
// Server response
{ _id: 5e2f57b577a8ce59c79e74af,
title: 'ok',
user_id: 5e2dc7961e6b840c315b5a03,
__v: 0,
members:
[ { _id: 5e447683b4f732cc9c4a9531,
name: 'Karl Taylor',
email: 'karl#queuey.dev',
project_id: 5e2f57b577a8ce59c79e74af,
position: 1,
__v: 0 },
{ _id: 5e45be128ed96a5eaef5d13e,
name: 'John Smith',
email: 'john#queuey.dev',
project_id: 5e2f57b577a8ce59c79e74af,
position: 2,
__v: 0 } ] }
However, when I query from the frontend using Apollo GraphQL, the id is null. (But it works on other items, as id is a getter for id but this does not happen on aggregate functions).
What is the best practice to map the id to the correct value? I would normally just use array.map but I feel like that might be overkill if I have too many members (at which point I would probably break this out to do pagination, but that's a different story.)
This is the response from frontend
// Client response
"project": {
"id": "5e2f57b577a8ce59c79e74af",
"title": "ok",
"members": [
{
"id": null, // <-- Notice here is null
"name": "Karl Taylor",
"email": "karl#queuey.dev",
"__typename": "Member"
},
{
"id": null, // <-- Notice here is null
"name": "John Smith",
"email": "john#queuey.dev",
"__typename": "Member"
}
],
"__typename": "Project"
}
This question here is similar, however, I do not believe it is a duplicate because we are querying different data. (the answer does not solve my question.)
I need to be able to return id otherwise cached redirects will not work.
I ran into this problem as well when I try to use aggregations.
simply you can you both ID's(id and _id) but it's not good thing.
what I use is to iterate the data
if the returning data of the query is not array use this
return {
...res._doc,
id: res._id,
}
but the returning data is an array you can add id to the response variable like below using forEach
res.forEach(element => {
element.id = element._id
});
return res;

How to remove Object from array using mongoose

I'm trying to remove an object from an array in a document using mongoose.
The Schema is the following:
var diveSchema = new Schema({
//irrelevant fields
divers: [{
user: { type: Schema.Types.ObjectId, ref: 'User', required: true },
meetingLocation: { type: String, enum: ['carpool', 'onSite'], required: true },
dives: Number,
exercise: { type: Schema.Types.ObjectId, ref: 'Exercise' },
}]
});
a possible entry can be
{
//irrelevant fields
"divers": [
{
"_id": "012345678",
"user": "123456789",
"meetingLocation": "carpool",
"exercise": "34567890",
},
{
"_id": "012345679",
"user": "123456780",
"meetingLocation": "onSite",
"exercise": "34567890",
}
]
}
Say I want to remove the entry where user is 123456789 (note I do not know the _id at this point).
How do I do this correctly?
I tried the following:
var diveId = "myDiveId";
var userIdToRemove = "123456789"
Dive.findOne({ _id: diveId }).then(function(dive) {
dive.divers.pull({ user: userIdToRemove });
dive.save().then(function(dive) {
//do something smart
});
});
This yieled no change in the document.
I also tried
Dive.update({ _id: diveId }, { "$pull": { "divers": { "diver._id": new ObjectId(userIdToRemove) } } }, { safe: true }, function(err, obj) {
//do something smart
});
With this I got as result that the entire divers array was emptied for the given dive.
What about this?
Dive.update({ _id: diveId }, { "$pull": { "divers": { "user": userIdToRemove } }}, { safe: true, multi:true }, function(err, obj) {
//do something smart
});
I solve this problem using this code-
await Album.findOneAndUpdate(
{ _id: albumId },
{ $pull: { images: { _id: imageId } } },
{ safe: true, multi: false }
);
return res.status(200).json({ message: "Album Deleted Successfully" });
Try this
Dive.update({ _id: diveId },{"$pull": { "drivers": {"user": "123456789"}}})
Try this async code
var diveId = "myDiveId";
var userIdToRemove = "123456789"
const dive=await Dive.findOne({ _id: diveId })
await dive.divers.pull({ user: userIdToRemove });
await dive.save();
Use this with try/catch:
await Group.updateOne(
{ _id: groupId },
{ $pull: { members: {id: memberId }}}
);

Resources