Sails js one to one populate conditions not working? - node.js

I'm newbie of Sails and I've got a problem with one to one association.
First, I have model User:
module.exports = {
schema: true,
identity : "User",
tableName: "user",
attributes: {
email: {
type: 'email',
unique: true,
required: true
},
password: {
type: 'string'
},
salt: {
type: 'string'
},
merchant: {
model: 'merchant',
defaultsTo: null
},
removed: {
type: 'boolean',
required: true,
defaultsTo: false
}
}
}
And my Merchant model:
module.exports = {
schema: true,
identity : "Merchant",
tableName: "merchant",
attributes: {
name: {
type: 'string',
unique: true,
required: true
},
code: {
type: 'string',
unique: true,
required: true
},
security_key: {
type: 'string',
required: true
},
active: {
type: 'boolean',
defaultsTo: false,
required: true
},
user: {
model: 'user'
}
}
}
So when I need to find records where merchant.active = true, I write this query:
var findUser = User.find(query).populate('merchant', {active: true});
return findUser;
But it was not working at all.
Anyone any ideas to solve this properly?
P.S. my Sails version is: 0.11.1. My DB is MongoDB

First of all, remove defaultsTo from your association attributes. I don't like this :) I don't know if it makes the problem, but in documentation I never see this.
Also you need to execute your query, not just return it. If I take your models' declarations then I can write populate query like this.
var findUser = User.find(query).populate('merchant', {active: true});
findUser.then(function(user) {
console.log('Your user is ' + user);
}).catch(function(error) {
console.log('Your error is ' + error);
});

Related

How to keep id on mongoose findByIdAndUpdate

I am trying to update a 'Board' model in mongoose using findByIdAndUpdate and the model has an array of 'items' (which are objects) on the model. I probably do not understand mongoose well enough but for some reason each item in the array gets an id generated, along with the Board. This is not a problem, it's quite handy actually, however, after doing a findByIdAndUpdate the id on each item has changed. This was quite surprising to me, I really thought they would stay the same. Could this be caused by updating all items in the array? Maybe mongoose is just throwing out the entire array and creating a new one when updating (maybe someone knows?). Anyways, my question is: Is there a way to update the model without changing these id's. I would really like them to stay consistent. The code I am using for update is
exports.updateBoard = asyncHandler(async (req, res, next) => {
let board = await Board.findById(req.params.id);
if (!board) {
return next(new CustomError(`Board not found with id of ${req.params.id}`, 404));
}
// Authorize user
if (board.user.toString() !== req.user.id) {
return next(new CustomError(`User ${req.user.id} is not authorized to update board ${board._id}`, 401));
}
req.body.lastUpdated = Date.now();
board = await Board.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true })
.select('-__v')
.populate({
path: 'user',
select: 'name avatar',
});
// 200 - success
res.status(200).json({ success: true, data: board });
});
and BoardSchema:
const BoardSchema = new Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: [true, 'Board must have a user'],
},
name: {
type: String,
required: true,
trim: true,
},
description: {
type: String,
required: false,
trim: true,
},
items: [
{
title: {
type: String,
required: true,
trim: true,
},
description: {
type: String,
required: false,
trim: true,
},
dateCreated: {
type: Date,
default: Date.now,
},
lastUpdated: {
type: Date,
default: Date.now,
},
},
],
columns: [
{
name: {
type: String,
required: true,
},
index: {
type: Number,
required: true,
},
show: {
type: Boolean,
required: true,
},
},
],
dateCreated: {
type: Date,
default: Date.now,
},
lastUpdated: {
type: Date,
default: Date.now,
},
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
},
);

Add data in an array of object with mongoDB

I need your help, I try to add(if it not exists) or update if exists datas in an array of Object in MongoDB.
Here is my Model
import { Schema, model } from "mongoose";
const userSchema = new Schema({
firstName: {
type: String,
required: true,
unique: false,
trim: true
},
pseudo: {
type: String,
required: true,
unique: true,
trim: true,
minlength: 3
},
email: {
type: String,
required: false,
trim: true
},
password: {
type: String,
required: true
},
// password2: {
// type: String,
// required: true
// },
tags: {
type: Array,
required: false
},
address: {
type: String,
required: true,
unique: false,
trim: true
},
coord: {
type: Object,
required: false,
unique: false,
trim: true
},
poll: [
{
tag: String,
dates: Array
}
]
},
{
timestamps: true,
});
const User = model('User', userSchema);
export default User;
My route
router.route('/calendar/:email').post((req, res) => {
User.findOne({ email: req.body.email }).then( (user) =>{
console.log("user 1", user)
User.bulkWrite([
{
insertOne: {
"poll": {
"tag": req.body.selectedTag,
"dates": req.body.datesArray
}
}
},
{
updateOne: {
"filter": {
"tag" : req.body.selectedTag
},
"update": {
$set: {
"dates": req.body.datesArray
}
},
}
}
])
})
});
and the datas sended :
email: 'john#gmail.com',
selectedTag: 'work',
dateArray: [ '2020-07-16T22:00:00.000Z' ]
I try many things like by findOneaAndUpdate, but I don't know how to add in the array "poll", objects with tag and the dates associated.
If somebody could help me it would be very nice !
I shoul use add $addToSet or $push, depending if the element is unique or not.
Something like this:
"update": {
"$addToSet": {
"poll": { /*...*/ }
}
}
For reference:
http://docs.mongodb.org/manual/reference/operator/update/addToSet/
http://docs.mongodb.org/manual/reference/operator/update/push/

