new Model() that has fields set as select:false are not available - mongodb

This is my Schema:
var userScheme = mongoose.Schema({
aField:String,
info: {
type:{
local: {
email:String
}
},
select:false
}
});
When I try to create a new user this works fine:
var newUser = new User()
newUser.aField="Something"
newUser.save()
But when I try to access the field that has select:false, I can't access the data. so this doesn't work:
var newUser = new User()
newUser.aField="something"
newUser.info.local.email="email#domain.com"
newUser.save()
The error I get is:
TypeError: Cannot read property 'local' of undefined
My guess is that the new Model is returned without the info field becuase it is set to select:false.
How can I make the new Model() return all the fields including those set to 'select:false'?
Thanks!

Turns out the select:false had nothing to do with it.
The culprit was the fact that info has no values by default and there for was not included in the model at all.
The solution was to create a new schema just for info, and to include it in the user schema like this:
var userScheme = mongoose.Schema({
aField:String,
info: {
type:infoSchema,
default:infoSchema, //Without this, the new document will still not include an 'info' document,
select:false
}
});
Hope this helps anyone. This was just 3 hours of my life.

Related

Adding a new property to mongo after 'save' in Keystone

I recently found out how to change the value of an existing property and saving it to the mongo database when using Keystone JS (How to alter a value before storing it to the database in Keystone JS).
Now I need to add a new property and save it to the database during the same pre('save') phase.
The aims is to say, if the result (existing property) of a game is 'Won', then add a new property 'won' which is a boolean (true). If it matters, the reason I want this is because in a handlebars template I want to say {{#if won}}class="success"{{/if}}
Game.schema.pre('save', function(next) {
if (this.isModified('result')) {
if (this.result === 'Won') {
this.won = true;
}
}
next()
});
But nothing happens. I read that you can't add properties unless they've been set in the schema. So I tried adding Game.schema.set('won', false); above that, but still nothing.
Is there a simple way to do this?
You could look at Mongoose virtuals which are properties that you can get and set but that do not get persisted to the database:
Game.schema.virtual('won').get(function() {
return this.result === 'Won'
})
http://mongoosejs.com/docs/guide.html#virtuals
If you just want to use it in your template then you could also set a specific property on locals in your view.
Perhaps something like this:
...
exports = module.exports = function(req, res) {
var view = new keystone.View(req, res)
var locals = res.locals
locals.games = []
view.on('init', function(next) {
var query = {} // Add query here
Game.model.find(query).exec(function(err, games) {
// Handle error
games.forEach(function(game) {
game.won = game.result === 'Won'
})
locals.games = games
})
})
}
...

bookshelf js save() command not updating rows in postgresql db

JS beginner trying to get a PostgreSQL DB talking to express.js through bookshelf.js.
github: https://github.com/duskyshelf/bookers-academy/blob/master/booker.js
var knex = require('knex')({
client: 'pg',
connection: "postgres://localhost/bookers"
});
var bookshelf = require('bookshelf')(knex);
var User = bookshelf.Model.extend({
tableName: 'users'
});
var bob = new User({id: 2});
bob.save()
bookshelf.js seems unable to add any content to the db.
Current error message is: "Unhandled rejection CustomError: No Rows Updated'
When you create your model providing your own id, like in
var bob = new User({id: 2});
Bookshelf assumes it is an update operation, not an insertion. It sets the internal isNew attribute to false, and when save() is invoked, instead of INSERT INTO user(id, ...) VALUES (2, ...);, it executes UPDATE user ... WHERE id = 2;.
If there is no user with id = 2 the update will almost silently DO NOTHING.
To force an insert you must change the save() to:
bob.save(null, {method: 'insert'});
Bookshelf save() documentation describes this behavior.
Did you create a User table using knex? One potential problem I can think of is that you do not have any logic that actually creates the table for your Postgres DB. Here is some sample code that will create a table in your database if it doesn't yet exist.
bookshelf.knex.schema.hasTable('User').then(function(exists) {
if(!exists) {
bookshelf.knex.schema.createTable('User'), function(user) {
user.increments('id').primary();
user.timestamps();
}).then(function(table){
console.log('Created Table:', table);
});
}
});
new User({id: 2}).
save().
then((model) => {
res.json({ success: true });
});
No no! new User returns a PROMISE! You can't use it like that.
Try
new User().save()

Mongoose - how to use model inheritance in a sub document collection

I have this model setup. I want a parent record with an array of sub docs.
The sub docs have a schema, and use inheritance.
//
// Children
function abstractSchema() {
Schema.apply(this, arguments);
this.add({
name: String
});
};
util.inherits(abstractSchema, Schema);
var mySchema = new abstractSchema();
//
// Inherited Types
var textPropertySchema = new abstractSchema({
length: Number
});
var numberPropertySchema = new abstractSchema({
dp: Number
});
//
// Parent Model
var myModelSchema = mongoose.Schema({
name: String,
properties : [mySchema]
});
When i save each an instance of numberPropertySchema or textPropertySchema, the _t (type is written) and is able to deserialise properly.
When however added as a sub doc array, they're all persisted with the base object properties only.
Is there any way round this? any extensions that could be used?
Thanks
sam

Mongoose - Restrict the fields on referenced model

I have users model that can hold multiple notifications. In the NotificationSchema the notifier holds users ID and it references the users model. When I execute the following query :
User.find().populate('notifications.notifier').exec(function(err,users){
//users[0].notifications[0].notifier
//I am getting all fields from the referenced user
//I don't want the whole document but some fields only
});
How can someone Limit / Restrict the fields that should be available while referencing to some model.
Here is the users model
var NotificationSchema =new Schema({
notifier : {type:Schema.Types.ObjectId, ref:'users'},
//How could I say :
//notifier : {type:Schema.Types.ObjectId, ref:'users', fields:['_id', 'name']}
//because I know what fields do I need from referenced model for this Schema.
__SOME__
__OTHER__
__FIELDS__
});
var UsersSchema = new Schema({
name : {given:String, middle:String, family:String}
email:String,
password:String,
notifications : [NotificationSchema]
});
var Users = mongoose.model('users', UsersSchema);
BTW, I do not have separate model for NotificationSchema.
If this feature is not available out of the box how could I implement it manually. Am I missing some docs? Please let me know the robust way of doing it.
I found it in mongoose docs
I found the answer in Field Selection Section of the documentation
User.find().populate('notifications.notifier', '_id name').exec(function(err, users) {
//users[0].notifications[0].notifier ^^^^^^^^^ HOW FUNNY
//Yes I am getting '_id' and 'name' fileds only
});

Mongoose -- Force collection name

I am trying to use mongoose to create a database and a collection in it. My code is:
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/testdb');
var Schema = mongoose.Schema;
var UserInfo = new Schema({
username : String,
password : String
});
mongoose.model('UserInfo', UserInfo);
var user = db.model('UserInfo');
var admin = new user();
admin.username = "sss";
admin.password = "ee";
admin.save();
When I run this code, mongoose created collection named UserInfo instead of userinfo.
How to force collection name in mongoose?
This should do it
var UserInfo = new Schema({
username : String,
password : String
}, { collection: 'userinfo' });
See this link from the Mongoose documentation for more information.
If you are using mongoose 2.0.0, pass the collectionName as the third argument
mongoose.model('UserInfo', UserInfo, 'UserInfo');
Mongoose will add 's' to collection name by default. If you want to avoid that, just pass as third argument the name of the collection:
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/testdb');
var Schema = mongoose.Schema;
var UserInfo = new Schema({
username: String,
password: String
});
mongoose.model('UserInfo', UserInfo, 'UserInfo')
tan = new user();
admin.username = 'sss';
admin.password = 'ee';
admin.save();
API structure of mongoose.model is this:
Mongoose#model(name, [schema], [collection], [skipInit])
What mongoose do is that, When no collection argument is passed, Mongoose produces a collection name by pluralizing the model name. If you don't like this behavior, either pass a collection name or set your schemas collection name option.
Example:
var schema = new Schema({ name: String }, { collection: 'actor' });
or
schema.set('collection', 'actor');
or
var collectionName = 'actor'
var M = mongoose.model('Actor', schema, collectionName);
You need to set the collection name in your schema.
new Schema({...},{collection: 'userInfo'});
Mongoose maintainer here. We recommend doing mongoose.model('UserInfo', UserInfo, 'UserInfo');, third arg to mongoose.model() is the collection name. Here's the relevant docs.
Passing a third argument on module.exports = mongoose.model('name', schema, 'collection') overrides the automatic collection name based on model name, which has already been answered.. but there are 2 other ways,
per mongoose.model doco link:
https://mongoosejs.com/docs/api.html#mongoose_Mongoose-model
there are 3 methods to manually enter a collection name:
var schema = new Schema({ name: String }, { collection: 'actor' });
// or
schema.set('collection', 'actor');
// or
var collectionName = 'actor'
var M = mongoose.model('Actor', schema, collectionName)
Answer:
mongoose.model('UserInfo', UserInfo, 'userinfo'); //3rd parameter 'userinfo': as collection name
Better explanation with syntax:
Mongoose.model(name, [schema], [collection], [skipInit])
Parameters Explanation:
1st parameter - name model name
2nd parameter [schema] schema name
3rd parameter [collection] collection name (optional, induced from model name)
4th parameter [skipInit] whether to skip initialization (defaults to false)
your model name : userInfo.js
in express route file or app.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/testdb');
then in your userInfo.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserInfo = new Schema({
username : String,
password : String
});
module.exports = mongoose.model('UserInfo', UserInfo);