removing element of an array that with a match of certain element - mongodb

I have a mongodb collection.
{ user_id: 1,
items : [ { _id: 1 }, { _id: 2}, {_id:3} ] }
I want to remove the items of the array having specific id. Can anybody explain what is wrong with the above query.
db.col.findOneAndUpdate({user_id:1},{$pull:{items:{$elemMatch:{_id:2}}}})

$pull takes an expression as a parameter so you don't have to use $elemMatch (doesn't work in this case). Try:
db.col.update({user_id:1},{$pull:{items:{_id:2}}})
So expression in this case means that MongoDB will remove the document having _id set to 2 but that document can have other properties as well.

Related

Mongodb - remove or replace all array elements

How can i replace all elements from a mongodb array element. As in below example, my requirement is to remove both object from skills array and add new elements in it.
{
"_id": "uniqueid",
"skills": [
{ "skill": "dancer" },
{ "skill": "singer" }
]
}
I need to replace all element of skills array field. How this can be achieved using mongodb java driver, or other query types?
You just need to use $set operator $set to set skills to a new value which will be an array of the new element you want to replace with.
db.collectionName.update(
{ _id: 'uniqueid' },
{ $set:
{
skills: [{'new elements'}]
}
}
)
if you want to remove all elements set the skills to an empty array {skills: []}
if you want to remove a certain elements based on a value use $pull operator $pull

How to check if multiple documents exist

