Exclude specific fields in all nested document in mongoose query - javascript

I have a ride Collection with trips as a field, trips is a map where the keys are different years. I want to query the collection but exclude the passengers field in each trip
const ride = new Schema(
{
boat_operator: {
type: Schema.Types.ObjectId,
required: true,
ref: 'User'
},
trips: {
type: Map,
of: {
passengers: [{ type: Schema.Types.ObjectId, ref: 'User' }],
available_seats: { type: Number, required: true }
},
default: new Map()
}
}
)
I tried this
const rides = await Ride.find({ status: 'waiting' }).select("-trips.*.passengers")
I tried to select all the items in value then remove the corresponding passengers field in each
It had no effect
this is what the response looks like
[
{
"_id": "632a1669279c86f4ab3a4bf5",
"boat_operator": "6328c434a98212a7f57c4edc",
"trips": {
"2019": {
"passengers": [],
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfe"
},
"2020": {
"passengers": [],
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfc"
},
"2021": {
"passengers": [],
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfa"
},
"2022": {
"passengers": [],
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bf8"
}
}
}
]
I want to exclude the passengers field in the returned document

This would solve it
const rides = await Ride.aggregate([
{ "$match": { "status": "waiting" } },
{ $project: { "trips": { $objectToArray: '$trips' } } },
{ $project: { 'trips.v.passengers': 0 } },
{ $project: { "trips": { $arrayToObject: '$trips' } } }
]);
nodejs javascript mongodb mongoose
Here's the returned document
{
"_id": "632a1669279c86f4ab3a4bf5",
"trips": {
"2019": {
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfe"
},
"2020": {
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfc"
},
"2021": {
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfa"
},
"2022": {
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bf8"
}
}
}

Related

how do i simplify aggregation pipeline result

i am trying to return aggregation result based on user's role and active status
sample doc
{
"email": "doe#gmail.com",
"firstName": "doe",
"lastName": "bghh",
"phone": "+919016703350",
"password":"$.5GIV3q9JqzRqY/lP2",
"status": "verified",
"role": "admin",
"isActive": true
}
const user = await User.aggregate([
{ $project: { isActive: 1, role: 1 } },
{ $group: { _id: { role: '$role', isActive: '$isActive' }, all: { $sum: 1 } } },
])
above query result below
{
"user": [
{
"_id": {
"role": "user",
"isActive": false
},
"all": 1
},
{
"_id": {
"role": "vendor",
"isActive": true
},
"all": 1
},
{
"_id": {
"role": "user",
"isActive": true
},
"all": 2
},
{
"_id": {
"role": "vendor",
"isActive": false
},
"all": 1
},
{
"_id": {
"role": "admin",
"isActive": true
},
"all": 1
}
]
}
expecting result like, help to write query to achieve below result
{ role:{user:{all:3, active:2 }, admin:{all:1, active:1}, vendor:{all:2, active:2}}}
Try this one:
db.collection.aggregate([
{ $project: { isActive: 1, role: 1 } },
{
$group: {
_id: '$role',
all: { $sum: 1 },
active: { $sum: { $cond: ['$isActive', 1, 0] } }
}
},
{
$group: {
_id: null,
role: { $push: "$$ROOT" }
}
},
{
$set: {
role: {
$map: {
input: "$role",
in: { k: "$$this._id", v: { all: "$$this.all", active: "$$this.active" } }
}
}
}
},
{ $set: { role: { $arrayToObject: "$role" } } },
{ $replaceWith: { role: "$role" } }
])
Mongo Playground

Aggregate framework returns empty array instead of single object (MongoDB/Mongoose)