How to implement many to many association using through in Sails?

I'm using Sails v0.11.2 and MongoDB 3.2 on Mac OS X El Capitan and I'm trying to implement Many-To-Many association using Through option which isn't supported yet.
However, googling I found this Waterline Github Issue and elennaro, a github user, gave me a couple of links with some examples:
First one
Second one
I have tried to adapt them to my own Sails app but I can't make it works. I got no errors on the console but the record or document on the intermediary table is not created only the Form document in it's table.
These are my models:
User.js
module.exports = {
schema: true,
tableName: 'users',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes:
{
email : { type: 'email', required: true, unique: true },
encrypted_password : { type: 'string' },
reset_password_token: { type: 'string', defaultsTo: null},
permission_level : { type: 'integer', required: true, min: 1, max: 3, defaultsTo: 0 },
belongs_to : { type: 'string', required: true, defaultsTo: 0 },
signin_count : { type: 'integer', required: true, defaultsTo: 1 },
status_active : { type: 'boolean', required: true, defaultsTo: false },
last_signin_at : { type: 'datetime', defaultsTo: function (){ return new Date(); } },
last_signin_ip : { type: 'string', defaultsTo: '0.0.0.0' },
// Add a reference to Person
person_id:
{
model: 'person'
},
// Add a reference to Forms collection
forms:
{
collection: 'form',
via: 'user_id',
through: 'userhasform'
},
has:
{
collection: 'userhasform',
via: 'form_id'
}
}
};
Form.js
module.exports = {
schema: true,
tableName: 'forms',
attributes:
{
name : { type: 'string', required: true, unique: true },
creator : { type: 'string', unique: false },
sequence: { type: 'integer', autoIncrement: true },
// Add a reference to Questions collection
questions:
{
collection: 'question',
via: 'form_id'
},
// Add a reference to the owners Users
owners: {
collection: 'user',
via: 'form_id',
through: 'userhasform'
}
}
};
UserHasForm.js
module.exports = {
schema: true,
tableName: 'users_have_forms',
attributes:
{
to_edit : { type: 'boolean' },
to_delete : { type: 'boolean' },
user_id : { model: 'user' },
form_id : { model: 'form' }
}
};
The controller in which I create a form and it is supposed the intermediary document is been created at the join table is:
FormController.js
module.exports = {
create: function (req, res)
{
var ownerJson = {},
tmpFolio;
// Get the logged user to make the Folio and then create the form
SessionService.getUser(req, createForm);
// Callback function
function createForm (err, session)
{
// If there's no logged user or any error
if (err || !session)
{
console.log(err);
return res.json(err.status, {error: err});
}
console.log('User to create Folio: ', session.id);
ownerJson.owner_a = session.first_name;
ownerJson.owner_b = session.second_name;
ownerJson.owner_c = session.last_name;
// Construct the Folio creator part like AVC
tmpFolio = FolioService.generateFolio(ownerJson);
Form.create({
name: req.body.name,
creator: tmpFolio
})
.then(function (form){
if (err)
{
console.log(err);
return res.json(err.status, {error: err});
}
// Create the jointable record
var createdRecord = UserHasForm.create({
to_edit: true,
to_delete: true,
user_id: session.id,
form_id: form.id
})
.then(function (createdRecord){
if (err)
{
console.log(err);
return res.json(err.status, {error: err});
}
return createdRecord;
});
return [form, createdRecord];
})
.spread(function (form, createdRecord){
return res.json(200,
{
message: 'The form was created successfuly!',
data: form,
sharing: createdRecord
});
})
.fail(function (err){
if (err)
{
console.log(err);
res.json(err.status, {error: err});
}
});
}
},
};
When I run this code I got the next error:
[ReferenceError: UserHasForm is not defined]
Unhandled rejection TypeError: Cannot read property 'toString' of undefined
So I suppose it can't find the model so I add the next line to the model at the beginning:
var UserHasForm = require('../models/UserHasForm');
And now I get the next error:
[TypeError: UserHasForm.create is not a function]
All this is following the the first example on the list.
Any idea why I'm getting this error?
Any kind of help will be welcomed!
Well after trying to many examples finally I found the solution thanks to #elennaro for all his support. The whole conversation could be found in the link to the chat we both started under the main question's comments.
Also I can tell you that the examples in the links provided by him (which are in the question above) works fine, the problem was that the version I was using didn't support the features that those examples show.
Basically what I had to do is to install the most recent version for NodeJS, SailsJS and Waterline.
In my case I actually have the next ones:
Node v5.3.0
Sails v0.11.3
Waterline v0.10.30
After that I have to make some changes to my models and at the end they look like this:
User.js
module.exports = {
schema: true,
tableName: 'users',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes:
{
// username : { type: 'string', unique: true, minLength: 5, maxLength: 15 },
email : { type: 'email', required: true, unique: true },
encrypted_password : { type: 'string' },
reset_password_token: { type: 'string', defaultsTo: null},
permission_level : { type: 'integer', required: true, min: 1, max: 3, defaultsTo: 0 },
belongs_to : { type: 'string', required: true, defaultsTo: 0 },
signin_count : { type: 'integer', required: true, defaultsTo: 1 },
status_active : { type: 'boolean', required: true, defaultsTo: false },
last_signin_at : { type: 'datetime', defaultsTo: function (){ return new Date(); } },
last_signin_ip : { type: 'string', defaultsTo: '0.0.0.0' },
// Add a reference to Forms collection
forms:
{
collection: 'form',
via: 'user',
through: 'userhasform'
// dominant: true
}
}
};
Form.js
module.exports = {
schema: true,
tableName: 'forms',
attributes:
{
name : { type: 'string', required: true, unique: true },
creator : { type: 'string', unique: false },
sequence: { type: 'integer', autoIncrement: true },
// Add a reference to the owners Users
owners: {
collection: 'user',
via: 'form',
through: 'userhasform'
}
}
};
UserHasForm.js
module.exports = {
schema: true,
tableName: 'users_have_forms',
attributes:
{
to_edit : { type: 'boolean' },
to_delete : { type: 'boolean' },
user : { model: 'User', foreignKey: true, columnName: 'user_id' },
form : { model: 'Form', foreignKey: true, columnName: 'form_id' }
}
};
FormController.js
Still the same as in the question
I hope it could be useful for anybody. And once again thanks to # Alexander Arutinyants for your support!
Any question, please leave a comment!

