How to remove array element in mongodb? - mongodb

Here is array structure
contact: {
phone: [
{
number: "+1786543589455",
place: "New Jersey",
createdAt: ""
}
{
number: "+1986543589455",
place: "Houston",
createdAt: ""
}
]
}
Here I only know the mongo id(_id) and phone number(+1786543589455) and I need to remove that whole corresponding array element from document. i.e zero indexed element in phone array is matched with phone number and need to remove the corresponding array element.
contact: {
phone: [
{
number: "+1986543589455",
place: "Houston",
createdAt: ""
}
]
}
I tried with following update method
collection.update(
{ _id: id, 'contact.phone': '+1786543589455' },
{ $unset: { 'contact.phone.$.number': '+1786543589455'} }
);
But it removes number: +1786543589455 from inner array object, not zero indexed element in phone array. Tried with pull also without a success.
How to remove the array element in mongodb?

Try the following query:
collection.update(
{ _id: id },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);
It will find document with the given _id and remove the phone +1786543589455 from its contact.phone array.
You can use $unset to unset the value in the array (set it to null), but not to remove it completely.

You can simply use $pull to remove a sub-document.
The $pull operator removes from an existing array all instances of a value or values that match a specified condition.
Collection.update({
_id: parentDocumentId
}, {
$pull: {
subDocument: {
_id: SubDocumentId
}
}
});
This will find your parent document against given ID and then will remove the element from subDocument which matched the given criteria.
Read more about pull here.

In Mongoose:
from the document:
To remove a document from a subdocument array we may pass an object
with a matching _id.
contact.phone.pull({ _id: itemId }) // remove
contact.phone.pull(itemId); // this also works
See Leonid Beschastny's answer for the correct answer.

To remove all array elements irrespective of any given id, use this:
collection.update(
{ },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);

To remove all matching array elements from a specific document:
collection.update(
{ _id: id },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);
To remove all matching array elements from all documents:
collection.updateMany(
{ },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);

Given the following document in the profiles collection:
{ _id: 1, votes: [ 3, 5, 6, 7, 7, 8 ] }
The following operation will remove all items from the votes array that are greater than or equal to ($gte) 6:
db.profiles.update( { _id: 1 }, { $pull: { votes: { $gte: 6 } } } )
After the update operation, the document only has values less than 6:
{ _id: 1, votes: [ 3, 5 ] }
If you multiple items the same value, you should use $pullAll instead of $pull.
In the question having a multiple contact numbers the same use this:
collection.update(
{ _id: id },
{ $pullAll: { 'contact.phone': { number: '+1786543589455' } } }
);
it will delete every item that matches that number. in contact phone
Try reading the manual.

Related

Delete elements from array in mongodb [duplicate]

Here is array structure
contact: {
phone: [
{
number: "+1786543589455",
place: "New Jersey",
createdAt: ""
}
{
number: "+1986543589455",
place: "Houston",
createdAt: ""
}
]
}
Here I only know the mongo id(_id) and phone number(+1786543589455) and I need to remove that whole corresponding array element from document. i.e zero indexed element in phone array is matched with phone number and need to remove the corresponding array element.
contact: {
phone: [
{
number: "+1986543589455",
place: "Houston",
createdAt: ""
}
]
}
I tried with following update method
collection.update(
{ _id: id, 'contact.phone': '+1786543589455' },
{ $unset: { 'contact.phone.$.number': '+1786543589455'} }
);
But it removes number: +1786543589455 from inner array object, not zero indexed element in phone array. Tried with pull also without a success.
How to remove the array element in mongodb?
Try the following query:
collection.update(
{ _id: id },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);
It will find document with the given _id and remove the phone +1786543589455 from its contact.phone array.
You can use $unset to unset the value in the array (set it to null), but not to remove it completely.
You can simply use $pull to remove a sub-document.
The $pull operator removes from an existing array all instances of a value or values that match a specified condition.
Collection.update({
_id: parentDocumentId
}, {
$pull: {
subDocument: {
_id: SubDocumentId
}
}
});
This will find your parent document against given ID and then will remove the element from subDocument which matched the given criteria.
Read more about pull here.
In Mongoose:
from the document:
To remove a document from a subdocument array we may pass an object
with a matching _id.
contact.phone.pull({ _id: itemId }) // remove
contact.phone.pull(itemId); // this also works
See Leonid Beschastny's answer for the correct answer.
To remove all array elements irrespective of any given id, use this:
collection.update(
{ },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);
To remove all matching array elements from a specific document:
collection.update(
{ _id: id },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);
To remove all matching array elements from all documents:
collection.updateMany(
{ },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);
Given the following document in the profiles collection:
{ _id: 1, votes: [ 3, 5, 6, 7, 7, 8 ] }
The following operation will remove all items from the votes array that are greater than or equal to ($gte) 6:
db.profiles.update( { _id: 1 }, { $pull: { votes: { $gte: 6 } } } )
After the update operation, the document only has values less than 6:
{ _id: 1, votes: [ 3, 5 ] }
If you multiple items the same value, you should use $pullAll instead of $pull.
In the question having a multiple contact numbers the same use this:
collection.update(
{ _id: id },
{ $pullAll: { 'contact.phone': { number: '+1786543589455' } } }
);
it will delete every item that matches that number. in contact phone
Try reading the manual.

Add new document into an array if there is no same field value in any other array embedded document

