How to perform update query on deeply nested JSON in mongodb? - arrays

I have a nested json data structure in mongodb which looks like:
{
'tid': 1,
'matches': [{
'dord': 1,
'matches': [{
'tord': 1,
'score': 11
},
{
'tord': 2,
'score': 12
}
]
},
{
'dord': 2,
'matches': [{
'tord': 1,
'score': 21
},
{
'tord': 2,
'score': 22
}
]
}]
}
I want to update the row with "dord": 1 and "tord": 1 and change value of score from 11 to 100. How do I do this?
What I already tried:
db.collection.update({'tid': 1}, {'matches': {$elemMatch: {'dord': 1}}}, {'matches': { $elemMatch: {'tord': 1}}}, {'score': 100})

Demo - https://mongoplayground.net/p/Mi2HnhzkPpE
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/
The filtered positional operator $[] identifies the array elements that match the arrayFilters conditions for an update operation
db.collection.update({ "tid": 1 },
{ $set: { "matches.$[m].matches.$[t].score": 100 } },
{
arrayFilters: [
{ "m.dord": 1 }, // to match where dord = 1
{ "t.tord": 1, "t.score": 11 } // and where tord = 1 and score = 11
]
})

Related

In MongoDB, why would this aggregate() query and this find() query return different results?

In MongoDB, why would this aggregate() query and this find() query return different results?
They both return a different set of 20 results, with a few results in common.
Aggregate
db.items.aggregate([
{
$geoNear: {
near: {
type: "Point",
coordinates: [
-79.3927217,
43.648358
],
},
distanceField: "distance",
minDistance: 0,
maxDistance: 100000,
spherical: true,
},
},
{
$match: {
date: {
$gt: 1529825292207,
$lt: 1659425292207,
}
},
},
{
$project: {
id: 1,
distance: 1,
_id: 0
}
},
{
$sort: {date: -1
}
},
{
$limit: 20
}
])
Find
db.items.find({
"loc": {
"$nearSphere": {
"$geometry": {
"type": "Point",
"coordinates": [
-79.3927217,
43.648358
]
},
"$minDistance": 0,
"$maxDistance": 100000
}
},
"date": {
$gt: 1529825292207,
$lt: 1659425292207,
},
},
{
id: 1,
_id: 0,
distance: 1
}
).sort({date: -1
}).limit(20)
In the aggregation pipeline, you are sorting after projecting out the find you are trying to sort, so at the point the $sort stage processes, none of the documents have a date field, so that stage doesn't change the order of the documents in the stream.
If you reorder the pipeline to put the $sort stage ahead of the $project stage, it should return the same result as the find.

MongoDB Add Field in nested array by other field

