Connect Mongodb to Sails JS - node.js

ive read various tutorials and instructions how to connect sails to js. Every tutorial is telling me to do this. I am new to mongodb btw.
I followed the instructions
install sails-mongo (npm install)
Edit the config/connection
mongo: {
adapter: 'sails-mongo',
host: 'localhost',
port: 54321,
database:'dbname'
}
Edit the config/models.js
connection:'mongo'
Edit the local.js
connections: {
mongodb: {
host : 'localhost',
port : 54321,
database : 'dbname'
}
}
so in my api/model/User.js
module.exports = {
attributes:{
name: {
type: 'string'
},
employedIn:{
collection:'company'
}
},
findUsers :function(opts,cb){
Users.findOne(opts).exec(function (err, theUser) {
// to do
// i wanna show the data of the user
});
}
}
I run console.log(Users) but I didnt find column/documents there.
Now, how am i going to get the collection named users from mongodb?
(Just like 'SELECT * FROM users' in SQL or db.users.find().pretty() )

You query a waterline model by models find or findOne method. You create a new record by create, update by update and delete by destroy methods. There are some more methods exposed by query interface. You have to call exec and pass a callback to it, to get it run. Documentation is here: Waterline Query Interface
So basically it's just:
User.create({
name: 'Max Mustermann'
}).exec(function(console.log));
User.find().exec(function(console.log));
User.create({
name: 'Peter Pan'
}).exec(function(console.log));
User.find().exec(console.log);
User.findOne({
where: { name: 'Max Mustermann' }
}).exec(function(err, user) {
user.destroy().exec(console.log);
});
You do not need a custom findUsers method on your model. This is just find:
// /api/model/User.js
module.exports = {
attributes:{
name: {
type: 'string'
},
employedIn:{
collection:'company'
}
}
}
You should use sails console to test.

Related

How to fix: ORA-00942: table or view does not exist' when querying Oracle with TypeORM and Loopback4

I'm trying to query an Oracle table with Loopback4 and TypeORM but i ORA-00942: table or view does not exist. Morover, after a while, i get another error like this:
'Cannot create a new connection named "default", because connection with such name already exist and it now has an active connection session.'
I'm new to Loopback4 and Nodejs - Typescript, so I called the createConnection method inside the controller.
Maybe i should call It somewhere centralized, but i don't know where and how to centralize this inside my project.
this is my entity
import { Entity, Column, PrimaryColumn, OneToOne } from "typeorm";
#Entity("Pratica")
export class Pratica {
#PrimaryColumn()
protocolloaci: number;
#Column()
codicepratica: string;
}
my controller method
#get('/pratiche/{id}', {
responses: {
'200': {
description: 'Pratica model instance',
content: { 'application/json': { schema: getModelSchemaRef(Pratica) } },
},
},
})
async findById(#param.path.number('id') id: number): Promise<Pratica> {
return new Promise((resolve) => {
createConnection({
type: 'oracle',
host: '10.64.2.226',
port: 1521,
sid: 'npra02s.svidbs003std',
username: 'sua03e',
password: 'iniziale',
entities: [
Pratica
],
logging: true
}).then(async connection => {
let praticaRepository = connection.getRepository(Pratica);
const pratica = await praticaRepository.find({ protocolloaci: id });
resolve(pratica[0]); // return using resolve
}).catch(error => console.log(error));
});
}
thank you
Does sua03e own the table you're trying to query? If yes, does the case of the table name match what's in the database (note that Oracle uppercases names by default)?
The "default" error message doesn't seem correct. Was the error prefixed with 'NJS-NNN'? If not, perhaps the error is coming from Loopback and not the driver.
You've put createConnection inside of a function that uses a connection to do work. I generally advise against this. It's better to create a pool in the initialization logic that's executed when you start the application. Then you only call getConnection on the pool inside the function that uses the connection. This is both more performant and easier to maintain.
Even though you're using Loopback, you might benefit from reading and working through this series on creating a REST API with Node.js and Oracle Database: https://jsao.io/2018/03/creating-a-rest-api-with-node-js-and-oracle-database/

