I've got some issues getting the balance of all my accounts. All accounts have references to different deposit items in deposits collection.
Accounts collection:
[{
"_id": "56b1ce63315748b44f1174e1",
"name": "Foo bar",
"deposits": [
{
"$oid": "56b1ce78315748b44f1174e2"
}
]
}]
Deposits collection:
{
"_id": {
"$oid": "56b1deb84f40bfa435e22f3f"
},
"account": {
"$oid": "56b1dea34f40bfa435e22f3e"
},
"amount": 300,
"date": {
"$date": "2016-02-01T00:00:00.000Z"
}
}
I've tried to aggregate the query but it always returns balance: 0. I guess I need to populate the item before the use of aggregate. But how do I do that?
Accounts.aggregate([
{ $unwind: "$deposits" },
{
$group: {
_id: "$_id",
name: { "$first": "$name" },
balance: { $sum: "$deposits.amount" }
}
}])
Solution:
{
$lookup:
{
from: 'deposits',
localField: 'amount',
foreignField: 'deposits',
as: 'deposits'
},
},
{ $unwind: "$deposits" },
{
$group: {
id: "$_id",
name: { "$first": "$name" },
balance: { $sum: "$deposits.amount" }
}
}
You need to use the $lookup operator in order to join the two collections, your current approach doesn't work. The $lookup operator is only available in Mongo 3.2 and higher.
{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}
Related
I have two collections in MongoDB: "carts" and another "products"
Carts:
[{
"_id": {
"$oid": "62af0fefebc0b42a875c7df1"
},
"uuid": "6ca05ae0-522a-4db3-b380-2d2330ee1e27",
"cartitems": [
"62a0b24680cc2891148daf7b",
"62a7339d91d01868921afa0a",
"62a72f7191d01868921afa08",
"62a7330291d01868921afa09"
],
"created": "2022-06-19T14:00:47.846958537+02:00[Europe/Brussels]",
"lastupdate": "2022-06-19T14:01:06.15165564+02:00[Europe/Brussels]"
},
{...},
{...}]
products:
[{
"_id": {
"$oid": "62a0b24680cc2891148daf7b"
},
"name": "Product1",
"created": "2022-06-11T09:41:54.461308647+02:00[Europe/Brussels]",
"category": "Workshops",
"pricein": "28900",
"lastupdate": "2022-06-17T16:09:53.385655474+02:00[Europe/Brussels]"
},
{...},
{...}]
I would like to use Aggregate:
db.carts.aggregate([
{ $match: { uuid: "6ca05ae0-522a-4db3-b380-2d2330ee1e27" } },
{
$lookup: {
from: "products",
localField: "cartitems",
foreignField: "_id",
as: "output"
}
}
])
This is not working because localField: "cartitems" should be converted:
"$addFields": {
"convertedIdStr": {
"$toString": "$cartitems"
}
But I don't manage to convert because cartitems is an array.
Any help would be great, Thanks a lot!
Use $lookup with pipeline. In $lookup pipeline stage, add $match stage by filtering the (converted to string) product's _id is in cartitems (array) variable.
db.carts.aggregate([
{
$match: {
uuid: "6ca05ae0-522a-4db3-b380-2d2330ee1e27"
}
},
{
$lookup: {
from: "products",
let: {
cartitems: "$cartitems"
},
pipeline: [
{
$match: {
$expr: {
$in: [
{
$toString: "$_id"
},
"$$cartitems"
]
}
}
}
],
as: "output"
}
}
])
Sample Mongo Playground
I have two collections:
employees
_id
office
jobTitle
offices
_id
city
I am trying to receive a list of all office locations with the job titles of employees of the respecitve office.
The end result would look like this:
[{
_id: ObjectId('6086f617cc0824cc4ce7c9f0'),
city: "Berlin",
jobTitles: ['SOFTWARE ENGINEER', 'CEO', 'CFO']
}, {
_id: ObjectId('60c08d36f925f3083488ea79'),
city: "Prague",
jobTitles: ['UX DESIGNER', 'BUSINESS ANALYST']
}]
This is the aggregation I've tried, with no success:
db.offices.aggregate([{
$lookup: {
from: 'employees',
localField: '_id',
foreignField: 'office',
as: 'jobTitles',
project: [{
$group: { _id: '$jobTitle'}
}]
}
}]);
One office can have thousands of employees, so I'm trying to make the query as efficient as possible.
Thank you for your ideas! :)
use $lookup
db.offices.aggregate([
{
"$lookup": {
"from": "employees",
"localField": "_id",
"foreignField": "office",
"as": "jobTitles"
}
},
{
"$set": {
"jobTitles": "$jobTitles.jobTitles"
}
},
{
"$unwind": "$jobTitles"
},
{
"$group": {
"_id": "$_id",
"city": {
"$first": "$city"
},
jobTitles: {
$addToSet: "$jobTitles"
}
}
}
])
mongoplayground
How to get a list of top ranked items based on their usage as a field in items of another collection?
Here is a mongodb playground explaining the scenario - https://mongoplayground.net/p/gTMm1JVv9uV
In the example below, category 245 is used twice and 276 is used once in the posts collection. The output will rank the categories based on their count of usage in posts
Note that the post collection only has the category id so looking up categories collection is necessary.
Based on this, the expected output is an array of category's text.
{
topCategories: ["category 245", "category 276"]
}
A sample data in the two collections is below:
db={
categories: [
{
"_id": 231,
"text": "category 231",
},
{
"_id": 245,
"text": "category 245",
},
{
"_id": 276,
"text": "category 276",
}
],
posts: [
{
"_id": 71,
category: "245"
},
{
"_id": 72,
category: "276"
},
{
"_id": 74,
category: "245"
}
]
}
I have used addToSet in earlier query but found out that it does not maintain the order. I have replaced it with push operator.
db.posts.aggregate([
{
$addFields: {
category: {
$toInt: "$category"
},
}
},
{
$lookup: {
from: "categories",
as: "category",
localField: "category",
foreignField: "_id"
}
},
{
"$unwind": "$category"
},
{
"$group": {
"_id": "$category._id",
"count": {
"$sum": 1
},
"category": {
"$first": "$category"
}
}
},
{
"$sort": {
"count": -1
}
},
{
"$project": {
categoriesText: "$category.text"
}
},
{
"$group": {
"_id": null,
"categoriesText": {
"$push": "$categoriesText"
}
}
},
{
"$project": {
_id: 0,
topCategories: "$categoriesText"
}
}
])
New Playground
You can try,
$group by category and convert to integer using $toInt, get count of total duplicate categories in count using $sum
$loopup with categories collection
$sort by count field descending order
$group by null for combine categories in a array field, get first element from category array using $arrayElemAt and push it in topCategories field
db.posts.aggregate([
{
$group: {
_id: { $toInt: "$category" },
count: { $sum: 1 }
}
},
{
$lookup: {
from: "categories",
as: "category",
localField: "_id",
foreignField: "_id"
}
},
{ $sort: { count: -1 } },
{
$group: {
_id: null,
topCategories: {
$push: { $arrayElemAt: ["$category.text", 0] }
}
}
}
])
Playground
I'm trying to set up a query on the mongo that searches in 3 different collections.
Document of client:
{
"_id": ObjectId("1a")
"razaosocial:"32423424",
"prepository": [
{
"$ref": "prepository",
"$id": ObjectId("2a")
}
]
}
Document of prepository:
{
"_id": ObjectId("2a")
"name:"Jonh",
"prepository": {
"$ref": "representative",
"$id": ObjectId("3a")
}
}
Document of representative:
{
"_id": ObjectId("3a")
"name:"Josh"
}
I'm doing it this way, but it doesn't return anything:
db.clients.aggregate(
[
{
$lookup: {
from: 'prepository',
localField: 'prepository',
foreignField: 'id',
as: 'prepository'
}
},
{ $unwind: "$prepository" },
{ $lookup: {
from: 'representative',
localField: 'id',
foreignField: 'prepository._id',
as: 'prepository.repre'
}
},
{ $group: {
_id: "$_id",
client: { $first: "$razaosocial" },
users: {
$push: "$prepository"
}
} }
])
I'm trying to return
{
"_id": "1a"
"razaosocial:"32423424",
"prepository": [
{
"_id": "2a"
"name:"Jonh",
"representative": {
"_id": "3a"
"name:"Josh"
}
}
]
}
I am grateful for any help
You can use nested lookup,
$lookup with prepository collection
$match with prepository's id
$lookup with representative collection
$unwind deconstruct representative array
db.client.aggregate([
{
$lookup: {
from: "prepository",
as: "prepository",
let: { prepository: "$prepository" },
pipeline: [
{ $match: { $expr: { $in: ["$_id", "$$prepository"] } } },
{
$lookup: {
from: "representative",
localField: "representative",
foreignField: "_id",
as: "representative"
}
},
{ $unwind: "$representative" }
]
}
}
])
Playground
I have a mongodb aggregation query
return this.collection.aggregate([
{ $match: { _id: ObjectID(locationId) } },
{
$lookup:{
from: "buildings",
localField: "userId",
foreignField: "ownerId",
as: "areas"
}
},
{
$project: {
"location_id": "$_id",
"locationName": "$locationName",
"areas": "$areas"
}
}
]).toArray();
where the areas field is an array of objects
[{
key: value
}]
Is it possible to rename this key during the aggregation process?
Yes, though maybe not as easily as you might expect.
First you need to use $unwind to get a document per area. Than $group the documents with the same location (thus same _id) back together.
The $group actually also does a projection. You can use $first in $group to select fields that ware not unwinded.
You haven't told the properties of an area object. In the example I assume it has a coords and shape property.
return this.collection.aggregate([
{ $match: { _id: ObjectID(locationId) } },
{
$lookup:{
from: "buildings",
localField: "userId",
foreignField: "ownerId",
as: "areas"
}
},
{
$unwind: "$areas"
},
{
$group: {
"_id": "$_id",
"location_id": { $first: "$_id" },
"locationName": { $first: "$locationName" },
"areas": {
$push: {
"latitude": "$coords.lat",
"longitude": "$coords.lon",
"shape": "$shape"
}
}
}
}
]).toArray();
Note that you do end up with an additional _id field (which may be ignored), because $group requires an _id.
Yes, you can unwind - group your areas as following:
return this.collection.aggregate([
{ $match: { _id: ObjectID(locationId) } },
{
$lookup:
{
from: "buildings",
localField: "userId",
foreignField: "ownerId",
as: "areas"
}
},
{
$project: { "location_id": "$_id",
"locationName": "$locationName",
"areas": "$areas"
}
},
{
$unwind: "$areas"
},
{
$group: { "_id": {"location_id": "$_id", "locationName": "$locationName"},
"areas": {$push: {new_key: "$areas.key"}} //<== renaming 'key' to 'new_key'
}
},
{
$project: { "_id": 0,
"location_id": "$_id.location_id",
"locationName": "$_id.locationName",
"areas": "$areas"
}
}
]).toArray();