I am building an api for a kanban task management app. I have this data stored in the database.
{
"_id": "62fa5aa25778ec97bc6ee231",
"user": "62f0eb5ebebd0f236abcaf9d",
"name": "Marketing Plan",
"columns": [
{
"name": "todo",
"_id": "62fa5aa25778ec97bc6ee233",
"tasks": [
{
"title": "Task Four testing 2",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes test",
"completed": false,
"_id": "62ff74bfe80b11ade2d34456"
},
{
"name": "do homework",
"completed": false,
"_id": "62ff74bfe80b11ade2d34457"
}
],
"_id": "62ff74bfe80b11ade2d34455"
}
]
},
{
"name": "doing",
"_id": "62fa5aa25778ec97bc6ee234",
"tasks": []
},
{
"name": "done",
"_id": "62fa5aa25778ec97bc6ee235",
"tasks": []
}
],
"__v":0
}
I tried to return a single object with the id of req.params.id which in this case is 62ff74bfe80b11ade2d34455 however, it returns an empty array instead of returning a single object.
const getTask = asyncHandler(async (req, res) => {
const task = await Board.aggregate([
{
$match: {
"columns.tasks._id": req.params.id,
},
},
{
$project: {
columns: {
$first: {
$filter: {
input: "$columns.tasks",
cond: {
$eq: ["$$this._id", req.params.id],
},
},
},
},
},
},
{
$replaceRoot: {
newRoot: "$columns",
},
},
]);
});
As #yung-shun suggested, I needed to cast req.params.id as an Object ID.
const task = await Board.aggregate([
{
$match: {
"columns.tasks._id": new ObjectId(req.params.id),
},
},
{ $unwind: "$columns" },
{
$match: {
"columns.tasks._id": new ObjectId(req.params.id),
},
},
{
$project: {
task: {
$first: {
$filter: {
input: "$columns.tasks",
cond: { $eq: ["$$this._id", new ObjectId(req.params.id)] },
},
},
},
},
},
{ $replaceWith: "$task" },
]);
res.status(200).json(task);

How to merge duplicates in an array of objects and sum a specific property in MongoDB?

I am building an URL shortener. So I want to track datewise click count when a URL is visited using the short URL. For example: on 30th January if the URL was visited 3 times by using the short URL it will show the click count 5 but my document looks like this -
{
"_id":{"$oid":"61f6322cd3e3484d97a25e7c"},
"dayWiseClicks":
[{"date":"01/30/2022","dailyClicks":1,"_id":{"$oid":"61f66ff95fadc3e9f01b0d34"}},
{"date":"01/30/2022","dailyClicks":1,"_id":{"$oid":"61f66ffd5fadc3e9f01b0d38"}},
{"date":"01/30/2022","dailyClicks":1,"_id":{"$oid":"61f66fff5fadc3e9f01b0d3c"}}]
}
I want my document to look like this:
{
"_id":{"$oid":"61f6322cd3e3484d97a25e7c"},
"dayWiseClicks":
[{"date":"01/30/2022","dailyClicks":3,"_id":{"$oid":"61f66ff95fadc3e9f01b0d34"}}]
}
How can I achieve this using the MongoDB aggregation pipeline?
UPDATE
Here's the full schema:
{
merchant_name: String,
store_id: String,
original_url: String,
url_hash: String,
subdomain: String,
// password: String,
totalClicks: {
type: Number,
default: 0,
},
igClicks: {
type: Number,
default: 0,
},
fbClicks: {
type: Number,
default: 0,
},
directClicks: {
type: Number,
default: 0,
},
dayWiseClicks: [
{
date: {
type: String,
default: moment(new Date()).format("L"),
},
dailyClicks: {
type: Number,
default: 0,
},
},
],
desktopClicks: {
device: { type: String, default: "Desktop" },
clicks: { type: Number, default: 0 },
},
mobileClicks: {
device: { type: String, default: "Mobile" },
clicks: { type: Number, default: 0 },
},
},
{ timestamps: true }
After the aggregation is done I want my document to look like this:
{
"_id": {
"$oid": "61f7a3b83dcebb77b05bd180"
},
"desktopClicks": {
"device": "Desktop",
"clicks": 5
},
"mobileClicks": {
"device": "Mobile",
"clicks": 0
},
"totalClicks": 5,
"igClicks": 0,
"fbClicks": 0,
"directClicks": 5,
"dayWiseClicks": [
{
"date": "01/31/2022",
"dailyClicks": 5,
"_id": {
"$oid": "61f7a3fe5bd4f779cc53f697"
}
}
],
"merchant_name": "Akash DTH",
"store_id": "3333",
"url_hash": "OGZhYTI",
"subdomain": "",
"original_url": "https://akashdth.com/",
"createdAt": {
"$date": "2022-01-31T08:54:16.472Z"
},
"updatedAt": {
"$date": "2022-01-31T09:03:55.925Z"
},
"__v": 0
}
try
https://mongoplayground.net/p/osWlBuYy0NZ
db.collection.aggregate([
{
$unwind: {
"path": "$dayWiseClicks"
}
},
{
$group: {
_id: {
"oid": "$_id",
"date": "$dayWiseClicks.date"
},
"dailyClicks": {
$sum: "$dayWiseClicks.dailyClicks"
},
"originalDayWiseClicks": {
$push: "$dayWiseClicks"
}
}
},
{
"$addFields": {
"dayWiseClicks": [
{
"date": "$_id.date",
"dailyClicks": "$dailyClicks",
"_id": {
"$first": "$originalDayWiseClicks._id"
}
}
]
}
},
{
"$project": {
_id: "$_id.oid",
"dayWiseClicks": 1
}
}
])
If you don't need the dayWiseClicks as nested or the first dayWiseClicks._id
It can be simplified to this:
https://mongoplayground.net/p/Zc9whjiLWdt
db.collection.aggregate([
{
$unwind: {
"path": "$dayWiseClicks"
}
},
{
$group: {
_id: {
"oid": "$_id",
"date": "$dayWiseClicks.date"
},
"dailyClicks": {
$sum: "$dayWiseClicks.dailyClicks"
}
}
},
{
$addFields: {
"_id": "$_id.oid",
"date": "$_id.date",
}
}
])

