Related
I have users, user_followers, and posts document.
I want to get a collection of the post posted by the followed users.
This is what I have thought I am not sure if it's the right way.
UserFollowers.find({'user_id': req.decoded._id}, function(err, data) {
userFollowers = data;
});
Post.paginate({'user_id': userFollowers.follower_id}, {page: page, limit: limit}, function(err, Posts) {
this the desired result.
{
"success": true,
"message": "Post found",
"data": {
"docs": [
{
"user_id": "5da5b54c90a96338cc847818",
"_id": "5daedc59fdce1029bb02f6c5",
"created_at": "2019-10-22T10:39:21.866Z",
"picture": "YfeB9lHzc9m5ScAmFcMLDTC27DNjPx2Y.png",
"caption": "Hello world",
"__v": 1,
"comments": [
{
"created_at": "2019-10-22T18:16:01.397Z",
"_id": "5daf4761fa981f794665a76c",
"comment": "testing comment",
"postedBy": "5d9ea2ae8a8af92d2102c129"
}
],
"likes": [
{
"_id": "5db0568881630d23c22fa11b",
"likedBy": "5d9ea94f8a8af92d2102c12a"
}
]
}
]
}
When i combine 2 table to fetch data from mongoDB collection struck with my expected out. please any one help me to fix the same pleas.
Collection:
recipecatagories
{
"_id":{"$oid":"5dada3c5761bb32a1201d4da"},
"CategoryName":"Biryani"
}
{
"_id":{"$oid":"5dada3c5761bb32a1201d4db"},
"CategoryName":"Mutton Biryani"
}
{
"_id":{"$oid":"5dada3c5761bb32a1201d4d4"},
"CategoryName":"Chicken Biryani"
}
{
"_id":{"$oid":"5daea43a517cf601a7e80a3b"},
"CategoryName":"Kathirikai gothsu"
}
recipes:
{
"_id":{"$oid":"5daffda85d9b4fd19ae4da30"},
"recipeTitle":"Mutton dum biryani",
"Recipetags":["Indian","NonVeg","Lunch"],
"cookTime":"30 Mins",
"recipeCategoryId":[{"$oid":"5dada3c5761bb32a1201d4da"},{"$oid":"5dada3c5761bb32a1201d4db"},{"$oid":"5dada3c5761bb32a1201d4dc"}],
"recipeCuisienId":"Indian",
"recepeType":false,
"availaleStreaming":"TEXT",
"postedOn":{"$date":{"$numberLong":"0"}},
"postedBy":"shiva#yopmail.com"
}
{
"_id":{"$oid":"5daffda85d9b4fd19ae4da30"},
"recipeTitle":"Mutton Chicken biryani",
"Recipetags":["Indian","NonVeg","Lunch"],
"cookTime":"30 Mins",
"recipeCategoryId":[{"$oid":"5dada3c5761bb32a1201d4da"},{"$oid":"5dada3c5761bb32a1201d4d4"},{"$oid":"5dada3c5761bb32a1201d4dc"}],
"recipeCuisienId":"Indian",
"recepeType":false,
"availaleStreaming":"TEXT",
"postedOn":{"$date":{"$numberLong":"0"}},
"postedBy":"shiva#yopmail.com"
}
users:
{
"_id":{"$oid":"5da428b85e3cbd0f153c7f3b"},
"emailId":"shiva#yopmail.com",
"fullName":"siva prakash",
"accessToken":"xxxxxxxxxxxxx",
"__v":{"$numberInt":"0"}
}
Current mongoose code in node js
RecipeCatagory.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(id) } },
{
"$lookup": {
"from": "recipes",
"localField": "_id",
"foreignField": "recipeCategoryId",
"as": "recipes"
}
},
{ "$unwind": "$recipes" },
{ "$unwind": "$recipes.recipeCategoryId" },
{
"$match": {
"recipes.recipeCategoryId": mongoose.Types.ObjectId(id)
}
},
{
"$lookup": {
"from": "users",
"localField": "emailId",
"foreignField": "recipes.postedBy",
"as": "users"
}
},
])
.exec(function (err, recipes) {
if (err) {
response
.status(400)
.json({
"status": "Failed",
"message": "Error",
"data": err | err.message
});
return
} else {
response
.status(200)
.json({
"status": "Ok",
"message": "Success",
"data": recipes
});
return
}
})
Current Output using above Query
{
"status": "Ok",
"message": "Success",
"data": [
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "Biryani",
"recipes": {
"_id": "5daffda85d9b4fd19ae4da30",
"recipeTitle": "Mutton dum biryani",
"Recipetags": [
"Indian",
"NonVeg",
"Lunch"
],
"cookTime": "30 Mins",
"recipeCategoryId": "5dada3c5761bb32a1201d4da",
"recipeCuisienId": "Indian",
"recepeType": false,
"availaleStreaming": "TEXT",
"postedOn": "1970-01-01T00:00:00.000Z",
"postedBy": "shiva#yopmail.com"
},
"users": [
{
"_id": "5da428b85e3cbd0f153c7f3b",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "42eb19a0-ee57-11e9-86f7-a7b758fb7271",
"__v": 0
}
]
},
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "Biryani",
"recipes": {
"_id": "5daffda85d9b4fd19ae4da31",
"recipeTitle": "Kumbakonam kathirikai gothsu",
"Recipetags": [
"Indian",
"Veg"
],
"cookTime": "30 Mins",
"recipeCategoryId": "5dada3c5761bb32a1201d4da",
"recipeCuisienId": "Indian",
"recepeType": true,
"availaleStreaming": "TEXT",
"postedOn": "1970-01-01T00:00:00.000Z",
"postedBy": "shiva#yopmail.com"
},
"users": [
{
"_id": "5da428b85e3cbd0f153c7f3b",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "xxxxxxxxxxxxx",
"__v": 0
}
]
}
]
}
**Expected Out:**
{
"status": "Ok",
"message": "Success",
"data": [
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "chiken Biryani",
"recipes": {
"_id": "5daffda85d9b4fd19ae4da30",
"recipeTitle": "Mutton dum biryani",
"Recipetags": [
"Indian",
"NonVeg",
"Lunch"
],
"cookTime": "30 Mins",
"recipeCategoryId": "5dada3c5761bb32a1201d4da",
"recipeCuisienId": "Indian",
"recepeType": false,
"availaleStreaming": "TEXT",
"postedOn": "1970-01-01T00:00:00.000Z",
"postedBy": "shiva#yopmail.com"
},
"users": [
{
"_id": "5da428b85e3cbd0f153c7f3b",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "42eb19a0-ee57-11e9-86f7-a7b758fb7271",
"__v": 0
}
]
},
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "Biryani",
"recipes": [
{
"_id": "5daffda85d9b4fd19ae4da31",
"recipeTitle": "Mutton dum biryani",
"Recipetags": [
"Indian",
"Veg"
],
"cookTime": "30 Mins",
"recipeCategoryId": "5dada3c5761bb32a1201d4da",
"recipeCuisienId": "Indian",
"recepeType": true,
"availaleStreaming": "TEXT",
"postedOn": "1970-01-01T00:00:00.000Z",
"postedBy": "shiva#yopmail.com"
},
{
"_id": "5daffda85d9b4fd19ae4da31",
"recipeTitle": "Chicken biryani",
"Recipetags": [
"Indian",
"Veg"
],
"cookTime": "30 Mins",
"recipeCategoryId": "5dada3c5761bb32a1201d4da",
"recipeCuisienId": "Indian",
"recepeType": true,
"availaleStreaming": "TEXT",
"postedOn": "1970-01-01T00:00:00.000Z",
"postedBy": "shiva#yopmail.com"
}
],
"users": [
{
"_id": "5da428b85e3cbd0f153c7f3b",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "xxxxxxxxxxxx",
"__v": 0
}
]
}
]
}
i am struck to get expected out put... i want recipes as array which has recipecategory has in recipe collection...
You are basically doing this the wrong way around and should instead be querying from the Recipe model. You do already have the "category id values" which are contained within an array of that document.
Basically you should have something like this:
const wantedCategories = [
ObjectId("5dada3c5761bb32a1201d4da"),
ObjectId("5dada3c5761bb32a1201d4db")
];
let data = await Recipe.aggregate([
// Match wanted category(ies)
{ "$match": {
"recipeCategoryId": { "$in": wantedCategories }
}},
// Filter the content of the array
{ "$addFields": {
"recipeCategoryId": {
"$filter": {
"input": "$recipeCategoryId",
"cond": {
"$in": [ "$$this", wantedCategories ]
}
}
}
}},
// Lookup the related matching category(ies)
{ "$lookup": {
"from": RecipeCategory.collection.name,
"let": { "recipeCategoryIds": "$recipeCategoryId" },
"pipeline": [
{ "$match": {
"$expr": { "$in": [ "$_id", "$$recipeCategoryIds" ] }
}}
],
"as": "recipeCategoryId"
}},
// Lookup the related user to postedBy
{ "$lookup": {
"from": User.collection.name,
"let": { "postedBy": "$postedBy" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$emailId", "$$postedBy" ] } } }
],
"as": "postedBy"
}},
// postedBy is "singular"
{ "$unwind": "$postedBy" }
]);
Which would return a result like this:
{
"data": [
{
"_id": "5dbce992010163139853168c",
"Recipetags": [
"Indian",
"NonVeg",
"Lunch"
],
"recipeCategoryId": [
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "Biryani",
"__v": 0
},
{
"_id": "5dada3c5761bb32a1201d4db",
"CategoryName": "Mutton Biryani",
"__v": 0
}
],
"recipeTitle": "Mutton dum biryani",
"cookTime": "30 Mins",
"recepeType": false,
"postedBy": {
"_id": "5dbce992010163139853168e",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "xxxxxxxxxxxxx",
"__v": 0
},
"__v": 0
},
{
"_id": "5dbce992010163139853168d",
"Recipetags": [
"Indian",
"NonVeg",
"Lunch"
],
"recipeCategoryId": [
{
"_id": "5dada3c5761bb32a1201d4da",
"CategoryName": "Biryani",
"__v": 0
}
],
"recipeTitle": "Mutton Chicken biryani",
"cookTime": "30 Mins",
"recepeType": false,
"postedBy": {
"_id": "5dbce992010163139853168e",
"emailId": "shiva#yopmail.com",
"fullName": "siva prakash",
"accessToken": "xxxxxxxxxxxxx",
"__v": 0
},
"__v": 0
}
]
}
Note: I do actually correct the english spelling of a model with RecipeCategory instead of RecipeCatagory as shown in the question. Apply to your own implementation as you wish.
You might note the usage of $in with a "list of ids" in both the query form and the aggregation operator form at different stages. Personally I would code this in this way even if there was only a single value supplied at the present time, since it means there would be little to change other than the input variable to the method in the event I wanted multiple values, such as "multiple categories" within a faceted search option for example.
So this demonstrates the "list" argument approach, though it still applies to singular values as in the question.
The whole process follows what the comments say on each pipeline stage, being that you first match wanted "documents" from the recipes collection by the selected "category" value(s). Then we just want to remove any non-matching data for the category within the array of those documents. This could actually be viewed as "optional" if you wanted to just show ALL categories associated with that recipe whether they matched the criteria or not. Where this is the case, all you need do is remove the stage containing the $filter statement, and the code will happily work in that way.
Then of course there are the $lookup stages, being one for each related collection. The example here actually shows the expressive form of the $lookup pipeline stage. This again is really only for demonstration as the standard localField and foreignField form is perfectly fine for the purposes of what you want to do here, and the further syntax is not needed. MongoDB will basically transform that older syntax into the newer expressive form as shown internally anyway.
You might note the usage of Model.collection.name in the from argument though. This is actually a handy thing to do when coding with mongoose. MongoDB itself expects the actual collection name as the argument here. Since mongoose will typically pluralize the model name provided for the actual collection referenced, or otherwise takes an explicit argument to the model definition, then using the .collection.name accessor from the model ensures you have the correct actual collection name, even if this changes at some time within the model definition.
The only other simple step here is the $unwind at the end, and only because the output of $lookup is always an array, and here the replacement of the postedBy property with matched related content is always expected to be only one item. So for simple readability of results, we can just make this a single value instead of having an array here.
For a bit more context into how that all comes together, here is the code for the statement and the creation of the data all in a self contained listing, from which of course the "output" posted above was actually obtained:
const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');
const uri = 'mongodb://localhost:27017/menu';
const options = { useNewUrlParser: true, useUnifiedTopology: true };
mongoose.set('debug', true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
const recipeCategorySchema = new Schema({
CategoryName: String
});
const recipeSchema = new Schema({
recipeTitle: String,
Recipetags: [String],
cookTime: String,
recipeCategoryId: [{ type: Schema.Types.ObjectId, ref: 'RecipeCategory' }],
recipeCuisineId: String,
recepeType: Boolean,
availableStreaming: String,
postedBy: String
});
const userSchema = new Schema({
emailId: String,
fullName: String,
accessToken: String
});
const RecipeCategory = mongoose.model('RecipeCategory', recipeCategorySchema);
const Recipe = mongoose.model('Recipe', recipeSchema);
const User = mongoose.model('User', userSchema);
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const conn = await mongoose.connect(uri, options);
// Clean data for demonstration
await Promise.all(
Object.values(conn.models).map(m => m.deleteMany())
);
// Insert some data
await RecipeCategory.insertMany([
{
"_id": ObjectId( "5dada3c5761bb32a1201d4da"),
"CategoryName":"Biryani"
},
{
"_id": ObjectId("5dada3c5761bb32a1201d4db"),
"CategoryName":"Mutton Biryani"
},
{
"_id": ObjectId("5dada3c5761bb32a1201d4d4"),
"CategoryName":"Chicken Biryani"
},
{
"_id": ObjectId("5daea43a517cf601a7e80a3b"),
"CategoryName":"Kathirikai gothsu"
}
]);
await Recipe.insertMany([
{
"recipeTitle":"Mutton dum biryani",
"Recipetags":["Indian","NonVeg","Lunch"],
"cookTime":"30 Mins",
"recipeCategoryId":[
ObjectId("5dada3c5761bb32a1201d4da"),
ObjectId("5dada3c5761bb32a1201d4db"),
ObjectId("5dada3c5761bb32a1201d4dc")
],
"recipeCuisienId":"Indian",
"recepeType":false,
"availaleStreaming":"TEXT",
"postedOn": new Date(),
"postedBy":"shiva#yopmail.com"
},
{
"recipeTitle":"Mutton Chicken biryani",
"Recipetags":["Indian","NonVeg","Lunch"],
"cookTime":"30 Mins",
"recipeCategoryId":[
ObjectId("5dada3c5761bb32a1201d4da"),
ObjectId("5dada3c5761bb32a1201d4d4"),
ObjectId("5dada3c5761bb32a1201d4dc")
],
"recipeCuisienId":"Indian",
"recepeType":false,
"availaleStreaming":"TEXT",
"postedOn": new Date(),
"postedBy":"shiva#yopmail.com"
}
]);
await User.create({
"emailId":"shiva#yopmail.com",
"fullName":"siva prakash",
"accessToken":"xxxxxxxxxxxxx",
});
const wantedCategories = [
ObjectId("5dada3c5761bb32a1201d4da"),
ObjectId("5dada3c5761bb32a1201d4db")
];
let data = await Recipe.aggregate([
// Match wanted category(ies)
{ "$match": {
"recipeCategoryId": { "$in": wantedCategories }
}},
// Filter the content of the array
{ "$addFields": {
"recipeCategoryId": {
"$filter": {
"input": "$recipeCategoryId",
"cond": {
"$in": [ "$$this", wantedCategories ]
}
}
}
}},
// Lookup the related matching category(ies)
{ "$lookup": {
"from": RecipeCategory.collection.name,
"let": { "recipeCategoryIds": "$recipeCategoryId" },
"pipeline": [
{ "$match": {
"$expr": { "$in": [ "$_id", "$$recipeCategoryIds" ] }
}}
],
"as": "recipeCategoryId"
}},
// Lookup the related user to postedBy
{ "$lookup": {
"from": User.collection.name,
"let": { "postedBy": "$postedBy" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$emailId", "$$postedBy" ] } } }
],
"as": "postedBy"
}},
// postedBy is "singular"
{ "$unwind": "$postedBy" }
]);
log({ data });
} catch (e) {
console.error(e)
} finally {
mongoose.disconnect();
}
})()
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"
}}
I'm fairly new to mongoose and I am trying to write a query that just returns a subdocument within my main document.
My document structure is like this:
PROFILE
- CONTACTS [ARRAY]
I've written the following endpoint to return one contact, where I pass the profile id and the id of the subdocument:
"/:owerId/getcontact/:contactId"
My Express function code looks like this:
router.route('/:ownerId/getcontact/:contactId')
.get(function(req, res){
Profile.findOne({'owner_id':req.params.ownerId}).then(function(err, profile){
if(err)
res.send(err);
profile.findOne({'_id':req.params.contactId}).then(function(err, contact){
if(err)
res.send(err);
res.json(contact);
})
})
})
But this code returns the whole profile.
When I try to get by ID it does the same thing. Here is my code and just below that is the entire profile that is returned:
router.route('/:ownerId/getcontact/:contactId')
.get(function(req, res){
Profile.findById(req.params.ownerId).then(function(err, profile){
if(err)
res.send(err);
var data = profile.contacts.id(req.params.contactId);
res.json(data);
})
})
{
"_id": "5886e3692ca4542c453431ee",
"initial": "u",
"last_name": "randal",
"first_name": "chris",
"owner_id": "5886e32c2ca4542c453431ed",
"__v": 1,
"contacts": [
{
"last_name": "Eltoro",
"first_name": "Peter",
"_id": "5886e3f5a219472cac886a9f",
"businesses": [],
"addresses": [],
"phones": [
{
"phone_type": "mobile",
"phone_number": "555-555-999",
"_id": "5886e3f5a219472cac886aa2"
},
{
"phone_type": "home",
"phone_number": "999-876-000",
"_id": "5886e3f5a219472cac886aa1"
}
],
"emails": [
{
"email_address": "tim#time.com",
"_id": "5886e3f5a219472cac886aa0"
}
]
},
{
"college": "University of WA",
"highschool": "Kalaheo",
"birthday": "1990-12-22T08:00:00.000Z",
"last_name": "Smith",
"first_name": "Suzanne",
"_id": "5886fb7fac288c2e31eabe0a",
"businesses": [],
"addresses": [
{
"zip": "98777",
"state": "WA",
"city": "Seattle",
"address_2": "Apt 234",
"address": "124 194th St. SW",
"_id": "5886fb7fac288c2e31eabe0e"
}
],
"phones": [
{
"phone_type": "mobile",
"phone_number": "206-899-9898",
"_id": "5886fb7fac288c2e31eabe0d"
},
{
"phone_type": "home",
"phone_number": "206-789-0987",
"_id": "5886fb7fac288c2e31eabe0c"
}
],
"emails": [
{
"email_type": "personal",
"email_address": "suzanne#smith.com",
"_id": "5886fb7fac288c2e31eabe0b"
}
]
},
{
"last_name": "Alabaster",
"first_name": "Cindy",
"_id": "5888b0bd3c025e54476828d9",
"businesses": [],
"addresses": [],
"phones": [
{
"phone_type": "home",
"phone_number": "999-999-0909",
"_id": "5888b0bd3c025e54476828dc"
},
{
"phone_type": "home",
"phone_number": "000-000-0000",
"_id": "5888b0bd3c025e54476828db"
}
],
"emails": [
{
"email_address": "Some#them.com",
"_id": "5888b0bd3c025e54476828da"
}
]
}
],
"businesses": [],
"addresses": [],
"phones": [
{
"phone_number": "999-999-9999",
"phone_type": "mobile",
"_id": "5886e37f2ca4542c453431ef"
}
],
"emails": []
}
Mongoose sub documents have special methods to retrieve them:
router
.route('/:ownerId/getcontact/:contactId')
.get(function(req, res) {
Profile.findOne({ owner_id: req.params.ownerId }, function(err, profile) {
if ( err ) {
res.send(err);
}
var contact = profile.contacts.id(req.params.contactId);
res.json(contact);
});
});
See Mongoose Sub Documents.
Another route is through the aggregation framework where you can use the $match, $arrayElemAt and $filter operators to return the data you want.
Consider running the following aggregate operation in your API endpoint implementation:
router.route('/:ownerId/getcontact/:contactId')
.get(function(req, res){
var ownerId = mongoose.Schema.Types.ObjectId(req.params.ownerId),
contactId = mongoose.Schema.Types.ObjectId(req.params.contactId);
Profile.aggregate([
{ "$match": { "owner_id": ownerId } },
{
"$project": {
"contact": {
"$arrayElemAt": [
{
"$filter": {
"input": "$contacts",
"as": "contact",
"cond": { "$eq": ["$$contact._id", contactId] }
}
},
0
]
}
}
}
]).exec(function(err, profile){
if(err) res.send(err);
res.json(profile[0].contact);
})
})
Cloudant is returning error message:
{"error":"invalid_key","reason":"Invalid key use-index for this request."}
whenever I try to query against an index with the combination operator, "$or".
A sample of what my documents look like is:
{
"_id": "28f240f1bcc2fbd9e1e5174af6905349",
"_rev": "1-fb9a9150acbecd105f1616aff88c26a8",
"type": "Feature",
"properties": {
"PageName": "A8",
"PageNumber": 1,
"Lat": 43.051523,
"Long": -71.498852
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-71.49978935969642,
43.0508382914137
],
[
-71.49978564033566,
43.052210148524
],
[
-71.49791499857444,
43.05220740550381
],
[
-71.49791875962663,
43.05083554852429
],
[
-71.49978935969642,
43.0508382914137
]
]
]
}
}
The index that I created is for field "properties.PageName", which works fine when I'm just querying for one document, but as soon as I try for multiple ones, I would receive the error response as quoted in the beginning.
If it helps any, here is the call:
POST https://xyz.cloudant.com/db/_find
request body:
{
"selector": {
"$or": [
{ "properties.PageName": "A8" },
{ "properties.PageName": "M30" },
{ "properties.PageName": "AH30" }
]
},
"use-index": "pagename-index"
}
In order to perform an $or query you need to create a text (full text) index, rather than a json index. For example, I just created the following index:
{
"index": {
"fields": [
{"name": "properties.PageName", "type": "string"}
]
},
"type": "text"
}
I was then be able to perform the following query:
{
"selector": {
"$or": [
{ "properties.PageName": "A8" },
{ "properties.PageName": "M30" },
{ "properties.PageName": "AH30" }
]
}
}