Building mongo query - mongodb

I have a model like this:
[{item: {
_id: 123,
field1: someValue,
price: {
[_id: 456,
field1: anotherValue,
type: []],
[_id: 789,
field1: anotherValue,
type: ['super']]
}
}]
I need to find an item by 3 parameters: item _id, price _id, and check if price type array is empty. And check it in one price field.
Model.findOneAndUpdate({_id: 123, "price._id": 456, "price.type": {size:0})
This query always returns item, cause search in different prices.
Model.findOneAndUpdate({_id: 123, price: {id: 456, type: {size:0})
This query returns error (cast array value or something like this).
tried to build query with $in, $and, but still getting an error

Use $elemMatch:
The $elemMatch operator matches documents that contain an array field
with at least one element that matches all the specified query
criteria.
db.inventory.find({
price: {
"$elemMatch": {
_id: 456,
type: {
$size: 0
}
}
}
})

Related

Pull Document By ID from Triple Nested Array MongoDB

I'd like to be able to pull a document by id from a triple nested array of documents. DB looks something like this:
[{
type: "Foods",
fruit: [{
name: "Apple",
kinds: [{
name: "Red Delicious"
details: [{
_id: ObjectId("123123123"),
colors: ["red", "yellow"]
}]
}]
}]
}]
I'd like to be able to pull the document with the _id: 123123123. I've tried many different ways, but it always says it matches, but won't modify the document.
I've tried:
db.stuff.update({}, {$pull: {fruits: {kinds: {details: {_id: "123123123"}}}}}),
db.stuff.update({}, {$pull: {"fruits.kinds.details' : {_id: "123123123"}}}),
db.stuff.update({}, {$pull: {"fruits.$[].kinds.$[].details' : {_id: "123123123"}}})
But everytime it matches, but won't delete the document.
Please help.
The last attempt is correct however you need to fix two things: fruit instead of fruit (according to your sample data) and types needs to match so you have to convert string to ObjectId
db.stuff.update({}, {$pull: {"fruit.$[].kinds.$[].details' : {_id: mongoose.Types.ObjectId("123123123")}}})

MongoDB query for two input arrays at same index?

I have two arrays A and B of length n defined by the input,
fruit_ids = [{id: "id1"}, {id: "id2"}, {id:"id3"}];
fruit_names = [{name: "Orange"},{name: "Kiwi"},{name: "Banana"}]
and MongoDB documents
{ farm_id: "3344", fruits: [{name: "Orange", id:"id1"}, {name: "Kiwi", id:"id67"}]}
Now I want to write a Mongo query such that it pulls items from particular farm_id specified at array fruit_ids and fruit_names but at same index,
for example for the above input, I want for farm_id: 3344 {name: "Orange", id:"id1"} to get deleted.
Can anyone please help me.
You can use $pullAll operator to remove all the matching elements and build your update statement dynamically using below code:
var fruit_ids = [{id: "id1"}, {id: "id2"}, {id:"id3"}];
var fruit_names = [{name: "Orange"},{name: "Apple"},{name: "Banana"}];
var pullAll = {
$pullAll: { fruits: fruit_ids.map((id, index) => Object.assign(fruit_names[index], id)) }
}
db.col.update({ farm_id: 3344 }, pullAll)
This will only try to update the farm_id: 3344.
I was trying $pullAll as suggested by #mickl in his answer, but the thing is I had other fields inside my embedded documents and because $pullAll works only for exact matches, that's why I currently I am using $pull with $or on the array of embedded docs. I found this solution from this answer how-to-force-mongodb-pullall-to-disregard-document-order.
let arr = [{name: "Orange", id:"id1"}, {name: "Kiwi", id:"id67"}];
db.col.update(
{ farm_id: 3344 },
{ "$pull": { "fruits": { "$or": arr } }}
)

How to query a document using mongoose and send the document to the client with only one relevant element from an array field to the client?

I have the following schema:
var lessonSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: String,
students: [{
_id: mongoose.Schema.Types.ObjectId,
attendance: {
type: Boolean,
default: false,
},
}],
});
The students array is an array of students who attended the particular lesson. I want to find a lesson using whether a particular user is present in the students array and then sent only that element of the students array which corresponds to the user making the request, along with all other fields as it is. For example, the query should return:
{
_id: 'objectid',
name: 'lesson-name'
students: [details of just the one student corresponding to req.user._id]
}
I tried using:
Lesson.find({'students._id': String(req.user._id)}, {"students.$": 1})
The query returns the document with just the id and the relevant element from the students array:
{
_id: 'objectid'
students: [details of the one student corresponding to req.user._id]
}
I tried using:
Lesson.find({'students._id': mongoose.Types.ObjectId(req.user._id)})
This returns the document with the details of all the students:
{
_id: 'objectid',
name: 'lesson-name'
students: [array containing details of all the students who attended the lesson]
}
How can I modify the query to return it the way I want?
You can return the name field by adding it to the projection object like this:
Lesson.find({ "students._id": String(req.user._id) }, { "name": 1, "students.$": 1 })
When you add a projection object (2nd parameter to find), the _id field is returned by default, plus whichever fields you set to 1.
Therefore, you were returning just the _id and the desired student but not the name field.
If you want to return all other fields and just limit the array to the matched item then you can make use of $slice in your projection:
Lesson.find({ "students._id": String(req.user._id) }, { "students.$": { $slice: 1 } })

MongoDB: Upsert document in array field

Suppose, I have the following database:
{
_id: 1,
name: 'Alice',
courses: [
{
_id: 'DB103',
credits: 6
},
{
_id: 'ML203',
credits: 4
}
]
},
{
_id: 2,
name: 'Bob',
courses: []
}
I now want to 'upsert' the document with the course id 'DB103' in both documents. Although the _id field should remain the same, the credits field value should change (i.e. to 4). In the first document, the respective field should be changed, in the second one, {_id: 'DB103', credits: 4} should be inserted into the courses array.
Is there any possibility in MongoDB to handle both cases?
Sure, I could search with $elemMatch in courses for 'DB103' and if I haven't found it, insert, otherwise update the value. But these are two steps and I would like to do both in just one.

mongodb: order and limit the collection, then order and limit the resulting document nested documents

I have the following data structure on my game collection:
{
name: game1
date: 2010-10-10
media: [{
id: 1,
created: 2010-10-10 00:00:59
}, {
id: 2,
created: 2010-10-10 00:00:30
}]
},
{
name: game2
date: 2010-10-09
media: [{
id: 1,
created: 2010-10-09 00:10:40
}, {
id: 2,
created: 2010-10-09 09:01:00
}]
}
I want to get the game with the highest date, then get the related media with the highest created to get it's id. In the example above, the result would be
{
name: game1
date: 2010-10-10
media: [{
id: 1,
created: 2010-10-10 00:00:59
}]
}
I tried to use the find and find_one, and also aggregation, but I could not figure a way to make this query.
Any suggestions?
You will need to $unwind the media array in order to get the subdocument in that array where created is the highest then you $sort your documents by date and created all in descending order. Use $limit to output n documents which is 1 in our case.
In [26]: import pymongo
In [27]: conn = pymongo.MongoClient()
In [28]: db = conn.test
In [29]: col = db.gamers
In [30]: list(col.aggregate([{"$unwind": "$media"}, {"$sort": {"date": -1, "media.created": -1}}, {"$limit": 1}]))
Out[30]:
[{'_id': ObjectId('553323ec0acf450bc6b7438c'),
'date': '2010-10-10',
'media': {'created': '2010-10-10 00:00:59', 'id': 1},
'name': 'game1'
}]