Adding property to mulitple documents in mongoDB

I have this data structure:
{
"_id": "5ebd08794bcc8d2fd893f4a7",
"username": "johan#gmail.com",
"password": "123",
"decks": [{
"cards": [{
"_id": "5ebd08794bcc8d2fd893f4a9",
"planeetnaam": "Venus",
"kleur": "Grijs"
},
{
"_id": "5ebd08794bcc8d2fd893f4aa",
"planeetnaam": "Neptunus",
"kleur": "Paars"
}
],
"_id": "5ebd08794bcc8d2fd893f4a8",
"name": "Planeten"
},
{
"cards": [{
"_id": "5ebd08794bcc8d2fd893f4ac",
"diernaam": "Hond",
"poten": "4"
},
{
"_id": "5ebd08794bcc8d2fd893f4ad",
"diernaam": "Kangoeroe",
"poten": "2"
}
],
"_id": "5ebd08794bcc8d2fd893f4ab",
"name": "Dieren"
}
],
"__v": 0
}
Now i want to add a new property to all the cards in deck with deckname: "Planeten". How do i do this with a mongoose query?
The cards array of deck "Planeten" should look like this after the query
"cards": [{
"_id": "5ebd08794bcc8d2fd893f4a9",
"planeetnaam": "Venus",
"kleur": "Grijs",
"newProp": null
},
{
"_id": "5ebd08794bcc8d2fd893f4aa",
"planeetnaam": "Neptunus",
"kleur": "Paars",
"newProp": null
}
],
EDIT:
This works in Robo3T:
db.getCollection('users').findOneAndUpdate(
{ '_id': ObjectId("5eba9ee0abfaf237f81fb104") },
{ $set: { 'decks.$[deck].cards.$[].newProp': null } },
{ arrayFilters: [{ 'deck._id': ObjectId("5eba9ee0abfaf237f81fb108") } ] }
)
But the server query doesnt edit any data:
User.findOneAndUpdate(
{ '_id': req.session.userid },
{ $set: { 'decks.$[deck].cards.$[].newProp': null } },
{ arrayFilters: [{ 'deck._id': req.params.deckid } ] }, function(err, user){
res.send('test');
})
Thanks in advance
you can use array update operators
the query may look something like that
db.collection.updateOne(
{ _id: <ObjectId> }, // the filter part
{ $set: { 'decks.$[deck].cards.$[].newProp': null } },
{ arrayFilters: [{ 'deck.name': 'Planeten' }] }
)
$[deck] refers to each element in the decks array
$[] is used to update all the elements in the cards array
your function may look something like that
User.updateOne(
{ '_id': req.session.userid },
{ $set: { 'decks.$[deck].cards.$[].newProp': null } },
{ arrayFilters: [{ 'deck.name': 'Planeten' }] })
.then(function (user) {
if (!user) {
res.status(404).send('Er ging helaas iets fout')
} else {
res.status(201).send("Card is toegevoegd");
}
})
hope it helps