Sails:How to join two different models using waterline

In MVC peoples are using join query to join the two different tables, but In sails.js what I have to use? There is any method in waterline?
The answer based on database you are using.
For instance, you need to populate values in Mongo not to join. Or you need to join tables if you are using MySQL or similar.
In a nutshell, all this stuff is covered via Waterline. So you can just declare model in api/models with associations. Joining and populating is executing under the Waterline adapter.
For instance, you have User and Comment.
// api/models/User.js
module.exports = {
attributes: {
name: {
type: 'string'
},
comments: {
collection: 'Comment',
via: 'user'
}
}
};
// api/models/Comment.js
module.exports = {
attributes: {
text: {
type: 'string'
},
user: {
model: 'User',
via: 'comments'
}
}
};
Then you are execute User.find() and get already joined\populated tables from database.
But, if you want to execute manual joining, you can use .populate() method on Model instance. For instance:
// api/controllers/AnyController.js
module.exports = {
action: function(req, res) {
User
.findOne('ID_HERE')
.populate('comments')
.then(function(result) {})
.catch(function(error) {});
}
};
You can read more about populate here - http://sailsjs.org/documentation/reference/waterline-orm/queries/populate

Sequelize findall for every findall is possible?

I have this tables. Clients have Projects and Users works in Projects
Clients
- id
- name
Projects
- id
- name
- client_id
Users
- id
- name
UserProject
- user_id
- project_id
I try to return all users of the every project of client for example id=1
Finally result, something like this JSON:
[{
id:1
name:"Project1"
users:[{
id:23
name:"Robert Stark"
},{
id:67
name: "John Snow"
}]
}, {
id:2
name:"Project2"
users:[{
id:1
name:"Aria Stark"
}]
}]
If I find projects it works fine
req.tables.Project.findAll({
where: {
client_id:1
}
}).success(function(projects) {
...
If I find Users of a project it works fine
req.tables.UserProject.findAll({
where: {
project_id:1
},
include: [
{ model: req.tables.User, as: 'User' }
]
}).success(function(UsersProject) {
...
But, how can I combine both finAlls to return all users in every project? Something like the next code, but that works well. How can I do it?
I found this: Node.js multiple Sequelize raw sql query sub queries but It doesn't work for me or I do not know how to use it, because I have 2 loops not only one. I have projects loop and users loop
req.tables.Project.findAll({
where: {
client_id:1
}
}).success(function(projects) {
var ret_projects=[];
projects.forEach(function (project) {
var ret_project={
id:project.id,
name:project.name,
data:project.created,
users:[]
});
req.tables.UserProject.findAll({
where: {
project_id:project.id
},
include: [
{ model: req.tables.User, as: 'User' }
]
}).success(function(UsersProject) {
var ret_users=[];
UsersProject.forEach(function (UserProject) {
ret_users.push({
id:UserProject.user.id,
name:UserProject.user.name,
email:UserProject.user.email
});
});
ret_project.users=ret_users;
ret_project.push(ret_project)
});
});
res.json(projects);
});
Sounds like you already have a solution, but I came across the same issue and came up with this solution.
Very similar to what cvng said, just using nested include. So use:
Project.belongsTo(Client);
Project.hasMany(User);
User.hasMany(Project);
Then:
req.tables.Client.find({
where: { id:req.params.id },
include: [{model: req.tables.Project, include : [req.tables.User]}]
}).success(function(clientProjectUsers) {
// Do something with clientProjectUsers.
// Which has the client, its projects, and its users.
});
}
The ability to 'Load further nested related models' is described through the param 'option.include[].include' here: Sequelize API Reference Model.
Maybe this will be useful to someone else in the future.
Cheers!
I think you would not have to query UserProject entity directly but instead use Sequelize Eager loading methods to retrieve your entities.
Your models associations should look something like this :
Project.belongsTo(Client);
Project.hasMany(User, { as: 'Workers' });
User.hasMany(Project);
and once you have all projects related to client, your finder method :
Project
.findAll({ include: [{ model: User, as: 'Workers' })
.success(function(users) {
// do success things here
}
Take a look at, http://sequelizejs.com/docs/1.7.8/models#eager-loading.
Hope it helps !
Finally!!
cvng, your example helpme a lot, thanks.
But I have 3 levels Client, Project, thi is my final solution, is this a good solution?
req.tables.Client.find({
where: { id:req.params.id },
include: [{ model: req.tables.Project, as: 'Projects' }]
}).success(function(client) {
var ret ={
id:client.id,
name:client.name,
projects:[]
};
done = _.after(client.projects.length, function () {
res.json(ret);
});
client.projects.forEach(function (project) {
project.getUsers().success(function(users) {
var u=[]
users.forEach(function (user) {
u.push({
id:user.id,
name:user.name,
});
});
ret.projects.push({
id:project.id,
name:project.name,
users:u
});
done();
});
});
});

Query mongodb with geddy

While trying out the node.js framework geddy (on windows) and i've run into a bit of a problem.
I'm trying to query mongodb, in my controller, using the .first() method from my Users Model like so:
geddy.model.User.first({name: 'jdoe'}, function (err, data) {
if (err) {
throw err;
} else {
console.log(data);
}
});
Strangely enough i'm not getting any output, error, nothing. The user jdoe exists in the collection so it should output something, right ? Am i doing something wrong ?
My model is defined as:
var User = function () {
this.defineProperties({
username: {type: 'string', required: true},
password: {type: 'string', required: true},
});
this.autoIncrementId = true;
};
User = geddy.model.register('User', User);
The default adapter is set to mongo in development.js, when i ran geddy for the first time it created my database and it has inserted the Users collection correctly.
Any idea on whats going wrong here ?
UPDATE:
added development.js as requested
var config = {
detailedErrors: true
, debug: true
, hostname: null
, port: 4000
, model: {
defaultAdapter: 'mongo',
}
,db: {
mongo: {
dbname: 'knowledgebase'
}
}
, sessions: {
store: 'memory'
, key: 'sid'
, expiry: 14 * 24 * 60 * 60
}
};
module.exports = config;
also my collections on mongo ( created by geddy )
> show collections
User
system.indexes
users
note that somehow geddy is creating two collections instead of one
It looks like you're being hit by this bug: https://github.com/mde/geddy/issues/240
As it is, Geddy accidentally creates two collections per model. It always uses the lowercased pluralized collection to do read/writes though. Are you sure that your data was in that collection and not in the other?
At any rate, from the comments, it sounds like you've got this one covered already.

How to organize a node app that uses sequelize?

I am looking for an example nodejs app that uses the sequelize ORM.
My main concern is that it seems next to impossible to define your models in separate js files if those models have complex relationships to one another because of require() dependency loops. Maybe people define all their models in one file that is very very long?
I am mainly interested in how the models are defined and use through out the app. I would like to have some validation that what i am doing on my own is the "good" way to do things.
The short story
The trick in this case is not to initialize the model in the file but just to provide the necesary information for its initialization and let a centralized module take care of the models setup and instantiation.
So the steps are:
Have several Model files with data about the model, like fields, relationships and options.
Have a singleton module which loads all those files and setup all the model classes and relationships.
Setup your singleton module at the app.js file.
Get the model classes from the singleton module do not use require on your model files, load the models from the singleton instead.
The longer story
Here is a more detailed description of this solution with the corresponding source code:
http://jeydotc.github.io/blog/2012/10/30/EXPRESS-WITH-SEQUELIZE.html
EDIT: This is a very old answer! (read down for info)
It's old and limited in many ways!
First, as #jinglesthula mentioned in comments (and I experienced it too) - there are problems with requiring those files. It's because require doesn't work the same way as readdirSync!
Second - you are very limited in relations - the code doesn't provide options to those associations so you are UNABLE to create belongsToMany as it needs through property. You can make the most basic assocs.
Third - you are very limited in model relations! If you read closely the code, you will see that relations is an Object instead of an Array, so if you want to make more than one associations of the same type (like having two times belongsTo) - you cannot!
Fourth - You don't need that singleton thingy. Every module in nodejs is singleton by itself, so all this makes is pretty complex for no reason.
You should see Farm's answer! (The link to the article is broken, but I'll fix it with this official sample from sequelize: https://github.com/sequelize/express-example/blob/master/models/index.js - you can browse the whole project to get an idea of what's going on).
p.s.
I'm editing this post as it's so upvoted that people won't even see any new answers (as I did).
Edit: Just changed the link to a copy of the same post, but in a Github Page
SequelizeJS has a article on their website which solves this problem.
Link is broken, but you can find the working sample project here and browse it. See edited answer above to see why this is a better solution.
Extract from article:
models/index.js
The idea of this file is to configure a connection to the database and to collect all Model definitions. Once everything is in place, we will call the method associated on each of the Models. This method can be used to associate the Model with others.
var fs = require('fs')
, path = require('path')
, Sequelize = require('sequelize')
, lodash = require('lodash')
, sequelize = new Sequelize('sequelize_test', 'root', null)
, db = {}
fs.readdirSync(__dirname)
.filter(function(file) {
return (file.indexOf('.') !== 0) && (file !== 'index.js')
})
.forEach(function(file) {
var model = sequelize.import(path.join(__dirname, file))
db[model.name] = model
})
Object.keys(db).forEach(function(modelName) {
if (db[modelName].options.hasOwnProperty('associate')) {
db[modelName].options.associate(db)
}
})
module.exports = lodash.extend({
sequelize: sequelize,
Sequelize: Sequelize
}, db)
I've create a package sequelize-connect to help people deal with this issue. It follows the Sequelize suggested convention here: http://sequelize.readthedocs.org/en/1.7.0/articles/express/
Additionally it also functions a bit more like Mongoose in terms of its interface. It allows you to specify a set of locations where your models are located and also allows you to define a custom matching function to match your model files.
The usage is basically like this:
var orm = require('sequelize-connect');
orm.discover = ["/my/model/path/1", "/path/to/models/2"]; // 1 to n paths can be specified here
orm.connect(db, user, passwd, options); // initialize the sequelize connection and models
Then you can access the models and sequelize like so:
var orm = require('sequelize-connect');
var sequelize = orm.sequelize;
var Sequelize = orm.Sequelize;
var models = orm.models;
var User = models.User;
Hopefully this helps someone out.
I started using Sequelize in Express.js app. Soon enough ran into issues of the nature you're describing. Maybe I did not quite understand Sequelize, but to me doing things more than just selecting from one table wasn't really convenient. And where ordinarily you would use select from two or more tables, or a union in pure SQL, you would have to run separate queries, and with the async nature of Node it's just added complexity.
Therefore I moved away from using Sequelize. Moreover I am switching from using ANY data fetching from DB in the models. In my opinion it is better to abstract getting data completely. And reasons are - imagine that you don't just use MySQL (in my case, I use MySQL and MongoDB side by side), but you can get your data from any data provider and any transport method, e.g. SQL, no-SQL, filesystem, external API, FTP, SSH etc. If you tried to do all of it in the models, you would eventually create complex and hard to understand code that would be hard to upgrade and debug.
Now what you want to do is to have models get data from a layer that knows where and how to get it, but your models only use API methods, e.g. fetch, save, delete etc. And inside this layer you have specific implementations for specific data providers. E.g. you can request certain data from a PHP file on a local machine or from Facebook API or from Amazon AWS or from remote HTML document, etc.
PS some of these ideas were borrowed from Architect by Cloud9: http://events.yandex.ru/talks/300/
I set it up as Farm and the documentation describe.
But I was having the additonal problem that in my instance methods and class methods that I would attach to the models in each function I would need to require the index file to get a hold of other database objects.
Solved it by making them accessible to all models.
var Config = require('../config/config');
var fs = require('fs');
var path = require('path');
var Sequelize = require('sequelize');
var _ = require('lodash');
var sequelize;
var db = {};
var dbName, dbUsername, dbPassword, dbPort, dbHost;
// set above vars
var sequelize = new Sequelize(dbName, dbUsername, dbPassword, {
dialect: 'postgres', protocol: 'postgres', port: dbPort, logging: false, host: dbHost,
define: {
classMethods: {
db: function () {
return db;
},
Sequelize: function () {
return Sequelize;
}
}
}
});
fs.readdirSync(__dirname).filter(function(file) {
return (file.indexOf('.') !== 0) && (file !== 'index.js');
}).forEach(function(file) {
var model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(function(modelName) {
if ('associate' in db[modelName]) {
db[modelName].associate(db);
}
});
module.exports = _.extend({
sequelize: sequelize,
Sequelize: Sequelize
}, db);
And in the model file
var classMethods = {
createFromParams: function (userParams) {
var user = this.build(userParams);
return this.db().PromoCode.find({where: {name: user.promoCode}}).then(function (code) {
user.credits += code.credits;
return user.save();
});
}
};
module.exports = function(sequelize, DataTypes) {
return sequelize.define("User", {
userId: DataTypes.STRING,
}, { tableName: 'users',
classMethods: classMethods
});
};
I only did this for the class methods but you could also do the same thing for instance methods.
I am following the official guide: http://sequelizejs.com/heroku, which has a models folder, set up each module in separate files, and have a index file to import them and set the relationship among them.
Sample model sequelize
'use strict';
const getRole = require('../helpers/getRole')
const library = require('../helpers/library')
const Op = require('sequelize').Op
module.exports = (sequelize, DataTypes) => {
var User = sequelize.define('User', {
AdminId: DataTypes.INTEGER,
name: {
type: DataTypes.STRING,
validate: {
notEmpty: {
args: true,
msg: 'Name must be filled !!'
},
}
},
email: {
type: DataTypes.STRING,
validate: {
notEmpty: {
args: true,
msg: 'Email must be filled !!'
},
isUnique: function(value, next) {
User.findAll({
where:{
email: value,
id: { [Op.ne]: this.id, }
}
})
.then(function(user) {
if (user.length == 0) {
next()
} else {
next('Email already used !!')
}
})
.catch(function(err) {
next(err)
})
}
}
},
password: {
type: DataTypes.STRING,
validate: {
notEmpty: {
args: true,
msg: 'Password must be filled !!'
},
len: {
args: [6, 255],
msg: 'Password at least 6 characters !!'
}
}
},
role: {
type: DataTypes.INTEGER,
validate: {
customValidation: function(value, next) {
if (value == '') {
next('Please choose a role !!')
} else {
next()
}
}
}
},
gender: {
type: DataTypes.INTEGER,
validate: {
notEmpty: {
args: true,
msg: 'Gender must be filled !!'
},
}
},
handphone: {
type: DataTypes.STRING,
validate: {
notEmpty: {
args: true,
msg: 'Mobile no. must be filled !!'
},
}
},
address: DataTypes.TEXT,
photo: DataTypes.STRING,
reset_token: DataTypes.STRING,
reset_expired: DataTypes.DATE,
status: DataTypes.INTEGER
}, {
hooks: {
beforeCreate: (user, options) => {
user.password = library.encrypt(user.password)
},
beforeUpdate: (user, options) => {
user.password = library.encrypt(user.password)
}
}
});
User.prototype.check_password = function (userPassword, callback) {
if (library.comparePassword(userPassword, this.password)) {
callback(true)
}else{
callback(false)
}
}
User.prototype.getRole = function() {
return getRole(this.role)
}
User.associate = function(models) {
User.hasMany(models.Request)
}
return User;
};
You can import models from other files with sequelize.import
http://sequelizejs.com/documentation#models-import
That way you can have one singleton module for sequelize, which then loads all the other models.
Actually this answer is quite similar to user1778770`s answer.
I am looking for an example nodejs app that uses the sequelize ORM.
You might be interested in looking at the PEAN.JS boilerplate solution.
https://github.com/StetSolutions/pean
PEAN.JS is a full-stack JavaScript open-source solution, which provides a solid starting point for PostgreSQL, Node.js, Express, and AngularJS based applications.
The PEAN project is a fork of the MEAN.JS project (not to be confused with MEAN.IO or the generic MEAN stack).
https://github.com/meanjs/mean
PEAN replaces MongoDB and the Mongoose ORM with PostgreSQL and Sequelize. A primary benefit of the MEAN.JS project is the organization it provides to a stack that has many moving pieces.
You can also use a dependency injection which provides an elegant solution to this. Here's one https://github.com/justmoon/reduct
What worked for me is to:
Create a file for each individual model like user.model.js in folder models/user.model.js.
Create index.js in models/index.js and import every model to it.
Define association, run sync method in index.js and export all models.
Create a database.js file that holds information about Sequalize and import it and initialize it in app.js
Example of one models/user.model.js
import { DataTypes } from 'sequelize';
import { sequelize } from '../database.js';
export const User = sequelize.define("user",{
uid:{
type:DataTypes.STRING,
allowNull:false,
unique: true
},
email:{
type:DataTypes.STRING,
allowNull:true
},
firstName:{
type:DataTypes.STRING,
allowNull:true
},
lastName:{
type:DataTypes.STRING,
allowNull:true
},
companyWebsite:{
type:DataTypes.STRING,
allowNull:true
},
domain:{
type:DataTypes.STRING,
allowNull:true
},
hsPortalId:{
type:DataTypes.INTEGER,
allowNull:true
},
integrations:{
type:DataTypes.STRING
},
brandedKeywords : {
type:DataTypes.STRING
},
companyName: {
type:DataTypes.STRING
},
companyStreet:{
type:DataTypes.STRING
},
companyZip:{
type:DataTypes.STRING
},
companyCountry:{
type:DataTypes.STRING
},
vatId:{
type:DataTypes.STRING
},
brand:{
type:DataTypes.STRING
},
markets:{
type:DataTypes.JSON
},
niche : {
type:DataTypes.JSON
}
},{schema:"api"})
Example of models/index.js
import { Billing } from './billing.model.js';
import { Competitor } from './competitors.model.js';
import { DemoAccount } from './demo.model.js';
import { Notification } from './notification.model.js';
import { Product } from './products.model.js';
import { Reseller } from './resellers.model.js';
import {Reseller_User} from './reseller_user.model.js'
import { Tag } from './tags.model.js';
import {User} from './user.model.js'
Reseller.belongsToMany(User, { through: Reseller_User });
User.belongsToMany(Reseller, { through: Reseller_User });
// this will create a UserId column on your Product table
// https://www.youtube.com/watch?v=HJGWu0cZUe8 40min
User.hasMany(Product,{onDelete: 'CASCADE',})
Product.belongsTo(User)
User.hasOne(DemoAccount,{onDelete: 'CASCADE',})
DemoAccount.belongsTo(User)
User.hasMany(Billing,{onDelete: 'CASCADE',})
Billing.belongsTo(User)
User.hasMany(Tag,{onDelete: 'CASCADE',})
Tag.belongsTo(User)
User.hasMany(Competitor,{onDelete: 'CASCADE'})
Competitor.belongsTo(User)
User.hasMany(Notification,{onDelete: 'CASCADE'})
Notification.belongsTo(User)
User.sync().then(
() => console.log("Sync complete")
);
Reseller.sync().then(
() => console.log("Sync complete")
);
Reseller_User.sync().then(
() => console.log("Sync complete")
);
Product.sync().then(
() => console.log("Product Sync complete")
);
Competitor.sync().then(
() => console.log("Competitor Sync complete")
);
Notification.sync().then(
() => console.log("Competitor Sync complete")
);
Billing.sync().then(
() => console.log("Billing Sync complete")
);
Tag.sync().then(
() => console.log("Tag Sync complete")
);
DemoAccount.sync()
export { User, Reseller, Product, Competitor, Notification, DemoAccount, Billing, Tag };
// DemoAccount.sync({force:true}).then(
// () => console.log("Sync complete")
// );

Resources