I have a collection equivalent to:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"sides": {
"0": {
"dist": 100
},
"1": {
"dist": 10
}
}
},
{
"_id": ObjectId("5a934e000102030405000001"),
"sides": {
"0": {
"dist": 100
}
}
}
]
I would like to perform a query that return any documents that has for any key nested in sides has the key dist with a specific value. Something like:
db.collection.find({"sides.*.dist": 10})
Here * acts as a wildcard, any key would be valid in its place.
That would retrieve:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"sides": {
"0": {
"dist": 100
},
"1": {
"dist": 10
}
}
}
]
On the other hand
db.collection.find({"sides.*.dist": 100})
Would retrive both documents.
the following song and dance won't be neccessary if sides field was an array...
db.collection.find(
{
$expr: {
$gt: [{
$size: {
$filter: {
input: { $objectToArray: "$sides" },
as: "x",
cond: { $eq: ["$$x.v.dist", 10] }
}
}
}, 0]
}
})
You could get the matching elements using this
db.collection.aggregate([
{
"$project": {
"sides_array": {//Reshape the sides
"$objectToArray": "$sides"
}
}
},
{//Denormalize to get more than one matches
"$unwind": "$sides_array"
},
{
"$match": {//Condition goes here
"sides_array.v.dist": 10
}
},
{
"$group": {//Group the data back, after unwinding
"_id": "$_id",
"sides": {
"$push": "$sides_array"
}
}
},
{
"$project": {//Reshape the data
"_id": 1,
"sides": {
"$arrayToObject": "$sides"
}
}
}
])
Related
My document:
[
{
"_id": "5f969419d40c1580f2d4aa36",
"users": {
"foo#bar.com": "baz",
"foo2#bar.com": "baz2"
}
},
{
"_id": "5f9694d4d40c1580f2d4aa38",
"users": {
"baz#test.com": "foo"
}
}
]
If i use this aggregate, i get two users. Ok. But how can i get only the value of "foo#bar.com"?
Test in https://mongoplayground.net/p/3kW2Rw6fSjh
db.collection.aggregate([
{
"$project": {
"users": {
"$objectToArray": "$users"
}
}
},
{
"$match": {
"users.k": "foo#bar.com"
}
},
{
"$project": {
"users": {
"$arrayToObject": "$users"
}
}
}
])
You can add a $filter stage after the $match stage:
{
$set: {
users: {
$filter: {
input: "$users",
cond: {
$eq: [
"$$this.k",
"foo2#bar.com"
]
}
}
}
}
},
See how it works on the playground example
This is my first question ever here so super excited to learn and apologies if the syntax is not up to mark, I will improve with time.
"item" is an Array ( this doc has only one element)
"adjudication" is a nested Array with a variable number of elements(same structure)
I want to create keys out of "adjudication.category.coding.code" without hardcoding as the values will be different with each document but will have the same string length
I tried using "$map" to apply the same logic to each array element but failed
"item": [
{
"adjudication": [
{
"amount": {"code": "USD", "system": "4217", "value": 22.51},
"category": {
"coding": [
{
"code": "bb.org/paid_amt",
"system": "bb.org/adjudication"
}
]
},
"reason": {
"coding": [
{
"code": "C",
"system": "bb.org/cvrg_status"
}
]
}
},
{
"amount": {"code": "USD", "system": "4217", "value": 0},
"category": {
"coding": [
{
"code": "bb.org/discount_amt",
"system": "bb.org/adjudication"
}
]
}
}
]
}
]
Output desired
adjudication: {paid_amt: 22.51, discount_amt: 0}
Welcome to SO. I hope the following code will solve your exception. Sometime you may modify based on you wish.
$unwind to deconstruct the array
$map to loop / modify through the array, and $arrayElementAt to. get the first object from the array
$let to find the last potion by $spliting for "paid_amt" and "discount_amt". So this will be an array of object. But you need objects. So the next part I make it as k:v pair.
$arrayToObject to make array to object by using above k:v pair. ("k" and "v" names are must. Can't replace by any other names)
$group to reconstruct the array that we did in 1st step
Here is the code,
db.collection.aggregate([
{ "$unwind": "$item" },
{
$project: {
"item.adjudication": {
"$map": {
"input": "$item.adjudication",
"in": {
amount: "$$this.amount.value",
code: {
"$arrayElemAt": [ "$$this.category.coding", 0 ]
}
}
}
}
}
},
{
$project: {
"item.adjudication": {
"$map": {
"input": "$item.adjudication",
"in": {
v: "$$this.amount",
k: {
"$let": {
"vars": {
"code": {
"$split": [ "$$this.code.code", "/" ]
}
},
"in": { "$arrayElemAt": [ "$$code", -1 ] }
}
}
}
}
}
}
},
{
$project: {
"item.adjudication": {
"$arrayToObject": "$item.adjudication"
}
}
},
{
"$group": {
"_id": "$_id",
"item": { "$push": "$item" }
}
}
])
Working Mongo playground
try this playground
db.collection1.aggregate(
[{
$unwind: {
path: '$item'
}
}, {
$unwind: {
path: '$item.adjudication'
}
}, {
$unwind: {
path: '$item.adjudication.category'
}
}, {
$unwind: {
path: '$item.adjudication.category.coding'
}
}, {
$group: {
_id: null,
data: {
$push: {
k: '$item.adjudication.category.coding.code',
v: '$item.adjudication.amount.value'
}
}
}
}, {
$project: {
_id:0,
adjudication: {$arrayToObject: '$data'}
}
}]
)
I have a MongoDB document like this:
[
{
"_id": {
"$oid": "5ff09030cd55d6d9f378d460"
},
"username": "a value",
"userid": "123456",
"last_access_ts": 1612426253,
"last_access": "2021-2-4 9:10:53",
"anotherid": 12345678910,
"verified_date": "2021-1-2 16:24:32",
"verified_ts": 1609601072,
"group_users": {
"-1001159747589": [
{
"anotherid": 12345678910,
"userid": "123456"
}
],
"-1001143137644": [
{
"anotherid": 12345678910,
"userid": "123456"
}
],
"-1001368608972": [
{
"anotherid": 12345678910,
"userid": "123456"
}
]
},
"registered_access": "2021-1-2 16:24:42",
}
]
I've two questions.
First one: I need to count the elements inside each group_users[key] object, and I'm stuck with this aggregate:
db.collection.aggregate([
{
$match: {
username: "a value"
}
},
{
$project: {
_id: 1,
userid: 1,
"groups": {
"$objectToArray": "$group_users"
}
}
},
{
$unwind: "$groups",
},
])
This aggregate gives me this result:
[
{
"_id": ObjectId("5ff09030cd55d6d9f378d460"),
"groups": {
"k": "-1001449720492",
"v": [
{
"anotherid": 12345678910,
"userid": "123456"
}
]
},
"userid": "123456"
},
{
"_id": ObjectId("5ff09030cd55d6d9f378d460"),
"groups": {
"k": "-1001159747589",
"v": [
{
"anotherid": 12345678910,
"userid": "123456"
}
]
},
"userid": "123456"
},
{
"_id": ObjectId("5ff09030cd55d6d9f378d460"),
"groups": {
"k": "-1001143137644",
"v": [
{
"anotherid": 12345678910,
"userid": "123456"
}
]
},
"userid": "123456"
}
]
How can I count each single groups[v] and then re-group the data? I would like to have a result like:
{
... some user data
"groups": {
"group_key": "count",
"second_group_key": "count",
"third_group_key": "count"
}
}
Is it possible with aggregate or I need to loop in the code?
My second question is always about the group_users. Is possible to have, recursively, the user data inside a group_users object?
I mean, every object inside group_users is an array of users; from this array can I have the user data (maybe with $graphLookup?) using the userid field or the anotherid field?
As a result from this second aggregate I would like to have something like this:
{
... some user data
"groups": {
"group_key": [{"userid": userid, "username": username}],
"second_group_key": [{"userid": userid, "username": username}],
"third_group_key": [{"userid": userid, "username": username}]
}
}
Obviously I can limit this "recursion" to 10 elements per time.
Thanks for any advice.
$objectToArray convert group_users object to array
$let to Bind variable group_arr for use in the specified expression, and returns the result of the expression.
$map to iterate loop of bind variable group_arr, get size of total element of v and return k and v,
$arrayToObject convert returned array from $map to object
db.collection.aggregate([
{ $match: { username: "a value" } },
{
$project: {
_id: 1,
userid: 1,
groups: {
$let: {
vars: { group_arr: { $objectToArray: "$group_users" } },
in: {
$arrayToObject: {
$map: {
input: "$$group_arr",
in: {
k: "$$this.k",
v: { $size: "$$this.v" }
}
}
}
}
}
}
}
}
])
Playground
Second question,
$unwind deconstruct groups array
$lookup with pipeline, match anotherid $in condition and return required fields
$group by _id and reconstruct groups array
$arrayToObject convert groups array to object
db.collection.aggregate([
{ $match: { username: "a value" } },
{
$project: {
_id: 1,
userid: 1,
groups: { $objectToArray: "$group_users" }
}
},
{ $unwind: "$groups" },
{
$lookup: {
from: "collection",
let: { anotherid: "$groups.v.anotherid" },
pipeline: [
{ $match: { $expr: { $in: ["$anotherid", "$$anotherid"] } } },
{
$project: {
_id: 0,
userid: 1,
username: 1
}
}
],
as: "groups.v"
}
},
{
$group: {
_id: "$_id",
groups: { $push: "$groups" },
userid: { $first: "$userid" }
}
},
{ $addFields: { groups: { $arrayToObject: "$groups" } } }
])
Playground
How can I use the $min function to get the min value within nested arrays (and add it to the document)?
[
{
"_id": "a357e77f-a76a-4bc2-8765-923280663e97",
"customers": [
{
"_id": "97170117-4660-4c6f-b8da-2b34d4d0c9ce",
"orders": [
{
"amount": 0.5
},
{
"amount": 6.400001525878906
}
]
},
{
"_id": "7b9ccf5b-3acb-4ed1-8df4-e3b5afc49cba",
"orders": [
{
"amount": 27.29999542236328
},
{
"amount": 0.29999542236328125
}
]
}
]
},
{
"_id": "58433224-8162-4f0a-8168-bc11b4306b0a",
"customers": [
{
"_id": "8a6055d0-9b94-40be-8f96-8fd9088d24aa",
"orders": [
{
"amount": 19.700000762939453
}
]
},
{
"_id": "a50a57b8-61e7-4727-a15a-4a4137b2f81a",
"orders": [
{
"amount": 43.80000305175781
}
]
}
]
}
]
How can I get the min amount value within the $customers.orders.amount path?
I've tried but it returns 0.
db.collection.aggregate([
{
$addFields: {
"amount": {
$sum: "$customers.orders.amount"
}
}
}
])
You can do as below for each customer
playground
db.collection.aggregate([
{//Destruct
"$unwind": "$customers"
},
{//Destruct
"$unwind": "$customers.orders"
},
{//Group by customer id,
$group: {
"_id": "$customers._id",
min: {
$push: {
"$min": "$customers.orders.amount"
}
}
}
}
])
You can use group by null if you want to find min across all the customers.
Found a solution using reduce to create a flat array of the nested arrays and then use $min on that. MongoPlayground
db.collection.aggregate([
{
"$addFields": {
"min": {
$min: {
"$reduce": {
"input": "$customers",
"initialValue": [],
"in": {
"$concatArrays": [
"$$value",
"$$this.orders.amount"
]
}
}
}
}
}
}
])
Here is my sample document structure e.g.
{
"my_object": {
"1": {
"seq": "1",
"time": "xyz",
},
"2": {
"seq": "2",
"time": "abc",
"sub_aray": {
"0": {
"value": 10
},
"1": {
"value": 10
},
"2": {
"value": -10
}
}
}
}
}
So what I want to achieve is, Sum of all the sub_array's value if exists, if sub_array isn't found, default it to 0
{"seq" : "1", "sub_array" : 0},
{"seq" : "2", "sub_array" : 10}
My Mongo version is 3.4.6 and I am using PyMongo as my driver.
You can do as below
playground
db.collection.aggregate([
{
$project: {
"array": { //To remove dynamic keys - 1,2,etc
"$objectToArray": "$my_object"
}
}
},
{//reshaping array
"$unwind": "$array"
},
{
$project: {//reshaping sub array to access via a static name
"k": {
"$objectToArray": "$array.v.sub_aray"
},
"seq": "$array.v.seq"
}
},
{
"$project": {//actual logic, output structure
"sub_array": {
$sum: "$k.v.value"
},
"_id": 0,
"seq": 1
}
}
])
The trick part is to use the $objectToArray operator to iterate my_object items.
db.collection.aggregate([
{
"$project": {
"my_object": {
"$map": {
input: {
"$objectToArray": "$my_object"
},
as: "obj",
in: {
seq: "$$obj.v.seq",
sub_array: {
$sum: {
$map: {
input: {
"$objectToArray": "$$obj.v.sub_aray"
},
as: "sub",
in: "$$sub.v.value"
}
}
}
}
}
}
}
},
{
"$unwind": "$my_object"
},
{
"$replaceRoot": {
"newRoot": "$my_object"
}
}
])
MongoPlayground