Here is a code I have:
const _ = require('lodash')
const Box = require('./models/Box')
const boxesToBePicked = await Box.find({ status: 'ready', client: 27 })
const boxesOriginalIds = _(boxesToBePicked).map('original').compact().uniq().value()
const boxesOriginal = boxesOriginalIds.length ? await Box.find({ _id: { $in: boxesOriginalIds } }) : []
const attributes = ['name']
const boxes = [
...boxesOriginal,
...boxesToBePicked.filter(box => !box.original)
].map(box => _.pick(box, attributes))
Let's say, we have following data in "boxes" collection:
[
{ _id: 1, name: 'Original Box #1', status: 'pending' },
{ _id: 2, name: 'Nested box', status: 'ready', original: 1 },
{ _id: 3, name: 'Nested box', status: 'ready', original: 1 },
{ _id: 4, name: 'Nested box', status: 'pending', original: 1 },
{ _id: 5, name: 'Original Box #2', status: 'ready' },
{ _id: 6, name: 'Original Box #3', status: 'pending' },
{ _id: 7, name: 'Nested box', status: 'ready', original: 6 },
{ _id: 8, name: 'Original Box #4', status: 'pending' }
]
Workflow
Find all boxes, which are ready to be picked:
const boxesToBePicked = await Box.find({ status: 'ready' })
// Returns:
[
{ _id: 2, name: 'Nested box', status: 'ready', original: 1 },
{ _id: 3, name: 'Nested box', status: 'ready', original: 1 },
{ _id: 5, name: 'Original Box #2', status: 'ready' },
{ _id: 7, name: 'Nested box', status: 'ready', original: 6 }
]
Get all the IDs of original (parent) boxes of those:
const boxesOriginalIds = _(boxesToBePicked).map('original').compact().uniq().value()
// Returns:
[1, 6]
Get those boxes by their IDs:
const boxesOriginal = boxesOriginalIds.length ? await Box.find({ _id: { $in: boxesOriginalIds } }) : []
// Returns
[
{ _id: 1, name: 'Original Box #1', status: 'pending' },
{ _id: 6, name: 'Original Box #3', status: 'pending' }
]
Join those boxes with not nested boxes to be picked:
const boxes = [
...boxesOriginal,
...boxesToBePicked.filter(box => !box.original)
].map(box => _.pick(box, attributes))
// Returns
[
{ name: 'Original Box #1' },
{ name: 'Original Box #3' },
{ name: 'Original Box #2' }
]
So basically what we are doing here is getting all the original boxes if they have at least one nested box with status "ready", and all not nested boxes with status "ready".
I think it can be simplified by using aggregation pipeline and projection. But how?
You can try something like below. Uses $lookUp to self join to collection and $match stage with $or in combination with $and for second condition and the next part of $or for first condition and $group stage to remove duplicates and $project stage to format the response.
db.boxes.aggregate([{
$lookup: {
from: "boxes",
localField: "original",
foreignField: "_id",
as: "nested_orders"
}
}, {
$unwind: {
path: "$nested_orders",
preserveNullAndEmptyArrays: true
}
}, {
$match: {
$or: [{
$and: [{
"status": "ready"
}, {
"nested_orders": {
$exists: false,
}
}]
}, {
"nested_orders.status": "pending"
}]
}
}, {
$group: {
"_id": null,
"names": {
$addToSet: {
name: "$name",
nested_name: "$nested_orders.name"
}
}
}
}, {
$unwind: "$names"
}, {
$project: {
"_id": 0,
"name": {
$ifNull: ['$names.nested_name', '$names.name']
}
}
}]).pretty();
Sample Response
{ "name" : "Original Box #1" }
{ "name" : "Original Box #2" }
{ "name" : "Original Box #3" }
To decompose the aggregation :
a $group which creates
an array ids which match ready status for which it will add the *original value
an array box_ready which match ready status and keep the other fields as is (it will be used later)
an array document which contain the whole original document ($$ROOT)
{
$group: {
_id: null,
ids: {
$addToSet: {
$cond: [
{ $eq: ["$status", "ready"] },
"$original", null
]
}
},
box_ready: {
$addToSet: {
$cond: [
{ $eq: ["$status", "ready"] },
{ _id: "$_id", name: "$name", original: "$original", status: "$status" },
null
]
}
},
document: { $push: "$$ROOT" }
}
}
$unwind document field to remove the array
{
$unwind: "$document"
}
use a $redact aggregation to keep or remove records based on matching of $document._id in the array ids previously created (that contain the matching original and status)
{
$redact: {
"$cond": {
"if": {
"$setIsSubset": [{
"$map": {
"input": { "$literal": ["A"] },
"as": "a",
"in": "$document._id"
}
},
"$ids"
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}
}
$group to push all documents that matched the previous $redact to another array named filtered (we have now 2 array which can be united)
{
$group: {
_id: null,
box_ready: { $first: "$box_ready" },
filtered: { $push: "$document" }
}
}
use a $project with a setUnion to union the arrays box_ready and filtered
{
$project: {
union: {
$setUnion: ["$box_ready", "$filtered"]
},
_id: 0
}
}
$unwind the array you have obtained to get distinct records
{
$unwind: "$union"
}
$match only those which have original missing and that are not null (as initially a the status:ready condition has obliged to get a null value on the first $group
{
$match: {
"union.original": {
"$exists": false
},
"union": { $nin: [null] }
}
}
The whole aggregation query is :
db.collection.aggregate(
[{
$group: {
_id: null,
ids: {
$addToSet: {
$cond: [
{ $eq: ["$status", "ready"] },
"$original", null
]
}
},
box_ready: {
$addToSet: {
$cond: [
{ $eq: ["$status", "ready"] },
{ _id: "$_id", name: "$name", original: "$original", status: "$status" },
null
]
}
},
document: { $push: "$$ROOT" }
}
}, {
$unwind: "$document"
}, {
$redact: {
"$cond": {
"if": {
"$setIsSubset": [{
"$map": {
"input": { "$literal": ["A"] },
"as": "a",
"in": "$document._id"
}
},
"$ids"
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}
}, {
$group: {
_id: null,
box_ready: { $first: "$box_ready" },
filtered: { $push: "$document" }
}
}, {
$project: {
union: {
$setUnion: ["$box_ready", "$filtered"]
},
_id: 0
}
}, {
$unwind: "$union"
}, {
$match: {
"union.original": {
"$exists": false
},
"union": { $nin: [null] }
}
}]
)
It gives you :
{ "union" : { "_id" : 1, "name" : "Original Box #1", "status" : "pending" } }
{ "union" : { "_id" : 5, "name" : "Original Box #2", "status" : "ready" } }
{ "union" : { "_id" : 6, "name" : "Original Box #3", "status" : "pending" } }
Use an additional $project if you want to select specific fields
For mongoose, you should be able to do like this to perform aggregation :
Box.aggregate([
//the whole aggregation here
], function(err, result) {
});
Several of the answers are close but here's the most efficient way. It accumulates the "_id" values of boxes to be picked up and then uses $lookup to "rehydrate" the full details of each (top-level) box.
db.boxes.aggregate(
{$group: {
_id:null,
boxes:{$addToSet:{$cond:{
if:{$eq:["$status","ready"]},
then:{$ifNull:["$original","$_id"]},
else:null
}}}
}},
{$lookup: {
from:"boxes",
localField:"boxes",
foreignField:"_id",
as:"boxes"
}}
)
Your result based on sample data:
{
"_id" : null,
"boxIdsToPickUp" : [
{
"_id" : 1,
"name" : "Original Box #1",
"status" : "pending"
},
{
"_id" : 5,
"name" : "Original Box #2",
"status" : "ready"
},
{
"_id" : 6,
"name" : "Original Box #3",
"status" : "pending"
}
] }
Note that the $lookup is done only for the _id values of boxes to be picked up which is far more efficient than doing it for all boxes.
If you wanted the pipeline to be more efficient you would need to store more details about original box in the nested box documents (like its name).
To achieve your goal you can follow bellow steps:
First of all select record for status is ready (because you want to get parent who has no nested box but status is ready and who
has nested box at least one with stats is ready )
Find parent box using $lookup
then $group to get unique parent box
then $project box name
So can try this query:
db.getCollection('boxes').aggregate(
{$match:{"status":'ready'}},
{$lookup: {from: "boxes", localField: "original", foreignField: "_id", as: "parent"}},
{$unwind: {path: "$parent",preserveNullAndEmptyArrays: true}},
{$group:{
_id:null,
list:{$addToSet:{"$cond": [ { "$ifNull": ["$parent.name", false] }, {name:"$parent.name"}, {name:"$name"} ]}}
}
},
{$project:{name:"$list.name", _id:0}},
{$unwind: "$name"}
)
OR
get record for status is ready
get desired recordID
get name according to recordID
db.getCollection('boxes').aggregate(
{$match:{"status":'ready'}},
{$group:{
_id:null,
parent:{$addToSet:{"$cond": [ { "$ifNull": ["$original", false] }, "$original", "$_id" ]}}
}
},
{$unwind:"$parent"},
{$lookup: {from: "boxes", localField: "parent", foreignField: "_id", as: "parent"}},
{$project: {"name" : { $arrayElemAt: [ "$parent.name", 0 ] }, _id:0}}
)
Using mongoose (4.x)
Schema:
const schema = mongoose.Schema({
_id: Number,
....
status: String,
original: { type: Number, ref: 'Box'}
});
const Box = mongoose.model('Box', schema);
Actual Query:
Box
.find({ status: 'ready' })
.populate('original')
.exec((err, boxes) => {
if (err) return;
boxes = boxes.map((b) => b.original ? b.original : b);
boxes = _.uniqBy(boxes, '_id');
console.log(boxes);
});
Docs on Mongoose#populate: http://mongoosejs.com/docs/populate.html
Related
I have a collection named Vote that looks like the following:
{
postId: "1",
comment:{
text_sentiment: "positive",
topic: "A"
}
}, // DOC-1
{
postId: "2",
comment:{
text_sentiment: "negative",
topic: "A"
}
}, // DOC-2
{
postId: "3",
comment:{
text_sentiment: "positive",
topic: "B"
}
},..//DOC-3 ..
I want to do an aggregation on this collection such that it returns the following structure.
[
{
_id: "hash",
topic: "A",
topicOccurance: 2,
sentiment: {
positive: 1,
negative: 1,
neutral: 0
},
postIds: [1,2]
},
..
]
I created the following aggregation:
db.Vote.aggregate([
{
$match: {
surveyId: "e6d38e1ecd",
"comment.topic": {
$exists: 1
},
}
},
{
$group: {
_id: {
topic: "$comment.topic",
text_sentiment: "$comment.text_sentiment"
},
total: {
$sum: 1
},
}
},
{
$group: {
_id: "$_id.topic",
total: {
$sum: "$total"
},
text_sentiments: {
$push: {
k: "$_id.text_sentiment",
v: "$total"
}
}
}
},
{
$project: {
topic: "$_id",
topicOccurance: "$total",
sentiment: {
"$arrayToObject": "$text_sentiments"
}
}
},
{
$sort: {
"topicOccurance": -1
}
}
])
This works fine but I do not know how can I also get an array in the response holding the key postIds. Each document inside the collection vote has postId and I want to group the posts having the same topic and push to an array. How can I do this?
2nd stage ($group) - Add postId into postIds array via $push.
3rd stage ($group) - Add postIds array into postIds array via $push. This will leads postIds become nested array.
[[1,2], ...]
4th stage ($project) - For postIds field, use $reduce operator to flatten the postIds array by $concat. Update: with $setUnion to distinct items in array.
db.collection.aggregate([
// match stage
{
$group: {
_id: {
topic: "$comment.topic",
text_sentiment: "$comment.text_sentiment"
},
total: {
$sum: 1
},
postIds: {
$push: "$postId"
}
}
},
{
$group: {
_id: "$_id.topic",
total: {
$sum: "$total"
},
text_sentiments: {
$push: {
k: "$_id.text_sentiment",
v: "$total"
}
},
postIds: {
"$push": "$postIds"
}
}
},
{
$project: {
topic: "$_id",
topicOccurance: "$total",
sentiment: {
"$arrayToObject": "$text_sentiments"
},
postIds: {
$setUnion: [
{
$reduce: {
input: "$postIds",
initialValue: [],
in: {
$concatArrays: [
"$$value",
"$$this"
]
}
}
}
]
}
}
},
// sort stage
])
Sample Mongo Playground
I have an aggregation query that is producing results in a mostly desired way except I need to only group by branchId ( not branchId and name ) and place the "name" values in an object with their relevant results.
SCHEMA:
{
process: { type: String, required: true },
name: { type: String, required: true },
error: { type: String },
action: {
type: String,
required: true,
enum: ['started', 'stopped', 'processing', 'completed', 'errored']
},
location: {
branchId: { type: String }
},
},
{ timestamps: true }
This is the current aggregation query:
[
{
$match: {
createdAt: { $gte: ISODate("2020-06-24T00:00:00.000+0000"),
$lte: ISODate("2020-06-25T00:00:00.000+0000")
}
}
},
{
$group: {
_id: { branchId: '$location.branchId', name: '$name' },
started: { $sum: { $cond: [{ $eq: ['$action', 'started'] }, 1, 0] } },
processing: { $sum: { $cond: [{ $eq: ['$action', 'processing'] }, 1, 0] } },
errored: { $sum: { $cond: [{ $eq: ['$action', 'errored'] }, 1, 0] } },
completed: { $sum: { $cond: [{ $eq: ['$action', 'completed'] }, 1, 0] }
}
}
},
]
CURRENT RESPONSE:
{
"_id" : {
"branchId" : "1",
"name" : "Product 1"
},
"started" : 1.0,
"processing" : 1.0,
"errored" : 0.0,
"completed" : 0.0
},
{
"_id" : {
"branchId" : "1",
"name" : "Product 2"
},
"started" : 1.0,
"processing" : 1.0,
"errored" : 1.0,
"completed" : 1.0
}
How would I modify the query to produce something similar to the following DESIRED RESPONSE: ?
{
"_id" : "1",
"product_1": {
"started" : true, // although 1.0 and 0.0 is acceptable
"processing" : true,
"errored" : true,
"completed" : false
},
"product_2": {
"started" : true,
"processing" : true,
"errored": false,
"completed" : true
}
},
I created this playground to assist in testing: https://mongoplayground.net/p/zDaxC-SYtN4
We need to use the $objectToArray operator to create an object based on the value of the document.
{k:"hello", v:"world"} --> {"hello":"world"}
Try this one:
db.collection.aggregate([
{
$match: {}
},
{
$group: {
_id: "$location.branchId",
data: {
$push: "$$ROOT"
}
}
},
{
$replaceWith: {
$arrayToObject: [
{
$concatArrays: [
[
{ "k": "_id", "v": "$_id" }
],
{
$map: {
input: "$data",
in: {
k: "$$this.name",
v: {
started: { $eq: ["$$this.action","started"},
processing: { $eq: ["$$this.action","processing"]},
errored: { $eq: ["$$this.action","errored"]},
completed: {$eq: ["$$this.action","completed"]}
}
}
}
}
]
}
]
}
}
])
MongoPlayground |Alternative 3.4
I have ten stations stored in the stations collection: Station A, Station B, Station C, Station D, Station E, Station F, Station G, Station H, Station I, Station J.
Right now, to create a count list of all inter-station rides between all possible pairs of stations, I do the following in my Node.js code (using Mongoose):
const stationCombinations = []
// get all stations from the stations collection
const stationIds = await Station.find({}, '_id name').lean().exec()
// list of all possible from & to combinations with their names
stationIds.forEach(fromStation => {
stationIds.forEach(toStation => {
stationCombinations.push({ fromStation, toStation })
})
})
const results = []
// loop through all station combinations
for (const stationCombination of stationCombinations) {
// create aggregation query promise
const data = Ride.aggregate([
{
$match: {
test: false,
state: 'completed',
duration: { $gt: 2 },
fromStation: mongoose.Types.ObjectId(stationCombination.fromStation._id),
toStation: mongoose.Types.ObjectId(stationCombination.toStation._id)
}
},
{
$group: {
_id: null,
count: { $sum: 1 }
}
},
{
$addFields: {
fromStation: stationCombination.fromStation.name,
toStation: stationCombination.toStation.name
}
}
])
// push promise to array
results.push(data)
}
// run all aggregation queries
const stationData = await Promise.all(results)
// flatten nested/empty arrays and return
return stationData.flat()
Executing this function give me the result in this format:
[
{
"fromStation": "Station A",
"toStation": "Station A",
"count": 1196
},
{
"fromStation": "Station A",
"toStation": "Station B",
"count": 1
},
{
"fromStation": "Station A",
"toStation": "Station C",
"count": 173
},
]
And so on for all other combinations...
The query currently takes a lot of time to execute and I keep getting alerts from MongoDB Atlas about excessive load on the database server because of these queries. Surely there must be an optimized way to do something like this?
You need to use MongoDB native operations. You need to $group by fromStation and toStation and with $lookup join two collections.
Note: I assume you have MongoDB >=v3.6 and Station._id is ObjectId
db.ride.aggregate([
{
$match: {
test: false,
state: "completed",
duration: {
$gt: 2
}
}
},
{
$group: {
_id: {
fromStation: "$fromStation",
toStation: "$toStation"
},
count: {
$sum: 1
}
}
},
{
$lookup: {
from: "station",
let: {
fromStation: "$_id.fromStation",
toStation: "$_id.toStation"
},
pipeline: [
{
$match: {
$expr: {
$in: [
"$_id",
[
"$$fromStation",
"$$toStation"
]
]
}
}
}
],
as: "tmp"
}
},
{
$project: {
_id: 0,
fromStation: {
$reduce: {
input: "$tmp",
initialValue: "",
in: {
$cond: [
{
$eq: [
"$_id.fromStation",
"$$this._id"
]
},
"$$this.name",
"$$value"
]
}
}
},
toStation: {
$reduce: {
input: "$tmp",
initialValue: "",
in: {
$cond: [
{
$eq: [
"$_id.toStation",
"$$this._id"
]
},
"$$this.name",
"$$value"
]
}
}
},
count: 1
}
},
{
$sort: {
fromStation: 1,
toStation: 1
}
}
])
MongoPlayground
Not tested:
const data = Ride.aggregate([
{
$match: {
test: false,
state: 'completed',
duration: { $gt: 2 }
}
},
{
$group: {
_id: {
fromStation: "$fromStation",
toStation: "$toStation"
},
count: { $sum: 1 }
}
},
{
$lookup: {
from: "station",
let: {
fromStation: "$_id.fromStation",
toStation: "$_id.toStation"
},
pipeline: [
{
$match: {
$expr: {
$in: [
"$_id",
[
"$$fromStation",
"$$toStation"
]
]
}
}
}
],
as: "tmp"
}
},
{
$project: {
_id: 0,
fromStation: {
$reduce: {
input: "$tmp",
initialValue: "",
in: {
$cond: [
{
$eq: [
"$_id.fromStation",
"$$this._id"
]
},
"$$this.name",
"$$value"
]
}
}
},
toStation: {
$reduce: {
input: "$tmp",
initialValue: "",
in: {
$cond: [
{
$eq: [
"$_id.toStation",
"$$this._id"
]
},
"$$this.name",
"$$value"
]
}
}
},
count: 1
}
},
{
$sort: {
fromStation: 1,
toStation: 1
}
}
])
For example, I have a users collection. Each user document has posts array, containing their posts. I want to query this collection like so:
Get 2 posts starting at index Nstart and ending at Nend from each user in username array: ['user1', 'user2'] and return a merged array of posts, ordered by ascending of post.postedOn int.
If this is the collection:
{
username: "user1",
posts: [
{ id: 1, text: "123" ... },
{ id: 2, text: "456" ... },
{ id: 3, text: "789" ... },
...
]
},
{
username: "user2",
posts: [
{ id: 1, text: "abc" ... },
{ id: 2, text: "def" ... },
{ id: 3, text: "ghi" ... },
...
]
}
...
I want to get this:
{
posts: {
{ id: 1, text: "123" ... },
{ id: 2, text: "456" ... },
{ id: 1, text: "abc" ... },
{ id: 2, text: "def" ... },
}
}
I have tried using aggregate multiple times. this is one of my queries:
db.getCollection("users").aggregate([{ $match: { username: { $in: ["admin", "admin2"] } } }, { $project: { posts: 1 } }])
I get two user objects, each containing all of their posts, not one posts array that I can manipulate later.
The final result I'm trying to achieve is this:
Inputs
users: ['user1', 'user2']
starting posts index: 4
ending posts index: 8
Result
Array of posts by selected users. Total count of 8 (id 4-8 from each user).
This requires the usage of the aggregation operators $slice and $push to select and add the required post elements (by index) in to an array, respectively.
For example the posts collection has three documents (I have omitted the _id field here):
{
"username" : "user2",
"posts" : [
{
"id" : 1,
"text" : "123"
},
{
"id" : 2,
"text" : "456"
},
{
"id" : 3,
"text" : "789"
},
{
"id" : 4,
"text" : "000"
}
]
}
{
"username" : "user1",
"posts" : [
{
"id" : 1,
"text" : "abc"
},
{
"id" : 2,
"text" : "def"
},
{
"id" : 3,
"text" : "ghi"
},
{
"id" : 4,
"text" : "zzz"
}
]
}
{
"username" : "user99",
"posts" : [ ]
}
In the mongo shell:
> var users = [ "user1", "user2" ]
> var startIx = 1, noOfElements = 2
> db.posts.aggregate([
{ $project: { _id: 0, username: 1, posts: 1, usersMatch: { $in: [ "$username", users ] } } },
{ $match: { usersMatch: true } },
{ $addFields: { posts: { $slice: [ "$posts", startIx, noOfElements ] } } },
{ $group: { _id: null, postsOfSelectedUsers: { $push: "$posts"} } },
{ $project: { _id: 0, selectedUsers: users, postsOfSelectedUsers: 1 } }
])
The Output:
{
"postsOfSelectedUsers" : [
[
{
"id" : 2,
"text" : "456"
},
{
"id" : 3,
"text" : "789"
}
],
[
{
"id" : 2,
"text" : "def"
},
{
"id" : 3,
"text" : "ghi"
}
]
],
"selectedUsers" : [
"user1",
"user2"
]
}
NOTE: This is a solution, but this can be refined or re-worked upon (I think).
I have an aggregation query that returns the sum / total number of reviews submitted for a given location ( not the average star rating ). Reviews are scored 1 - 5 stars. This particular query groups these reviews into two categories, "internal" and "google".
I have a query that returns results that are almost what I'm looking for. However, I need to add an additional condition for internal reviews. I want to ensure that the internal reviews "stars" value exists / is not null and contains a value of at least 1. So, I was thinking adding something similar to this would work:
{ "stars": {$gte: 1} }
This is the current aggregation query:
[
{
$match: { createdAt: { $gte: fromDate, $lte: toDate } }
},
{
$lookup: {
from: 'branches',
localField: 'branch',
foreignField: '_id',
as: 'branch'
}
},
{ $unwind: '$branch' },
{
$match: { 'branch.org_id': branchId }
},
{
$group: {
_id: '$branch.name',
google: {
$sum: {
$cond: [{ $eq: ['$source', 'Google'] }, 1, 0]
}
},
internal: {
$sum: {
$cond: [ { $eq: ['$internal', true]}, 1, 0 ],
},
}
}
}
]
Truncated Schema:
{
branchId: { type: String, required: true },
branch: { type: Schema.Types.ObjectId, ref: 'branches' },
wouldRecommend: { type: String, default: '' }, // RECOMMENDATION ONLY
stars: { type: Number, default: 0 }, // IF 1 - 5 DOCUMENT IS A REVIEW
comment: { type: String, default: '' },
internal: { type: Boolean, default: true },
source: { type: String, required: true },
},
{ timestamps: true }
I need to make sure that I'm not counting "wouldRecommend" recommendations in the sum of the internal reviews. Do determine if something is a review it will have a star rating of 1 or more stars. Recommendations will have a star value of 0.
How can I add the condition that ensures the internal "$stars" value is >= 1 ( greater than or equal to 1 ) ?
Using Ashh's answer I was able to form this query:
[
{
$lookup: {
from: 'branches',
localField: 'branch',
foreignField: '_id',
as: 'branch'
}
},
{ $unwind: '$branch' },
{
$match: {
'branch.org_id': branchId
}
},
{
$group: {
_id: '$branch.name',
google: {
$sum: {
$cond: [{ $eq: ['$source', 'Google'] }, 1, 0]
}
},
internal: {
$sum: {
$cond: [
{
$and: [{ $gte: ['$stars', 1] }, { $eq: ['$internal', true] }]
},
1,
0
]
}
}
}
}
];
You can use $and with the $cond operator
{ "$group": {
"_id": "$branch.name",
"google": { "$sum": { "$cond": [{ "$eq": ["$source", "Google"] }, 1, 0] }},
"internal": { "$sum": { "$cond": [{ "$eq": ["$internal", true] }, 1, 0 ] }},
"rating": {
"$sum": {
"$cond": [
{
"$and": [
{ "$gte": ["$stars", 1] },
{ "$eq": ["$internal", true] }
]
},
1,
0
],
}
}
}}