In Sails.JS, Is it possible to populate more than one attribute for a Model? - sails.js

For a given model named "Message"
module.exports = {
attributes:{
subject: {type: 'string', required: true},
body: {type: 'string', required: true},
from: {model: 'user, required: true},
to: {model: 'user', required: true},
}
}
The following find populates the attribute "from"
let msg = Message.find({id:1}).populate('from');
Is it possible to populate both "from" and "to" attributes for that find ?

I found the answer, just call populate twice.
let msg = Message.find({id:1}).populate('from').populate('to');

Related

Mongoose Nested Schema - required fields inside array field

I have following mongoose schema -
const UserSchema = new mongoose.Schema({
name: {type: String, required: true, trim: true},
email: {type: String, required: true, trim: true},
password: {type: String, required: true, trim: true},
addresses: [{
type: {type: String, required: true, trim: true},
pinCode: {type: String, required: true, trim: true},
city: {type: String, required: true, trim: true},
state: {type: String, required: true, trim: true},
landmark: {type: String, required: false, trim: true},
}]
})
only name, email and password are required for registration. User can have more than one address , and each address has some required fields like in flipkart.com/account/addresses user's account page.
Each address will be stored in addresses array field in mongodb database.
I want to keep addresses array [ ] when user register. address are only provided through user's account page web page.
But I am getting ValidatorError because schema of doc inside addresses array having required fields.
when user register -
{
name: 'rahul',
email: 'rahul#example.com',
password: 'pass123',
addresses: []
}
user is already registered, and then add address
{
name: 'rahul',
email: 'rahul#example.com',
password: 'pass123',
addresses: [
{
type: 'home',
pinCode: '111222',
city: 'city1',
state: 'state1'
},
{
type: 'work',
pinCode: '333444',
city: 'city2',
state: 'state2',
landmark: 'landmark2'
}
]
}
How to achieve above ??
Any help would be appreciated.
You just have to remove required:true field from address array not even required:false.
Just type and trim will the job and required is false by default so you dont have to include it.
I was doing this -
const userDoc = new UserModel({
name: nm,
email: em,
password: pass,
addresses: []
})
Not passing addresses: [], prevent error ValidatorError
const userDoc = new UserModel({
name: nm,
email: em,
password: pass
})
Try the below code. Hope this will help you.
const ToySchema = new mongoose.Schema({ name: String });
const ToyBoxSchema = new mongoose.Schema({
toys: [ToySchema],
bucketName: String,
numToys: Number
});
const ToyBox = mongoose.model('ToyBox', ToyBoxSchema);
var toyBoxData = {
toys : [
{ name : "Dummy Toy 1"},
{ name : "Dummy Toy 2"},
{ name : "Dummy Toy 3"},
],
bucketName: "My Toy Bucket",
numToys: 3
}
var toyBucket = new ToyBox(toyBoxData);
toyBucket.save( (err: any)=> {
console.log(err);
})
And when you will run the above code, you will be able to see a collection and document like this in your DB, as in the below screenshot.
Hope this will help you. For more information please visit this mongoose documentation link.

MongoDB findOne() returning empty subdocuments array

I am using Mongoose and MongoDB v. 6.4.1. I have defined a document collection with embedded subdocuments using the following Mongoose schemas:
import mongoose, { Collection } from 'mongoose';
const connectStr = 'mongodb://localhost/appdb';
mongoose.set('useFindAndModify', false);
//Open connection to database
mongoose.connect(connectStr, {useNewUrlParser: true, useUnifiedTopology: true})
.then(
() => {console.log(`Connected to ${connectStr}.`)},
err => {console.error(`Error connecting to ${connectStr}: ${err}`)}
);
//Define schema that maps to a document in the Users collection in the appdb
//database.
const Schema = mongoose.Schema;
const roundSchema = new Schema({
date: {type: Date, required: true},
course: {type: String, required: true},
type: {type: String, required: true, enum: ['practice','tournament']},
holes: {type: Number, required: true, min: 1, max: 18},
strokes: {type: Number, required: true, min: 1, max: 300},
minutes: {type: Number, required: true, min: 1, max: 240},
seconds: {type: Number, required: true, min: 0, max: 60},
SGS: {type: Number,
default: function(){return (this.strokes * 60) + (this.minutes * 60) + this.seconds}
},
notes: {type: String, required: true}
});
const userSchema = new Schema({
id: {type: String, required: true}, //unique identifier for user
password: String, //unencrypted password (for now!)
displayName: {type: String, required: true}, //Name to be displayed within app
authStrategy: {type: String, required: true}, //strategy used to authenticate, e.g., github, local
profileImageUrl: {type: String, required: true}, //link to profile image
rounds: [roundSchema],
securityQuestion: {type: String},
securityAnswer: {type: String, required: function() {return this.securityQuestion ? true: false}}
});
//Convert schema to model
const User = mongoose.model("User",userSchema);
In an Express.js GET route, I am using the following code to query for a specific document:
try {
let thisUser = await User.findOne({id: req.params.userId});
console.log("thisUser: " + JSON.stringify(thisUser));
if (!thisUser) {
return res.status(400).send("No user account with specified userId was found in database.");
} else {
return res.status(200).json(thisUser.rounds);
}
} catch (err) {
console.log(err);
return res.status(400).message("Unexpected error occurred when looking up user in database: " + err);
}
My console.log statement confirms that the above route in fact obtains the desired document, e.g.:
thisUser: {"_id":"5e6704234f3864318caedd12","id":"chundhau#gmail.com","password":"GoCougs20","displayName":"chundhau#gmail.com","authStrategy":"local","profileImageUrl":"https://www.gravatar.com/avatar/4b565c54d37b3f5ad4caa1c129e865b8","securityQuestion":"First pet?","securityAnswer":"Daisy","__v":0,"rounds":[]}
When I look at this same document in MongoDB Compass Community, I can confirm that its rounds subdocument array has several elements:
However, as shown in the console.log output above, rounds is coming back as an empty array. I have confirmed that (a) rounds is in fact an array (using Array.isArray()) and that (b) rounds has no elements (thisUser.rounds.length === 0).
Shouldn't I be able to access all of the subdocuments through thisUser.rounds? What have I done wrong?
I have discovered a solution. I changed:
let thisUser = await User.findOne({id: req.params.userId});
to
let thisUser = await User.findOne({id: req.params.userId}).lean();
Miraculously, thisuser.rounds was no longer empty. It instead contained all of the array elements I could see when I inspected the document in MongoDB Compass Community!
While this solution worked, I do not know why it worked. If anyone could help me understand what's going on here, I'd appreciate it!

Get count number from mongoDB

I'm creating questions and answers web site using mango and express.So there are two schema for Question and Answer.
My Question and Answer Schema like this,
const questionSchema = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
user: { type:mongoose.Schema.Types.ObjectId, ref: 'User', required: true},
title: {type: String , required:true},
question: {type: String , required:true},
tags: {type: Array , required: true},
time : { type : Date, default: Date.now },
vote: {type: Number, default: 0},
views: {type: Number, default: 0} });
const answerSchema = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
user: { type:mongoose.Schema.Types.ObjectId, ref: 'User', required: true},
question:{ type:mongoose.Schema.Types.ObjectId, ref: 'Question', required: true},
comment: [{type: mongoose.Schema.Types.ObjectId, ref: 'Comment', default: null }],
answer: {type: String, required: true},
vote: {type: Number , default: 0},
time : { type : Date, default: Date.now } });
In here there are many answers related to the one question.My home page get the all questions to preview.How to get the count number of answers related to the each question?
Since your questionSchema doesn't have any direct relation with Answers collection (like doesn't store Answers reference) then try this :
Query :
db.Question.aggregate([
{
$lookup:
{
from: "Answer",
localField: "_id",
foreignField: "question",
as: "answers"
}
},
/** In case if you need all details of question replace 'project' with 'addFields' & remove 'question: 1' */
{ $project: { question: 1, count: { $size: '$answers' } } }
])
Test : MongoDB-Playground