Waterline trying to access ID field

Currently Im using this model (with sails.js)
module.exports = {
tableName: 'player_deaths',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
player_id: {
required: true,
type: 'integer'
},
time: {
required: true,
type: 'integer'
},
level: {
required: true,
type: 'integer'
},
killed_by: {
required: true,
type: 'string'
},
is_player: {
required: true,
type: 'integer'
},
mostdamage_by: {
required: true,
type: 'string'
},
mostdamage_is_player: {
required: true,
type: 'integer'
},
unjustified: {
required: true,
type: 'integer'
},
mostdamage_unjustified: {
required: true,
type: 'integer'
}
},
autoPk: false,
}
And im calling it like this
PlayersDeaths.find().sort('time desc').exec(function(err, data) {
if(err || data.length === 0) {
console.log(err,data)
req.flash('errors', 'No deaths found');
return res.redirect('/');
}
return res.view('community/deaths', { deaths: data});
});
Thing is im getting this error
Unknown column 'playersdeath.id' in field list
Checking my model I dont even see the ID attribute listed... so why is it trying to access it?!
(the table does not have a pk)
Looks like a primary key is required. If you use autoPk:false then you need to define a PK your self see https://github.com/balderdashy/waterline-docs/blob/master/models.md#autopk
However, you still maybe able to avoid this by using model.native() or model.query() (depending on your adapter)
http://sailsjs.org/#!/documentation/reference/waterline/models/native.html
http://sailsjs.org/#!/documentation/reference/waterline/models/query.html
If you have a database Schema defined, maybe you need set you model like this
module.exports = {
migrate: 'safe',
autoPK : false,
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
// ------
}

How use a findOne query method in a custom instance method

I'm trying to do the following:
// Task Model
module.exports = {
schema: true,
attributes: {
directProy: {
type: 'string',
required: true
},
user: {
type: 'string',
required: true
},
checkUser: {
type: 'boolean',
defaultsTo: false
},
proy: {
type: 'string',
required: true
},
pieza: {
type: 'string',
required: true
},
hours: {
type: 'string',
required: true
},
obs: {
type: 'text',
defaultsTo: "lorem ipsum"
},
check: {
type: 'boolean',
defaultsTo: false
},
userName: function() {
User.findOne(this.user).done(function(err, user){
if(err){ return err;}
return user.name;
});
}
}
};
In the method "userName" I'm trying to get the name of a user with the ID it stored in the "user" attribute.
but when I run the "username" method, brings me back "undefined", I think this has to be a problem of asynchronous type
Would greatly appreciate the help they can give me since I have no idea how to associate values ​​between models and this is very useful
try passing a callback.
userName: function(cb) {
User.findOne(this.user).done(function(err, user){
cb(err, user.name);
});
}
Then when you are calling it, make sure to pass a callback.
model.userName(function(err, username) {
console.log(username);
});
your should use .exec instead of .done since it will not be avalible in sails#0.10
http://beta.sailsjs.org/#/documentation/reference/Models

Resources