Sequelize create belongTo instance with reference - node.js

I'm using Sequelize for my new NodeJs project
I defined two models: BusinessUnit and Store with this association: Store.belongsTo(BusinessUnit);
module.test_data_insertion = function() {
models.BusinessUnit.findOne({
where: {
BUID: "001"
}
}).then(element => {
fs.readdirSync(dir).forEach(function(file) {
var contents = fs.readFileSync(dir + file);
var jsonContent = JSON.parse(contents);
models.Store.create({
StoreId: jsonContent.StoreId,
StoreName: jsonContent.StoreName,
businessUnitId: element.id
});
});
});
};
I don't find to right way to reference the element in my Store, I would like something like this where I don't have to reference an id field directly
module.test_data_insertion = function() {
models.BusinessUnit.findOne({
where: {
BUID: "001"
}
}).then(element => {
fs.readdirSync(dir).forEach(function(file) {
var contents = fs.readFileSync(dir + file);
var jsonContent = JSON.parse(contents);
models.Store.create({
StoreId: jsonContent.StoreId,
StoreName: jsonContent.StoreName,
businessUnit: element
});
});
});
};
It should be simple but I don't see it. Thanks

Assuming your association are setup correctly, you are looking for something like this:
module.test_data_insertion = function() {
models.BusinessUnit.findOne({
where: {
BUID: "001"
}
}).then(element => {
fs.readdirSync(dir).forEach(function(file) {
var contents = fs.readFileSync(dir + file);
var jsonContent = JSON.parse(contents);
element.setStore({
StoreId: jsonContent.StoreId,
StoreName: jsonContent.StoreName,
});
});
});
};
please read belongsTo from doc which enables a set method,
However please note if this is a one to many relationship(one business unit with many stores) you may need to switch to belongsToMany it has add method. because setStore would override previous set.
Also, i'm not sure if any of those method would work inside .forEach correctly since they are promise, you may need to switch to a traditional for loop.

Related

How to retrieve from Firebase based on current user