MongoDB not saving User

Trying to do a simple CRUD for a user.
I am passing name, email, password, location, avatar through a sign up form from React/Redux.
I am getting an error during the save which says the following about a duplicate key error --
(node:30190) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): WriteError({"code":11000,"index":0,"errmsg":"E11000 duplicate key error index: heroku_10vr0vgl.users.$googleId_1 dup key: { : null }","op":{"avatar":"avatar.jpg","slug":"jane","id":"cj7nl39w50000amcaool50zcp","name":"Jane","email":"admin#jane.com","password":"$2a$10$envMKYq6xFCkZGpYVd4rEel4g5TGijFOEnr.ayMymHM1ph0/1luGC","location":"Los Angeles","_id":"59bd5e9478537875ee6b8939","createdAt":"2017-09-16T17:25:40.863Z","campaigns":[],"role":"NORMAL","__v":0}})
Looks to be something about a duplicate val, but googleID for ex is not required.
I only have a mock user in the user collection that looks like the following --
[
{
"_id": {
"$oid": "59a070dea4de3af502af7d5c"
},
"avatar": "avatar.jpg",
"name": "John Doe",
"role": "ADMIN",
"slug": "john-doe",
"id": "1",
"email": "admin#johndoe.com",
"location": "Los Angeles",
"campaigns": ["1"],
"twitterId": "refinery29",
"password": "test1234"
}
]
The user model looks like the following --
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
const userSchema = new Schema({
name: {type: 'String', required: true},
role: {type: 'String', default: 'NORMAL', required: true},
location: {type: 'String'},
slug: {type: 'String', required: true, index: {unique: true, dropDups: true}},
campaigns: {type: ['String'], default: []},
avatar: {type: 'String', required: true},
email: {type: 'String', index: {unique: true, dropDups: true}},
id: {type: 'String', required: true, index: {unique: true, dropDups: true}},
googleId: {type: 'String'},
facebookId: {type: 'String'},
twitterId: {type: 'String'},
createdAt: {type: 'Date', default: Date.now, required: true},
password: {type: 'String'},
});
export default mongoose.model('User', userSchema);
Looks like I had an existing index reference. I dropped my user collection and saved. All is well!

