Remove entire objects by array of ids - mongodb

I have an array of objects that contains metadata and looks similar to this.
Data:
metadata:[
{ matchid: '1', region: 'europe' },
{ matchid: '2', region: 'africa' },
{ matchid: '3', region: 'asia' },
]
I have an endpoint setup to receive an array of IDS ['1', '2'] which would the remove all the objects containing these IDS.
This is my current query:
Query to remove objects
xx.findByIdAndUpdate(
id,
$pullAll: {
"metadata.matchid": {
$in: req.body.matches
}
}
)
I am expecting both objects with the ids of 1 and 2 to be removed
Expected Results:
metadata:[
{ matchid: '3', region: 'asia' },
]
I am recieving an error I have never seen before it is an object that says codeName: "BadValue"

As documentation says:
The $pullAll operator removes all instances of the specified values from an existing array. Unlike the $pull operator that removes elements by specifying a query.
$pullAll requires and exact match and $pull is like to use a filter. So you can use $pull in this way.
yourModel.findByIdAndUpdate(
id,
$pull: {
metadata:{
matchid: { $in: req.body.matches}
}
}
)
Example here

Related

Inserting data to nested array in mongodb

I have a document which looks like this
{
_id:1,
list_id:23,
name:'list01'
cards:[
{
id:3,
name:'card01'
categories:[{
id:10,
category:'section01',
tags:[{id:11,name:'tag01',is_selected: true}]
}]
}
]
}
I need to insert/push some data to tags array in a selected category for a given list_id but I'm getting an error saying
MongoError: Too many positional (i.e. '$') elements found in path
'cards.$.categories.$.tags'
This is the query that I have tried out. What's wrong with this query any idea on how to achieve this?
db.collection(TABLE)
.updateOne(
{ list_id: 23, 'cards.categories.category': 'section01'},
{ $push: { 'cards.$.categories.$.tags': { name: 'tag02', id: uuidv4(), is_selected: true } } }
);
You can not use multiple $ positional, for your case you can use single positional and arrayFilters,
The filtered positional operator $[<identifier>] identifies the array elements that match the arrayFilters conditions for an update operation,
db.collection(TABLE).updateOne({
list_id: 23,
"cards.categories.category": "section01"
},
{
$push: {
"cards.$.categories.$[elem].tags": {
name: "tag02",
id: uuidv4(),
is_selected: true
}
}
},
{
arrayFilters: [
{ "elem.category": "section01" }
]
})
Playground
In short, it is not possible.
Nested Arrays
The positional $ operator cannot be used for queries which traverse
more than one array, such as queries that traverse arrays nested
within other arrays, because the replacement for the $ placeholder is
a single value
https://docs.mongodb.com/manual/reference/operator/update/positional/
However, you may want to try $[]
Nested Arrays The filtered positional operator $[] can be
used for queries which traverse more than one array and nested arrays.
For an example, see Update Nested Arrays in Conjunction with $[].
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#position-nested-arrays-filtered
You can use $[identifier]
db.collection.update({
"list_id": 23,
"cards.categories.category": "section01"
},
{
$push: {
"cards.$.categories.$[elem].tags": {
name: "tag02",
id: uuidv4(),
is_selected: true
}
}
},
{
arrayFilters: [
{
"elem.category": "section01"
}
],
multi: true
})
try it here

Mongodb: Get last item from array in document

I have a collection in MongoDB with elements looking like this
{
userId: 'X',
access: [
{ deviceId: 'a', time: "A timestamp" },
{ deviceId: 'b', time: "Another timestamp" },
]
}
I want to match documents based on userId and then I want to get the last element in the access array. The value I am most interested in here for user with id "X" is "Another timestamp".
I do not want mongodb to return the entire document, just that last element and always the last one.
How can I write a query/aggregation that solves this?
Try using $slice:
db.collection.find({ userId: 'value' }, { access: { $slice: -1 } } )

Using upsert with subdocument and positional operator '$'

I have a collection like the following:
{
_id: ...,
userId: test,
cards: [
{ cardId: 166, qty: 2 },
...
]
}
I can send the following query to mongo to update a specific card:
db.getCollection('collections').update(
{ 'userId': 'test', 'cards.cardId': 166},
{ $set: {"cards.$.qty": 3} }
)
I also want to be able to create the card if it doesn't exist (ie. no card with such id) but the documentation says:
Do not use the positional operator $ with upsert operations because
inserts will use the $ as a field name in the inserted document.
Is there any way around this? Can I do an update-if-exists/create with a single request to the database?
try this way
db.getCollection('collections').update(
{ 'userId': 'test', 'cards.$.cardId': 166},
{ $set: {"cards.$.qty": 3} }
)
or
db.getCollection('collections').update(
{ 'userId': 'test', 'cards.0.cardId': 166},
{ $set: {"cards.0.qty": 3} }
)

MongoDB: Add and remove from array field at the same time

I want to rename tags in our documents' tags array, e.g. change all tags a in the collection to c. The documents look something like this:
[ { _id: …, tags: ['a', 'b', 'c'] },
{ _id: …, tags: ['a', 'b'] },
{ _id: …, tags: ['b', 'c', 'd'] } ]
I need to keep tags unique. This means, an update like this will not work, because the first document will end up containing tag c twice:
db.docs.update(
{ tags: 'a' },
{ $set: { 'tags.$': 'c' } }
)
So, I tried this alternatively:
db.docs.update(
{ tags: 'a' },
{
$pull: { 'a' },
$addToSet: { 'c' }
}
)
But this gives a MongoError: Cannot update 'tags' and 'tags' at the same time.
Any chance of renaming the tags with one single update?
According to official MongoDB documentation, there is no way of expressing "replace" operation on a set of elements. So I guess, there isn't a way to do this in single update.
Update:
After some more investigation, I came across this document. If I understand it correctly, your query should look like this:
db.docs.update({
tags: 'a'
}, {
$set: { 'tags.$': 'c'}
})
Where 'tags.$' represents selector of the first element in "tags" array that matches the query, so it replaces first occurence of 'a' with 'c'. As I understand, your "tags" array does not contain duplicates, so first match will be the only match.

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..."