Mongoose/MongoDB find Document with array of objects by multiple values - mongodb

I have the following document structure:
recipients: [
{
name: String,
hidden: Boolean,
},
{
name: String,
hidden: Boolean,
},
// more ...
];
I want to query all documents for a given name and a given hidden value in the same object, meaning at the same index of the recipients array. How can I query for example "all documents for name = test and hidden = false" (where hidden is in the same object as the name)? I tried the following
const chats = await Model.find(
{
'recipients.name': name,
'recipients.hidden': false,
},
But this still returns the document because it does not seem to use those 2 conditions for the same object, but across all objects in the array.

Nevermind, got it. See the MongoDB docs for $elemMatch (https://docs.mongodb.com/manual/reference/operator/query/elemMatch/#array-of-embedded-documents)
{
"recipients": {
"$elemMatch": {
"name": name,
"hidden": false
},
},
}

Related

Issue using $pull from nested Mongoose array atomic operation

I can't seem to get this mongoose $pull operation to work at all. I have tried many solutions, but would ultimately like to use an atomic operation to achieve this as that is the design pattern in the rest of my code.
In my List schema, I have an array of Item objects. Each Item object has an array of Notes which are all string values. Creating, Reading, and Updating these Notes arrays has been no issue. But I can't seem to get the Delete functionality to work.
Schemas:
List
const listSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "Users"
},
name: {
type: String
},
items: {
type: Array,
default: [itemSchema]
}
});
Item
const itemsSchema = {
item: String,
style: String,
moved: Boolean,
notes: Array
};
Sample Document:
_id: 6186940ce10fbd7cec9fb01f
items: Array
0: Object
notes: Array
0: "Updated again x2"
1: "Test one to delete edit"
2: "New one"
_id: 6186d98dcef2ae43605becc4
item: "Test 1"
style: ""
moved: false
1: Object
notes: Array
_id: 6186d98fcef2ae43605becc5
item: "Test 2"
style: ""
moved: false
2: Object
notes: Array
_id: 6186d991cef2ae43605becc6
item: "Test 3"
style: ""
moved: false
3: Object
notes: Array
0: "Add from none"
1: "typing a really long sentence here to see what happens when I get to t..."
2: "Test"
_id: 6186d994cef2ae43605becc7
item: "Test 4"
style: ""
moved: false
user: 611083d8baed4458d8dcd273
name: "Nov-06-2021"
__v: 0
Methods:
Create
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$push": { "items.$.notes": newNote } },
{ new: true },
(err, newList) => {}
)
Update
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$set": { "items.$.notes.$[note]": newNoteText } },
{
arrayFilters: [{"note": note}],
new: true
},
(err, newList) => {}
)
Delete (not working)
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$pull": { "items.$.notes.$[note]": note } },
{
arrayFilters: [{"note": note}],
new: true
},
(err, newList) => {}
)
The current Delete code block is close to what I'd like my solution to look like. By all rights, and everything I've read, it should already work. Can anyone show me what will work, and possibly why this isn't currently working?
I have tried many solutions including using $in, $[], $elemMatch, and a bunch more outside the box solutions. I don't see why $push and $set work without issue, but $pull is deciding to do nothing.
The current response I get from MongoDb with this operation doesn't include an error message, but the returned newList is a null value, and no change is reflected in my DB. I am currently on the latest version of mongoose: 5.13.13.
The solution to this was to remove the $[note]from my operator as $pull requires an array to operate on, not the array object.
I definitely tried that before, before there was an issue with my request body that was coming from the front end. I wasn't using the correct syntax for an axios.delete, however, and that was my main issue.
Solution
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$pull": { "items.$.notes": note } },
{ new: true },
(err, newList) => {}
)
I removed $[note] as a variable because $pull operates on the entire notes array. Therefore, I no longer need the arrayFilters because it is already looking for the note variable.

update Object inside array of Object with mongoose

I'm trying to update and object inside array in MongoDB.
my model is:
let userSchema = new mongoose.Schema({
userName: {
type: String
},
password: {
type: String
},
history: []
});
And inside history each element is from the next type:
id, array(named ing_array) and boolean field called favorite.
I'm trying to update the favorite field with mongoose with the userName and the id.
I tried to do this query and I didn't succed.
Could some one tell me whats worng?
[object photo]: https://i.stack.imgur.com/2mYpP.png
User.findOneAndUpdate(
{ "userName": user_name, "history.id": id },
{ "$set": { "history.$.favorite": true }}
);
You have to use arrayFilters in this way:
db.collection.update({
"userName": "uname",
"history.id": 1
},
{
"$set": {
"history.$[element].favorite": false
}
},
{
"arrayFilters": [
{
"element.id": 1
}
]
})
Note that update query has the format: update(query, update, options) (Check the docs).
When you do { "userName": user_name, "history.id": id } you are telling mongo "Give me all documents where userName is user_name and array history has an id with value id. This return all history array because it belows to the document.
To update an specific object into the array is neccessary to use arrayFilters to tell mongo which object do you want to update. In this case the object where id is equal to 1. You can use as you want to match wit your requirements.
Example here

