Show the output in an array of values - arrays

Hello I have this query result
{
sac: 1,
sac_db: 0,
kafka: 1,
platform: 13700,
}
now I just want to show the values in an array, but I can't find how to do it:
[1,0,1,13700]

You can get this done using $map and $objectToArray, like so:
db.collection.aggregate([
{
$project: {
_id: 0,
results: {
$map: {
input: {
$filter: {
input: {
"$objectToArray": "$$ROOT"
},
cond: {
$ne: [
"$$this.k",
"_id"
]
}
}
},
in: "$$this.v"
}
}
}
}
])
Mongo Playground

Related

Create an object to already existing collection

I have a collection "product_reviews" with this document structure
{
_id: 'B000000OE4',
'product/title': 'Working Class Hero',
'product/price': '16.99',
reviews: [
{
'review/userId': 'unknown',
'review/profileName': 'unknown',
'review/helpfulness': '2/3',
'review/score': '4.0',
'review/time': '27/05/1999/00:00:00',
'review/summary': 'Worth it for one song',
'review/text': "I really like Joan Baez'..."
},
{
'review/userId': 'A1W0RKM6J6J73L',
'review/profileName': 'Aaron Woodin (purchagent#aol.com)',
'review/helpfulness': '1/1',
'review/score': '3.0',
'review/time': '09/02/1999/00:00:00',
'review/summary': 'The critical lambasting on the Amazon Page Missed one thing.',
'review/text': "They forgot to mention Mary Chapin..."
},
...
]
}
My goal is to add object for each product (each product has unique _id) that will have following structure:
{
avgReviewScore: 4.5
reviewsCount: 105
reviewScoreDistrib: {
1: 15
2: 0
3: 30
4: 40
5: 20
}
}
I tried numerous aggregation pipelines but couldn't find a solution.
You can try this code:
db.product_reviews.aggregate([{
$unwind: "$reviews"
},
{
$group: {
_id: "$_id",
avgReviewScore: {
$avg: "$reviews.review/score"
},
reviewsCount: {
$sum: 1
},
scores: {
$push: "$reviews.review/score"
}
}
},
{
$project: {
avgReviewScore: 1,
reviewsCount: 1,
reviewScoreDistrib: {
$arrayToObject: {
$map: {
input: [1, 2, 3, 4, 5],
as: "num",
in: {
k: {$toString: "$$num"},
v: {
$size: {
$filter: {
input: "$scores",
as: "s",
cond: {
$eq: ["$$s", "$$num"]
}
}
}
}
}
}
}
}
}
},
{
$merge: {
into: "product_reviews",
on: "_id"
}
}
])
If you have any issue, you can ask
No need to $unwind and $group again (which can be very inefficient). You can use a simple updateMany:
db.collection.updateMany({},
[
{$set: {
reviewsData: {$map: {
input: "$reviews.review/score",
in: {$toDouble: "$$this"}
}}
}},
{$set: {
reviewScoreDistrib: {
$arrayToObject: {$map: {
input: {$range: [1, 6]},
as: "num",
in: {
k: {$toString: "$$num"},
v: {$size: {$filter: {
input: "$reviewsData",
cond: {$eq: ["$$this", "$$num"]}
}}}
}
}}
},
avgReviewScore: {$avg: "$reviewsData"},
reviewsCount: {$size: "$reviewsData"}
}}
])
See how it works on the playground example

How to add field to nested array that looks at another field in the same array item MongoDb

for example i have
{
...
myObjects = [ {nmbr: 1}, {nmbr:2}]
}
now I want:
{
...
myObjects = [ {nmbr: 1, id: 1}, {nmbr:2, id :2}]
}
using:
db.collection.aggregate([
{
"$addFields": {
"myObjects.id": "$myObjects.nmbr"
}
}
])
has this result
{
...
myObjects = [ {nmbr: 1, id_:[1,2]}, {nmbr:2, id:[1,2]}]
}
which is not what I expected, any solution?
$unwind: Deconstructs myObjects array field from the source documents to output a document for each element.
$addFields: Create id property with value myObject.nmbr in myObject field.
$group: Group by $id (ObjectId) to combine into myObjects array (reverse $unwind).
db.collection.aggregate([
{
"$unwind": "$myObjects"
},
{
"$addFields": {
"myObjects.id": "$myObjects.nmbr"
}
},
{
$group: {
_id: "$id",
"myObjects": {
$push: "$myObjects"
}
}
}
])
Output
[
{
"_id": null,
"myObjects": [
{
"id": 1,
"nmbr": 1
},
{
"id": 2,
"nmbr": 2
}
]
}
]
Sample Mongo Playground

How to query two collections with related data?

