I'm using the mongoose plugin autopopulate to populate docs from a separate collection. Here's a dumbed down schema:
const PostSchema = new Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: 'user',
required: true,
autopopulate: {
select: ['firstName', 'lastName', 'email', 'username']
}
},
comment: { type: String }
}
)
Now, when I create a post with
const post = await Post.create({ user, comment });
It returns null for the populated user even though it shows up in the database and subsequent queries. I've used the { new: true } option on findByIdAndUpdate but it doesn't seem to work on create. Any thoughts on how to get create to return the populated docs?
Related
This question was asked several times, but despite that, I wasn't able to solve my problem. In my mongoose collection, I store 30 users with the following mongoose schema. I want to implement a newsletter on my site, therefore I want to add the new field:
newsletter: {
type: Boolean,
default: true
},
My question is: How can I add newsletter false/true to every user?
I found that, but it didn't work.
User.updateMany({}, [{ $set: { newsletter: false }}])
My Schema:
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true },
password: { type: String, required: true },
date: { type: Date, default: Date.now },
token: { type: String, required: true }
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
Adding to the schema "newsletter" does solve the problem for new users, but doesn't add the field to the already existing ones.
I'm using mongoose in my Node micro service application.
I have a model with 'createdAt' that created automatically:
const mongoose = require('mongoose');
const recordSchema = new mongoose.Schema({
source: {
type: String,
required: true,
trim: true,
lowercase: true
},
coin: {
type: String,
required: true,
trim: true,
lowercase: true
},
rate: {
type: Number,
required: true
},
isError: {
type: Boolean,
required: true
},
}, { timestamps: { createdAt: 'created_at' } });
module.exports = mongoose.model('Record', recordSchema);
And I try to insert a bulk of documents into it, like this:
// Save the fetched data into the database.
const saveCoinsData = (coinsData) => {
RecordModel.collection.insertMany(coinsData, (err) => {
if (err) { logger.error(err); }
});
};
For some reason, if I insert a single document the 'createdAt' is created as expected. But if I use the insertMany function, it's not working and no 'createdAt' (or created_at) is created.
Is this normal?
Is this a bug in mongoose?
You're not using mongoose schema here. You're using default node drivers by using [schema].collection.[method]. Referring to this you can just use
RecordModel.insertMany instead, that should add the timestamps.
Instead of insertMany, maybe try create?
create triggers middleware save() which supports timestamps.
Reference:
Model.create
Unfortunately I cannot try this now, above is just based on Mongoose documentations
I'm trying to create a one to many relationship between Category and Services models.
I have two schemas: Category and Service.
Category schema holds services array.
Service schema hold category _id.
I'm trying to retrieve all Categories including services.
Category schema:
import mongoose from 'mongoose';
const categoriesModel = new mongoose.Schema(
{
category: {
type: String,
required: true,
},
services: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'Service' }],
},
{ timestamps: true }
);
export const Category = mongoose.model('Category', categoriesModel);
Service schema:
import mongoose from 'mongoose';
const serviceSchema = new mongoose.Schema(
{
serviceName: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
images: {
type: Array,
image: Buffer,
required: true,
},
category: {
type: mongoose.SchemaTypes.ObjectId,
ref: 'Category',
required: true,
},
},
{ timestamps: true }
);
export const Service = mongoose.model('Service', serviceSchema);
HTTP get request to retrieve all Categories including Services:
export const getAllCategories = async (req, res) => {
try {
const docs = await Category.find({}).populate({
path: 'services',
model: 'Service',
});
console.log(docs);
res.status(200).json({ data: docs });
} catch (err) {
res.status(400).json(err.message);
}
};
This is what I get as a response from the request above:
{
"services": [],
"_id": "60affe06c71901281d3d820d",
"category": "Electricity"
},
I've tried different ways to populate services but none of them worked.
What I've tried so far:
Add additional nesting to services attribute.
Adding object to the populate params:
{
path: 'services',
model: 'Service',
}
Removing existing data ant posting additional one.
Fun fact. Retrieving each Service individually, service data includes the actual category, that it's been assigned to:
export const getAllServices = async (req, res) => {
try {
const doc = await Service.find({}).populate('category');
res.status(200).json({ data: doc });
} catch (err) {
res.status(400).json(err);
}
};
Is there something I'm missing regarding including services within retrieving categories? It's really interesting, because somehow the actual services data is [].
EDIT: Each category in Categories collection has empty services array. The services are added dynamically, they might be null for specific category until user adds a service and assigns a category to it. Is this forbidden? Do I need to find specific category and add a specific service to category while creating new service? If yes, this would require to add additional Category model to the services.controller.js and find the category. Is this really an appropriate solution?
I'm going through the (excellent) Sails.js book, which discusses creating a User model User.js in Chapter 6 like so:
module.exports = {
connection: "needaword_postgresql",
migrate: 'drop',
attributes: {
email: {
type: 'string',
email: "true",
unique: 'string'
},
username: {
type: 'string',
unique: 'string'
},
encryptedPassword: {
type: 'string'
},
gravatarURL: {
type: 'string'
},
deleted: {
type: 'boolean'
},
admin: {
type: 'boolean'
},
banned: {
type: 'boolean'
}
},
toJSON: function() {
var modelAttributes = this.toObject();
delete modelAttributes.password;
delete modelAttributes.confirmation;
delete modelAttributes.encryptedPassword;
return modelAttributes;
}
};
Using Postgres, a new record correctly populates the boolean fields not submitted by the login form as null, as the book suggests should be the case:
But I want to use MongoDB instead of PostgreSQL. I had no problem switching the adaptor. But now, when I create a new record, it appears to ignore the schema in User.js and just put the literal POST data into the DB:
I understand that MongoDB is NoSQL and can take any parameters, but I was under the impression that using a schema in Users.js would apply to a POST request to the /user endpoint (via the blueprint routes for now) regardless of what database was sitting at the bottom. Do I need to somehow explicitly tie the model to the endpoint for NoSQL databases?
(I've checked the records that are created in Postgres and MongoDB, and they match the responses from localhost:1337/user posted above)
I understand that MongoDB is NoSQL
Good! In sails the sails-mongo waterline module is responsible for everything regarding mongodb. I think I found the relevant code: https://github.com/balderdashy/sails-mongo/blob/master/lib/document.js#L95 So sails-mongo simply does not care about non existent values. If you think this is bad then feel free to create an issue on the github page.
A possible workaround might be using defaultsTo:
banned : {
type : "boolean",
defaultsTo : false
}
You can configure your model to strictly use the schema with this flag:
module.exports = {
schema: true,
attributes: {
...
}
}
I eventually settled on performing the validations inside my controller.
// a signup form
create: async (req, res) => {
const { name, email, password } = req.body;
try {
const userExists = await sails.models.user.findOne({ email });
if (userExists) {
throw 'That email address is already in use.';
}
}
I am currently trying to learn how to work with NoSQL, coming from a relational database background. In this project, I am using Express with Mongoose.
I am struggling with callbacks as I try to merge two models together, which reference each other. I am trying to edit each item in a group of one model (Ribbits) to contain the attributes of another (Users who posted a Ribbit). Because the call to find the User associated with a Ribbit is asynchronous, I am unable to return the collection of edited Ribbits (with user info).
In my website, I have ribbits (a.k.a. tweets) which belong to users. Users can have many ribbits. In one of my pages, I would like to list all of the ribbits on the service, and some information associated with the user who posted that ribbit.
One solution I found was embedded documents, but I discovered that this is, in my case, limited to showing ribbits which belong to a user. In my case, I want to start by getting all of the ribbits first, and then, for each ribbit, attach info about who posted that.
Ideally, I'd want my schema function to return an array of Ribbit objects, so that I can then render this in my view.
// models/user.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var userSchema = Schema({
username: String,
email: String,
password: String,
name: String,
profile: String,
ribbits: [{
type: Schema.Types.ObjectId,
ref: 'Ribbit',
}]
});
module.exports = mongoose.model('User', userSchema);
// models/ribbit.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
User = require('./user');
var ribbitSchema = Schema({
content: { type: String, maxlength: 140 },
created: { type: Date, default: Date.now() },
owner: { type: Schema.Types.ObjectId, ref: 'User' },
});
ribbitSchema.methods.getOwnerObj = function(cb) {
return User.findOne({ _id: this.owner }, cb);
}
ribbitSchema.statics.getAllRibbits = function(cb) {
this.find({}, function(err, ribbits) {
console.log('Before Transform');
console.log(ribbits);
ribbits.forEach(function(ribbit) {
ribbit.getOwnerObj(function(err, owner) {
ribbit = {
content: ribbit.content,
created: ribbit.created,
owner: {
username: owner.username,
email: owner.email,
name: owner.name,
profile: owner.profile,
}
};
});
});
});
}
module.exports = mongoose.model('Ribbit', ribbitSchema);
If I understand correctly, you can use Mongoose populate method for this scenario:
ribbitSchema.statics.getAllRibbits = function(cb) {
this.find({}).populate('owner').exec(function(err, ribbits){
console.log(ribbits[0].owner)
return cb(err, ribbits);
})
}