Reference to schema (not a particular document) in mongoose

Please, note that this is not a duplicate of this, nor this, nor this, since what I need is not a reference to a document from another collection, but a reference to the collection itself.
I'm using mongoose-schema-extend to create a hierarchic structure for contents.
Let's say I have this:
/**
* Base class for content
*/
var ContentSchema = new Schema({
URI: {type: String, trim: true, unique: true, required: true },
auth: {type: [Schema.Types.ObjectId], ref: 'User'},
timestamps: {
creation: {type: Date, default: Date.now},
lastModified: {type: Date, default: Date.now}
}
}, {collection: 'content'}); // The plural form of content is content
/**
* Pages are a content containing a body and a title
*/
var PageSchema = ContentSchema.extend({
title: {type: String, trim: true, unique: true, required: true },
format: {type: String, trim: true, required: true, validate: /^(md|html)$/, default: 'html' },
body: {type: String, trim: true, required: true}
});
/**
* Articles are pages with a reference to its author and a list of tags
* articles may have a summary
*/
var ArticleSchema = PageSchema.extend({
author: { type: Schema.Types.ObjectId, ref: 'User', required: true },
summary: { type: String },
tags: { type: [String] }
});
Now, I want to create another schema, which is a subtype of content, but which represents a set of contents, like so:
/**
* Content sets are groups of content intended to be displayed by views
*/
var ContentSetSchema = ContentSchema.extend({
name: {type: String, trim: true, unique: true, required: true },
description: {type: String },
content: [{
source: { type: [OTHER_SCHEMA] }, // <- HERE
filter: {type: String, trim: true },
order: {type: String, trim: true }
}]
})
So, the content attribute of this schema should be a reference to any of the other schemas.
Is it possible?
The best I came up whit, was using a string, a discriminator key, and a validator:
var ContentSchema = new Schema({
// ...
}, {collection: 'content', discriminatorKey : '_type'});
var ContentSetSchema = ContentSchema.extend({
// ...
content: [{
source: { type: [String], validate: doesItExist }
}]
});
function doesItExist(type, result) {
ContentModel.distinct('_type').exec()
.then((contentTypes) =>
respond(contentTypes.some((validType) => validType === type)));
}
But with this solution (good enough an this moment) I can only create ContentSets for types of content that have already some register in the database.