Aggregate array field and text field in a collection mongo Node - node.js

I want to merge two document in same collection by grouping two fields, one field is just a normal text field (WorkFlow), and another one is an array field (Service), i was able to group the normal field(WorkFlow) but am stuck to group the array field, please help me I have a short in time to do it, I've gone through some article but I couldn't find any solution, please help me to overcome this..
** i need to get the output, in any method if its not in aggregate then any other solutions
I have the document in collection like below:
[{
"_id": {
"$oid": "62f9fd54259335683bc54ac3"
},
"WorkFlow": "Vendor",
"Service": [
{
"value": "6235b52ea216f20e1b00ad43"
},
{
"value": "6235b538a216f20e1b00ad46"
}
],
"AssetServiceClassification": "Business",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a23bbc7e6a4378ff8ad74",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620b4d4995c3061565e63b08",
"Designation": "61efa3aca444008633b223e0"
}
]
},
{
"_id": {
"$oid": "62f9f5f9e26c8912b86c61b8"
},
"WorkFlow": "Vendor",
"Service": [
{
"value": "6235b538a216f20e1b00ad46"
},
{
"value": "6235b52ea216f20e1b00ad43"
}
],
"AssetServiceClassification": "Normal",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a2351c7e6a4378ff8ad4c",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620a2387c7e6a4378ff8ad60",
"Designation": "61efa3aca444008633b223e0"
}
]
}]
and I need an output structure like below:
[{
"WorkFlow": "Vendor",
"Service": [
{
"value": "6235b52ea216f20e1b00ad43"
},
{
"value": "6235b538a216f20e1b00ad46"
}
],
"Assets": [
{
"_id": "62f9fd54259335683bc54ac3",
"AssetServiceClassification": "Business",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a23bbc7e6a4378ff8ad74",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620b4d4995c3061565e63b08",
"Designation": "61efa3aca444008633b223e0"
}
]
},
{
"_id": "62f9f5f9e26c8912b86c61b8",
"AssetServiceClassification": "Normal",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a2351c7e6a4378ff8ad4c",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620a2387c7e6a4378ff8ad60",
"Designation": "61efa3aca444008633b223e0"
}
]
}
]
}]

I think this pipeline will help you get the output you want:
db.collection.aggregate([
{
"$unwind": "$Service"
},
{
"$sort": {
"Service.value": 1
}
},
{
"$group": {
"_id": {
"_id": "$_id",
"WorkFlow": "$WorkFlow",
"LevelArray": "$LevelArray",
"AssetServiceClassification": "$AssetServiceClassification"
},
"Service": {
"$push": "$Service"
}
}
},
{
"$project": {
"_id": "$_id._id",
"WorkFlow": "$_id.WorkFlow",
"LevelArray": "$_id.LevelArray",
"AssetServiceClassification": "$_id.AssetServiceClassification",
"Service": "$Service"
}
},
{
"$group": {
"_id": {
"WorkFlow": "$WorkFlow",
"Service": "$Service"
},
"Assets": {
"$push": {
"_id": "$_id",
"LevelArray": "$LevelArray",
"AssetServiceClassification": "$AssetServiceClassification"
}
}
}
}
])

Related

How to bulkWrite decrease quantity in products when order mongodb?

