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
Related
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.
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)
This question already has answers here:
Project first item in an array to new field (MongoDB aggregation)
(3 answers)
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 4 years ago.
{
"_id" : ObjectId("5b04e9d891081234f8b69199"),
"simpleType" : "somethingSomething",
"embeddedArray" : [
{
"type" : "theTypeIWant",
"data" : [...]
},
{
"type" : "notThatOne",
"data" : [...]
},
{
"type" : "notThatOne",
"data" : [...]
},
{
"type" : "notThatOne",
"data" : [...]
},
{
"type" : "notThatOne",
"data" : [...]
}
]
}
I have a collection which the documents are structured like my sample above. I already started with an aggregation to match the type I want, but I get the whole array with it which I don't want.
db.collection.aggregate(
[
{$project: {"simpleType" : 1, "embeddedArray.type": 1, "embeddedArray.data": 1}},
{$match: {"embeddedArray.type" : "theTypeIWant"}}
]
)
Is there a way to get only that element of the embeddedArray, that matches the type I am searching for, for my projection?
I cannot guarantee that the element with the type I am searching for will always be the first element in my embedded array.
I would like to have a result set like following:
{
"simpleType" : "somethingSomething",
"type": "theTypeIWant",
"data": [...]
}
or:
{
"simpleType" : "somethingSomething",
"embeddedArray":{
"type": "theTypeIWant",
"data": [...]
}
}
I have a really simple question which has troubled me for some time. I have a list of objects containing an array of Measurements, where each of these contains a time and multiple values like below:
{
"_id" : ObjectId("5710ed8129c7f31530a537bc"),
"Measurements" : [
{
"_t" : "Measurement",
"_time" : ISODate("2016-04-14T12:31:52.584Z"),
"Measurement1" : 1
"Measurement2" : 2
"Measurement3" : 3
},
{
"_t" : "DataType",
"_time" : ISODate("2016-04-14T12:31:52.584Z"),
"Measurement1" : 4
"Measurement2" : 5
"Measurement3" : 6
},
{
"_t" : "DataType",
"_time" : ISODate("2016-04-14T12:31:52.584Z"),
"Measurement1" : 7
"Measurement2" : 8
"Measurement3" : 9
} ]
},
{
"_id" : ObjectId("5710ed8129c7f31530a537cc"),
"Measurements" : [
{
"_t" : "Measurement",
"_time" : ISODate("2016-04-14T12:31:52.584Z"),
"Measurement1" : 0
....
I want to create a query which projects the following data set into the one below. For example, query for Measurement1 and create an array of objects containing the time and value of Measurement1 (see below) via mongo aggregation framework.
{ "Measurement": [
{
"Time": ISODate("2016-04-14T12:31:52.584Z"),
"Value": 1
}
{
"Time": ISODate("2016-04-14T12:31:52.584Z"),
"Value": 4
}
{
"Time": ISODate("2016-04-14T12:31:52.584Z"),
"Value": 7
}
]}
Seems like a pretty standard operation, so I hope you guys can shed some light on this.
You can do this by first unwinding the Measurements array for each doc and then projecting the fields you need and then grouping them back together:
db.test.aggregate([
// Duplicate each doc, once per Measurements array element
{$unwind: '$Measurements'},
// Include and rename the desired fields
{$project: {
'Measurements.Time': '$Measurements._time',
'Measurements.Value': '$Measurements.Measurement1'
}},
// Group the docs back together to reassemble the Measurements array field
{$group: {
_id: '$_id',
Measurements: {$push: '$Measurements'}
}}
])
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"
}
]
}