MongoDB. Remove subelement from array using parent elements for query [duplicate] - mongodb

This question already has answers here:
How to remove an element from a doubly-nested array in a MongoDB document
(5 answers)
Closed 2 years ago.
Here is my collection object structure
"_id" : ObjectId("xxxxxxxxxxxxxxxxxxxxxxxx"),
"Name": "HelloWorld",
"OtherFields": "OtherValues",
"Projects" : [
{
"Project" : {
"key" : 111
},
"Category" : [
{
"No" : "123"
},
{
"No" : "987"
}
]
},
{
"Project" : {
"key" : 222
},
"Category" : [
{
"No" : "123"
},
{
"No" : "987"
}
]
}
]
I want to delete element with Category No「123」in Project with key 111
Closer thing that I did was
db.project_collection.updateOne({"_id": ObjectId("collection_obj_id")}, {"$pull": { "Projects": {"$and": [ {"Project.key": 111)}, {"Category.No": "123"} ] }} })
Live example
This command removing whole element inside "Projects" object
But I want to delete only "Projects" → "Category" → [0] subelement which has No 123.
When I trying to change "Projects" after "$pull" to something like "Projects.Category" or "Projects.$.Category"
The positional operator did not find the match needed from the query
error occures.
So is it possible to delete subelement without creating own backend logic?

Knowing project.key and category.no you can use this query:
db.collection.update({
"_id": 1,
"Projects.Project.key": 111
},
{
"$pull": {
"Projects.$.Category": {
"No": "123"
}
}
})
With the updated JSON, the $ operator is nedded.
This query basically point to a document with key: 111 and _id: 1 and using $ do the pull.
So it will pull the 'pointed' element which match no: "123".
Updated example here
I have added another document with _id: 2 to check that only one with _id:1 is updated.

Related

How to remove an element from inner array of nested array pymongo using $ pull

Here is my news document structure
{
"_id" : ObjectId("5bff0903bd9a221229c7c9b2"),
"title" : "Test Page",
"desc" : "efdfr",
"mediaset_list" : [
{
"_id" : ObjectId("5bfeff94bd9a221229c7c9ae"),
"medias" : [
{
"_id" : ObjectId("5bfeff83bd9a221229c7c9ac"),
"file_type" : "png",
"file" : "https://aws.com/gg.jpg",
"file_name" : "edf.jpg"
},
{
"_id" : ObjectId("5bfeff83bd9a221229c7c9ad"),
"file_type" : "mov",
"file" : "https://aws.com/gg.mov",
"file_name" : "abcd.mov"
}
]
}
]}
The queries that i've tried are given below
Approach 1
db.news.find_and_modify({},{'$pull': {"mediaset_list": {"medias": {"$elemMatch" : {"_id": ObjectId('5bfeff83bd9a221229c7c9ac')}} }}})
Approach 2
db.news.update({},{'$pull': {"mediaset_list.$.medias": {"_id": ObjectId('5bfeff83bd9a221229c7c9ac')}} })
Issue we are facing
The above queries are removing entire elements inside 'mediaset_list' . But i only want to remove the element inside 'medias' matching object ID.
Since you have two nested arrays you have to use arrayFilters to indicate which element of outer array should be modified, try:
db.news.update({ _id: ObjectId("5bff0903bd9a221229c7c9b2") },
{ $pull: { "mediaset_list.$[item].medias": { _id: ObjectId("5bfeff83bd9a221229c7c9ad") } } },
{ arrayFilters: [ { "item._id": ObjectId("5bfeff94bd9a221229c7c9ae") } ] })
So item is used here as a placeholder which will be used by MongoDB to determine which element of mediaset_list needs to be modified and the condition for this placeholder is defined inside arrayFilters. Then you can use $pull and specify another condition for inner array to determine which element should be removed.
From #micki's mongo shell query (Answer above) , This is the pymongo syntax which will update all news document with that media id .
db.news.update_many({},
{
"$pull":
{ "mediaset_list.$[item].medias": { "_id": ObjectId("5bfeff83bd9a221229c7c9ad") } } ,
},
array_filters=[{ "item._id": ObjectId("5bfeff94bd9a221229c7c9ae")}],
upsert=True)

How to update document with nested array in mongodb [duplicate]