I have a document
{
_id:1,
persons:[
{name:"Jack", age:10},
{name:"Ma",age:20}
]
}
I want to push new document {name:"Ho",age;22} in persons array. But there is a condition-
the new document will be added to array if name name:"Ho" does not exist in other embedded array documents. If exists, {name:"Ho",age;22} will not be added to persons array.
What is the way?
You can use the $not operator along with $elemMatch to query the document that doesn't a person with that name.
Your update would look like this
db.collection.update({
_id: 1,
persons: {
$not: { $elemMatch: { name: "Ho" } }
}
}, { $push: { persons: { name: "Ho", age: 30 } } })
This works for me
db.collection('test').update({
_id: 1,
"persons.name" : {$ne:"Ho"}
}, { $push: {persons: { name: "Ho", age: 30 } } })

Remove element from array aggregate

How can I loop through an array and remove a specific element based on a field.
Here is the layout I have - it is in a collection called cases:
** The collection contains a companyID, cases [Array], lastModified **
So I will have to use an aggregate to unwind the cases and then search for the casenumber where it equals '17':
db.cases.aggregate([
{ $match: { companyID: 218}},
{ $unwind: '$cases' },
{ $match: {'cases.casenumber': '17'} }
])
This returns:
But now I want to delete just that specific item.
Thanks.
You can use of an updateMany request. First argument is the matching condition, the second is the action.
$pull is a special keyword that will remove matching elements from arrays.
db.collection.updateMany({
companyID: 218,
}, {
$pull: {
cases: {
casenumber: 17,
},
},
})
https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/
https://docs.mongodb.com/manual/reference/operator/update/pull/
Example from the doc :
db.profiles.update( { _id: 1 }, { $pull: { votes: { $gte: 6 } } } )

AddToSet new object or update counter in existings

I have document {id:1, data: [{'name': 'Bob', 'counter':1}, {'name':'Jack', 'counter':1}]}
What I'm expecting:
query:
db.inventory.update(
{ _id: 1 },
{ $addToSet: { data: { $each: [{'name': 'Bob'}, {'name':'Jack'}, {'name':'John'}] } } }
)
result:
{id:1, data: [{'name': 'Bob', 'counter':2}, {'name':'Jack', 'counter':2}, {'name':'John', 'counter':1}]}
You won't be able to do this in a single query. The $addToSet operator will only work if the element being added and the element that exists are an exact match. Instead, you'll need to do it in multiple parts:
// Insert element for Bob if it doesn't exist.
db.inventory.update(
{
_id: 1,
"data.name": {$ne: "Bob"}
},
{
$push: {
data: {
name: "Bob",
counter: 0 // Initialized to 0 so that the first increment results in the expected value of 1.
}
}
}
)
// Increment the counter for Bob.
db.inventory.update(
{
_id: 1,
"data.name": "Bob"
},
{
$inc: {
"data.$.counter": 1
}
}
)
// Repeat as necessary for each element you wish to insert.
This is simply a limitation that you need to work around with your existing document structure. If you modify your document structure such that data is a nested sub-document with each name being a field within that sub-document, you could make this work with a single query:
// Version 1: flat value.
db.inventory.update(
{ _id: 1 },
{ $inc: {
"data.Bob": 1,
"data.Jack": 1
}}
)
/*
Document will look like this:
{
_id: 1,
data: {
Bob: 2,
Jack: 2,
John: 1
}
}
*/
// Version 2: nested sub-document.
db.inventory.update(
{ _id: 1 },
{ $inc: {
"data.Bob.counter": 1,
"data.Jack.counter": 1
}}
)
/*
Document will look like this:
{
_id: 1,
data: {
Bob: {
counter: 2
},
Jack: {
counter: 2
},
John: {
counter: 1
}
}
}
*/
Be warned, however, that you will not be able to index data effectively if you go with this solution, so querying efficiently on e.g. all documents containing elements with data.$.counter > 1 simply will not be possible.
The trade-offs are yours to consider. You can either have efficient updates or efficient lookups, but having both is unlikely to happen. I would personally recommend updating each element individually, but you will know your program's needs far better than I will.

How do I query by field order in MongoDB?

Let's say I have the following documents in my MongoDB collection outfits:
{ _id: ..., shirt: { type: "T-shirt", size: "M" } }
{ _id: ..., shirt: { type: "Long-sleeve", size: "M" } }
{ _id: ..., shirt: { type: "T-shirt", size: "S" } }
{ _id: ..., shirt: { size: "M", type: "Long-sleeve" } }
{ _id: ..., shirt: { type: "T-shirt", size: "L" } }
How do I get all documents where the size-field is the first field in the embedded shirt-document?
I would also be satisfied with a solution that simply tells me which documents where size is before type in shirt.
Why?
When querying an embedded document in MongoDB, the order of the fields matter. For example:
db.outfits.find({ "shirt": { size: "M", type: "Long-sleeve" } })
will get me just one outfit, even though there are two outfits with a shirt of the type Long-sleeve in size M.
Basically I need to run a query to standardize the field order in my collection, and optimally I would like to fix the few ones that aren't in the right order instead of re-inserting everything.
Reference: https://softwareengineering.stackexchange.com/questions/319468/why-does-mongodb-check-for-order-of-keys-when-matching-embedded-documents
For MongoDB > 3.4.4 you can do this:
db.outfits.aggregate({
$project: {
firstField: { // create a new field "firstField"
$arrayElemAt: [{ // get the first element of...
$objectToArray: "$shirt" // ... the "shirt" subdocument represented as an array
}, 0]
}
}
}, {
$match: {
"firstField.k": "size" // just return the ones we are interested in
}
})
Also note that you can query like this which will eliminate the problem with the order:
db.outfits.find({ "shirt.size": "M", "shirt.type": "Long-sleeve" })
I ended up using the following:
db.outfits.find({ $where: function() {
return Object.keys(this.shirt)[0] === 'type'
} })
But I also found the more archaic:
db.outfits.find({ $where: function() {
return tojsononeline(this)[64] === 't'
} })