Hello i have simple collection:
{
_id: 1,
books: [
{ bookId: 55, c: 5},
{ bookId: 66, c: 6},
{ bookId: 77, c: 7},
]
}
How i can add new field by calulate other field?
here i add field “Z” to current found object in nested array by it calculate field “C”
updateOne(
{
_id : 1,
'books.bookId' : 66
} ,
{
[
{ $addFields: { "books.$.z" : { "$sum" : ["$books.$.c", 1] } } }
]
}
Expected result:
{
_id: 1,
books: [
{ bookId: 55, c: 5},
{ bookId: 66, c: 6, z:7},
{ bookId: 77, c: 7},
]
}
I think there is a short entry (possibly using new $getField ?!), I think mango is still able to combine ‘position operator $’ + (‘varible operator reference by prefix $’ or ‘combine with $getField’) how i try in my sample
Use the aggregation pipeline in the update method to leverage the operators $map, $mergeObjects and $cond to achieve the desired results:
.updateOne(
// The query will find docs where *at least* one bookId in the
// books array equals 66 -- but remember it does not pinpoint
// the location in the array! We will tackle that in the update
// pipeline to follow...
{ _id: 1, 'books.bookId': 66 },
[
{ $set: {
books: {
$map: {
input: '$books',
in: {
$mergeObjects: [
'$$this',
{
$cond: [
{ $eq: ['$$this.bookId', 66] }, // IF books.bookId == 66
{ z: { $sum: ['$$this.c', 1] } }, // THEN merge a new field 'z' with calced value
null // ELSE merge null (a noop)
]
}
]
}
}
}
} }
]
)

MongoDB - Geometry object to return the total pairs/points in linear ring

I am trying to write a performance report based on shapefile data I have stored within docs stored in collections.
Here is a sample of data:
The following function works quite well as it returns the amount of bytes for each document - great, however I would also like to know how many points/pairs are stored within each polygon's linear string for each document.
db.getCollection("_collectionName").aggregate([{"$project": {"object_size": { $bsonSize: "$$ROOT" }}}])
This returns the following set of data (sample):
{ _id: ObjectId("5ef7da26ae8659149c97657e"), rootSize: 42215 },
{ _id: ObjectId("5ef7da45ae8659149c97657f"), rootSize: 118574 },
{ _id: ObjectId("5ef7daf1ae8659149c976585"), rootSize: 11886 },
{ _id: ObjectId("5f216685dbef0f7c3339ec03"), rootSize: 43136 },
{ _id: ObjectId("5ef7daa6ae8659149c976582"), rootSize: 40823 },
{ _id: ObjectId("5f3495129861ce45eb4e9728"), rootSize: 394884 },
{ _id: ObjectId("5ef7d7f6ae8659149c97657c"), rootSize: 125309 },
{ _id: ObjectId("5ef7dad6ae8659149c976584"), rootSize: 127447 },
{ _id: ObjectId("5fa56ef26538cd3bddd8389e"), rootSize: 17670 },
{ _id: ObjectId("5fa56ef26538cd3bddd8389f"), rootSize: 11398 },
{ _id: ObjectId("5fa56ef16538cd3bddd8389c"), rootSize: 2415 },
{ _id: ObjectId("5fa56ef36538cd3bddd838ae"), rootSize: 1757 },
{ _id: ObjectId("5fa56ef36538cd3bddd838b0"), rootSize: 4866 },
{ _id: ObjectId("5fa56ef36538cd3bddd838a8"), rootSize: 1510 },
{ _id: ObjectId("5fa56ef26538cd3bddd838a7"), rootSize: 39631 },
{ _id: ObjectId("5fa56ef36538cd3bddd838ab"), rootSize: 3662 },
{ _id: ObjectId("5fa56ef36538cd3bddd838aa"), rootSize: 15844 },
{ _id: ObjectId("5fa56ef16538cd3bddd8389d"), rootSize: 17196 },
{ _id: ObjectId("5fa56ef26538cd3bddd838a3"), rootSize: 34940 },
{ _id: ObjectId("5fa56ef36538cd3bddd838af"), rootSize: 468367 }
Which is great but it does not tell me how many elements are in the array/linear string within geometry.coordinates.
I have tried the following, but no cigar:
db.getCollection("_collectionName").aggregate([{$project: { count: { $size: { "$ifNull": [ "$geometry", [] ] } } } }])
MongoServerError: The argument to $size must be an array, but was of type: object
It comes back with an error, which i understand - so i referenced the coordinates array:
db.getCollection("_collectionName").aggregate([{$project: { count: { $size: { "$ifNull": [ "$geometry.coordinates", [] ] } } } }])
Which, returned the following data, again correct, if you understand GeoJSON files this is normal as this is the top level of the linear ring, sample data:
{ _id: ObjectId("5ef7da26ae8659149c97657e"), count: 1 }
{ _id: ObjectId("5ef7da45ae8659149c97657f"), count: 1 }
{ _id: ObjectId("5ef7daf1ae8659149c976585"), count: 1 }
{ _id: ObjectId("5f216685dbef0f7c3339ec03"), count: 1 }
So I then added the top level array of 0 to my aggregate function:
db.getCollection("_collectionName").aggregate([{$project: { count: { $size: { "$ifNull": [ "$geometry.coordinates.0", [] ] } } } }])
And this is what was returned:
{ _id: ObjectId("5ef7da26ae8659149c97657e"), count: 0 }
{ _id: ObjectId("5ef7da45ae8659149c97657f"), count: 0 }
{ _id: ObjectId("5ef7daf1ae8659149c976585"), count: 0 }
{ _id: ObjectId("5f216685dbef0f7c3339ec03"), count: 0 }
And that is not possible, here is a screenshot from Studio3T software:
Anybody who might be able to help or point me in the right direction please do so....
(I would be very grateful!)
The dot notation won't work on array elements within an aggregation. You'll want to use the $arrayElemAt operator, as follows:
db.getCollection("_aGStbl").aggregate([{
$project: {
count: { $size: { $arrayElemAt: [ "$geometry.coordinates", 0 ]}}
}
}])
To cater for Null values, you can use a $cond, depending on your objective for the output:
INSERT SOME DATA INTO A TESTDB:
db.arrayTest.insertMany([
{ _id: 1, arrayOfArrays: [ [ 1, 2, 3 ], [ 1, 2, 3, 4 ], [ 1, 2, 3, 4, 5, 6, 7 ] ] },
{ _id: 2, arrayOfArrays: [ [ 4, 5 ], [ 5, 6, 7 ] ] },
{ _id: 3, arrayOfArrays: [ [], [] ] },
{ _id: 4, arrayOfArrays: [ [], [], [] ] },
{ _id: 5 }
] )
{ acknowledged: true,
insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 } }
TRY THESE AGGREGATE CALLS:
db.arrayTest.aggregate([{$project: { count: { $size: { "$ifNull": [ { $arrayElemAt: [ "$arrayOfArrays", 0 ] }, [ ] ] } } } } ] )
{ _id: 1, count: 3 }
{ _id: 2, count: 2 }
{ _id: 3, count: 0 }
{ _id: 4, count: 0 }
{ _id: 5, count: 0 }
db.arrayTest.aggregate([{$project: { count: { $cond: { if: {$arrayElemAt: [ "$arrayOfArrays", 0 ]}, then: { $size: { $arrayElemAt: [ "$arrayOfArrays", 0 ] } }, else: null} } } } ] )
{ _id: 1, count: 3 }
{ _id: 2, count: 2 }
{ _id: 3, count: 0 }
{ _id: 4, count: 0 }
{ _id: 5, count: null }