I have 2 collections, collection A has some documents like {'id':1,'field':'name'},{'id':1,'field':'age'},and collection B has some documents like
{'_id':1,'name':'alice','age':18,'phone':123},{'_id':2,'name':'bob','age':30,'phone':321}
and I want to find all the document whose '_id' is in collectionA, and just project the corresponding field.
for example:
collection A
{'id':1,'field':'name'},
{'id':1,'field':'age'}
collection B
{'_id':1,'name':'alice','age':18,'phone':123},
{'_id':2,'name':'bob','age':30,'phone':321}
the result is:
{'name':'alice','age':18},
I don't know if there is an easy way to do that?
You can use $lookup to join two collection
db.col1.aggregate([
{
$match: {
id: 1
}
},
{
"$lookup": {
"from": "col2",
"localField": "id",
"foreignField": "_id",
"as": "listNames"
}
},
{
$project: {
listNames: {
$first: "$listNames"
}
}
},
{
$project: {
_id: 0,
name: "$listNames.name",
age: "$listNames.age"
}
}
])
Mongo Playground: https://mongoplayground.net/p/E-0WvK_SUS_
So the idea is:
Convert the documents in to key, value pair for both the collections using $objectToArray.
Then perform a join operation based on key k and (id <-> _id) using $lookup.
Replace the result as root element using $replaceRoot.
Convert array to object using $arrayToObject and again $replaceRoot.
Query:
db.colB.aggregate([
{
$project: {
temp: { $objectToArray: "$$ROOT" }
}
},
{
$lookup: {
from: "colA",
let: { temp: "$temp", colB_id: "$_id" },
pipeline: [
{
$addFields: {
temp: { k: "$field", v: "$id" }
}
},
{
$match: {
$expr: {
$and: [
{ $in: ["$temp.k", "$$temp.k"] },
{ $eq: ["$temp.v", "$$colB_id"] }
]
}
}
},
{
$replaceRoot: {
newRoot: {
$first: {
$filter: {
input: "$$temp",
as: "item",
cond: { $eq: ["$field", "$$item.k"] }
}
}
}
}
}
],
as: "array"
}
},
{
$replaceRoot: {
newRoot: { $arrayToObject: "$array" }
}
}
]);
Output:
{
"name" : "alice",
"age" : 18
}

Access same field of every object in array of objects in mongodb aggregation

I have the following documents in collection of mongodb:
banks:[{name:"ABC", amt:0},{name:"PQR", amt:-1},{name"XYZ", amt:3400}]
banks:[{name:"ABC", amt:-2},{name:"PQR", amt:2344},{name"XYZ", amt:7600}]
Like this say I have 10 documents and each document contains one banks array. Each banks array has 30 objects in it as shown above.
I am trying to write aggregation query in mongodb to get the count of objects that have "amt" less than equal to zero and greater than zero but so far unable to get it. Please help. Thanks in advance!
The output for above sample documents should be
{"greaterThanZero": 1, "lessThanEqualToZero": 2 }
{"greaterThanZero": 2, "lessThanEqualToZero": 1 }
First you have to separate yours documents with $unwind
Then with a $project and a $cond you tell for each document if it's greaterThanZero or lessThanEqualToZero
Finally you sum up greaterThanZero and lessThanEqualToZero with a $group
You can test it here : Mongo Playground
[
{
"$unwind": "$banks"
},
{
"$project": {
"greaterThanZero": {
"$cond": [
{
"$gt": [
"$banks.amt",
0
]
},
1,
0
]
},
"lessThanEqualToZero": {
"$cond": [
{
"$lte": [
"$banks.amt",
0
]
},
1,
0
]
}
}
},
{
"$group": {
"_id": "$_id",
"greaterThanZero": {
"$sum": "$greaterThanZero"
},
"lessThanEqualToZero": {
"$sum": "$lessThanEqualToZero"
}
}
}
]
You can do it with $reduce,
it checks condition using $cond if match then add one to value,
db.collection.aggregate([
{
$project: {
lessThanEqualToZero: {
$reduce: {
input: "$banks",
initialValue: 0,
in: {
$cond: [
{ $lte: ["$$this.amt", 0] },
{ $add: ["$$value", 1] },
"$$value"
]
}
}
},
greaterThanZero: {
$reduce: {
input: "$banks",
initialValue: 0,
in: {
$cond: [
{ $gt: ["$$this.amt", 0] },
{ $add: ["$$value", 1] },
"$$value"
]
}
}
}
}
}
])
Playground

mongodb array aggregation extract to parent

I am trying to get first date from inner array in mongodb object and add it to it's parent with aggregation. Example:
car: {
"model": "Astra",
"productions": [
"modelOne": {
"dateOfCreation": "2019-09-30T10:15:25.026+00:00",
"dateOfEstimation": "2017-09-30T10:15:25.026+00:00",
"someOnterInfo": "whatever"
},
"modelTwo": {
"dateOfCreation": "2017-09-30T10:15:25.026+00:00",
"dateOfEstimation": "2019-09-30T10:15:25.026+00:00",
"someOnterInfo": "whatever"
}
]
}
to be turned in
car: {
"model": "Astra",
"earliestDateOfEstimation": "2017-09-30T10:15:25.026+00:00",
"earliestDateOfCreation": "2017-09-30T10:15:25.026+00:00"
}
How can I achieve that?
I'm assuming that modelOne and modelTwo are unknown when you start your aggregation. The key step is to run $map along with $objectToArray in order to get rid of those two values. Then you can just use $min to get "earliest" values:
db.collection.aggregate([
{
$addFields: {
dates: {
$map: {
input: "$car.productions",
in: {
$let: {
vars: { model: { $arrayElemAt: [ { $objectToArray: "$$this" }, 0 ] } },
in: "$$model.v"
}
}
}
}
}
},
{
$project: {
_id: 1,
"car.model": 1,
"car.earliestDateOfEstimation": { $min: "$dates.dateOfEstimation" },
"car.earliestDateOfCreation": { $min: "$dates.dateOfCreation" },
}
}
])
Mongo Playground
EDIT:
First step can be simplified if there's always modelOne, 'modelTwo'... (fixed number)
db.collection.aggregate([
{
$addFields: {
dates: { $concatArrays: [ "$car.productions.modelOne", "$car.productions.modelTwo" ] }
}
},
{
$project: {
_id: 1,
"car.model": 1,
"car.earliestDateOfEstimation": { $min: "$dates.dateOfEstimation" },
"car.earliestDateOfCreation": { $min: "$dates.dateOfCreation" },
}
}
])
Mongo Playground (2)

Resources