Sails.js - Many-to-Many fails to save - mongodb

I have a simple need to add tags to patients. I followed the Sails and Waterline documentation concerning many-to-many associations, but it's failing at some point (no errors). I'm using MongoDB for data storage. Code below:
Tag Model
module.exports = {
attributes: {
name: 'STRING',
color: {
type: 'STRING',
defaultsTo: '#777777'
},
tagged: {
collection: 'patient',
via: 'tags',
dominant: true
}
}
};
Patient Model
module.exports = {
attributes: {
name: 'STRING',
tags: {
collection: 'tag',
via: 'tagged'
}
}
};
And this is the controller method that tries to associate data:
module.exports = {
addToPatient: function(req, res) {
Patient.findOne({id: req.param('patientId')}).exec(function(err, patient) {
// Queue up a record to be inserted into the join table
patient.tags.add(req.param('tagId'));
// Save the user, creating the new associations in the join table
patient.save(function(err) {});
});
res.send("tag assigned");
}
};
I've inspected the responses at various breaks and everything seems to be passing just fine. The patient is found. The save function shows a tag association in the patient object, but nothing is added in the database. I assume I will see either a join table being created or something in the patient/tag collections to signify an association, but I see nothing. I'm so very confused. If I do an HTTP get, I'm presented with a "tag assigned" response. What am I missing?

Works fine for me, but you're right in the tag and patient collections, you won't see a populated field with the associations. You'll see new join collections that are created that contains the relationships like #sgress454 pointed out.

Related

Sailsjs - Prevent non-model fileds to be saved in mongo document

I recently started working with Sails and mongo.
I use Sails blueprints to generate part of my api.
The problem is, that the request body I send is being saved to the mongo collection, regardless of the fields defined in the model.
So for example, let's say I have the following Event model:
module.exports = {
attributes: {
title: {
type: 'string',
required: true
},
}
}
When I Send a POST request to the /event/ endpoint with the following params:
{"title":"Some Event", "random":"string"}
The saved mongo document contains also the "random":"string" value, even though it's not part of the model.
I've tried to come up with some common method to remove non-model attributes before creation for all models, but the possible solutions seemed not right and dirty.
Am I missing something?
Any help would be appreciated!
You can use schema option in your model. Just add it to model declaration and that's it.
// api/models/Model.js
module.exports = {
schema: true,
attributes: {
title: {
type: 'string',
required: true
}
}
};

Updating association in Sails

I am using SailsJs. Two models exist. Model 'person' contains the following attribute:
books: {
collection: 'book',
via: 'owner'
},
Model 'book' contains:
owner: {
model: 'person'
},
When I create an instance of person, I can use http request and simply put something like the following
Post /person?books=1001,1002
As long as 1001 and 1002 are valid ids of book, it works.
However, when I try to person's books attribute, this does not work
Post /person/1?books=1002,1003
books of person with id 1 becomes empty.
But,
Post /person/1?books=1002
would work.
Why is this? How can I modify collection attribute?
There are 2 options to update a model using the blueprints API:
Send the data as the body of the post request. send the data as a json object in the request body
Post /person/1
body: { "books": [1002,1003]}
Override the blueprint update method
Add this the PersonController.js:
update: function (req, res) {
Person.findOne(req.param('id')).populate('books').exec(function (err, person){
if (err) {
sails.log.error(err);
return;
}
var books = req.param('books').split(',').map(function (book){return parseInt(book);}),
booksToRemove = _.difference(person.books.map(function(book) {return book.id;}), books);
// Remove books that are not in the requested list
person.books.remove(booksToRemove);
// Add new books to the list
person.books.add(books);
person.save(function (err, r) {;
res.json(r);
});
});
}

Call native blueprint actions from custom route

