Extracting Particular object from MongoDB from nested Object Array in Document - mongodb

I have this collection in MongoDB in this Structure.Now I need to query the on this data to get the relatedPeople with weight 60. I searched on this but found noting specific most query give data using $ symbol but it gives you rootnode but not the info that which nested node is maching.
{
"_id": "7686348264868327",
"Name": "myName",
"phoneNo": "12434576896",
"ExtraDetails": {
"TotalPeople": 10,
"activePeople": 8,
"lostPeople": 2,
"relatedPeople": [{
"Name": "reev",
"Relation": "Father",
"Weight": 60
},
{
"Name": "magen2",
"Relation": "Mother",
"Weight": 60
},
{
"Name": "neo",
"Relation": "Gardian",
"Weight": 70
}
]
}
}
{
"_id": "76866898698798",
"Name": "myName2",
"phoneNo": "867867868",
"ExtraDetails": {
"TotalPeople": 8,
"activePeople": 6,
"lostPeople": 2,
"relatedPeople": [{
"Name": "amazing",
"Relation": "Father",
"Weight": 60
},
{
"Name": "caring",
"Relation": "Mother",
"Weight": 90
},
{
"Name": "neo",
"Relation": "Gardian",
"Weight": 75
}
]
}
}
The Output Should be Something Like This
"relatedPeople":[
{
"Name": "reev",
"Relation": "Father",
"Weight": 60
},
{
"Name": "magen2",
"Relation": "Mother",
"Weight": 60
},
{
"Name": "amazing",
"Relation": "Father",
"Weight": 60
}
]
Or
[{
"Name": "reev",
"Relation": "Father",
"Weight": 60
},
{
"Name": "magen2",
"Relation": "Mother",
"Weight": 60
},
{
"Name": "amazing",
"Relation": "Father",
"Weight": 60
}]

You can try following solution:
db.collection.aggregate([
{ $match: { "ExtraDetails.relatedPeople.Weight": 60 } },
{ $project: { "ExtraDetails.relatedPeople": 1 } },
{ $unwind: "$ExtraDetails.relatedPeople" },
{ $match: { "ExtraDetails.relatedPeople.Weight": 60 } },
{ $replaceRoot: { newRoot: "$ExtraDetails.relatedPeople" } }
])
I'm using $unwind to query relatedPeople as a separate documents in $match and $replaceRoot to get rid of nestings.
Alternatively, to get first expected shape you can use $group with $push:
db.collection.aggregate([
{ $match: { "ExtraDetails.relatedPeople.Weight": 60 } },
{ $project: { "ExtraDetails.relatedPeople": 1 } },
{ $unwind: "$ExtraDetails.relatedPeople" },
{ $match: { "ExtraDetails.relatedPeople.Weight": 60 } },
{
$group: {
_id: null,
relatedPeople: { $push: "$ExtraDetails.relatedPeople" }
}
},
{ $project: { _id: 0 } }
])

Related

Remove unwanted key on nested unique keys MongoDB

I have this kind of mongodb document example
"data": {
"2023-02-01": {
"123": {
"price": 100,
},
"234": {
"price": 100,
},
},
"2023-02-02": {
"123": {
"price": 100,
},
"234": {
"price": 100,
},
},
"2023-02-03": {
"123": {
"price": 100,
},
"234": {
"price": 100,
},
},
}
I have list of mapped ID on my aystem, it should be like
ids = [123]
I want to remove the key that not in the list (ids) from the document, started from a specific date (today/"2023-02-02"), the date always updated and so the ID, my expected result is
"data": {
"2023-02-01": {
"123": {
"price": 100,
},
"234": {
"price": 100,
},
},
"2023-02-02": {
"123": {
"price": 100,
},
},
"2023-02-03": {
"123": {
"price": 100,
},
},
}
Could I achieve that on MongoDB aggregation? I'm using pymongo
Following the discussion in comments, if refactoring the schema is an option, you can achieve what you need in very simple query.
db.collection.update({
"date": {
$gte: ISODate("2023-02-02")
}
},
[
{
$set: {
value: {
$filter: {
input: "$value",
as: "v",
cond: {
$in: [
"$$v.key",
[
"123"
]
]
}
}
}
}
}
],
{
multi: true
})
Mongo Playground
The schema I am proposing:
[
{
"date": ISODate("2023-02-01"),
"value": [
{
"key": "123",
"price": 100
},
{
"key": "234",
"price": 100
}
]
},
{
"date": ISODate("2023-02-02"),
"value": [
{
"key": "123",
"price": 100
},
{
"key": "234",
"price": 100
}
]
},
{
"date": ISODate("2023-02-03"),
"value": [
{
"key": "123",
"price": 100
},
{
"key": "234",
"price": 100
}
]
}
]
You can see there is a few things:
avoided using dynamic value as field name
formatted date as proper date objects
avoided highly nesting arrays/objects