I have table "products" in mongodb example:
{
"_id": "62ab02ebd3e608133c947798",
"status": true,
"name": "Meat",
"type": "62918ab4cab3b0249cbd2de3",
"price": 34400,
"inventory": [
{
"_id": "62af007abb78a63a44e88561",
"locator": "62933b3fe744ac34445c4fc0",
"imports": [
{
"quantity": 150,
"_id": "62aefddcd5b52c1da07521f2",
"date_manufacture": "2022-03-01T10:43:11.842Z",
"date_expiration": "2023-05-20T10:43:20.431Z"
},
{
"quantity": 200,
"_id": "62af007abb78a63a44e88563",
"date_manufacture": "2022-04-01T10:45:01.711Z",
"date_expiration": "2023-05-11T10:45:06.882Z"
}
]
},
{
"_id": "62b3c2545a78fb4414dd718f",
"locator": "62933e07c224b41fc48a1182",
"imports": [
{
"quantity": 120,
"_id": "62b3c2545a78fb4414dd7190",
"date_manufacture": "2022-03-01T01:30:07.053Z",
"date_expiration": "2023-05-01T10:43:20.431Z"
}
]
}
],
}
I want to decrease quantity in one locator by id in imports of inventory with multiple product (bulkWrite). And can I decrease quantity sort by date_expiration?
Example: when customer order product with quantity 300 and locator 62933b3fe744ac34445c4fc0, I want to product update belike:
{
...
"name": "Meat",
"price": 34400,
"inventory": [
{
"_id": "62af007abb78a63a44e88561",
"locator": "62933b3fe744ac34445c4fc0",
"imports": [
{
"quantity": 50,
"_id": "62aefddcd5b52c1da07521f2",
"date_manufacture": "2022-03-01T10:43:11.842Z",
"date_expiration": "2023-05-20T10:43:20.431Z"
}
]
},
{
"_id": "62b3c2545a78fb4414dd718f",
"locator": "62933e07c224b41fc48a1182",
"imports": [
{
"quantity": 120,
"_id": "62b3c2545a78fb4414dd7190",
"date_manufacture": "2022-03-01T01:30:07.053Z",
"date_expiration": "2023-05-01T10:43:20.431Z"
}
]
}
],
}
Thank you so much!
You should refactor your schema as nesting array as it is considered an anti-pattern and introduces unnecessary complexity to query.
One of the options:
db={
"products": [
{
"_id": "62ab02ebd3e608133c947798",
"status": true,
"name": "Meat",
"type": "62918ab4cab3b0249cbd2de3",
"price": 34400,
"inventory": [
"62af007abb78a63a44e88561",
"62b3c2545a78fb4414dd718f"
]
}
],
"inventory": [
{
"_id": "62af007abb78a63a44e88561",
"locator": "62933b3fe744ac34445c4fc0",
"imports": [
{
"quantity": 150,
"_id": "62aefddcd5b52c1da07521f2",
"date_manufacture": ISODate("2022-03-01T10:43:11.842Z"),
"date_expiration": ISODate("2023-05-20T10:43:20.431Z")
},
{
"quantity": 200,
"_id": "62af007abb78a63a44e88563",
"date_manufacture": ISODate("2022-04-01T10:45:01.711Z"),
"date_expiration": ISODate("2023-05-11T10:45:06.882Z")
}
]
},
{
"_id": "62b3c2545a78fb4414dd718f",
"locator": "62933e07c224b41fc48a1182",
"imports": [
{
"quantity": 120,
"_id": "62b3c2545a78fb4414dd7190",
"date_manufacture": ISODate("2022-03-01T01:30:07.053Z"),
"date_expiration": ISODate("2023-05-01T10:43:20.431Z")
}
]
}
]
}
You can then do something relatively simple. Use $sortArray to sort the date_expiration and start to iterate through the arrays using $reduce.
db.inventory.aggregate([
{
$match: {
locator: "62933b3fe744ac34445c4fc0"
}
},
{
"$set": {
"imports": {
$sortArray: {
input: "$imports",
sortBy: {
date_expiration: 1
}
}
}
}
},
{
$set: {
result: {
"$reduce": {
"input": "$imports",
"initialValue": {
"qtyToDecrease": 300,
"arr": []
},
"in": {
"qtyToDecrease": {
$subtract: [
"$$value.qtyToDecrease",
{
$min: [
"$$value.qtyToDecrease",
"$$this.quantity"
]
}
]
},
"arr": {
"$concatArrays": [
"$$value.arr",
[
{
"$mergeObjects": [
"$$this",
{
"quantity": {
$subtract: [
"$$this.quantity",
{
$min: [
"$$value.qtyToDecrease",
"$$this.quantity"
]
}
]
}
}
]
}
]
]
}
}
}
}
}
},
{
$set: {
imports: "$result.arr",
result: "$$REMOVE"
}
},
{
"$merge": {
"into": "inventory",
"on": "_id"
}
}
])
Mongo Playground
Here is another version that keeps your original schema. You can see it is much more complex.

