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
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
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);
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",
}
}
])
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
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);
}
);