How to findOneAndUpdate single field with multi nested array documents

I'm stuck on how to update single value in multi nested array documents value with findOneAndUpdate.
My condition goes like this:
Update warehouse amount where the productCode is "abc123", size "41" in warehouse "Hamburg".
I just get back null or bot sizes 41 and 42.
Here is the part of the doc:
{
"_id": ObjectId("xxxx636309f84479ec0c7b"),
"productCode": "abc123",
"brand": "Nike",
"name": "aaa",
"model": "Runner",
"color": "Brown",
"image": "shoe.jpg",
"sizes": [{
"_id": ObjectId("xxxxc636309f84479ec0c7e"),
"size": "41",
"wares": [{
"_id": ObjectId("xxxx2c636309f84479ec0c80"),
"ware": "Hamburg",
"amount": 7
},
{
"_id": ObjectId("5db72c636309f84479ec0c7f"),
"ware": "Berlin",
"amount": 7
}
]
},
{
"_id": ObjectId("5db72c636309f84479ec0c7c"),
"size": "42",
"wares": [{
"_id": ObjectId("5db72c636309f84479ec0c7d"),
"ware": "Hamburg",
"amount": 16
}]
}
],
"__v": 0
}
This is what I've tried:
Product.findOneAndUpdate({
"productCode": "abc123",
"sizes.size": 41,
"sizes.wares.ware": "Hamburg"
}, {
"$set": {
"sizes.0.wares.amount": 99
}
}, {
useFindAndModify: false
},
(err, products) => {
if (err) {
return res.status(422).send(err)
}
return res.json(products)
}
);
How can I solve this?
And to fulfill #ambianBeing, this is how it would be done with findOneAndUpdate:
Product.findOneAndUpdate({
"productCode": "abc123",
"sizes": {
$elemMatch: {
$and: [
{ size: "41" },
{
wares: {
$elemMatch: {
ware: "Hamburg"
}
}
}]
}
}
}, {
$set: {
"sizes.$[theSize].wares.$[theWare].amount": 99
}
}, {
arrayFilters: [{
"theSize.size": "41"
}, {
"theWare.ware": "Hamburg"
}]
})
Can be done using filtered positional operator $[<identifier>] which is nifty in use cases of nested array updation.
Query (Mongo Shell):
db.collection.update(
{ productCode: "abc123" },
{ $set: { "sizes.$[outer].wares.$[inner].amount": 99 } },
{
arrayFilters: [{ "outer.size": "41" }, { "inner.ware": "Hamburg" }],
multi: false
}
);
Query with Mongoose Model:
Product.update(
{ productCode: "abc123" },
{ "sizes.$[outer].wares.$[inner].amount": 99 },
{
arrayFilters: [{ "outer.size": "41" }, { "inner.ware": "Hamburg" }],
multi: false
},
(err, rawDoc) => {
if (err) {
console.error(err);
}
console.info(rawDoc);
}
);

Categories