Get all matching subdocuments and add property from parent document

Let's say i have following documents:
[
{
"key": 1,
"sub": [
{
"id": 4,
"value": 23
},
{
"id": 1,
"value": 24
}
]
},
{
"key": 2,
"sub": [
{
"id": 1,
"value": 92
},
{
"id": 2,
"value": 93
}
]
},
{
"key": 4,
"sub": [
{
"id": 3,
"value": 22
},
{
"id": 2,
"value": 43
}
]
}
]
I now want to find subdocuments by their id and also see the corresponding parent property key. I have tried following query:
db.collection.aggregate([
{
"$match": {
"sub.id": 1
}
},
{
"$addFields": {
"value": {
"$filter": {
"input": "$sub",
"cond": {
$eq: [
"$$this.id",
1
]
}
}
}
}
},
{
"$project": {
sub: 0
}
}
])
This essentially returns the right information:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"key": 1,
"value": [
{
"id": 1,
"value": 24
}
]
},
{
"_id": ObjectId("5a934e000102030405000001"),
"key": 2,
"value": [
{
"id": 1,
"value": 92
}
]
}
]
But it is not formatted how I need it and also adding more properties from the subdocuments is annoying because of the $addFields. I would rather have it formatted like this:
[
{
"id": 1,
"value": 24,
"key": 1
},
{
"id": 1,
"value": 92,
"key": 2
}
]
So I can have just an array of the matching subdocuments with additional parent properties added.
How would I do that?
Mongo Playground
After you have filtered out the unwanted subdocuments, $reduce over the response to build the subdocuments you want to see:
{"$project": {
_id: 0,
"sub": {
$reduce: {
input: {
"$filter": {
"input": "$sub",
"cond": {$eq: ["$$this.id", 1]}
}
},
initialValue: [],
in: {
$concatArrays: [
[{
key: "$key",
id: "$$this.id",
value: "$$this.value"
}],
"$$value"
]
}
}
}
}
}
Playground

data stored in map, need all values of all object in an array

We have data like
[{
"parameterId": "5f914ca2679bae721d38410b",
"average": 574998.153846154,
"count": 26.0,
"date": "2020-09-08T18:30:00.000Z",
"dataPerHour": {
"0": {
"min": 92570.0,
"max": 995170.0,
"avg": 578268.826086957,
"count": 23,
"date": "2020-09-04T19:07:41.000Z",
"values": [{
"paramValue": "100414",
"time": "2020-09-04T19:07:41.000Z"
},
{
"paramValue": "705811",
"time": "2020-09-04T19:08:41.000Z"
}
]
},
"1": {
"min": 92570.0,
"max": 995170.0,
"avg": 678268.826086957,
"count": 23,
"date": "2020-09-03T19:07:41.000Z",
"values": [{
"paramValue": "100414",
"time": "2020-09-03T19:07:41.000Z"
},
{
"paramValue": "705811",
"time": "2020-09-03T19:08:41.000Z"
}
]
}
}
}, {
"parameterId": "5f914ca2679bae721d38410b",
"average": 574998.153846154,
"count": 26.0,
"date": "2020-09-08T18:30:00.000Z",
"dataPerHour": {
"0": {
"min": 92570.0,
"max": 995170.0,
"avg": 778268.826086957,
"count": 23,
"date": "2020-09-04T19:07:41.000Z",
"values": [{
"paramValue": "100414",
"time": "2020-09-04T19:07:41.000Z"
},
{
"paramValue": "705811",
"time": "2020-09-04T19:08:41.000Z"
}
]
}
}
}]
We need output:
[
"2020-09-08T18:30:00" : "578268.826086957",
"2020-09-03T19:07:41" : "678268.826086957",
"2020-09-08T18:30:00" : "778268.826086957"
]
I need mongo query for this. I need data like key = date and value = avg of each data in dataPerHour.
Does this help?
Playground
out
[
{
"data": [
{
"2020-09-03T19:07:41": 678268.826086957,
"2020-09-04T19:07:41": 578268.826086957
},
{
"2020-09-04T19:07:41": 778268.826086957
}
]
}
]
pipe
db.collection.aggregate([
{
$project: {
data: {
"$arrayToObject": {
$map: {
input: {
"$objectToArray": "$dataPerHour"
},
as: "d",
in: {
$cond: [
"$$d",
[
{
"$dateToString": {
"date": {
$toDate: "$$d.v.date"
},
"format": "%Y-%m-%dT%H:%M:%S"
}
},
{
"$toDouble": "$$d.v.avg"
}
],
"$$d"
]
}
}
}
}
}
},
{
$group: {
_id: null,
data: {
$push: "$$ROOT.data"
}
}
},
{
$unset: "_id"
}
])