How to only update one match of an array filter in mongodb

Problem:
I need to only update one document in the spots available array that has an id of "empty". My previous query was updating all matching sub documents with "empty" as the id; which is no good Example Below. So I decided to use aggregation so that I could add a limit stage so that I could only update one item, but come to find out I cannot update the original document with an aggregation. This leaves the only option to use an array filter that only updates one/first of its matches. Is this possible? I feel like there has to be a way to only update one match on an array filter and if there isn't this is definitely something that should be added.
My code:
This code updates every object with "empty"
const client = await clientPromise;
const db = client.db();
// const query = db.collection('events').aggregate(agg);
const query = await db.collection('events').updateOne({
_id: new ObjectId("6398c34ca67dbe3286452f23"),
createdBy: new ObjectId("636c1778f1d09191074f9690"),
"weights.weight": 12
},
{
$set: {
"weights.$.spotsAvailable.$[el2]": {
"name": "Wayne Wrestler",
"userId": new ObjectId("636c1778f1d09191074f9690")
}
}
},
{
arrayFilters: [{ "el2": { "userId": "empty" } }]
})
Example documents:
Event:
{
"_id": {
"$oid": "6398c34ca67dbe3286452f23"
},
"name": "test",
"createdBy": {
"$oid": "636c1778f1d09191074f9690"
},
"description": "testing",
"date": {
"$date": {
"$numberLong": "1645488000000"
}
},
"location": {
"type": "Point",
"coordinates": [
0,
0
]
},
"weights": [
{
"spotsAvailable": [
{
"name": "empty",
"userId": "empty"
},
{
"name": "empty",
"userId": "empty"
},
{
"name": "empty",
"userId": "empty"
}
],
"weight": 12
},
{
"spotsAvailable": [
{
// only one of these should've been updated, but both were
"name": "Wayne Wrestler",
"userId": {
"$oid": "636c1778f1d09191074f9690"
}
},
{
"name": "Wayne Wrestler",
"userId": {
"$oid": "636c1778f1d09191074f9690"
}
}
],
"weight": 15
}
],
"eventApplicants": [
{
"userId": {
"$oid": "636c1778f1d09191074f9690"
},
"name": "Wayne Wrestler",
"weight": 12
}
]
}
User:
{
"_id": {
"$oid": "636c1778f1d09191074f9690"
},
"name": "Wayne Wrestler",
"email": "wakywayne80#gmail.com",
"image": "https://lh3.googleusercontent.com/a/ALm5wu32gXjDIRxncjjQA9I4Yl-sjFH5EWsTlmvdM_0kiw=s96-c",
"emailVerified": {
"$date": {
"$numberLong": "1670864727212"
}
},
"createdEvents": [
{
"createdEventName": "test",
"createdEventDate": {
"$date": {
"$numberLong": "1645488000000"
}
},
"createdEventDescription": "testing",
"createdEventWeights": [
{
"weight": "12",
"filled": [
false,
false,
false
]
},
{
"weight": "15",
"filled": [
false,
false
]
}
],
"createdEventId": {
"$oid": "6398c34ca67dbe3286452f23"
}
}
],
"userSignedUpEvents": [],
"availableWeights": [
1,
123
],
"signedUpEvents": [
{
"eventId": {
"$oid": "636c722f67642c30dc5ffc30"
},
"eventName": "Utah",
"eventDate": {
"$date": {
"$numberLong": "1667913330000"
}
},
"accepted": false
},
{
"eventId": {
"$oid": "636c722f67642c30dc5ffc30"
},
"eventName": "Utah",
"eventDate": {
"$date": {
"$numberLong": "1667913330000"
}
},
"accepted": false
},
{
"eventId": {
"$oid": "637ec484ac2d675b30590b47"
},
"eventName": "Maybe?",
"eventDate": {
"$date": {
"$numberLong": "1672272000000"
}
},
"accepted": false
},
{
"eventId": {
"$oid": "636c722f67642c30dc5ffc30"
},
"eventName": "Utah",
"eventDate": {
"$date": {
"$numberLong": "1667913330000"
}
},
"accepted": false
},
{
"eventId": {
"$oid": "638d5274628db2a7bf61df49"
},
"eventName": "Eva's",
"eventDate": {
"$date": {
"$numberLong": "1698019200000"
}
},
"accepted": false
},
{
"eventId": {
"$oid": "636c722f67642c30dc5ffc30"
},
"eventName": "Utah",
"eventDate": {
"$date": {
"$numberLong": "1667913330000"
}
},
"accepted": false
},
{
"eventId": {
"$oid": "6398a922abb5c168ede595fb"
},
"eventName": "Nikko's event",
"eventDate": {
"$date": {
"$numberLong": "1670976000000"
}
},
"accepted": false
},
{
"eventId": {
"$oid": "6398a922abb5c168ede595fb"
},
"eventName": "Nikko's event",
"eventDate": {
"$date": {
"$numberLong": "1670976000000"
}
},
"accepted": false
},
{
"eventId": {
"$oid": "6398c34ca67dbe3286452f23"
},
"eventName": "test",
"eventDate": {
"$date": {
"$numberLong": "1645488000000"
}
},
"accepted": false
},
{
"eventId": {
"$oid": "6398c34ca67dbe3286452f23"
},
"eventName": "test",
"eventDate": {
"$date": {
"$numberLong": "1645488000000"
}
},
"accepted": false
},
{
"eventId": {
"$oid": "6398c34ca67dbe3286452f23"
},
"eventName": "test",
"eventDate": {
"$date": {
"$numberLong": "1645488000000"
}
},
"accepted": false
},
{
"eventId": {
"$oid": "6398c34ca67dbe3286452f23"
},
"eventName": "test",
"eventDate": {
"$date": {
"$numberLong": "1645488000000"
}
},
"accepted": false
}
]
}
I have tried:
Pluging in variables without the new ObjectId syntax
Plugin in variables with the new ObjectId syntax
Using the exact same hardcoded values that I got from copying the aggregation code from compass for the node driver
All of these either don't work or result in every subdocument with "empty" getting filled
One option is to use update with pipeline:
Since this is a double nested array, it is easier to do it in two steps - internal and external
First create the "external" item to replace in weights array and call it newItem. It is calculated using $reduce which allow us to manipulate the internal array while looping on it.
Replace the relevant item on weights array with our newItem using $map with $cond
db.collection.update(
{_id: ObjectId("6398c34ca67dbe3286452f23"), "weights.weight": 12},
[
{$set: {
newItem: {$reduce: {
input: {$getField: {
input: {$first: {
$filter: {
input: "$weights",
as: "item",
cond: {$eq: ["$$item.weight", 12]}
}
}},
field: "spotsAvailable"
}},
initialValue: [],
in: {$concatArrays: [
"$$value",
{$cond: [
{$and: [
{$eq: ["$$this.userId", "empty"]},
{$not: {$in: [ObjectId("636c1778f1d09191074f9690"), "$$value.userId"]}}
]},
[{
name: "Wayne Wrestler",
userId: ObjectId("636c1778f1d09191074f9690")
}],
["$$this"]
]}
]}
}}
}},
{$set: {
weights: {$map: {
input: "$weights",
in: {$cond: [
{$eq: ["$$this.weight", 12]},
{$mergeObjects: [
"$$this",
{spotsAvailable: "$newItem"}
]},
"$$this"
]}
}},
newItem: "$$REMOVE"
}}
])
See how it works on the playground example
You can first $unwind the weights for easier processing first. Use $reduce to iterate through the weights.spotsAvailable array and use a compound object to store the result and a flag to indicate whether it is updated or not. Finally use the result to $merge back to the original document.
db.collection.aggregate([
{
$match: {
"_id": ObjectId("6398c34ca67dbe3286452f23"),
createdBy: ObjectId("636c1778f1d09191074f9690"),
"weights.weight": 12,
"weights.spotsAvailable.userId": "empty"
}
},
{
"$unwind": "$weights"
},
{
"$addFields": {
"results": {
"$reduce": {
"input": "$weights.spotsAvailable",
"initialValue": {
result: [],
updated: false
},
"in": {
"$cond": {
"if": {
$and: [
{
$eq: [
false,
"$$value.updated"
]
},
{
$eq: [
"empty",
"$$this.userId"
]
}
]
},
"then": {
result: {
"$concatArrays": [
"$$value.result",
[
{
"name": "Wayne Wrestler",
"userId": ObjectId("636c1778f1d09191074f9690")
}
]
]
},
updated: true
},
"else": {
result: {
"$concatArrays": [
"$$value.result",
[
"$$this"
]
]
},
updated: "$$value.updated"
}
}
}
}
}
}
},
{
$set: {
"weights.spotsAvailable": "$results.result",
"results": "$$REMOVE"
}
},
{
$group: {
_id: "$_id",
"name": {
$first: "$name"
},
"createdBy": {
$first: "$createdBy"
},
"description": {
$first: "$description"
},
"date": {
$first: "$date"
},
"location": {
$first: "$location"
},
"weights": {
$push: "$weights"
},
"eventApplicants": {
$first: "$eventApplicants"
}
}
},
{
"$merge": {
"into": "collection",
"on": "_id"
}
}
])
Mongo Playground