Mongodb update nested array by key-value

How can I update nested array by list key value?
{
"_id": "mainId",
"events": [{
"id": 1,
"profile": 10,
} {
"id": 2,
"profile": 10,
} {
"id": 3,
"profile": 20,
}
]
}
and I have a list to update:
var list = {id: 2, newval: 222}, {id: 3, newval: 333}
How can I do an update in one query? Or in MongoDB, it will be like a loop?
for({id, val} in list){
update({_id: "mainId", events.$.id: id}, {setField: {events.$.profile: val}})
}
If you have a copy of the events array, you could make the necessary updates in your code and then send the updated array to MongoDB in a single query. Something like this
db.Test.updateOne({_id: "mainId"}, {$set: { "events": [{id: 1, profile: 222}, {id: 2, profile: 10}, {id: 3, profile: 333}] } } )
If you don't have a copy of the events array, you could do a bulk operation. Something like
db.Test.bulkWrite(
[
{ updateOne : {
"filter": {_id: "mainId", "events.id": 1},
"update": { $set: { "events.$.profile": 222 } }
}
},
{ updateOne : {
"filter": {_id: "mainId", "events.id": 3},
"update": { $set: { "events.$.profile": 333 }}
}
}
]
)
For more on bulkWrite, see the MongoDB docs: https://docs.mongodb.com/manual/core/bulk-write-operations/#bulkwrite-methods

MongoDB group query - count by result

db.tickets.aggregate([
{$project:
{_id: 0, dayssince:
{$divide: [{ $subtract: [ 2020, {$convert:{input:{$substrCP:["$data.DATE_BIRTH", 6, 4]}, to: "int"}}]}, 45]}}},
{$match:{dayssince:{$gte: 1}}},
{$group:{_id:{day:"$dayssince"}},count:{$sum:1}}]);
Please, tell me whats wrong, i cant understend, i need to find count of all values
Please explain your problem. Just by indenting your code, its seems count property is outside of the $group operator.
here is your indented and fixed query:
db.tickets.aggregate([
{
$project: {
_id: 0,
dayssince: {
$divide: [
{
$subtract: [2020, { $convert: { input: { $substrCP: ["$data.DATE_BIRTH", 6, 4] }, to: "int" } }]
},
45]
}
}
},
{
$match: { dayssince: { $gte: 1 } }
},
{
$group: {
_id: {
day: "$dayssince"
},
count: {
$sum: 1
}
}
}
]);

Resources