Need to return matched data from mongo db JSON

I have Json which have values like state_city details this contains information like which city belongs to which state -
Need to query it for particular state name which will gives me all cities that belongs to that state.
db.collection.find({
"count": 10,
"state.name": "MP"
})
[
{
"collection": "collection1",
"count": 10,
"state": [
{
"name": "MH",
"city": "Mumbai"
},
{
"name": "MH",
"city": "Pune"
},
{
"name": "UP",
"city": "Kanpur"
},
{
"name": "CG",
"city": "Raipur"
}
]
},
{
"collection": "collection2",
"count": 20,
"state": [
{
"name": "MP",
"city": "Indore"
},
{
"name": "MH",
"city": "Bhopal"
},
{
"name": "UP",
"city": "Kanpur"
},
{
"name": "CG",
"city": "Raipur"
}
]
}
]
You have to use aggregate query to get only matching elements in array :
db.collection.aggregate([{
$unwind: "$content.state"
},
{
$match: {
"content.state.name": "MH",
"count": 10
}
},
{
$group: {
_id: "$content.state.city",
}
},
{
$addFields: {
key: 1
}
},
{
$group: {
_id: "$key",
cities: {
$push: "$_id"
}
}
},
{
$project: {
_id: 0,
cities: 1
}
}
])
This query will return :
{
"cities": [
"Pune",
"Mumbai"
]
}
The following query would be the solution.
db.collection.find({ "count": 10, "state":{"name": "MP"}})
For more complex queries, $elemMatch is also available.

MongoDB projection. Operator $add field|expression array awareness or after $slice

I've got collection that looks like:
[{
"org": "A",
"type": "simple",
"payFor": 3,
"price": 100
},
{
"org": "A",
"type": "custom",
"payFor": 2,
"price": 115
},
{
"org": "B",
"type": "simple",
"payFor": 1,
"price": 110
},
{
"org": "B",
"type": "custom",
"payFor": 2,
"price": 200
},
{
"org": "B",
"type": "custom",
"payFor": 4,
"price": 220
}]
And need to produce result with query to perform group by "org" where payments appears for only first "payFor" prices in "type".
I'm trying to use expression result by $slice operator in $add but this is not works.
pipeline:
[{
"$group": {
"_id": {
"org": "$org",
"type": "$type"
},
"payFor": {
"$max": "$payFor"
},
"count": {
"$sum": 1
},
"prices": {
"$push": "$price"
}
}
},
{
"$group": {
"_id": "$_id.org",
"payments": {
"$push": {
"type": "$_id.type",
"forFirst": "$payFor",
"sum": {
"$cond": [
{
"$gte": [
"$payFor",
"$count"
]
},
{
"$add": {
"$prices": {
"$slice": "$count"
}
}
},
{
"$add": "$prices"
}
]
}
}
}
}
}]
I know that it is possible to traverse unwinded prices and pick only "payFor" count of them. but result collections are more rich than in example above and this operation will produce some unecessary overheads.
Need some advice from community. Please. Thanks.