I am trying to take advantage of the Waterline ORM in Sails.js to build an example app that has a model called 'Category'. Because a category can have multiple sub categories, I have the following one-to-many association for this model:
module.exports = {
adapter: 'mongo',
// adapter: 'someMysqlServer',
attributes: {
categoryTitle: {
type: 'string',
required: true
},
parentCat: {
model: 'category'
},
subCategories: {
collection: 'category',
via: 'parentCat'
},
articles: {
collection: 'article',
via: 'category',
required: false
}
}
};
In the CategoryController.js, I have the create method that first tries to see if the new category has a parent category assigned to it; however, I feel the code is quite messy, and the parentCat in Mongodb is always empty even if I tried to assign a parent category in the form submission. So I am wondering if this is the right way to do it:
create: function(req, res, next) {
var params = req.allParams();
// set parent category if exists
if (params.parentCat) {
Category.findOne({categoryTitle : params.parentCat})
.exec(function(err, category) {
if (err) {
return false; //not found
} else {
params.parentCat = category.id; //found the parent category
console.log('parent cat id is: ', category.id);
}
});
}
Category.create(params, function(err, newCategory) {
if (err) {
return next(err);
} else {
console.log('new category created');
}
console.log('successfully added the category: ' + newCategory.categoryTitle)
res.redirect('/category');
}); // create the category
}
The issue of your code is the callback.
I created a new version of code with the async feature (which is already in your sails app), hope it will help you.
create: function(req, res, next) {
var params = req.allParams();
async.waterfall([
function(callback) {
// set parent category if exists
if (params.parentCat) {
Category.findOne({
categoryTitle: params.parentCat
})
.exec(function(err, category) {
if (err) {
return false; //not found
}
params.parentCat = category.id; //found the parent category
console.log('parent cat id is: ', category.id);
callback(null, params);
});
} else {
callback(null, params);
}
},
function(params, callback) {
Category.create(params, function(err, newCategory) {
if (err) {
return next(err);
}
console.log('successfully added the category: ' + newCategory.categoryTitle);
callback(null, newCategory);
}); // create the category
}
], function(err, result) {
console.dir(result);
res.redirect('/category');
});
}
Related
I want to search my User collection via full text search on both the firstName and lastName property.
I create my index like this:
//connect to connector
server.datasources.db.connector.connect(function (err, db) {
//create index on firstName and lastName
db.collection('User').createIndex(
{ firstName: "text", lastName: "text" },
function (err) {
if (!err) {
//run autoupdate on each model
Promise.each(server.models(), function (model) {
if (model.dataSource) {
var autoupdate = Promise.promisify(model.dataSource.autoupdate);
if (autoupdate) {
console.log("running autoupdate on model: " + model.modelName);
return autoupdate.call(model.dataSource, model.modelName);
}
}
});
}
else {
console.log(err);
}
});
});
But when i query the database for a search string i always get zero results via direct access:
var mongodb = models.User.getDataSource().connector.collection(models.User.modelName);
mongodb.find({ $text: { $search: "Test" } },
function (err, resp) {
resp.toArray(function (err, docs) {
//docs always empty
cb(null, docs);
});
});
Or i get every entry when i use the Angular client like:
User.find({ where: { '$text': { search: "Test" } } }).$promise.then(function (resp) {
console.log(resp)
});
What am i missing?
I'm using async each to iterate over objects and perform a query to populate their children. Each site has locations which have floors which have areas. My sites and locations populate just fine, however, that is where it stops. Sails outputs that it is looking for floors and areas though but they are never assigned. Any ideas?
gg: function (req, res) {
var userID = req.param('id');
User.findOne({ id: userID }).populate('sites').exec(function afterFind(err, foundUser) {
if (err) return res.json({ status: 'failure', message: 'Server error' });
if (foundUser === undefined) return res.json({ status: 'failure', message: 'User was not found' });
var resultToJson = foundUser.toJSON();
var sites = resultToJson.sites;
async.each(sites, function (site, siteCb) {
sails.log.debug('Finding locations for ' + site.name);
Locations.find({ site: site.id }).exec(function afterFind(err, foundLocations) {
if (err) {
siteCb(err);
} else {
site['locations'] = foundLocations;
async.each(site['locations'], function (location, locCb) {
sails.log.debug('Finding floors for ' + location.name);
Floor.find({ location: location.id }).exec(function afterFind(err, foundFloors) {
if (err) {
locCb(err);
} else {
location['floors'] = foundFloors;
async.each(location['floors'], function (floor, floorCb) {
sails.log.debug('Finding areas for ' + floor.name);
Area.find({ floor: floor.id }).exec(function afterFind(err, foundAreas) {
if (err) {
floorCb(err);
} else {
floor['areas'] = foundAreas;
floorCb();
}
});
}, function (floorError) {
if (floorError) {
locCb(floorError);
}
else {
locCb();
}
});
}
});
}, function (locError) {
if (locError) {
siteCb(locError);
} else {
siteCb();
}
});
}
});
}, function (siteError) {
if (siteError) {
sails.log.debug(siteError);
return res.json({ status: 'failure', message: 'Server error' });
} else {
return res.json({ status: 'success', message: 'Sites for user retrieved', sites: sites });
}
});
});
}
This code should be fixed with use of toJSON() on foundLocations. Anytime when you're overriding a defined attribute with populated one (or something else) it will not work when using a ORM returned object, use toJSON() or something and assign to plain JS object.
Ref: https://stackoverflow.com/a/43500017/1435132
Also, any reason to not use populate with Locations.find?
Im trying to find all users that are doing a certain subject. Here is the relationship:
User.js
attributes: {
subjects: { collection: 'subject', via: 'users', dominant: true }, // Many to Many
levels: { collection: 'level', via: 'users', dominant: true } // Many to Many
}
Subject.js
attributes: {
users : { collection: 'user', via: 'subjects' } // Many to Many
}
Level.js
attributes: {
users : { collection: 'user', via: 'levels' } // Many to Many
}
I would like to be able to do a find on User and only return users that have a relationship to a specific subject. Something like this:
User.find({ subjects: { 'contains': 1 } })
.exec(function(err, results){
if(err) return res.serverError(err);
users = results;
return res.json(users);
});
I know that I can do it by doing:
Subject.findOne({id:1})
.populate('users')
.exec(function(err, results){
if(err) return res.serverError(err);
users = results.users;
return res.json(users);
});
However I do not want to do it this way as I may want to filter by more than just subject. For example using the above I may want to find all users that are doing subject 1 AND are doing level 2.
User.find({ subjects: { 'contains': 1 }, levels: { 'contains': 2 } })
.exec(function(err, results){
if(err) return res.serverError(err);
users = results;
return res.json(users);
});
I am using sails v0.12.3
There is no such mechanism as far as I know where you can search as you mentioned.
Though you can do it in following ways:
Using condition in populate
User.find()
.populate('subjects', {
name: 'subject-1'
})
.populate('levels', {
name: 'level-1'
})
.exec(function(err, user) {
if (err) {
// Error
}
sails.log.verbose(user);
});
Using async
async.auto({
subject: function(cb) {
Subject.findOne({
name: 'subject-1'
})
.exec(cb);
},
level: function(cb) {
Level.findOne({
name: 'level-1'
})
.exec(cb);
},
user: ['subject', 'level', function(cb, results) {
User.find({
subjects: results.subject.id,
levels: results.level.id
})
.exec(function(err, users) {
if (err) {
return cb(err);
}
sails.log.verbose(users); // Required user
});
}]
}, function(err, results) {
// Callback
});
Using .query() method where you have to write a JOIN query to fetch data.
How do i Find all registers of one only column in sails
my model is:
models/Blog.js
module.exports = {
schema: true,
attributes: {
title:'string',
content:{
type:'string',
required: true
}
}
};
my controller:
controllers/BlogController.js
module.exports = {
index: function (req, res) {
Blog.find()
.exec(function (err, content) {
if (err) {
res.send(400);
} else {
res.view({OnlyContent:content}); // Only view all Content ?
}
});
},
_config: {}
};
To only send the data of content to your client, you need to loop through the returned array of blogs and filter out the content.
index: function (req, res) {
Blog.find().exec(function (err, allBlogs) {
if (err) {
res.send(400);
} else {
var allContent = [];
allBlogs.forEach(function (blog) {
allContent.push(blog.content);
})
res.view({
OnlyContent: allContent
});
}
});
}
RouteHandler
function getProfile(req, res) {
var graphs = dataSaver.getGraphs(req.user.email)
console.log(graphs)
res.render('profile', {
title: 'EZgraph | User Home',
userGraphs: graphs
})
}
Db code
function getGraphs(username) {
model.findOne({
email: username
},
function(err, user) {
if (err) {
console.log('err')
}
if (!user) {
console.log('no user found!')
} else {
var graphs = user.savedGraphs
console.log(graphs)
return graphs
}
}
)
}
using the above two methods I'm trying to pass data read from the DB to a jade view. The problem is that within the scope of the 2nd method that reads from the db, the object is read fine, the console.log call shows me that. Once I return this object though and return to the scope of the route handler, the variable that should be equal to the object no prints as undefined. How do I fix this?
EDIT
In repsonse to the comments I tried the following, it isn't pretty at all but I run into the same problem.
Handler + helper
function getProfile(req, res) {
var graphs = dataSaver.getGraphs(req.user.email, readSuccess)
console.log(graphs);
res.render('profile', {
title: 'EZgraph | User Home',
userGraphs: graphs
})
}
function readSuccess(data) {
return data
}
db code
function getGraphs(username, callback) {
model.findOne({
email: username
},
function(err, user) {
if (err) {
console.log('err')
}
if (!user) {
console.log('no user found!')
} else {
callback(user.savedGraphs)
}
}
)
}