Populate Virtuals - populate the existing array its referencing . - node.js

Is it possible to use mongoose's Populate Virtuals to populate the array of values it is referencing?
User Model
var userSchema = new mongoose.Schema({
auth_key: {type: String},
channels: [{
name: {type: String},
last_access: { type: Date, default: Date.now },
status: {type: String, default: false },
new_messages: {type: Number},
user_channel_group: {type: String}
}]
}, {
toJSON: {
virtuals: true
}
});
userSchema.virtual('channels.details', {
ref: 'channel',
localField: 'channels.name',
foreignField: 'name'
});
var user = mongoose.model('user', userSchema, 'user');
Channel Model
var channelSchema = new mongoose.Schema({
name: {type: String, required: true},
members: [{ type: String, required: true }],
displayName: {type: String},
type: {type: Number},
create_at: {type: Date, default: Date.now}
});
var channel = mongoose.model('channel', channelSchema, 'channel');
My query that I am using I want the virtual to present the channel details from each channel alongside the channels a user is subscribed to. so something like this..
[{
"_id": "588a82ff7fe89686fd2210b0",
"channels": [{
"details": {
"_id": "588a80fd7fe89686fd2210a8",
"name": "channel1",
"members": []
},
"name": "channel1"
}, {
"details": {
"_id": "588a80fd7fe89686fd2210a9",
"name": "channel2",
"members": []
},
"name": "channel2"
}],
"id": "588a82ff7fe89686fd2210b0"
}
...
]
My Query for this is ..
user.find({}).populate('channels.details').exec()

Related

Building Mongoose models for an Express Nodejs API with refpath

I'm new to building rest api's with mongoose and express and have a question on how to use refPath correctly on my Models files and allowing for an array of items.
Below I have included the code for a model (built thus far) and would love any input on if I'm even close to building this correctly.
I will also include a screenshot that visually depicts the relationships I'm trying to create.
Those who answer questions here are GODS and I appreciate all the help this community has given me over the years!
const mongoose = require("mongoose");
const slugify = require("slugify");
const AlertSchema = new mongoose.Schema({
parentId: {
type: mongoose.Schema.ObjectId,
required: true,
refPath: "parentModel",
},
parentModel: {
type: String,
required: true,
enum: ["orgs", "clients"],
},
status: { type: String, default: "no-status" },
departments: [{ type: mongoose.Schema.Types.ObjectId, ref: "orgs" }],
createdAt: { type: Date, default: Date.now },
createdByType: [{ type: mongoose.Schema.Types.ObjectId, ref: "users" }],
createdById: [{ type: mongoose.Schema.Types.ObjectId, ref: "users" }],
groups: [{ type: String, default: "unGrouped" }],
stage: [{ type: mongoose.Schema.Types.ObjectId, ref: "stages" }],
children: { type: String },
resource: {
type: String,
match: [
/https?:\/\/(www\.)?[-a-zA-Z0-9#:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()#:%_\+.~#?&//=]*)/,
"Please use a valid URL with HTTP or HTTPS",
],
},
notes: [{ type: mongoose.Schema.Types.ObjectId, ref: "notes" }],
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: "comments" }],
priority: { type: String },
assignedTo: [{ type: mongoose.Schema.Types.ObjectId, ref: "users" }],
title: {
type: String,
required: [true, "Please add a title"],
maxlength: [50, "Title cannot be more than 50 characters"],
},
message: {
type: String,
required: [true, "Please add a message"],
maxlength: [500, "Message cannot be more than 500 characters"],
},
slug: String,
});
//create alert slug from the title
AlertSchema.pre("save", function (next) {
console.log("Slugify Ran", this.name);
this.slug = slugify(this.title, { lower: true });
next();
});
module.exports = mongoose.model("Testalert", AlertSchema);
Desired relationships diagram:

How we can perform sort on ObjectId column in mongodb using mongoose with expressjs-nodejs?

