How can I write query in mongodb? - node.js

I have a collection of mongodb like this :
[{
"_id":"ObjectId(""51780fb5c9c41825e3e21fc4"")",
"name":"CS 101",
"students":[
{
"name":"raj",
"year":2016
},
{
"name":"rahul",
"year":2017
},
{
"name":"anil",
"year":2018
}
]
},
{
"_id":"ObjectId(""51780fb5c9c41825e3e21fs4"")",
"name":"CS 102",
"students":[
{
"name":"mukesh",
"year":2016
},
{
"name":"mohan",
"year":2017
},
{
"name":"mangal",
"year":2018
}
]
}
]
I've been looking for similar questions like this one: Mongo db - Querying nested array and objects but in that question they're looking for a specific element inside the "messages" object (in my case) for example. Same as in this other question: Query for a field in an object in an array with Mongo? where they're using $mapan d I don't think it fits my needs.
The documents to find have this structure:
[{
"_id":"ObjectId(""51780fb5c9c41825e3e21fc4"")",
"name":"CS 101",
"students":[
"raj","rahul","anil"
]
},
{
"_id":"ObjectId(""51780fb5c9c41825e3e21fs4"")",
"name":"CS 102",
"students":[
"mukesh","mohan","mangal"
]
}
]
how to solve this?

From the question and datasets, you are trying to return students with an array of student's name (string) instead of the array of student object.
Use $project to display students as students.name array.
db.collection.aggregate([
{
$project: {
"_id": "$_id",
"name": "$name",
"students": "$students.name"
}
}
])
Sample Solution 1 on Mongo Playground
OR
Use $set to replace the students field with students.name array.
db.collection.aggregate([
{
$set: {
"students": "$students.name"
}
}
])
Sample Solution 2 on Mongo Playground

Related

MongoDB: Access current value of document when adding element to array via $push

I have collection MyCollection which basically consists of its _id and a string called comment.
This collection should be bulk-updatable.
That's done like this:
for (const obj of inputObjects) {
bulkObjectsToWrite.push({
updateOne: {
filter: { _id: obj._id },
update: {
$set: {
comment: obj.comment
}
}
}
})
}
await MyCollection.bulkWrite(bulkObjectsToWrite)
So far so good.
However, now the requirement is, that a commentHistory should be added which should look like [{"oldValue": "oldValueOfComment", "newValue": "newValueOfComment"}, ...]
I know I need to use $push for adding a new object to the commentHistory array. But how do I access the comment of the document updated right now, i.e. its current value?
I've tried
$push: {
commentHistory: {
newValue: obj.comment,
oldValue: '$comment',
},
},
but to no avail. The string $comment is added hard-coded, instead of the field being accessed.
(Using Mongoose 5.12.10 and Mongo 4.4.18)
You need to use update with aggregate pipeline.
db.collection.update({
"key": 1
},
[
{
$set: {
"comment": "New",
"commentHistory": {
"$concatArrays": [ //concatenate existing history array with new array entry
"$commentHistory",
[
{
"newValue": "New",
"oldValue": "$comment" //referencing the existing value
}
]
]
}
}
}
])
Demo

Updating documents in MongoDb collection without an exact full match of the query filters

I'm trying to update documents in a collection, and as per mongodb syntax, for some update function, I do this:
Collection.updateMany(
{
filter1: 'filter1-val',
filter2: 'filter2-val',
filter3: 'filter3-val'
},
{ $set: { filed: 'value' } }
)
and mongodb performs the given update operation on documents that match the query. However, it only updates documents that match every single filter in the query. My question, and what I'm wondering is there a way to perform the update on documents, as long as they match at least one of the filters in the query, and not every single one? So basically, a form of relaxed match, so that as long as a document meets one of the filters, then it should be updated.
Thanks in advance; any help is greatly appreciated.
You can use $expr and $or in your search criteria.
db.collection.find({
$expr: {
$or: [
{
$eq: [
"$filter1",
"filter1-val"
]
},
{
$eq: [
"$filter2",
"filter2-val"
]
},
{
$eq: [
"$filter3",
"filter3-val"
]
}
]
}
})
Here is a mongo playground for your reference.
You can use the $or operator.
Collection.updateMany(
{
$or: [
{ filter1: 'filter1-val' },
{ filter2: 'filter2-val' },
{ filter3: 'filter3-val' }
]
},
{ $set: { filed: 'value' } }
);