This question already has answers here:
Mongodb $push in nested array
(4 answers)
Closed 4 years ago.
I've got a document in a collection 'events' with the following structure:
{
"_id" : "abc",
"text" : "some text",
}
I wish to update the document by inserting an array named guestOwner. The result I want would update the document like this:
{
"_id" : "abc",
"text" : "some text",
"guestOwner" : [
{
"name" : "JACK BLACK",
"guests" : [
"GUEST1",
"GUEST2"
]
}
]
}
So I tried an mongo update with the following:
db.events.update({ _id: eventid }, { $push: { guestOwner: { name: username, guests: allGuests } } });
where 'username' is a string, and 'allGuests' is an array of names ["Guest1", "Guest2"]
The issue for me is when a subsequent update to the document occurs, I would want to push a the new 'allGuests' array into the existing one if the name is the same. For example, if a second update occurs with 'allGuests' = ["GUEST3"], and with the same name = "JACK BLACK", I would want the document to be:
{
"_id" : "abc",
"text" : "some text",
"guestOwner" : [
{
"name" : "JACK BLACK",
"guests" : [
"GUEST1",
"GUEST2"
"GUEST3"
]
}
]
}
BUT, if the document were updated with a different name = 'JOHN SMITH' where allGuests array = ["GUEST3"], it would create:
{
"_id" : "abc",
"text" : "some text",
"guestOwner" : [
{
"name" : "JACK BLACK",
"guests" : [
"GUEST1",
"GUEST2"
]
},
{
"name" : "JOHN SMITH",
"guests" : [
"GUEST3"
]
}
]
}
Would I need conditional statements surrounding the mongo update to check for guestOwner[0].name? Not sure if mongo could do this on its own, or if a bunch of logic is going to be necessary.
You could simply do an update where in the query section you would specify the name:
db.events.update({
"_id" : ObjectId("someId"), "guestOwner.name": "JACK BLACK"}, {
$push: { "guestOwner.$.guests": "GUEST11" // etc }
})
If this returns the number of updated elements to be 1 you are good to go since the name exists etc.
If it returns 0 then that name does not exists so you can run:
db.events.update({"_id" : ObjectId("someId")}, {
$addToSet: { guestOwner: { name: "JACK BLACK" // etc }}
})
It would save you one call since if you have to check if the record exists you would always do 2 rounds. One to check and another to take action based on the result. Here if the records is already there you only do one update.

mongodb get object inside array that contains property [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 4 years ago.
I have a mongodb table, that contains a fixture of a soccer tournament, so every matchweek looks like this:
"number": 1,
"games" : [
{
"estadio" : "G C",
"arbitro" : "M",
"resultado" : "vs",
"editor" : "Pepe",
"_id" : ObjectId("5af0889e14553f0788bc9db8"),
"equipo_local" : "Las Palmas",
"equipo_visitante" : "Villareal",
"fecha" : "10/05/2018",
"horario" : "16:00",
"hora" : "12:12"
},
{
"estadio" : "Ciudad de Valencia",
"arbitro" : "A Confirmar",
"resultado" : "vs",
"editor" : "No Asignado",
"_id" : ObjectId("5af0889e14553f0788bc9db7"),
"equipo_local" : "Levante",
"equipo_visitante" : "Deportivo",
"fecha" : "28/01/2019",
"horario" : "18:00"
},
..
]
And I want to find all the objects inside "games" of every matchweek that contains "editor" : "pepe",
I've tried something like this
db.fechas.find({"games.editor": "Pepe"})
But that is showing every "games" array that contains an object with "editor": "Pepe", and I want just that specific object, not the whole array.
find method checks whether your nested array contains any document matching your condition and returns entire document.
You need aggregation framework's $filter operator to get a subset of your array, try:
db.fechas.aggregate([
{
$addFields: {
games: {
$filter: {
input: "$games",
as: "game",
cond: {
$eq: [ "$$game.editor", "Pepe" ]
}
}
}
}
},
{
$match: {
$expr: {
$gt: [ { $size: "$games" }, 0 ]
}
}
}
])
EDIT: to remove the documents where games array is empty you can use $match stage specifying condition as expression ($expr) checking array $size

mongoDB: Querying for documents that may have some specifics options

I'm quite new to mongodb and there is one thing I can't solve right now:
Let's pretend, you have the following document structure:
{
"_id": ObjectId("some object id"),
name: "valueName",
options: [
{idOption: "optionId", name: "optionName"},
{idOption: "optionId", name: "optionName"}
]
}
And each document can have multiples options that are already classified.
I'm trying to get all the documents in the collection that have, at least one, of the multiples options that I pass for the query.
I was trying with the operator $elemMatch something like this:
db.collectioName.find({"options.name": { $elemMatch: {"optName1","optName2"}}})
but it never show me the matches documents.
Can someone help and show me, what I'm doing wrong?
Thanks!
Given a collection which contains the following documents:
{
"_id" : ObjectId("5a023b8d027b5bd06add627a"),
"name" : "valueName",
"options" : [
{
"idOption" : "optionId",
"name" : "optName1"
},
{
"idOption" : "optionId",
"name" : "optName2"
}
]
}
{
"_id" : ObjectId("5a023b9e027b5bd06add627d"),
"name" : "valueName",
"options" : [
{
"idOption" : "optionId",
"name" : "optName3"
},
{
"idOption" : "optionId",
"name" : "optName4"
}
]
}
This query ...
db.collection.find({"options": { $elemMatch: {"name": {"$in": ["optName1"]}}}})
.. will return the first document only.
While, this query ...
db.collection.find({"options": { $elemMatch: {"name": {"$in": ["optName1", "optName3"]}}}})
...will return both documents.
The second example (I think) meeets this requirement:
I'm trying to get all the documents in the collection that have, at least one, of the multiples options that I pass for the query.

Getting only Sub Elements [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 8 years ago.
If I have the following document
{
"_id" : ObjectId("54986d5531a011bb5fb8e0ee"),
"owner" : "54948a5d85f7a9527a002917",
"type" : "group",
"deleted" : false,
"participants" : [
{ "_id": "54948a5d85f7a9527a002917", "name": "user1" },
{ "_id": "5491234568f7a9527a002918", "name": "user2" },
{ "_id": "5491234568f7a9527a002918", "name": "user3" },
{ "_id": "1234567aaaa7a9527a002917", "name": "user2" }
]
}
How would I get all records where name = 'user2'?
I'm trying the followoing:
db.users.find({ _id: ObjectId('54a7103b1a57eee00bc0a9e4') },
{ 'participants.$.name': 'user2') }).pretty()
...and I get the following:
error: {
"$err" : "Can't canonicalize query: BadValue Positional projection 'participants.$.name' does not match the query document.",
"code" : 17287
}
Though the positional operator($) would give you the first matching element from the participant array. If you need all the participants in with the name user2, you need to aggregate the results.
Match the document with the required _id.
Use the redact operator to only keep all the sub documents that have
participants, who have their name as user2.
Code:
var search = "user2";
db.users.aggregate([
{$match:{"_id":ObjectId("54986d5531a011bb5fb8e0ee")}},
{$redact:{$cond:[{$eq:[{$ifNull:["$name",search]},search]},
"$$DESCEND",
"$$PRUNE"]}},
{$project:{"participants":1,"_id":0}} // set _id:1, if you need the _id.
])
o/p:
{
"participants" : [
{
"_id" : "5491234568f7a9527a002918",
"name" : "user2"
},
{
"_id" : "1234567aaaa7a9527a002917",
"name" : "user2"
}
]
}
Coming to your query,
db.users.find({ _id: ObjectId('54a7103b1a57eee00bc0a9e4') },
{ 'participants.$.name': 'user2'}).pretty()
The positional operator can be applied only on the array, that is referred in the query document of the find function. The above query document doesn't have a reference to the array named participants and only refers to the _id field to match a document. Hence you get the error.
From the docs,
The field being limited must appear in the query document
So, changing the query to include the participants array in the query document would fix the error.
db.users.find({ "_id":ObjectId('54a7103b1a57eee00bc0a9e4'),
"participants.name": "user2"
},
{"participants.$.name":"user2"}).pretty()
But it would return you only the first participant that has matched the criteria in the query document.
From the docs,
Use $ in the projection document of the find() method or the findOne()
method when you only need one particular array element in selected
documents.
o/p:
{
"_id" : ObjectId("54986d5531a011bb5fb8e0ee"),
"participants" : [
{
"_id" : "5491234568f7a9527a002918",
"name" : "user2"
}
]
}