We are struct at one mongodb point which I would like to describe here.
We are using mongoose 5.4 and create a model like below :
var userSchema = mongoose.Schema({
id:{ type: Number, default: 1 },
first_name: String,
last_name: String,
mail: String,
password: String,
dob: { type: String, default: '' },
gender: { type: String, default: '' },
profile_photo: { type: String, default: '' },
ethnicity: { type: String, default: '' },
contact_number: { type: String, default: '' },
user_type: Number,
address1: { type: String, default: '' },
address2: { type: String, default: '' },
area: { type: String, default: '' },
city: { type: String, default: '' },
country: { type: String, default: '' },
postcode: { type: String, default: '' },
business_name: { type: String, default: '' },
ip_address: String,
status: Number,
tag_line: { type: String, default: '' },
is_influencer: Number,
wallet_id: String,
token_balance: { type: Number, default: 0 },
point_balance: { type: Number, default: 0 },
badges: [String],
membership: { type: Schema.Types.ObjectId, ref: 'Membership' },
transaction: [{ type: Schema.Types.ObjectId, ref: 'Transaction' }],
property: [{ type: Schema.Types.ObjectId, ref: 'Property' }],
reviews: [{ type: Schema.Types.ObjectId, ref: 'Review' }],
created_date: String,
});
var User = mongoose.model('User', userSchema);
var propertySchema = mongoose.Schema({
id: Number,
property_name: String,
address1: String,
area: String,
post_code: String,
category: [{ type: Schema.Types.ObjectId, ref: 'Category' }],
category_id: [Number],
property_desc: String,
property_images: String,
slug : String,
user_id: Number,
business_key: String,
user: { type: Schema.Types.ObjectId, ref: 'User' },
reviews: [{ type: Schema.Types.ObjectId, ref: 'Review' }],
status: Number,
is_claimed: Number,
created_date: String,
});
var Property = mongoose.model('Property', propertySchema);
var categorySchema = mongoose.Schema({
id: Number,
category_name: String,
status: Number,
user: { type: Schema.Types.ObjectId, ref: 'User' },
property: [{ type: Schema.Types.ObjectId, ref: 'Property' }],
created_date: String,
updated_date: String
});
var Category = mongoose.model('Category', categorySchema);
we are also using async-await for this project. we are fetching locations using below method to get location data as well User and Category.
sortClause = {};
sortClause.user=parseInt(-1);
let properties = await Property.find({'status':{$in:[1,2]}}).populate({path: 'user',
model: 'User',select: 'first_name last_name mail contact_number'}).populate({path: 'category',
model: 'Category',select: 'category_name id'}).sort(sortClause).skip(skip).limit(perpage).exec();
so when we get data in properties object after the execution of above line we are getting properties but as we are sorting by user's column of property model it does fetch data properly. It sorts on objectID and we would like to fetch on user's first_name.
I have tried by specifing `{'user.first_name':-1} in sort order but it doesn't work at all.
When we sort on string or Number column it works fine, but here my expected result will be bit different, Here we would like apply sort on user.first_name (Here in above example user is the ObjectId column which populate Users data and I would like to sort on first_name column of user)
How can we get properties based on user's first_name column? Is there any suggestion for this problem.
You can try below aggregation from mongodb 3.6 and above
Property.aggregate([
{ "$match": { "status": { "$in": status }} },
{ "$lookup": {
"from": "users",
"let": { "user": "$user" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$user" ] } } },
{ "$project": { "first_name": 1, "last_name": 1, "mail": 1, "contact_number": 1 }}
],
"as": "user"
}},
{ "$lookup": {
"from": "categories",
"let": { "category": "$category" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$category" ] } } },
{ "$project": { "category_name": 1, "id": 1 }}
],
"as": "category"
}},
{ "$unwind": "user" },
{ "$sort": { "user.first_name": -1 }},
{ "$skip": skip },
{ "$limit": perpage }
])

Find nested array elements

This is my schema:
Appliance = new Schema({
appliance_name: {type:String},
appliance_id: {type:String},
appliance_description: {type:String},
keywords: [{ type: String}],
appliance_type: { type: String},
appliance_status: { light: { write_state: Number, read_state: Number },
fan: {write_state: Number, read_state: Number, write_speed: Number, read_speed: Number}
}
});
Room= new Schema({
name: {type: String, required:true},
device_auth_code: {type: String},
alt_name: {type:String},
keywords: [{type: String}],
appliance: [Appliance]
});
Home = new Schema({
name: { type: String, required: true},
description: {type: String},
administrator: {type : mongoose.Schema.Types.ObjectId, ref: 'User', required: true},
users: [{
_id: {type : mongoose.Schema.Types.ObjectId, ref: 'User'},
email: {type: String},
name: { type: String},
status: { type: Number}
}],
rooms: [Room]
});
And here is the typical home.
"type": true,
"code": "GET_SUCCESS",
"homes": {
"_id": "58760ff6045e332b81449b42",
"description": "",
"administrator": "586df1e06485de5fc48b72a5",
"name": "CM",
"__v": 9,
"rooms": [
{
"name": "RK",
"alt_name": "RKa",
"_id": "58775437234451346ce3d967",
"appliance": [
{
"_id": "5877546f234451346ce3d968",
"appliance_type": "Light",
"appliance_name": "TubeLights",
"keywords": []
}
],
"keywords": []
}
],
"users": []
}
}
Home has nested array of rooms and each room has nested array of appliances.
Home.findOne({'room.appliance._id': appliance_id}) would return the whole document. The $ operator wouldn't work I would have to imagine.
Is it possible to receive that particular appliance or the whole document with that particular room and appliance only being returned?
How do I find a particular appliance and return that appliance?
Here you've used mongoose subdocs architecture in your home schema definition. So you could retrieve any particular room:
Home.findById(home_id, function(err, home){
var room = home.rooms.id(room_id);
res.json(room)
});
or a particular appliance:
Home.findById(home_id, function(err, home){
var room = home.rooms.id(room_id);
var appliance = room.appliance.id(appliance_id);
res.json(appliance)
});

Mongoose returns null array when populate

I'm trying to create a webapp with mongoose and nodejs. This is the first time i'm using mongoose and nodejs so i'm not quit good at it.
I have a person model:
var PERSON = new mongoose.Schema({
name: {type: String, required: true},
zipcode: {type: String, required: true},
city: {type: String, required: true},
street: {type: String, required: true},
address: {type: Number, required: true},
email: {type: String, required: true},
type: {type: String, enum: ['lid', 'passant'], required: true},
ships: {
type: [{
name: {type: String, required: true},
length: {type: Number, required: true},
type: {type: String, required: true},
picture: {type: Buffer, required: false}
}],
required: false
},
user: {
type: {
userName: {type: String, required: true},
password: {type: String, required: true},
passwordKey: {type: String, required: true},
roles: {type: [{type: mongoose.Schema.Types.ObjectId, ref: 'role'}], required: true}
},
required: false
}
},
{
collection: 'PERSON'
});
And the rol model:
var ROLE = new mongoose.Schema({
name: {type: String, required: true}
},
{
collection: 'ROLE'
});
When i'm trying to run a find query on the person model, I want to populate the user.roles array. But as a result it remains empty.
/ GET a specific person by id or type/
server.get('/api/person/:id', function (req, res, next) {
if (helper.isValidObjectID(req.params.id)) {
person.findById(req.params.id).populate("user.roles").exec(function (err, result) {
if (err) {
res.status(404).json(err);
} else {
res.status(200).json(result);
}
}
);
} else {
next();
}
}, function (req, res, next) {
person.find({type: req.params.id}).populate('user.roles').exec(function (err, result) {
if (err) {
res.status(404).json(err);
} else {
res.status(200).json(result);
}
});
});
and as a result i get:
{
"_id":"553f79d4f1481c0c14b42d59",
"name":"Wilco Boogert",
"zipcode":"4305RH",
"city":"Ouwerkerk",
"street":"baalpapenweg",
"address":2,
"email":"wilcoboogert17#gmail.com",
"type":"lid",
"user":{
"userName":"wboogert",
"password":"sha1$168fd599$1$502b965cb083ebdfcafb17e655455ef63779e1a1",
"passwordkey":"kfjvlksdfm",
"roles":[
]
},
"__v":0,
"ships":[
{
"name":"titanic",
"length":269,
"type":"Olympic-klasse",
"_id":"553f79d4f1481c0c14b42d5a"
}
]
}
The orginal object in the database is:
{
"_id": {
"$oid": "553f79d4f1481c0c14b42d59"
},
"name": "Wilco Boogert",
"zipcode": "4305RH",
"city": "Ouwerkerk",
"street": "baalpapenweg",
"address": 2,
"email": "wilcoboogert17#gmail.com",
"type": "lid",
"user": {
"roles": [
{
"$oid": "5522996f0fff331ed03cae6c"
}
],
"passwordkey": "kfjvlksdfm",
"password": "sha1$168fd599$1$502b965cb083ebdfcafb17e655455ef63779e1a1",
"userName": "wboogert"
},
"ships": [
{
"name": "titanic",
"length": 269,
"type": "Olympic-klasse",
"picture": "<Binary Data>",
"_id": {
"$oid": "553f79d4f1481c0c14b42d5a"
}
}
],
"__v": 0
}
so as you an see the roles is empty. And I search the web but i cannot find what the problem is. Can annyone help me?
You should replace this line :
roles: {type: [{type: mongoose.Schema.Types.ObjectId, ref: 'role'}], required: true}
with this one :
roles: {type: [{type: mongoose.Schema.Types.ObjectId, ref: 'ROLE'}], required: true}
In fact, the reference must be the exact same name as it's defined :
collection: 'ROLE'
It's case sensitive.

Mongoose query reference

I have the below schema structure for my application:
var User = new Schema({
name: {type: String, required: true},
screen_name: {type: String, required: true, index:{unique:true}},
email: {type: String, required: true, unique:true},
created_at: {type: Date, required: true, default: Date}
});
var Line = new Schema({
user: {type: Schema.ObjectId, ref: 'User'},
text: String,
entered_at: {type: Date, required: true, default: Date}
});
var Story = new Schema ({
sid: {type: String, unique: true, required: true},
maxlines: {type: Number, default: 10}, // Max number of lines per user
title: {type: String, default: 'Select here to set a title'},
lines: [Line],
created_at: {type: Date, required: true, default: Date}
});
var Story = db.model('Story', Story);
var User = db.model('User', User);
When i query a Story using the sid i want it to output the lines in that story but also the users screen_name.
Currently i'm using the below to query the story:
app.get('/api/1/story/view/:id', function(req, res){
Story.findOne({ sid: req.params.id }, function(err, story){
if (err) {
res.json(err);
}
else if(story == null) {
res.json(err);
}
else{
res.send(story);
}
});
});
Which just brings back the result like this:
{
"__v": 1,
"_id": "5117878e381d7fd004000002",
"sid": "DIY0Ls5NwR",
"created_at": "2013-02-10T11:42:06.000Z",
"lines": [
{
"user": "511782cab249ff5819000002",
"text": "TESTING",
"_id": "51178795381d7fd004000003",
"entered_at": "2013-02-10T11:42:13.000Z"
}
],
"title": "Select here to set a title",
"maxlines": 10
}
There is a user set-up with that _id but i'm not entirely sure how to output the story and for it to include the users screen_name or even the entire user information along with the output
{
"__v": 0,
"screen_name": "Tam",
"email": "test#test.com",
"name": "Tam",
"_id": "511782cab249ff5819000002",
"created_at": "2013-02-10T11:21:46.000Z"
}
I haven't tested it, but you should be able to use populate to get the full user doc for each lines element like this:
Story.findOne({sid: req.params.id})
.populate('lines.user')
.exec(function(err, story){ ...

Resources