Mongo Query to get a result

My mongo collection name tests and whose having the following documents in it.
[
{
"title": "One",
"uid": "1",
"_metadata": {
"references": [
{
"uid": "2"
},
{
"asssetuid": 10
}
]
}
},
{
"title": "Two",
"uid": "2",
"_metadata": {
"references": [
{
"uid": "3"
},
{
"asssetuid": 11
}
]
}
},
{
"title": "Three",
"uid": "3",
"_metadata": {
"references": []
}
}
]
And I want the result in the following format (for uid:1)
[
{
"title": "One",
"uid": 1,
"_metadata": {
"references": [
{
"asssetuid": 10
},
{
"asssetuid": 11
},
{
"title": "Two",
"uid": "2",
"_metadata": {
"references": [
{
"title": "Three",
"uid": "3"
}
]
}
}
]
}
}
]
for uid:2 I want the following result
[
{
"title": "Two",
"uid": 2,
"_metadata": {
"references": [
{
"asssetuid": 11
},
{
"title": "Three",
"uid": "3"
}
]
}
}
]
Which query I used here to get a respected result. according to its uid. here I want the result in the parent-child relationship. is this possible using MongoDB graph lookup query or any other query that we can use to get the result. Please help me with this.
New Type Output
[{
"title": "One",
"uid": 1,
"_metadata": {
"assets": [{
"asssetuid": 10,
"parent": 1
}, {
"asssetuid": 11,
"parent": 2
}],
"entries": [{
"title": "Two",
"uid": "2",
"parent": 1
}, {
"title": "Three",
"uid": "3",
"parent": 2
}]
}
}]
Mongo supports the automatic reference resolution using $ref but for that, you need to change your schema a little and resolve resolution is only supported by some drivers.
You need to store your data in this format:
[
...
{
"_id": ObjectId("5a934e000102030405000000"),
"_metadata": {
"references": [
{
"$ref": "collection",
"$id": ObjectId("5a934e000102030405000001"),
"$db": "database"
},
{
"asssetuid": 10
}
]
},
"title": "One",
"uid": "1"
},
....
]
For more details on $ref refer to official documentation: label-document-references
OR
you can resolve the reference using the $graphLookup but the only problem with the $graphlookup is that you will lose the assetuid. Here is the query and it will resolve references and give output in flat map
db.collection.aggregate([
{
$match: {
uid: "1"
}
},
{
$graphLookup: {
from: "collection",
startWith: "$_metadata.references.uid",
connectFromField: "_metadata.references.uid",
connectToField: "uid",
depthField: "depth",
as: "resolved"
}
},
{
"$addFields": {
"references": "$resolved",
"metadata": [
{
"_metadata": "$_metadata"
}
]
}
},
{
"$project": {
"references._metadata": 0,
}
},
{
"$project": {
"references": "$references",
"merged": {
"$concatArrays": [
"$metadata",
"$resolved"
]
}
}
},
{
"$project": {
results: [
{
merged: "$merged"
},
{
references: "$references"
}
]
}
},
{
"$unwind": "$results"
},
{
"$facet": {
"assest": [
{
"$match": {
"results.merged": {
"$exists": true
}
}
},
{
"$unwind": "$results.merged"
},
{
"$unwind": "$results.merged._metadata.references"
},
{
"$match": {
"results.merged._metadata.references.asssetuid": {
"$exists": true
}
}
},
{
"$project": {
_id: 0,
"asssetuid": "$results.merged._metadata.references.asssetuid"
}
}
],
"uid": [
{
"$match": {
"results.references": {
"$exists": true
}
}
},
{
"$unwind": "$results.references"
},
{
$replaceRoot: {
newRoot: "$results.references"
}
}
]
}
},
{
"$project": {
"references": {
"$concatArrays": [
"$assest",
"$uid"
]
}
}
}
])
Here is the link to the playground to test it: Mongo Playground

