I have a collection in MongoDB:
[
{
"uid": "a1"
},
{
"uid": "a2"
}
]
and a dictionary in my JS code
let dict = { "a1": "ref1", "a1": "ref2" };
I want to do an aggregate that will somehow join the two.
let k = ;
this.model.aggregate([
{
$match: {
uid: { $in: Object.keys(dict) }
}
},
{
$project: {
ref: // here is where I want to add the equivalent reference
}
}
])
The expected output would be something like this:
[{uid: "a1", ref: "ref1"}, {uid: "a2", ref: "ref2}]
Is there a way to get the reference from the dict into the $project?
I don't know if there is an easier way to do it, but here's how I achieved it:
db.collection.aggregate([
{
$match: {
uid: {
$in: [
"a1",
"a2"
]
}
}
},
{
"$project": {
"array": {
"$objectToArray": {
"a1": "ref1",
"a2": "ref2"
}
},
"uid": 1,
}
},
{
"$project": {
"ref": {
"$filter": {
"input": "$array",
"as": "elem",
"cond": {
"$eq": [
"$$elem.k",
"$uid"
]
}
}
},
uid: 1,
},
},
{
"$project": {
ref: {
"$arrayElemAt": [
"$ref.v",
0
]
},
uid: 1,
}
}
])
See it working here. I hope you get the idea and convert it into the nodejs equivalent version.
Related
I've used Mongodb aggregation, $facet as I wanted to count every value of "reli" and "prov" from the collection.
This is my code to get results from db.
const keyy = await db.aggregate([
$facet: { "reli": [
{ $group: { _id: '$reli', count: { $sum: 1 } } } ],
"prov": [
{ $group: { _id: '$prov', count: { $sum: 1 } } }
],
])
Output Looks like this:
[
{
"reli": [
{
"_id": "abcdef",
"count": 6
},
{
"_id": "ghij",
"count": 1
},
],
"prov": [
{
"_id": "hello",
"count": 63
},
{
"_id": "hey",
"count": 9
},
]
}
]
But I want That my Expected output is :
[
{
"reli":[
{abcdef: 6},
{ghij: 1}
],
"prov":[
{"hello": 63},
{"hey": 9}
]
}
]
You can iterate using $map and generate the new array with the values you want using $arrayToObject like this:
This query simply overwrite values reli and prov with the result of the map. That result is an array compound by objects where the key k is the _id value and the value v is the count value.
db.collection.aggregate([
{
"$project": {
"reli": {
"$map": {
"input": "$reli",
"in": {
"$arrayToObject": [
[
{
k: "$$this._id",
v: "$$this.count"
}
]
]
}
}
},
"prov": {
"$map": {
"input": "$prov",
"in": {
"$arrayToObject": [
[
{
k: "$$this._id",
v: "$$this.count"
}
]
]
}
}
}
}
}
])
Example here
Assume I have the following document:
[
{
"callId": "17dac51e-125e-499e-9064-f20bd3b1a9d8",
"caller": {
"firstName": "Test",
"lastName": "Testing",
"phoneNumber": "1231231234"
},
"inquiries": [
{
"inquiryId": "b0d14381-ce75-49aa-a66a-c36ae20b72a8",
"routeHistory": [
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-01T06:00:00.000Z",
"status": "routed"
},
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-03T06:00:00.000Z",
"status": "ended"
}
]
},
{
"inquiryId": "9d743be9-7613-46d7-8f9b-a04b4b899b56",
"routeHistory": [
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-01T06:00:00.000Z",
"status": "routed"
},
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-03T06:00:00.000Z",
"status": "ended"
}
]
}
]
}
]
I want to get results where inquiries.routeHistory.routeDate is equal to the $max routeDate value in routeHistory. I would expect my results to look like the following:
[
{
"callId": "17dac51e-125e-499e-9064-f20bd3b1a9d8",
"caller": {
"firstName": "Test",
"lastName": "Testing",
"phoneNumber": "1231231234"
},
"inquiries": [
{
"inquiryId": "b0d14381-ce75-49aa-a66a-c36ae20b72a8",
"routeHistory": [
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-03T06:00:00.000Z",
"status": "ended"
}
]
},
{
"inquiryId": "9d743be9-7613-46d7-8f9b-a04b4b899b56",
"routeHistory": [
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-03T06:00:00.000Z",
"status": "ended"
}
]
}
]
}
]
Is there a clean way to do this in a single aggregate, so that additional $match criteria can be applied? One caveat is that I can only use operators supported by DocumentDB: https://docs.aws.amazon.com/documentdb/latest/developerguide/mongo-apis.html
I have tried the following code, but to no avail:
{
$addFields: {
maxDate: {
$max: '$inquiries.routeHistory.routeDate',
},
},
},
{
$addFields: {
routeHistory: [
{
$arrayElemAt: [
{
$filter: {
input: '$inquiries.routeHistory',
cond: {
$eq: ['$maxDate', '$$this.routeDate'
],
},
},
},
0,
],
},
],
},
}
You have to use $map to scan outer array and to $filter to compare inner array's elements against $max date:
db.collection.aggregate([
{
$addFields: {
inquiries: {
$map: {
input: "$inquiries",
as: "inquiry",
in: {
inquiryId: "$$inquiry.inquiryId",
routeHistory: {
$filter: {
input: "$$inquiry.routeHistory",
cond: {
$eq: [ { $max: "$$inquiry.routeHistory.routeDate" }, "$$this.routeDate" ]
}
}
}
}
}
}
}
}
])
Mongo Playground
EDIT: by looking at your link I've noticed that $map is not supported, you can use below combination as a workaround:
db.collection.aggregate([
{
$unwind: "$inquiries"
},
{
$addFields: {
"inquiries.routeHistory": {
$filter: {
input: "$inquiries.routeHistory",
cond: {
$eq: [ { $max: "$inquiries.routeHistory.routeDate" }, "$$this.routeDate" ]
}
}
}
}
},
{
$group: {
_id: "$_id",
callId: { $first: "$callId" },
caller: { $first: "$caller" },
inquiries: { $push: "$inquiries" }
}
}
])
Mongo Playground (2)
I am trying to export Mongo data to XLSX which requires all the data to be in the parent map but currently I have data in this format:
[
{
"_id": "eatete",
"competition": {
"_id": "eatete"
"name": "Some competition name"
},
"members": [
{
"_id": "eatete",
"name": "Saad"
},
{
"_id": "eatete",
"name": "Saad2"
}
],
"leader": {
"name": "Saad",
"institute": {
"_id": "eatete",
"name": "Some institute name"
}
},
}
]
Ideally, the data should be:
[
{
"_id": "eatete",
"competition": "Some competition name"
"member0name": "Saad",
"member1name": "Saad2",
"leadername": "Saad",
"institute": "Some institute name"
}
]
So basically what I want is to refer the data of fields of subdocuments as if those were part of parent document, like competitions = competitions.name.
Can you please help me how can I do so using Mongoose.
Thanks
With some more aggregation trick
db.collection.aggregate([
{ "$unwind": { "path": "$members", "includeArrayIndex": "i" }},
{ "$group": {
"_id": "$_id",
"competition": { "$first": "$competition.name" },
"leadername": { "$first": "$leader.name" },
"institute": { "$first": "$leader.institute.name" },
"data": {
"$push": {
"k": { "$concat": ["members", { "$toLower": "$i" }, "name"] },
"v": "$members.name"
}
}
}},
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": ["$$ROOT", { "$arrayToObject": "$data" }]
}
}},
{ "$project": { "data": 0 }}
])
You can try below aggregation on your Model:
let resultt = await Model.aggregate([
{
$project: {
_id: 1,
competition: "$competition.name",
leadername: "$leader.name",
institute: "$leader.institute.name",
members: {
$map: {
input: { $range: [ 0, { $size: "$members" } ] },
in: {
k: { $concat: [ "member", { $toString: "$$this" }, "name" ] },
v: {
$let: {
vars: { current: { $arrayElemAt: [ "$members", "$$this" ] } },
in: "$$current.name"
}
}
}
}
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [ "$$ROOT", { $arrayToObject: "$members" } ]
}
}
},
{
$project: {
members: 0
}
}
])
Since you need to dynamically evaluate your keys based on indexes you can use $map with $range to map a list of indexes into keys of a new object. Then you can use $arrayToObject to get an object from those keys and $mergeObjects with $replaceRoot to flatten this object structure.
I am new to MongoDB and currently working on a recipe App for school which suggests diet plans. Therefore I need to join the "meal" ObjectId in the diet plan of the user (collection "Users") with the ObjectIds in the collections "Meals".
Afterwards I need to join an "ingredient" ObjectID in the "Meals" collection with the ID of the "ingredient" in the "Ingredients" collection. The problem is, that the "ingredient" ObjectID in collection "Meals" is situated in an Object with another integer variable "amount". This object is nested in an array called "ingredients" with many objects such as the one just described.
Below is my Structure:
Users
{
"_id": ObjectId("5b28cab902f28e18b863bd36"),
"username: "testUser1",
"password": "$2a$08$KjddpaSQPjp6aF/gseOhVeddYdqWJCJ4DpFwxfNgsk81G.0TOtN5i",
"dietPlans": Object
{
"dietPlanCurrent":Object
{
"monday":Object
{
"breakfast":Object
{
"meal": ObjectId("5b2b9a8bbda339352cc39ec4")
},
…
},
…
},
…
},
}
Meals
{
"_id" : ObjectId("5b2b9a8bbda339352cc39ec4"),
"name": "Gulasch-breakfast",
"cuisine": "International",
"ingredients":[
{
"ingredient": ObjectId("5b1ec0f939b55efcd4e28a2d"),
"amount": 20
},
{
"ingredient": ObjectId("5b1ec42474fc1f58d84264d4"),
"amount": 20
},
{
"ingredient": ObjectId("5b1ec42474fc1f58d84264d5"),
"amount": 20
},
…
],
"comments": [
…
]
}
Ingredients
{
{
"_id": ObjectId("5b1ec0f939b55efcd4e28a2d"),
"name": "Walnut",
"calories": 654
…
}
{
"_id": ObjectId("5b1ec0f939b55efcd4e28a3d"),
"name": "Apple",
"calories": 123
…
}
…
}
What I am trying to get is:
{
"_id": ObjectId("5b28cab902f28e18b863bd36"),
"username: "testUser1",
"password": "$2a$08$KjddpaSQPjp6aF/gseOhVeddYdqWJCJ4DpFwxfNgsk81G.0TOtN5i",
"dietPlans": Object
{
"dietPlanCurrent":Object
{
"Monday":Object
{
"breakfast":Object
{
"meal": ObjectId("5b2b9a8bbda339352cc39ec4")
"matchedIngredients": [
{
"_id": ObjectId("5b1ec0f939b55efcd4e28a2d"),
"name": "Walnut",
"calories": 654
…
}
…
]
},
…
},
…
},
…
},
}
My approach which is not working (only returning empty matchedIngredients Array)
{
$match: {
'_id': mongoose.Types.ObjectId(req.params.userId)
}
},
{
$lookup: {
from: 'meals',
localField: 'dietPlans.dietPlanCurrent.monday.breakfast.meal',
foreignField: '_id',
as: "dietPlans.dietPlanCurrent.monday.breakfast.mealObject"
}
},
{
$unwind: {
path: "$dietPlans.dietPlanCurrent.monday.breakfast.mealObject",
preserveNullAndEmptyArrays: true
}
},
{
$unwind: {
path: "$dietPlans.dietPlanCurrent.monday.breakfast.mealObject.ingredients",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: 'ingredients',
localField: 'dietPlans.dietPlanCurrent.monday.breakfast.mealObject.ingredients.ingredient',
foreignField: '_id',
as: "dietPlans.dietPlanCurrent.monday.breakfast.matchedIngredients"
}
}
Help is very much appreciated. I already checked out this approach, but it somehow didn't work:
Approach that didn't work for me
Thank you very much!
What you are trying to do is not possible with mongodb version 3.4 but if you upgrade to 3.6 then you can try below aggregation
db.collection.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(req.params.userId) } },
{ "$lookup": {
"from": Meals.collection.name,
"let": { "meal_id": "$dietPlans.dietPlanCurrent.monday.breakfast.meal" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$meal_id" ] } } },
{ "$unwind": "$ingredients" },
{ "$lookup": {
"from": Ingredients.collection.name,
"let": { "ingredient_id": "$ingredients.ingredient" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$ingredient_id" ] } } }
],
"as": "matchedIngredients"
}},
{ "$unwind": "$ingredients.matchedIngredients" },
{ "$group": {
"_id": "$_id",
"name": { "$first":"$name" },
"cuisine": { "$first":"$cuisine" },
"ingredients": { "$push":"$ingredients" }
}}
],
"as": "dietPlans.dietPlanCurrent.monday.breakfast.mealObject"
}},
{ "$unwind": "$dietPlans.dietPlanCurrent.monday.breakfast.mealObject" }
])
I have a collection in mongodb and indexed on the field name , i do a free search using the following query to get matched results and limit to 5,
db.getCollection('_event').aggregate([
{
"$match": {
"$and": [
{
"$text": {
"$search": "liver"
}
},
{},
{}
]
}
},
{
"$group": {
"_id": null,
"count": {
"$sum": 1
},
"results": {
"$push": "$$ROOT"
}
}
},
{
"$project": {
"count": 1,
"results": {
"$slice": [
"$results",
5
]
}
}
}
])
but there is a data with liverpool . when i do replace it with "$search": "liverpool" it returns data.
what is the issue here?