Imagine I have this collection:
{
id: 1,
b: {
"field1": ['foo'],
"field2": ['bar']
}
}
{
id: 2,
b: {
"field2": ["foobar"],
"field3": ["foofoo"]
}
}
And I want to obtain a new collection with MongoDB:
{
id: 1,
b_grouped: ['foo', 'bar']
}
{
id: 2,
b_grouped: ["foobar", "foofoo"]
}
I don't know all the name of the fields in the documents, anyone would have an idea of how to perform something like this:
db.collection.aggregate(
[
{ "$project": { "b_grouped": { $concatArrays: ["$b.*"] } } }
]
)
You can try,
$reduce input b as a array after converting from object to array using $objectToArray, this will convert object in "k" (key), "v" (value) format array of object,
$concatArrays to concat initialValue ($$value) of $raduce and array of b object's field $$this.v
db.collection.aggregate([
{
$project: {
b_grouped: {
$reduce: {
input: { $objectToArray: "$b" },
initialValue: [],
in: {
$concatArrays: ["$$this.v", "$$value"]
}
}
}
}
}
])
Playground
Related
I would like to move an array stored in old_field that looks like this:
[{id: "XXX", ...}, {"id": "YYY", ...}, ...]
Into new_field looking like this:
{"XXX": {id: "XXX", ...}, "YYY":, {id: "YYY", ...}, ...}
As such, I attempted to do a few iterations of the following:
$addFields: {
new_field: {
$reduce: {
input: "$old_field",
initialValue: {},
in: {
{$getField: {field: "id", input: "$$this"}}: "$$this"
}
}
}
}
All of which failed. Note that doing:
$addFields: {
new_field: {
$reduce: {
input: "$old_field",
initialValue: {},
in: {
"1": {$getField: {field: "id", input: "$$this"}}
}
}
}
}
Returns a new_field w/ value 1: {the_correct_id_here}, so I know that the $getField works properly (besides likely using $$this in the wrong context).
Why isn't $getField working in this context? How would I go about doing this transformation?
$addField
1.1. $arrayToObject - Convert the array result from 1.1.1 to object.
1.1.1. $map - Iterate each element in the old_field array and return a new array. Convert each element to an object with k and v properties.
db.collection.aggregate([
{
$addFields: {
new_field: {
$arrayToObject: {
$map: {
input: "$old_field",
in: {
k: "$$this.id",
v: "$$this"
}
}
}
}
}
}
])
Demo # Mongo Playground
I am trying to count how many times does a particular value occur in a collection.
{
_id:1,
field1: value,
field2: A,
}
{
_id:2,
field1: value,
field2: A,
}
{
_id:3,
field1: value,
field2: C,
}
{
_id:4,
field1: value,
field2: B,
}
what I want is to count how many times A occurs, B occurs and C occurs and return the count.
The output I want
{
A: 2,
B: 1,
C: 1,
}
You can use $facet in an aggregate pipeline like this:
$facet create "three ways" where in each one filter the values by desired key (A, B or C).
Then in a $project stage you can get the $size of the matched values.
db.collection.aggregate([
{
"$facet": {
"first": [
{
"$match": {
"field2": "A"
}
}
],
"second": [
{
"$match": {
"field2": "B"
}
}
],
"third": [
{
"$match": {
"field2": "C"
}
}
]
}
},
{
"$project": {
"A": {
"$size": "$first"
},
"B": {
"$size": "$second"
},
"C": {
"$size": "$third"
}
}
}
])
Example here
This is typical use case for $group stage in Aggregation Pipeline. You can do it like this:
$group - to group all the documents by field2
$sum - to count the number of documents for each value of field2
db.collection.aggregate([
{
"$group": {
"_id": "$field2",
"count": {
"$sum": 1
}
}
}
])
Working example
Leverage the $arrayToObject operator and a final $replaceWith pipeline to get the desired result. You would need to run the following aggregate pipeline:
db.collection.aggregate([
{ $group: {
_id: { $toUpper: '$field2' },
count: { $sum: 1 }
} },
{ $group: {
_id: null,
counts: {
$push: { k: '$_id', v: '$count' }
}
} },
{ $replaceWith: { $arrayToObject: '$counts' } }
])
Mongo Playground
Need to format the date and make a sum per day but sometimes a or b values are not available.
in the end, get one document with respect to date and sum. I'm using MongoDB 4.2.
Data Structure:
{
"data": {
"11-10-2001": {
"a": 17.281150000000001,
"b": 11.864060000000006
},
"13-10-2020": {
"b": 2.7616699999999994
},
"12-10-2001": {
"b": 4.0809599999999997
},
"09-10-2001": {
"b": 4.1286300000000005
},
"17-10-2001": {
"a": 15.140560000000123,
"b": 5.017139999999998
},
"18-10-2001": {
"b": 1.975189999999997,
"a": 7.093789999999976
}
}
}
Expected Output one document that contains the day and sum:
{
{
day: 11-10-2001,
sum : 29.145
},
{
day: 13-10-201,
sum : 2.7616699
},
{
day: 12-10-2001,
sum : 4.0809599999999997
},
{
day: 17-10-2001,
sum : 20.114
},
{
day: 18-10-2001,
sum : 9.145
}
}
You can try,
$map to iterate loop of data object after converting to array using $objectToArray
add key day, and sum, $reduce to loop of number object after converting to array using $objectToArray, $add to sum the value of number
$unwind deconstruct data array
$replaceRoot to replace data object to root
db.collection.aggregate([
{
$addFields: {
data: {
$map: {
input: { $objectToArray: "$data" },
in: {
day: "$$this.k",
sum: {
$reduce: {
input: { $objectToArray: "$$this.v" },
initialValue: 0,
in: { $add: ["$$this.v", "$$value"] }
}
}
}
}
}
}
},
{ $unwind: "$data" },
{ $replaceRoot: { newRoot: "$data" } }
])
Playground
You can do like following
db.collection.aggregate([
{
$project: { data: { "$objectToArray": "$data" } }
},
{
$unwind: "$data"
},
{
"$replaceRoot": { "newRoot": "$data" }
},
{
$addFields: { v: { "$objectToArray": "$v" } }
},
{
$addFields: {
v: {
$reduce: {
input: "$v",
initialValue: 0,
in: {
$add: [ "$$this.v", "$$value" ]
}
}
}
}
},
{
$group: {
_id: null,
data: {
$push: {
day: "$k",
sum: "$v"
}
}
}
}
])
Working Mongo playground
I am new to mongodb and wanted to convert my array to object using pipeline. For example,
{
field1: [1,2,3,4,5],
field2: [‘a’,’b’,’c’,’d’,’e’],
}
I want the above document to be converted to,
{
fields: [
{
field1: 1,
field2: ‘a’
},
......
{
field1: 5,
field2: ‘e’
}
]
}
Any idea how I can achieve this?
You can use $unwind to separate your arrays.
And then format your new list with $project without forgetting to remove the duplicates created by the $unwind.
db.collection.aggregate({
"$unwind": {
path: "$field1",
includeArrayIndex: "field1_index"
}
},
{
"$unwind": {
"path": "$field2",
"includeArrayIndex": "field2_index"
}
},
{
"$project": {
"fields": {
"field1": "$field1",
"field2": "$field2"
},
"diff": {
$cmp: [
"$field1_index",
"$field2_index"
]
}
}
},
{
"$match": {
"diff": 0
}
},
{
$group: {
_id: "$_id",
fields: {
$push: "$fields"
}
}
})
Try it here
You can use $zip and $map and $reduce to achieve this:
db.collection.aggregate([
{
"$addFields": {
fields: {
$reduce: {
input: {
$zip: {
inputs: [
{
$map: {
input: "$field1",
as: "f1",
in: {
field1: "$$f1"
}
}
},
{
$map: {
input: "$field2",
as: "f2",
in: {
field2: "$$f2"
}
}
}
]
}
},
initialValue: [],
in: {
"$concatArrays": [
[
{
"$mergeObjects": "$$this"
}
],
"$$value"
]
}
}
}
}
}
])
MongoPlayground
Make sure both field1 and field2 are of equal length or you will lose some data.
I have a document which looks like this
{
name:'John Doe',
phone: 'XXXXXXX',
field1: [
{I:20},
{J:60},
{K:20}
]
}
How do I get about restructuring the field1 to have a name instead of being anonymous. something like this:
field1: [
{
key: I,
value:20
},
{
key: J,
value:60
},
{
key: K,
value:20
}
]
Also is it possible to do in a bulk op coz I have a large number of data.
You can do it using both way,
update with aggregation pipeline from MpngoDB 4.2
using aggregate() and $out to collection
using update with aggregation pipeline updateMany()
first $set pipeline for convert object to array in k and v format
second $set pipeline for changing the name from k => key and v => value
db.collection.updateMany({},
[{
$set: {
field1: {
$reduce: {
input: "$field1",
initialValue: [],
in: {
$concatArrays: [
"$$value",
{
$objectToArray: "$$this"
}
]
}
}
}
}
},
{
$set: {
field1: {
$map: {
input: "$field1",
as: "f",
in: {
key: "$$f.k",
value: "$$f.v"
}
}
}
}
}
]
)
using aggregate() and $out
both $set are same as above method
$out Takes the documents returned by the aggregation pipeline and writes them to a specified collection
db.collection.aggregate([
{
$set: {
field1: {
$reduce: {
input: "$field1",
initialValue: [],
in: {
$concatArrays: [
"$$value",
{
$objectToArray: "$$this"
}
]
}
}
}
}
},
{
$set: {
field1: {
$map: {
input: "$field1",
as: "f",
in: {
key: "$$f.k",
value: "$$f.v"
}
}
}
}
},
{ $out: <"collection name"> }
])
Playground