Mongodb apply condition on $lookup fetched data

I am having data from two different collections on which I am applying $lookup with $match which is working fine. What I am trying to do is on lookup's result I am applying $match again which is having some conditions. Here is the collection of User on which I am applying lookup.
Users collection :-
{
"_id": {
"$oid": "5b714631faaae220d7cc07cf"
},
"name": "abc",
"surname": "xyz",
"email": "abc#gmail.com",
"hotel_data": [
{
"location": {
"type": "Point",
"coordinates": [
30.7052881,
76.84470799999997
]
},
"_id": {
"$oid": "5b7fb8559849fd485dc47240"
},
"status": true,
"activityname": "Sparrow",
},
{
"location": {
"type": "Point",
"coordinates": [
30.733315,
76.779419
]
},
"_id": {
"$oid": "5b7f9ecb9960053dac7ce6f1"
},
"status": true,
"activityname": "Raj Hotel",
},
]
}
and this is my availabilities collection on which I am applying $lookup with $match.
{
"_id": {
"$oid": "5b867766d63c4e2cdd5534d2"
},
"businessid": {
"$oid": "5b7fb8559849fd485dc47240"
},
"userid": {
"$oid": "5b714631faaae220d7cc07cf"
},
"hotel_filters": [
{
"_id": {
"$oid": "5b867766d63c4e2cdd5534d3"
},
"hotelservice": [
{
"service_id": "5b472fff25556c3f02a875aa",
"service_name": "Pool",
"_id": {
"$oid": "5b867767d63c4e2cdd5534d7"
}
},
{
"service_id": "5b472fdb25556c3f02a875a9",
"service_name": "AIR",
"_id": {
"$oid": "5b867767d63c4e2cdd5534d8"
}
}
],
"location_type": [
{
"locationtype_id": "5b18f4d08c63f42019763b12",
"locationtype_name": "Scenic View",
"_id": {
"$oid": "5b867767d63c4e2cdd5534d5"
}
},
{
"locationtype_id": "5b18f4e38c63f42019763b13",
"locationtype_name": "Central",
"_id": {
"$oid": "5b867767d63c4e2cdd5534d6"
}
}
],
"hotel_type": [
{
"hoteltype_id": "5b081452edefe23318834a28",
"hoteltype_name": "3 Star",
"_id": {
"$oid": "5b867767d63c4e2cdd5534d4"
}
}
]
}
]
}
and what I am trying to do is- after $lookup I only want those availabilities data which are having "hotelservice.service_id" and "location_type.locationtype_id" that I will pass. And I have tried this by applying this query to it(In this query I have only applied "hotelservice.service_id" but later on I also want to apply query with $and on "location_type.locationtype_id").
User.aggregate([
{
$match:{
"hotel_data.location": {
"$geoWithin": {
"$centerSphere": [
[30.7052881, 76.84470799999997], 50/ 6371
]
}
}
}
},{
"$unwind": "$hotel_data"
},
{
$match:{
"hotel_data.location": {
"$geoWithin": {
"$centerSphere": [
[30.7052881, 76.84470799999997], 50/ 6371
]
}
}
}
},
{
$lookup: {
from: "availabilities",
localField: "hotel_data._id",
foreignField: "businessid",
as: "availabilitiesdata"
}
},
{$match:{$and: [{"availabilitiesdata.hotel_filters.hotelservice.service_id":{$in:[5b472fb725556c3f02a875a8]}}]}}
], function(err, data) {
if (err) {
return res.send({data: err, status: false, msg:"No Hotel Found" });
}else{
return res.send({status: true, msg:"Hotel Found", data:data });
}
});
This query gives me result as follow.
{
"status": true,
"msg": "Hotel Found",
"data": [
{
"_id": "5b714631faaae220d7cc07cf",
"name": "abc",
"surname": "xyz",
"email": "abc#gmail.com",
"hotel_data": {
"location": {
"type": "Point",
"coordinates": [
30.7052881,
76.84470799999997
]
},
"_id": "5b7fb8559849fd485dc47240",
"status": true,
"activityname": "Sparrow",
},
"availabilitiesdata": [
{
"_id": "5b864fe68ab0b71f4f28021e",
"businessid": "5b7fb8559849fd485dc47240",
"userid": "5b714631faaae220d7cc07cf",
"hotel_filters": [
{
"_id": "5b864fe78ab0b71f4f28021f",
"hotelservice": [
{
"service_id": "5b472fb725556c3f02a875a8",
"service_name": "Parking",
"_id": "5b864fe78ab0b71f4f280223"
},
{
"service_id": "5b472fff25556c3f02a875aa",
"service_name": "Pool",
"_id": "5b864fe78ab0b71f4f280224"
}
],
"location_type": [
{
"locationtype_id": "5b18f4798c63f42019763b11",
"locationtype_name": "Quiet",
"_id": "5b864fe78ab0b71f4f280221"
},
{
"locationtype_id": "5b18f4e38c63f42019763b13",
"locationtype_name": "Central",
"_id": "5b864fe78ab0b71f4f280222"
}
],
"hotel_type": [
{
"hoteltype_id": "5b0813e2edefe23318834a27",
"hoteltype_name": "5 Star",
"_id": "5b864fe78ab0b71f4f280220"
}
]
}
]
},
{
"_id": "5b867766d63c4e2cdd5534d2",
"businessid": "5b7fb8559849fd485dc47240",
"userid": "5b714631faaae220d7cc07cf",
"hotel_filters": [
{
"_id": "5b867766d63c4e2cdd5534d3",
"hotelservice": [
{
"service_id": "5b472fff25556c3f02a875aa",
"service_name": "Pool",
"_id": "5b867767d63c4e2cdd5534d7"
},
{
"service_id": "5b472fdb25556c3f02a875a9",
"service_name": "AIR",
"_id": "5b867767d63c4e2cdd5534d8"
}
],
"location_type": [
{
"locationtype_id": "5b18f4d08c63f42019763b12",
"locationtype_name": "Scenic View",
"_id": "5b867767d63c4e2cdd5534d5"
},
{
"locationtype_id": "5b18f4e38c63f42019763b13",
"locationtype_name": "Central",
"_id": "5b867767d63c4e2cdd5534d6"
}
],
"hotel_type": [
{
"hoteltype_id": "5b081452edefe23318834a28",
"hoteltype_name": "3 Star",
"_id": "5b867767d63c4e2cdd5534d4"
}
]
}
]
}
]
}
]}
but what I want is something like this.
{
"status": true,
"msg": "Hotel Found",
"data": [
{
"_id": "5b714631faaae220d7cc07cf",
"name": "abc",
"surname": "xyz",
"email": "abc#gmail.com",
"hotel_data": {
"location": {
"type": "Point",
"coordinates": [
30.7052881,
76.84470799999997
]
},
"_id": "5b7fb8559849fd485dc47240",
"status": true,
"activityname": "Sparrow",
},
"availabilitiesdata": [
{
"_id": "5b864fe68ab0b71f4f28021e",
"businessid": "5b7fb8559849fd485dc47240",
"userid": "5b714631faaae220d7cc07cf",
"hotel_filters": [
{
"_id": "5b864fe78ab0b71f4f28021f",
"hotelservice": [
{
"service_id": "5b472fb725556c3f02a875a8",
"service_name": "Parking",
"_id": "5b864fe78ab0b71f4f280223"
},
{
"service_id": "5b472fff25556c3f02a875aa",
"service_name": "Pool",
"_id": "5b864fe78ab0b71f4f280224"
}
],
"location_type": [
{
"locationtype_id": "5b18f4798c63f42019763b11",
"locationtype_name": "Quiet",
"_id": "5b864fe78ab0b71f4f280221"
},
{
"locationtype_id": "5b18f4e38c63f42019763b13",
"locationtype_name": "Central",
"_id": "5b864fe78ab0b71f4f280222"
}
],
"hotel_type": [
{
"hoteltype_id": "5b0813e2edefe23318834a27",
"hoteltype_name": "5 Star",
"_id": "5b864fe78ab0b71f4f280220"
}
]
}
]
}
]
}
]}
I want only those availabilities which match the condition on my $lookup availability data. can someone help me out.
You can use $lookup pipeline variant to apply the $match inside the joined collection in 3.6
Something like ( replace $lookup & $match stage with below lookup pipeline )
{"$lookup":{
"from":"availabilities",
"let":{"hotel_data_id":"$hotel_data._id"},
"pipeline":[
{"$match":{
"hotel_filters.hotelservice.service_id":{"$in":["5b472fb725556c3f02a875a8"]},
"hotel_filters.location_type.locationtype_id":{"$in":["5b18f4798c63f42019763b11"]},
"$expr":{"$eq":["$$hotel_data_id","$businessid"]}
}}
],
"as":"availabilitiesdata"
}}