Here is the deal.
I am using VueFire and would like to retrieve data from a Firebase database I have set up. In there I have a node with users and each one is defined by a unique id. Also for every user I have an array with a cart full of items they would to purchase. To make it dynamic I am calling the reference in the firebase hook like so:
export default {
firebase:{
cart: app.database().ref('users')
}, //and so on and so on
}
Instead of .ref('users') I want to retrieve the current user, using his unique id like so: .ref('users/' + user.uid + '/cart')
To get the current user uid I do the observer that is firebase.auth().onAuthStataChanged(user=>{//code here})
Problem is, since this is asynchronous, the firebase hook activates before the current user is retrieved. I have tried to simply call firebase.auth().currentUser, but this is also slow and unreliable to use.
I am open to any kinds of suggestions!
I made a solution that worked for my case and might work for yours.
What I did is attach value listeners when the user gets authenticated and then I turn them off when the user loses authentication. With this method I'm only asking to retrieve user data if the user is actually authenticated. Here's how I did it:
this.$root.firebase.auth().onAuthStateChanged(user => {
if (user) {
this.$store.dispatch('setUserUID', user.uid)
// Listen for User Data
let dataRef = this.$root.db.ref('/userdata/' + user.uid)
let self = this
dataRef.on('value', function(snap) {
let value = snap.val()
self.$store.dispatch('setUserData', value)
})
}
else {
// Stop Listening for User Data
this.$root.db.ref('/userdata/' + this.$store.state.user.uid).off()
this.$store.dispatch('setUserUID', null)
}
})
Okay I got it. Thanks to #Daniel D for his tip on binding as an array or object. So as it turns out I don't have to do it in the firebase reference hook, I just have to bind it as array in the mounted() hook for example. I just declare an empty cart: [] in data and then fill it like so:
<script>
import changeColorMixin from '../mixins/changeColorMixin'
import animateFunction from '../mixins/animationMixin'
import Firebase from 'firebase'
import app from '../firebaseApp'
export default {
data(){
return{
isLoggedIn: '',
cart: []
}
},
methods:{
changeColor: changeColorMixin,
animateEntrance: animateFunction,
promptLogin: function(){
console.log('you need to login!');
},
chooseSize: function($event){
$($event.target).parents().eq(2).addClass('chosen');
},
closeOrder: function($event) {
$($event.target).parents().eq(2).removeClass('chosen');
},
makeOrder: function($event){
var $this = this;
Firebase.auth().onAuthStateChanged(function(user){
if(user){
var cartRef = app.database().ref('users/' + user.uid + '/cart');
var currentCart;
cartRef.once('value', function(result) {
currentCart = result.val();
var size = $($event.target).html();
var name = $($event.target).parents().eq(2).attr('id');
app.database().ref('users/' + user.uid + '/cart/' + name + '/count').once('value', function(snap){
var count = snap.val();
if(count > 0){
count = count + 1;
}else{
count = 1;
}
cartRef.child(name).set({
size: size,
count: count,
name: name
});
});
});
}else{
console.log('gotta login mate');
}
});
}
},
mounted(){
Firebase.auth().onAuthStateChanged(user => {
if(user){
this.isLoggedIn = true;
var cartRef = app.database().ref('users/' + user.uid + '/cart');
this.$bindAsArray('cart', cartRef);
}else{
this.isLoggedIn = false;
}
});
this.animateEntrance();
this.changeColor();
}
}
</script>
As I said big thanks to #Daniel D

Meteor, MongoDb: Double inserting to collection on few occurences

I noticed this strange behaviour, when for few users only on production, it inserts every item multiple times to collection on asynchronous Meteor call. I tried multiple things, but nothing worked. I can't test on localhost, bc it never happens to me on localhost or in production.
I spent the whole night solving this, but didn't find any solution. I suppose it's caused by new Date(), but I have to call it somewhere. The production server is in Amsterdam and it seems like it happens only for users located further outside of Europe.
I found this to be similar issue, but can't really wrap my head on how to implement - https://github.com/meteor/meteor/issues/4263
Slug is what same songs are supposed to have the same.
This is the workflow, user clicks on song that triggers addNewSong function:
addNewSong = function (track) {
Globals.current_track = track;
checkIfSongAlreadySaved();
}
I need to check if song is already in collection, if it's -> route to it, else create the new song and route to it.
checkIfSongAlreadySaved = function() {
loadPrimaryGlobalItems();
Meteor.call('checkIfSongAlreadySaved', Globals.current_song_item_slug, function(error, result) {
if( result.item ) {
Globals.current_song_item_id = result.item._id;
Globals.current_song_item_slug = result.item.slug;
routeToSongPage();
if (! (result.item.download && result.item.mp3) ) {
downloadSong();
}
}
else {
loadSecondaryGlobalItems();
var item = {
slug:Globals.current_song_item_slug,
duration:Globals.current_duration,
thumbnail:Globals.current_song_thumbnail,
title:Globals.current_cleaned_song,
album:Globals.current_track.album,
artist:Globals.current_track.artists[0],
track:Globals.current_track.name,
date:result.date,
}
Globals.current_song_item_id = Songs.insert(item);
routeToSongPage();
downloadSong();
recentSongItem(result.date);
}
});
}
Add recent song
recentSongItem = function (date) {
Recentsongs.insert({
slug:Globals.current_song_item_slug,
songId:Globals.current_song_item_id,
title:Globals.current_cleaned_song,
duration:Globals.current_duration,
date:date,
});
}
If creating new song,
downloadSong = function() {
Meteor.call('findSong', Globals.current_song, function(error, result) {
console.log(result);
if (result) {
Globals.current_song_mp3 = true;
updateSongItemDownload(result.itemDetails);
}
else {
alert('not found')
}
});
}
and update song, to add download and mp3 values.
updateSongItemDownload = function(link) {
Songs.update({
_id: Globals.current_song_item_id
},
{
$set: {
download: link,
mp3: Globals.current_song_mp3,
}
});
}
Server methods:
Meteor.methods({
checkIfSongAlreadySaved: function(slug) {
return {item: Songs.findOne({slug:slug}), date: new Date()};
},
findSong:function(song) {
ServerGlobals.current_song = song;
var result = searchSite();
return result;
},
});
EDIT:
This is my subscription, just in case it might be causing the problem:
Template.songPage.onCreated(function() {
Session.set('processing', true);
var self = this;
self.autorun(function() {
var id = Router.current().params.id;
self.subscribe('singleSong', id);
var item = Songs.findOne({_id: id});
if (item) {
if (item.download) {
createSong(item.download);
}
else if( item.download === false ) {
console.log('item not found');
}
Session.set('loader', false);
Session.set('processing', false);
}
});
});
Meteor.publish('singleSong', function(id) {
check(id, String);
return Songs.find({_id: id});
});
You can apply a unique index on the slug field to ensure the same slug can only exist once and the second operation to insert will fail and show up as an error in your callback which you can discard or alert user as you desire.
db.collection.createIndex( { slug: 1 }, { unique: true } )
You will need to clear or modify the slug name on the dups from the db before applying the index though

sequelize.js - Find by id and return result

I have a function,
var findUserDevice = function(userDeviceId){
var device = db.DeviceUser.find({
where: {
id: userDeviceId
}
}).then(function(device) {
if (!device) {
return 'not find';
}
return device.dataValues;
});
};
but this function does not return anything...
var UserDevice = findUserDevice(req.body.deviceUserId);
console.log(UserDevice);// undefined
The operation you are trying to do is async, which means that you need to use a callback. Since sequelize is build on top of Promises, you should actually write your code like this :
var findUserDevice = function(userDeviceId){
// return the promise itself
return db.DeviceUser.find({
where: {
id: userDeviceId
}
}).then(function(device) {
if (!device) {
return 'not find';
}
return device.dataValues;
});
};
And later use it like :
findUserDevice(req.body.deviceUserId).then( function(UserDevice) {
console.log(UserDevice);
});
It's 2020, async & await are becoming more popular. You can change your code to:
const findUserDevice = async function (userDeviceId) {
const device = await db.DeviceUser.findOne({
where: {
id: userDeviceId
}
});
if (device === null) {
return 'device not found';
}
return device.dataValues;
};
(async () => {
// ...
const UserDevice = await findUserDevice(req.body.deviceUserId);
console.log(UserDevice);
// ...
})()
IMHO, the code above is way more readable.
If you are getting undefined instead of 'not find' on the console, it means your function is returning a value. The problem might be dataValues is actually undefined. You need to check for the content of device.
Hint: Try returning just device or device.id
PS. If you want to do the search based on id, should go for findById() function of your model.
var device = db.DeviceUser.findById(userDeviceId).then(function(device) {
if (!device) {
return 'not find';
}
return device.dataValues;
});
This function received params id, this worker for me:
const { customer } = require('../models');
const get = async function(req, res){
let id = req.params.id;
[err, singleCustomer] = await to(customer.findByPk(id, { raw : true }));
return ReS(res, { message :'Obtener cliente: : ', data : JSON.stringify(singleCustomer) });
}

Loopback discoverAndBuildModels not generating models

I'm trying to get Loopback to discover and build my first table. I've used the simple example on their page at the bottom here:
http://docs.strongloop.com/display/LB/Database+discovery+API#DatabasediscoveryAPI-Exampleofbuildingmodelsviadiscovery
and I see the output of the table I'm discovering, but the API Explorer doesn't show the table or any newly generated endpoints. Also, the model-config.js file is not updated with the new table object. Here is the basic section of the code done on server start:
var loopback = require('loopback');
var boot = require('loopback-boot');
var DataSource = require('loopback-datasource-juggler').DataSource;
var mysqlSource = require('./datasources.json');
var dataSource = new DataSource('mssql', mysqlSource.mysqlserver);
var app = module.exports = loopback();
// Set up the /favicon.ico
app.use(loopback.favicon());
// request pre-processing middleware
app.use(loopback.compress());
// -- Add your pre-processing middleware here --
dataSource.discoverAndBuildModels('CATS', {owner: 'mamacat'}, function (err, models) {
models.Cat.find(function (err, cat) {
if (err) {
console.error(err);
} else {
console.log(cat);
}
dataSource.disconnect();
});
});
// boot scripts mount components like REST API
boot(app, __dirname);
To summarize, this runs, no errors. But no new models show on http://localhost:3000/explorer
Seems that discovery scripts only shows the output and doesn't create the model files. I found some instructions on loopback docs:
http://docs.strongloop.com/display/public/LB/Discovering+models+from+relational+databases
In section Basic Procedure, the second step:
2. Use fs.writeFile() to save the output in common/models/model-name.json.
So you can try the following approach:
Setup your mysql data in yourloopbackproject/server/datasources.json file:
{
"db": {
"name": "db",
"connector": "memory"
},
"accountDs": {
"host": "mysqlServerName",
"port": 3306,
"database": "databaseName",
"username": "username",
"password": "password!",
"name": "accountDs",
"connector": "mysql"
}
}
Create the models folder if doesn't exist: yourloopbackproject/common/models.
Create discovery-and-build.js script on yourloopbackproject/server/bin folder:
var path = require('path');
var fs = require('fs');
var app = require(path.resolve(__dirname, '../server'));
var outputPath = path.resolve(__dirname, '../../common/models');
var dataSource = app.dataSources.accountDs;
function schemaCB(err, schema) {
if(schema) {
console.log("Auto discovery success: " + schema.name);
var outputName = outputPath + '/' +schema.name + '.json';
fs.writeFile(outputName, JSON.stringify(schema, null, 2), function(err) {
if(err) {
console.log(err);
} else {
console.log("JSON saved to " + outputName);
}
});
}
if(err) {
console.error(err);
return;
}
return;
};
dataSource.discoverSchema('tableName',{schema:'schemaName'},schemaCB);
This script is based on: http://www.reddit.com/r/strongloop/comments/2upy76/autodiscoveryjs_recipe/
After the script execution you will find a .json file on models folder. Go to step 3 on Basic Procedure section:
http://docs.strongloop.com/display/public/LB/Discovering+models+from+relational+databases
Follow these steps to expose your model over REST:
http://docs.strongloop.com/display/public/LB/Exposing+models+over+REST
I hope this helps!
Use Arc for this.
Run slc arc from the project folder and it will show up the gui tool called arc in default browser. If you've not already registered, sign up and log in. You will be directed to GUI tool of StrongLoop, the Arc. Select your model from list on the left pane. You'll be able to see save and migrate button. Just click the migrate button and your table will be created into model.(within millisecs!)
Cheers!
discovery api is used to only discover the schema not to create models for now.
please use the following project to create models with one to one and one to many relationships and all the models.
https://github.com/savsharma2/loopback-sql-create-model-with-relation/
Building off of #Underskay's answer, I did something like
var fs = require('fs');
var app = require(__dirname + '/server/server');
function makePromise(f, parent) {
return function(...args) {
return new Promise((resolve, reject) => {
f.call(parent, ...args, (err, ...data) => {
if (err) return reject(err);
resolve(data.length === 1 ? data[0] : data);
});
});
};
}
var readFile = makePromise(fs.readFile, fs);
var writeFile = makePromise(fs.writeFile, fs);
function writeSchemas(schemas) {
return Promise.all(schemas.map(data => {
var schema = data[Object.keys(data)[0]];
return writeFile('common/models/' + schema.name + '.json', JSON.stringify(schema, null, '\t'));
}))
.then(() => readFile('server/model-config.json'))
.then(JSON.parse)
.then(conf => {
for (let schema of schemas)
conf[schema[Object.keys(schema)[0]].name] = { "dataSource": "mysql" };
return conf;
})
.then(conf => writeFile('server/model-config.json', JSON.stringify(conf, null, '\t')));
}
function getSchemas(ds) {
var discoverSchemas = makePromise(ds.discoverSchemas, ds);
return makePromise(ds.discoverModelDefinitions, ds)({})
.then(tables => Promise.all(tables.map(t => discoverSchemas(t.name, { relations: true }))))
.then(data => { ds.disconnect(); return data; });
}
Promise.resolve(app.datasources.mysql)
.then(ds => getSchemas(ds))
.then(schemas => writeSchemas(schemas))
.catch(err => log.error(err));

Mongoose Query Data in Express.js Node.js ,get the wrong result

The Problem is .. sometimes it shows up the wrong user on the screen (someone gets session of another one). but it's hardly happen and i think it only happen when there're some concurrent.
if anything in this code can make this behaviour happen , please suggest.
app.js -- this file has a schema and initiation of model and routes component
var userSchema = mongoose.Schema({
"_id": mongoose.Schema.Types.ObjectId,
"name": String,
"username":String,
"etc":String
});
userMongooseModel = mongoose.model('users',userSchema);
var sessionSchema = mongoose.Schema({
"userID": mongoose.Schema.Types.ObjectId,
"sessionID": String,
"expire":Number
});
sessionMongooseModel = mongoose.model('sessions',sessionSchema);
var UserModel = require(basePath+'/models/UserModel.js').UserModel;
userModel = new UserModel();
var user = require(basePath+'/routes/user');
routes/user.js -- this file is the detail about each route.
exports.editProfilePage = function(req,res){
var httpRes = res;
userModel.checkSession(req.cookies.session,function(res){
if(res.status=='success' && res.type=='photographer')
{
userModel.getByID(res.userID,{},function(resp){
httpRes.render(basePath+'/views/photographer-edit.html',{currentUser:res.user,user:resp.user,etc:'etc'});
});
}
else
{
//if not login or state != 0
httpRes.redirect(baseURL+'/photographerRedirect');
}
});
}
usermodel.js -- this file is to retrieve data from database
var mongoose = require('mongoose');
var ObjectId = mongoose.Types.ObjectId;
var request = require('request');
UserModel.prototype.checkSession = function(sessionID,callback){
sessionMongooseModel.findOne({sessionID:sessionID},function (err, user) {
if(err)
{
callback({status:'fail',errorMsg:'notFound'});
return;
}
if(user==null)
{
callback({status:'fail',errorMsg:'notFound'});
}
else
{
if(user.expire > Date.now())
{
userMongooseModel.findOne({_id:user.userID},{studioName:1,state:1,etc:1},function (err, user) {
if(err || user==null)
{
callback({status:'fail',errorMsg:'notFound'});
return;
}
if(user.type=='photographer' && user.state==0)
{
callback({status:'fail',errorMsg:'wrongUserState',userID:user._id,user:user,etc:1});
}
else
callback({status:'success',userID:user._id,user:user,type:user.type,etc:1});
});
}
else
{
callback({status:'fail',errorMsg:'notFound'});
}
}
});
}
UserModel.prototype.getByIDs = function(userIDs,options,callback){
userMongooseModel.find({_id:{$in:userIDs}},options,function (err, users) {
if(err){
callback({status:'fail',errorMsg:'UserModel.find'});
return;
}
callback({status:'success',users:users});
});
}
Thanks a lot !
(not sure if this is causing the problem, but it seems worth mentioning anyway)
Here, you're passing req.cookies.session, which is an object:
userModel.checkSession(req.cookies.session, ...);
But in checkSession, you're assuming it's an id:
UserModel.prototype.checkSession = function(sessionID, callback) {
sessionMongooseModel.findOne({sessionID:sessionID}, ...);
Just out of curiosity: any reason why you're not using one of the existing MongoDB-backed session stores for Express (like this one)?
Also, Mongoose has it's own way of adding class methods to models, using statics.
The answer is ... cache
Someone in the middle cache the content ,and it also send the wrong content to the wrong person so it seems like session was mixed
the way out is .. make server send flag no cache to the client
res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');

Resources