Update multi nested mongoDB schema - javascript

This is my model schema
{
"_id" : ObjectId("59999454bad567268f78dc09"),
"chapters" : [
{
"_id" : ObjectId("59999454bad567268f78dc0a"),
"individualChapters" : [
{
"_id" : ObjectId("59999454bad567268f78dc0b"),
"photos" : [
{
"_id" : ObjectId("59999454bad567268f78dc0c"),
image: ObjectId("599993e980c3ba2681639163")
},
{
"_id" : ObjectId("59999595bad567268f78dc12"),
image: ObjectId("599993e980c3ba2681639163")
}
]
},
{
"_id" : ObjectId("5999957dbad567268f78dc10"),
"photos" : [
{
"_id" : ObjectId("5999957dbad567268f78dc11"),
image: ObjectId("599993e980c3ba2681639163")
}
]
}
]
}
]
}
i am trying to push another element in "photos" of second "individualChapters" using the below query, but its always pushing in "photos" of first "individualChapters".
Model.findAndModify(
{
'chapters.individualChapters._id' : ObjectId("5999957dbad567268f78dc10"),
'chapters._id' : ObjectId("59999454bad567268f78dc0a"),
_id: ObjectId("59999454bad567268f78dc09")
},
{
'$push': {
'chapters.0.individualChapters.$.photos': {
image: ObjectId("599993e980c3ba2681639163")
}
}
}, { new: false, upsert: false })
Its always updating first individualChapters. Please help!!

You are referring to the wrong element. $push should have this
chapters.0.individualChapters.1.photos.0._id like this

Here's what works in your specific case.
Model.findAndModify(
{
'chapters.individualChapters._id' : ObjectId("5999957dbad567268f78dc10"),
'chapters._id' : ObjectId("59999454bad567268f78dc0a"),
'_id': ObjectId("59999454bad567268f78dc09")
},
{
'$push': {
'chapters.0.individualChapters.1.photos': {
'image': ObjectId("599993e980c3ba2681639163")
}
}
}, { new: false, upsert: false })

Related

Mongo return data based on nested date params

I'm scraping and saving a bunch of data into a mongodb collection by pushing it into an array, an example of the saved data looks like this:
{
"_id" : ObjectId("616eafce1e7edf6e9b033938"),
"collectibleId" : "34e05c2c-19dd-4509-a98c-bb11cd275d34",
"__v" : NumberInt(0),
"createdAt" : ISODate("2021-10-19T11:45:16.845+0000"),
"history" : [
{
"storePrice" : 6.99,
"value" : NumberInt(27),
"date" : ISODate("2021-10-19T11:45:16.683+0000"),
"totalListings" : NumberInt(904),
"hourlyChange" : {
"market" : NumberInt(0)
}
},
{
"storePrice" : 6.99,
"value" : 27.5,
"date" : ISODate("2021-10-19T11:59:01.192+0000"),
"totalListings" : NumberInt(902),
"hourlyChange" : {
"market" : NumberInt(0)
}
},
... loads more items ...
],
"updatedAt" : ISODate("2021-10-27T18:59:01.109+0000")
}
I'm trying to now write a query for the data that can return only the recorded data within a specific timeframe. So far I've tried this:
Prices.findOne({
collectibleId: slug,
history: {
date: {
$gte: new Date("2021-10-26T00:00:00.000Z")
}
}
}).exec((err, data) => {
if (err){
return res.status(400).json({
error: errorHandler(err)
})
}
res.json(data)
})
But the result im getting is always null. I'm wondering if it's because date is nested in history and perhaps im writing that wrong ? Any advice appreciated thank you.
$match then $set
db.collection.aggregate([
{
"$match": {
collectibleId: "slug",
"history.date": {
$gte: ISODate("2021-10-26T00:00:00.000Z")
}
}
},
{
"$set": {
"history": {
"$filter": {
"input": "$history",
"as": "h",
"cond": {
$gte: [
"$$h.date",
ISODate("2021-10-26T00:00:00.000Z")
]
}
}
}
}
}
])
mongoplayground

Mongodb - Aggregate Sorting by Restricted Array Elements

