I have a document that looks as follows.
{
_id: "1234",
orderIds: [1,2,3,4,5]
}
I want to publish this document, however, I want to lookup and filter this reactively. With an aggregation in 3.2, it would be something like this:
var id = "1234"
db.users.aggregate([
{ $match: { _id: id } },
{ $unwind: '$orderIds' },
{
$lookup: {
from: 'orders',
as: 'orders',
localField: 'orderIds',
foreignField: '_id'
}
},
{ $match: { 'orders.status': { $ne: 'closed' } } },
{
$group: {
_id: "$_id",
orders: { $push: '$orders' }
}
}
])
However, I can't find a way to do this with plain publications, and mongo 2.6.7 that meteor ships with. Is there a way to do it?
Related
I currently have a Mongo query that looks like this:
const user = await User.findOne({ userId }).lean() || []
const contributions = await Launch.aggregate([
{ $sort: { addedAt: -1 } },
{ $limit: 10 },
{
$match: {
_id: { $in: user.contributions }
}
},
{
$addFields: {
activity: 'contribution',
launchName: '$name',
launchId: '$_id',
date: '$addedAt',
content: '$description'
}
}
])
But instead of having two different Mongo queries (findOne and aggregate), how can I combine them into one query?
I tried this but it just errors out immediately in the lookup part:
const contributions = await Launch.aggregate([
{ $sort: { addedAt: -1 } },
{ $limit: 10 },
{
$lookup: {
from: 'user',
let: { id: $user.contributions },
pipeline: [
{ $match: { $expr: { $in: [$_id, $$user.contributions] } } }
],
localField: '_id',
foreignField: 'userId',
as: 'user'
}
},
{
$addFields: {
activity: 'contribution',
launchName: '$name',
launchId: '$_id',
date: '$addedAt',
content: '$description'
}
}
])
I've never used the pipeline option so a little confused onn how to fix this problem?
Enclose these $user.contributions, $_id with quotes in order to make the query valid.
Since you declare the id variable with the value of user.contributions. You should use the variable with $$id instead of $$user.contributions.
I don't think the localField and foreignField are needed as you are mapping/joining with pipeline.
Your aggregation query should be looked as below:
const contributions = await Launch.aggregate([
{ $sort: { addedAt: -1 } },
{ $limit: 10 },
{
$lookup: {
from: 'user',
let: { id: "$user.contributions" },
pipeline: [
{ $match: { $expr: { $in: ["$_id", "$$id"] } } }
],
as: 'user'
}
},
{
$addFields: {
activity: 'contribution',
launchName: '$name',
launchId: '$_id',
date: '$addedAt',
content: '$description'
}
}
])
I am just wondering if its possible to use a $lookup aggregation operator inside of the in field of a $map operator. I am trying to map the VINs inside the carData objects to corresponding document ids in a VIN collection. Is there a way to accomplish this using $lookup inside of $map where I can match the vin field inside my carData objects with vin fields inside of my VIN collection to map to an id as opposed to the vin.
CarData Collection:
carData: [
{vin: 123456789,
make: "Dodge"},
{vin: 987654321,
make: "Honda"}
]
Vin Collection:
[
{
_id: ObjectId("1dsf1234125")
Vin: 123456789
},
{
_id: ObjectId("1dsf1234124")
Vin: 987654321
},
]
Expected result
carData: [
{vin: ObjectId("1dsf1234125"),
make: "Dodge"},
{vin: ObjectId("1dsf1234124"),
make: "Honda"}
]
Using the data samples from the question post, this aggregation returns the expected result (change the collection names appropriately):
db.cars_collection.aggregate([
{
$lookup:
{
from: "vins_collection",
localField: "vin",
foreignField: "Vin",
as: "car_vins"
}
},
{
$project: {
_id: 0,
make: 1,
vin: {
$arrayElemAt: [
{ $map: { input: "$car_vins", in: "$$this._id" } }, 0
]
}
}
},
])
The result will look like this:
{ "make" : "Dodge", "vin" : ObjectId("626a12a1ac1cc4f9d0bbd7e7") }
{ "make" : "Honda", "vin" : ObjectId("626a12a1ac1cc4f9d0bbd7e8") }
Method 1
db.carDatas.aggregate([
{
$unwind: "$carData"
},
{
$lookup: {
from: "Vins",
localField: "carData.vin",
foreignField: "Vin",
as: "docs"
}
},
{
$project: {
make: "$carData.make",
vin: { $first: "$docs._id" },
_id: 0
}
},
{
$group: {
_id: null,
carData: { $push: "$$ROOT" }
}
},
{
$unset: "_id"
}
])
mongoplayground
I am new to MongoDB and I am stuck in the below scenario.
I have a collection that contains duplicate docs.
I just want to get the sum of the property in each doc excluding the duplicate docs.
My Docs looks like this:
{"_id":"5dd629461fc50b782479ea90",
"referenceId":"5dd581f10859d2737965d23a",
"sellingId":"319723fb80b1a297cf0803abad9bc60787537f14a6a37d6e47",
"account_name":"mrfsahas1234",
"vendor_name":"testaccount2",
"action_type":"purchase",
"product_name":"Bottle",
"product_quantity":10,
"transactionId":"319723fb80b1a297cf0803abad9bc60787537f14a6a37d6e47",
"uid":"2019-11-20T17:39:17.405Z",
"createdAt":"2019-11-21T08:56:56.589+00:00",
"updatedAt":"2019-11-21T08:56:56.589+00:00","__v":0
},
{
"_id":"5dd629461fc50b782479ea90",
"referenceId":"5dd581f10859d2737965d23a",
"sellingId":"320a9a2f814a45e01eb98344c9af708fa2864d81587e5914",
"account_name":"mrfsahas1234",
"vendor_name":"testaccount2",
"action_type":"purchase",
"product_name":"Bottle",
"product_quantity":50,
"transactionId":"320a9a2f814a45e01eb98344c9af708fa2864d81587e5914",
"uid":"2019-11-20T17:39:17.405Z",
},
{
"_id":"5dd629461fc50b782479ea90",
"referenceId":"5dd581f10859d2737965d23a",
"sellingId":"320a9a2f814a45e01eb98344c9af708fa2864d81587e5914",
"account_name":"mrfsahas1234",
"vendor_name":"testaccount2",
"action_type":"purchase",
"product_name":"Bottle",
"product_quantity":50,
"transactionId":"320a9a2f814a45e01eb98344c9af708fa2864d81587e5914",
"uid":"2019-11-20T17:39:17.405Z",
},
Currently, I am doing this:
MaterialsTrack.aggregate([
{
$match: {
$and: [
{product_name: product_name},
{account_name: account_name},
{action_type: 'purchase'},
{uid:uid}
]
}
},
{
$group: {_id: "$sellingId", PurchseQuantity: {$sum: "$product_quantity"}}
},
])
It returns the sum of product_quantity all the matching docs (including the duplicate docs).
Current Output:
{_id: "320a9a2f814a45e01eb98344c9af708fa2864d81587e5914", PurchseQuantity:110}
Expected Output:
{_id: "320a9a2f814a45e01eb98344c9af708fa2864d81587e5914", PurchseQuantity:60}
I want to get the sum of only unique docs. How can I achieve it?
Thanks in advance!
You need to sum inside of the $group _id field, and then use the replaceRoot to achieve the the result you wanted.
MaterialsTrack.aggregate([
{
$match: {
$and: [
{
product_name: "Bottle"
},
{
account_name: "mrfsahas1234"
},
{
action_type: "purchase"
},
{
uid: "2019-11-20T17:39:17.405Z"
}
]
}
},
{
$group: {
_id: {
sellingId: "$sellingId",
PurchaseQuantity: {
$sum: "$product_quantity"
}
}
}
},
{
$replaceRoot: {
newRoot: {
_id: "$_id.sellingId",
PurchaseQuantity: "$_id.PurchaseQuantity"
}
}
}
]);
Sample Output:
[
{
"PurchaseQuantity": 50,
"_id": "320a9a2f814a45e01eb98344c9af708fa2864d81587e5914"
}
]
Playground:
https://mongoplayground.net/p/MOneCRiSlO0
What about adding $addToSet to your aggregations pipeline
MaterialsTrack.aggregate([
{
$match: {
$and: [
{product_name: product_name},
{account_name: account_name},
{action_type: 'purchase'},
{uid:uid}
]
}
},
{
$group: {_id: "$sellingId", PurchseQuantity: {$sum: "$product_quantity"},"list" : {$addToSet : "$list"}}
},
])
I'm trying to get some results and group the by an id, but I don't know a way to do that, I was trying with $unwind and $lookup, but when I use the $group, it doesn't give me results, so, I will appreciate any feedback.
usercalification model
{
calification: Number,
status: Number,
place: {
type: Schema.ObjectId,
ref: 'Place',
}
}
place model
{
name: String,
description: String
}
I was trying to do something like this:
mongoose.model('usercalification').aggregate([
{
$lookup: {
from: 'places',
localField: 'place',
foreignField: '_id',
as: 'places'
}
},
{
{
$unwind: '$places'
}
}
]);
It works like it should, the issue is when I try to $groupBy
mongoose.model('usercalification').aggregate([
{
$lookup: {
from: 'places',
localField: 'place',
foreignField: '_id',
as: 'places'
}
},
{
{
$unwind: '$places'
}
},
{
$group: {
_id: '$place',
calification: { $avg: '$calification' },
count: {$sum: 1}
}
}
]);
I'm grouping by $place cuz that's the Place Id that I need to group by.
I'll appreciate any feedback.
The issue was that those were not ordered in the correct way.
I'm doing a $lookup from a _id. So the result is always 1 document. Hence, I want the result to be an object instead an array with one item.
let query = mongoose.model('Discipline').aggregate([
{
$match: {
project: mongoose.Types.ObjectId(req.params.projectId)
},
},
{
$lookup: {
from: "typecategories",
localField: "typeCategory",
foreignField: "_id",
as: "typeCategory"
}
},
{
$project: {
title: 1, typeCategory: "$typeCategory[0]"
}
}
]);
This notation: "$typeCategory[0]" is not working. Is there any smart way of doing this?
You can just use $unwind. It deconstructs an array field from the input documents to output a document for each element
let query = mongoose.model('Discipline').aggregate([
{
$match: {
project: mongoose.Types.ObjectId(req.params.projectId)
},
},
{
$lookup: {
from: "typecategories",
localField: "typeCategory",
foreignField: "_id",
as: "typeCategory"
}
},
{$unwind: '$typeCategory'},
{
$project: {
title: 1, typeCategory: "$typeCategory"
}
}
]);
You can use $arrayElemAt in $project stage.
Syntax of $arrayElemAt is { $arrayElemAt: [ <array>, <idxexOfArray> ] }
like:
mongoose.model('Discipline').aggregate([
{
$match: {
project: mongoose.Types.ObjectId(req.params.projectId)
},
},
{
$lookup: {
from: "typecategories",
localField: "typeCategory",
foreignField: "_id",
as: "typeCategory"
}
},
{
$project: {
name: 1, typeCategory: {$arrayElemAt:["$typeCategory",0]}
}
}
]);
Use $first to return the first element in the array:
$project: {
title: 1,
typeCategory: {$first: "$typeCategory"}
}
For merging two collections:
{$replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$typeCategory", 0 ] }, "$$ROOT" ] } } },
{ $project: { typeCategory: 0 } }