Is there such a query that gets multiple fields, and returns which of these exists in the collection?
For example, if the collection has only:
{id : 1}
{id : 2}
And I want to know which of [{id : 1} , {id : 3}] exists in it, then the result will be something like [{id : 1}].
You are looking for the $in-operator.
db.collection.find({ id: { $in: [ 1, 3 ] } });
This will get you any documents where the id-field (different from the special _id field) is 1 or 3. When you only want the values of the id field and not the whole documents:
db.collection.find({ id: { $in: [ 1, 3 ] } }, { _id: false, id:true });
If you want to check provided key with value is present or not in collection, you can simply check by matching values and combining conditions using $or operator.
By considering id is different than _id in mongo.
You can use $or to get expected output and query will be as following.
db.collection.find({$or:[{"id":1},{"id":3}]},{"_id":0,"id":1})
If you want to match _id then use following query:
db.collection.find({$or:[{"_id":ObjectId("557fda78d077e6851e5bf0d3")},{"_id":ObjectId("557fda78d077e6851e5bf0d5")}]}

MongoDB $pull syntax

I'm having a (hopefully) small syntax problem with $pull in Mongodb.
bulk.find({_id: new mongo.ObjectID(req.session._id)}).updateOne({$pull: {
firstArray: {id: req.params.id},
'secondArray.firstArrayIds': req.params.id
'secondArray.$.firstArrayIds': req.params.id
}});
The firstArray $pull works just fine.
But the secondArray.firstArrayIds and/or secondArray.$.firstArrayIds does not. What am I doing wrong here?
This is my data structure:
clients: {
firstArray: [
{
_id: '153'.
someField1: 'someVal1',
}
...
]
secondArray: [
{
_id: '7423'.
someField1: 'someVal1',
firstArrayIds: [153, 154, 155, ...]
}
...
]
}
EDIT What if there are more than one embedded object in secondArray which firstArrayIds can contain the id i want to delete. In other words, when deleting an object in firstdArray i want to delete references in all secondArray's firstArrayIds Not just the first match.
Your "secondArray" has a nested element structure, so you must identify the outer element you want to match in your query when using a positional $ operator in the update. You basically need something like this:
bulk.find({
"_id": new mongo.ObjectID(req.session._id),
"secondArray._id": "7423"
}).update({
"$pull": {
"firstArray": { "_id": "153" },
"secondArray.$.firstArrayIds": 153
}
});
So there are in fact "two" id values you need to pass in with your request in addition to the general document id. Even though this is nested it is okay since you are only matching on the "outer" level and only on one array. If you tried to match the position on more than one array then this is not possible with the positional operator.

mongodb - project element in array

a doc {"m":[1,2,3], "others":xxx}, get the first element in array 'm' by:
db.find({query},{"m":{$slice:1}, "m":1})
the return is {"m":[1]}, the element in doc is an array. But in this query, only one element in array will be get, so I don't need the return doc contain array which has only one element. This is like SQL local variable in sub-query. The element I want has no name in original doc, if I want get it then I need to make a name for it, the doc returned I wanted is like: {"localVariable":1} rather than {"m":[1]}
I try to project out the first element by:
db.find({query},{"m":{$slice:1}, "m.1":1})
but this don't work.
You seem to be looking for a positional $ operator match. It works like this:
db.collection.find({ m: 2 },{ "m.$": 1 })
That will return you the matched element
If you are in fact looking to always get the second element, then you need the two argument form of $slice:
db.collection.find({ },{ "m": { "$slice": [1,1] } })
Tested output:
db.test.insert({ m: [1,2,3] })
db.test.find({ },{ "m": {$slice: [1,1]} })
{ "_id" : ObjectId("5323cc2770fde63cf1146ba3"), "m" : [ 2 ] }

How to update field in array, only if other field matches value in MongoDB

I tangle a bit in jungles of query and update selectors of MongoDb.
Situation: We have Companies collection. smth like this
Companies.find({}) =>
{_id: 1, item_amount: 5},
{_id: 2, item_amount: 7},
{_id: 3, item_amount: 10}
And we have Users collection with some structure. User want to buy any item and it may decrement value in Companies and increment in Users, with one condition - user may know where he buy the item.
{userId: _id,
ownedItems:
[{company_id: 2, item_amount: 3},
{company_id: 1, item_amount: 5}]
}
Ok. How i may Users.update(), if user want to buy, for example, 5 items from {_id: 3} (we don't know if user has field for company_id: 3)
I thought it may be smth like this:
Companies.update({_id: 3}, {$inc: {item_amount: - 5}})
&&
Users.update({userId: _id}, {$set: {'ownedItems[x].company_id': 3}, $inc{'ownedItems[x].item_amount': 5})
But of cause there are some problems.
How i may know the [x] for each clint? Sort it? and what if I will need to add new Company?
Is $set will work if field 'ownedItems[x].company_id' does not exist?
May be I may check smth in IF/Else statement. With, for example, $exist, or $cond( for aggregation) selectors. But .find() always return a cursor (is it?), not true or false. So what may do? How may I use aggregation selectors? Like this:
Users.find({_id: userId}, {$cond: [{$eq: ['owenedItem[x].company_id', _someId_]}, {$set:{...}}, {$push: {...}}]})
Will it work?
Sounds like you want to use the $elemMatch operator in conjunction with the '$' positional operator:
http://docs.mongodb.org/manual/reference/operator/elemMatch/
http://docs.mongodb.org/manual/reference/operator/positional/
Let's take your example. Let's say you want to find the element in the user's "ownedItems" list where the "company_id" is 3, and update that element's "item_amount" by 5. That can be done in a single update statement like so:
Users.update( { userId: _id, ownedItems : { $elemMatch : { company_id : 3 } } },
{ $inc : { ownedItems.$.item_amount : 5 } } )
The $elemMatch operator makes it explicit which of the elements of the list you're querying for. You can then use the $ operator in the modifier clause to refer to that matched element and make changes to it.
If the "ownedItems" list of the user does not have any entry matching { "company_id" : 3 } then the above update will not do anything. If that happens, you can do a second update with the $addToSet operator to add a new entry like so:
Users.update( { userId: _id },
{ $addToSet : { ownedItems : { company_id : 3, item_amount : 5 } } } )
http://docs.mongodb.org/manual/reference/operator/addToSet/