MongoDB - Query nested objects in nested array with array of strings filter

So basically I need to filter my data with my own filter, which is array of strings, but problem is, that that exact field is inside nested object in array in DB. so, part of my Schema looks like this:
members: [
{
_id: { type: Schema.Types.ObjectId, ref: "Users" },
profilePicture: { type: String, required: true },
profile: {
firstName: { type: String },
lastName: { type: String },
occupation: { type: String },
gender: { type: String }
}
}
]
and my filter looks like this
gender: ["male","female"]
expected result with this filter is to get a team which has both male users and female users, if it has only male, or only female, it should not give me that team. but everything i've tried was giving me everything what included males and females even tho there were only male members.
what i've tried:
db.teams.find(members: { $elemMatch: { "profile.gender": { $in: gender } } })
This works only when there is one gender specified in the filter, and well, i know it must not work on what i am trying to achieve, but i dont know how to achieve it. any help will be appreciated
Edit: I've tried to do it in this way
db.teams.find({
$and: [
{ members: { $elemMatch: { "profile.gender": gender[0] } } },
{ members: { $elemMatch: { "profile.gender": gender[1] } } }
]
})
and this gives me result only when both filters are specified, however, if there is only one filter(either "male", or "female") it is giving me nothing.
Use $and operator instead of $in.
db.teams.find(members: {$elemMatch: {$and: [{'profile.gender': 'male'}, {'profile.gender': 'female'}]}})
This query works no matter how many elements you want to compare
db.teams.find({$and: [{'members.profile.gender': 'male'}, {'members.profile.gender': 'female'}]})
You need to dynamically generate the query before passing it to find, if you want to cover more than one case.
You can do this with the $all operator that finds docs where an array field contains contains all specified elements:
var gender = ['male', 'female'];
db.teams.find({'members.profile.gender': {$all: gender}});

What is the better implementation? MongoDb Query

I need some help with MongoDb. I need to check if an object exists in the database. If it's true, then I need to check if this object has a specific element into array (Products). If not, I need to create this object(Order) with this element(Cookie) in to array(Products).
Example data:
Order {
_id: ObjectId("580bc55f54101f1d18152d88"),
code: "AVG223424",
products: [
{
name: "Cookie"
},
{
name: "Soda"
}
]
}
Finally, what is the better implementation?
Assuming you are using a collection with the name 'Orders'
db
.Orders
.update({
code: "12345"
}, {
$addToSet: {
products: {
name: "Cookie"
}
},
$setOnInsert: {
code: "12345"
}
}, {
upsert: true
});
This query looks for a document with the same 'code,' and if found, will add the object '{name: "Cookie"}' if there is no other Object with the same key/val pairs. If the document does not exist, the '$setOnInsert' command will set the specified fields only if a new document is created.

Unexpected result when trying to filter fields in mongo db

I have a document structured as follows:
{
_id: "someid",
games: [{
slug: "world-of-warcraft",
class: 'shaman'
}, {
slug: 'starcraft-ii',
class: 'zerg'
}],
roles: {
'starcraft-ii': ['player'],
'world-of-warcraft': ['player']
}
}
I am trying to filter it so that only starcraft-ii within the games array will show up for all players in the role of player in starcraft-ii. I do the following:
function getUsersInGame(slug) {
return Users.find({
'games.slug': slug,
[`roles.${slug}`]: 'player'
}, {
fields: {
'games.$': 1,
'roles': 1
}
});
}
However, this does not match within the games array, and instead returns a 1-element array with world-of-warcraft instead.
What is the appropriate way to filter this array in mongo?
Use $elemMatch in your fields, since the $ will return the first element of the array. So your query should look like this:
function getUsersInGame(slug) {
return Users.find(
{
'"roles.'+ slug + '"': { $in : ['player']}
},
{
'games': {
$elemMatch: {'slug': slug}
},
'roles': 1
});
Please note the difference from the docs:
"The $ operator projects the array elements based on some condition from the query statement.
The $elemMatch projection operator takes an explicit condition argument. This allows you to project based on a condition not in the query..."