On different projects, I've been stucking on a very basic idea.
Everytime, that's the same concern. I want to add a new record to an association, but grabbing the parent without it's primary key.
For example, let's take a api/models/car.js model :
module.exports = {
attributes: {
licensePlate: {
type: 'integer',
required: true,
unique: true
},
locations: {
collection: 'location',
via: 'car'
}
}
};
And an api/models/location.js model :
module.exports = {
attributes: {
coordinates: {
type: 'array',
required: true
},
car: {
model: 'car'
}
}
};
A car can have multiple locations, a location have a single car.
I'm able to add a location to car using the native addTo blueprint action
POST /car/1/locations
{"coordinates":[2.13654,50.323654]}
Now what if, for some reason, I've no access to the car identifier, and feel like using another field, like a unique licensePlate ?
Basically, I would make a custom route inside config/routes, like
POST /car/byplate/:licensePlate/locations': {
controller: 'Car',
action: 'addLocationByPlate'
}
In order to be able to call
POST /car/byplate/AW45RE65/locations
{"coordinates":[2.13654,50.323654]}
And here is the problem... opening my fresh new action controller, I realize that, despite selecting my car by plate, the following logic (validation, location creation, location creation publish, location add to car's locations collection, location addition publish, error handling) is already implemented in sails.js core.
So here is the question:
How to properly call a native blueprint action with a custom route ?
In your controller you can write something like this:
YourModelName.Query(data, function(err, items){
if(err) return err;
res.json(items);
})
So for example if you want to create a new object in Car model you can do something like this:
Car.create({"carID": req.param("carID")}, function(err, items){
if(err) return err;
res.json(items);
})
this will createa new object with the ID you sent as a param.
Same goes for the other queries like add to, update, destroy etc.

Sails.js Associations many-to-many relation

I have a problem with many to many relationship in sails and if somebody can help me it would be great :D
I have two models User and Message and the associations is defined as follows
api/models/User.js
module.exports = {
attributes: {
messages: {
collection: 'message',
via: 'owner',
dominant: true
}
}
};
api/models/Message.js
module.exports = {
attributes: {
owner: {
model: 'user'.
via: 'messages'
}
}
};
I checked in the DB (MySQL) the middle table is created and i successfully inserted the data but i cant retrive the data.
i start sails console and type
User
.find()
.populate('messages')
.exec(function(err,r){
while(r.length){
var thisUser=r.pop();
console.log(thisUser.toJSON())
}
});
But i always recive no data for messages, the messages field is always empty messages: []
my Current version of sails is 0.10.0-rc4

Sailsjs and Associations

Getting into sails.js - enjoying the cleanliness of models, routes, and the recent addition of associations. My dilemma:
I have Users, and Groups. There is a many-many relationship between the two.
var User = {
attributes: {
username: 'string',
groups: {
collection: 'group',
via: 'users'
}
}
};
module.exports = User;
...
var Group = {
attributes: {
name: 'string',
users: {
collection: 'user',
via: 'groups',
dominant: true
}
}
};
module.exports = Group;
I'm having difficulty understanding how I would save a user and it's associated groups.
Can I access the 'join table' directly?
From an ajax call, how should I be sending in the list of group ids to my controller?
If via REST URL, is this already accounted for in blueprint functions via update?
If so - what does the URL look like? /user/update/1?groups=1,2,3 ?
Is all of this just not supported yet? Any insight is helpful, thanks.
Documentation for these blueprints is forthcoming, but to link two records that have a many-to-many association, you can use the following REST url:
POST /user/[userId]/groups
where the body of the post is:
{id: [groupId]}
assuming that id is the primary key of the Group model. Starting with v0.10-rc5, you can also simultaneously create and a add a new group to a user by sending data about the new group in the POST body, without an id:
{name: 'myGroup'}
You can currently only add one linked entity at a time.
To add an entity programmatically, use the add method:
User.findOne(123).exec(function(err, user) {
if (err) {return res.serverError(err);}
// Add group with ID 1 to user with ID 123
user.groups.add(1);
// Add brand new group to user with ID 123
user.groups.add({name: 'myGroup'});
// Save the user, committing the additions
user.save(function(err, user) {
if (err) {return res.serverError(err);}
return res.json(user);
});
});
Just to answer your question about accessing the join tables directly,
Yes you can do that if you are using Model.query function. You need to check the namees of the join tables from DB itself. Not sure if it is recommended or not but I have found myself in such situations sometimes when it was unavoidable.
There have been times when the logic I was trying to implement involved a lot many queries and it was required to be executed as an atomic transaction.
In those case, I encapsulated all the DB logic in a stored function and executed that using Model.query
var myQuery = "select some_db_function(" + <param> + ")";
Model.query(myQuery, function(err, result){
if(err) return res.json(err);
else{
result = result.rows[0].some_db_function;
return res.json(result);
}
});
postgres has been a great help here due to json datatype which allowed me to pass params as JSON and also return values as JSON