I have the following documents and here I want to sort them by the fields 'ranks.rank' within a restricted range.
How to do this kind of sorting ? 'ranks.date': { '$gte': 20200516 }
I have tried something like
{ $match: selector },
{
$project: {
views: 1,
'ranks': {
$cond: {
if: { $gte: ["$ranks.date", 20200516] },
then: "$ranks",
else: "$$REMOVE"
}
},
}
},
{ $addFields: { totalRank: { $sum: '$ranks.rank' } } },
{ $sort: { 'totalRank': 1 } }
Documents
{
"_id" : "Qvpbjpjqexko4XFGH",
"views" : NumberInt(15),
"ranks" : [
{
"date" : NumberInt(20200415),
"rank" : NumberInt(1)
},
{
"date" : NumberInt(20200418),
"rank" : NumberInt(13)
},
{
"date" : NumberInt(20200503),
"rank" : NumberInt(1)
}
]{
"_id" : "bLQKR39qmJcwuzm8r",
"views" : NumberInt(16),
"ranks" : [
{
"date" : NumberInt(20200415),
"rank" : NumberInt(1)
},
{
"date" : NumberInt(20200418),
"rank" : NumberInt(12)
},
{
"date" : NumberInt(20200501),
"rank" : NumberInt(2)
},
{
"date" : NumberInt(20200521),
"rank" : NumberInt(1)
}
]
It looks like your post is mostly code; please add some more details.
I think i figured out
{ $match: selector },
{
$project: {
views: 1,
ranks: {
$filter: {
input: "$ranks",
as: "rank",
cond: { $gte: ["$$rank.date", monAgo] }
}
},
},
},
{ $addFields: { totalRank: { $sum: '$ranks.rank' } } },
{ $sort: { 'totalRank': -1 } }

How to use $filter in nested child array with mongodb?

My mongodb data is like this,i want to filter the memoryLine.
{
"_id" : ObjectId("5e36950f65fae21293937594"),
"userId" : "5e33ee0b4a3895a6d246f3ee",
"notes" : [
{
"noteId" : ObjectId("5e36953665fae212939375a0"),
"time" : ISODate("2020-02-02T17:24:06.460Z"),
"memoryLine" : [
{
"_id" : ObjectId("5e36953665fae212939375ab"),
"memoryTime" : ISODate("2020-02-03T17:54:06.460Z")
},
{
"_id" : ObjectId("5e36953665fae212939375aa"),
"memoryTime" : ISODate("2020-02-03T05:24:06.460Z")
}
]
}
]}
i want to get the item which memoryTime is great than now as expected like this,
"userId" : "5e33ee0b4a3895a6d246f3ee",
"notes" : [
{
"noteId" : ObjectId("5e36953665fae212939375a0"),
"time" : ISODate("2020-02-02T17:24:06.460Z"),
"memoryLine" : [
{
"_id" : ObjectId("5e36953665fae212939375ab"),
"memoryTime" : ISODate("2020-02-03T17:54:06.460Z")
},
{
"_id" : ObjectId("5e36953665fae212939375aa"),
"memoryTime" : ISODate("2020-02-03T05:24:06.460Z")
}
]
}]
so is use code as below.i use a $filter in memoryLine to filter to get the right item.
aggregate([{
$match: {
"$and": [
{ userId: "5e33ee0b4a3895a6d246f3ee"},
]
}
}, {
$project: {
userId: 1,
notes: {
noteId: 1,
time: 1,
memoryLine: {
$filter: {
input: "$memoryLine",
as: "mLine",
cond: { $gt: ["$$mLine.memoryTime", new Date(new Date().getTime() + 8 * 1000 * 3600)] }
}
}
}
}
}]).then(doc => {
res.json({
code: 200,
message: 'success',
result: doc
})
});
but i got this,memoryLine is null,why?I try to change $gt to $lt, but also got null.
"userId" : "5e33ee0b4a3895a6d246f3ee",
"notes" : [
{
"noteId" : ObjectId("5e36953665fae212939375a0"),
"time" : ISODate("2020-02-02T17:24:06.460Z"),
"memoryLine" : null <<<------------- here is not right
}]
You can use $addFields to replace existing field, $map for outer collection and $filter for inner:
db.collection.aggregate([
{
$addFields: {
notes: {
$map: {
input: "$notes",
in: {
$mergeObjects: [
"$$this",
{
memoryLine: {
$filter: {
input: "$$this.memoryLine",
as: "ml",
cond: {
$gt: [ "$$ml.memoryTime", new Date() ]
}
}
}
}
]
}
}
}
}
}
])
$mergeObjects is used to avoid repeating fields from source memoryLine object.
Mongo Playground

Filter data using mongoose populate

I have two data structures "database" and "components"
const DatabaseSchema = mongoose.Schema({
components: [{ type: Schema.Types.ObjectId, ref: 'Components', required: false }],
});
const ComponentsSchema = mongoose.Schema({
name: { type: String, required: true, trim: true, unique: true, lowercase: true },
updatedAt: Date,
});
I want to filter all items in the database by component names
search rule I'm using
Database.find({
components: { $elemMatch: { name: /antr/i } }
}).populate({
path: 'components',
select: 'name -_id'
}).select(['descript','components']).exec( (err,data) => {
console.log(err);
res.json(data);
});
however always return an empty element
Please try this :
As I've already suggested you can use this :
Database.find({})
.populate({ path: 'components', match: { name: /antr/i }, select: 'name -_id' })
.exec((err, data) => { console.log(err); res.json(data); });
Since you're seeing empty array's is because of the filter query in match which doesn't find appropriate documents in components collection w.r.t. ObjectIds in components array of database document, this is normal. May be you can filter those out in code, as you aren't looking in that way, You can use mongoDB's $lookup from aggregation framework which is equivalent to .populate() from mongoose.
Database.aggregate(
[{
$lookup: {
from: "components",
"let": { "ids": "$components" },
pipeline: [
{ $match: { $expr: { $in: ['$_id', '$$ids'] } } }],
as: "dbComponentsArray"
}
}, { $unwind: '$dbComponentsArray' }, { $match: { 'dbComponentsArray.name': /antr/i } },
{ $group: { _id: '$_id', dbComponentsArray: { $push: '$dbComponentsArray' }, data: { $first: '$$ROOT' } } }, { $addFields: { 'data.dbComponentsArray': '$dbComponentsArray' } },
{ $replaceRoot: { 'newRoot': '$data' } }])
Sample Data in collections :
components :
/* 1 */
{
"_id" : ObjectId("5d481cd098ba991c0857959f"),
"name" : "antracito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
}
/* 2 */
{
"_id" : ObjectId("5d481cd098ba991c0857958f"),
"name" : "anacito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
}
/* 3 */
{
"_id" : ObjectId("5d481cd098ba991c0857951f"),
"name" : "antracito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
}
/* 4 */
{
"_id" : ObjectId("5d481cd098ba991c0857952f"),
"name" : "anacito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
}
database :
/* 1 */
{
"_id" : ObjectId("5d4979d52a17d10a6c8de81b"),
"components" : [
ObjectId("5d481cd098ba991c0857951f"),
ObjectId("5d481cd098ba991c0857952f"),
ObjectId("5d481cd098ba991c0857953f"),
ObjectId("5d481cd098ba991c0857959f")
]
}
Output :
/* 1 */
{
"_id" : ObjectId("5d4979d52a17d10a6c8de81b"),
"components" : [
ObjectId("5d481cd098ba991c0857951f"),
ObjectId("5d481cd098ba991c0857952f"),
ObjectId("5d481cd098ba991c0857953f"),
ObjectId("5d481cd098ba991c0857959f")
],
"dbComponentsArray" : [
{
"_id" : ObjectId("5d481cd098ba991c0857959f"),
"name" : "antracito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
},
{
"_id" : ObjectId("5d481cd098ba991c0857951f"),
"name" : "antracito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
}
]
}

Simplify forEach statement to update a mongoDB document with nested objects and arrays

I'd like to update the value of the key shouldSendAlert in the following document:
{
"_id" : ObjectId("5c61c4db46d18e1092c5b024"),
"service" : "SRVPVD",
"menu" : [
{
"sub" : [
{
"options" : [
{
"item" : [
{
"name" : "",
"actions" : [
{
"name" : "communicateClient",
"value" : true
},
{
"name" : "shouldSendAlert",
"value" : false
}
]
}
],
"name" : "Technology Support"
},
{
"item" : [
{
"name" : "",
"actions" : [
{
"name" : "communicateClient",
"value" : true
}
]
}
],
"name" : "Company Support"
}
],
"name" : "Support"
},
{
"name" : " FAQ"
}
],
"name" : "Help"
}
]
}
I've managed to do this, querying the document using a multiple $elemMatch query, and using forEach to run through the nested arrays in order to alter the value of shouldSendAlert:
{
let menuItems = db.getCollection('menumodels').find({menu: {$elemMatch: {name: 'Help',sub: {$elemMatch: {name: 'Support',motivos: {$elemMatch: {name: 'Technology Support'}}}}}}});
menuItems.forEach((r) => {
r.menu.forEach(menuItem => {
if (menuItem.name == 'Help') {
menuItem.sub.forEach(sub => {
if (sub.name == 'Support') {
sub.motivos.forEach(motivo => {
if (motivo.name == "Technology Support") {
motivo.item[0].actions.forEach(action => {
if (action.name == 'shouldSendAlert') {
action.value = true;
db.getCollection('menumodels').update({_id: r._id}, {$set: {menu: r.menu}})
}
})
}
})
}
})
}
})
});
}
Is it - regarding performance - necessary, to code this MongoDB query
or update logic into a smarter form? Does the multiple use of $elemMatch affect performance?

Categories

Resources