nodejs + mongodb error exception: FieldPath 'progress' doesn't start with $

I'm trying to modify the second pipeline from this query (which I got from here nodejs + mongoose - query aggregate
db.todos.aggregate([
{
"$group": {
"_id": "$pic",
"open_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "open" ] }, 1, 0 ]
}
},
"progress_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "progress" ] }, 1, 0 ]
}
},
"done_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "done" ] }, 1, 0 ]
}
},
"archive_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "archive" ] }, 1, 0 ]
}
}
}
},
{
"$group": {
"_id": "$_id",
"detail": {
"$push": {
"name": "open",
"$todos": "$open_count"
},
"$push": {
"name": "progress",
"$todos": "$progress_count"
},
"$push": {
"name": "done",
"$todos": "$done_count"
},
"$push": {
"name": "archive",
"$todos": "$archive_count"
}
}
}
},
{
"$project": {
"_id": 0, "pic": "$_id", "detail": 1
}
}
])
I want this kind of JSON structure so I can put it on google chart, which the format is like this:
[
{
"pic": "A",
"detail": [
{
"name": "open",
"todos": 2
},
{
"name": "progress",
"todos": 1
},
{
"name": "done",
"todos": 8
},
{
"name": "archive",
"todos": 20
}
],
"pic": "B",
"detail": [
{
"name": "open",
"todos": 5
},
{
"name": "progress",
"todos": 2
},
{
"name": "done",
"todos": 5
},
{
"name": "archive",
"todos": 10
}
],
}
]
But I got this error
exception: FieldPath 'progress' doesn't start with $
Try with this aggregation query:
db.todos.aggregate([
{
"$group": {
"_id": {
"pic": "$pic",
"name": "$status"
},
"todos": {
"$sum": 1
}
}
},
{
"$project": {
"_id": 0,
"pic": "$_id.pic",
"detail": {
"name": "$_id.name",
"todos": "$todos"
}
}
},
{
"$group": {
"_id": "$pic",
"detail": {
"$push": "$detail"
}
}
},
{
"$project": {
"_id": 0, "pic": "$_id", "detail": 1
}
}])

Resources