Mongoose - Query deeply nested Objects

I currently have a problem where I have to update entries in a deeply nested Document. Now to simplify my problem I have this example. Let's assume I store cars in my MongoDB. A Document would look like this
{
Make: "BMW",
Model: "3Series",
Wheels: [
{
_id: someObjectId
Size: "19 inch",
Screws: [
{
_id: someObjectId
Type : "M15x40"
},
{
_id: someObjectId
Type : "M15x40"
}
]
}
]
}
Now if I want to update a specific Wheel, my code would look somewhat like this
CarModel.findOneAndUpdate({
"_id": CarId, "Wheels._id": WheelId
}, {
"$set" : {
"Wheels.$.Size": NewSize
}
})
Now this works. But I am pretty lost on how I would update an specific screw as I am going through 2 Arrays. Any Idea how I could make this work?
You need arrayFilters functionality to define the path for more than one nested array:
CarModel.findOneAndUpdate(
{ "_id": CarId },
{ $set: { "Wheels.$[wheel].Screws.$[screw].Type": "something" } },
{ arrayFilters: [ { 'wheel._id': WheelId }, { 'screw._id': screwId } ] })

Mongoose How to push to nested array with condition

Suppose I have the following schema:
{
userId: docId,
skills: [
{
_id: SkillId,
endorsers: [
{
userId: idOfUser
}
]
}
]
}
What I want is a user should not be able to endorse a specific skill in a document more than once. First should find a specific document using its docId, and inside that, a specific skill using skillId, and then check if its endorsers array is empty or a specific user not endorsed yet, then push.
What I tried so far:
{
"userId": req.params.userId,
"skills": {
"$elemMatch": {
"skillId": ObjectId(req.params.skillId),
"$or": [
{"endorsers": []},
{"endorsers.userId":{$ne: endorser._id}}
]
}
}
},
{
"$push": { "skills.$[outer].endorsers": endorserData }
},
{
"arrayFilters": [
{ "outer.skillId": ObjectId(req.params.skillId) }
]
}
But this is not working, and along update, I need the updated result.
You can use addToSet function of mongoose arrays to keep their elements unique. https://mongoosejs.com/docs/api.html#mongoosearray_MongooseArray-addToSet

Mongodb. Method to limit subdocuments that only have the given fields of the `projection` (that is, filter)?

Because there is no way to filter subdocuments in MongoDB (reference: How to select subdocuments with MongoDB )
Is there another way/method within MongoDB to quickly drop/filter fields that don't have the given field, that don't have the subdocument field in the example below?
Is the only way to do this by processing the result outside of mongodb and filter out all empty documents?
(Imagine a situation when you have a thousand subdocs with varying schemas. I do a .find() and get the 1000 subdocs, but 900 are empty. I just want to get the 100, so I'm not always having to process and drop the empty ones outside MongoDB.)
For example, you have this JSON that is in a collection named monday:
{
document : [
{
subdocument : "Hello World"
},
{
subdocument : "Hello Moon"
},
{
another_field: "Hello Sun"
}
]
}
And you make this query db.monday.find({},{_id:0,"document.subdocument":1})
The result will be:
{ "document" : [
{ "subdocument" : "Hello World" },
{ "subdocument" : "Hello Moon" },
{ }
] }
You see the another_field is still returned although as empty.
I believe that empty fields are returned in this case because document is an array and Mongodb does not want to change the array index of any elements.
You can get an array of subdocuments with the aggregation framework:
db.monday.aggregate(
[
{ $match:
{'document.subdocument' :
{ $exists: 1 }
}
},
{ $unwind: "$document" },
{ $match:
{'document.subdocument' :
{ $exists: 1 }
}
},
{ $project: { subdocument: "$document.subdocument", _id: 0 } }
])
Yields:
[
{
"subdocument" : "Hello World"
},
{
"subdocument" : "Hello Moon"
}
]
The first $match is not necessary, it just speeds up the processing if there are a lot of documents that do not have any of the subdocuments you are looking for.

Resources