Loopback: remote method - multiple update - loopback

So, I want to have a function, that would reorder my records with orderId
I am passing an array like this:
[
{id: "5b1007aeb39c961a40540db9", orderId: 0},
{id: "5b150352184eb8471c34cf7c", orderId: 1}
]
and what I want is to multiple update all records with that ids with the orderId
so how can I do that?
I am trying something like this.... but it's not working, I imagine it is not chaining the promises at all ...
'use strict';
module.exports = function(Matchtimelineevents) {
Matchtimelineevents.reorder = function(items, cb) {
let count = 0;
if (typeof items !== 'undefined' && items.constructor === Array) {
items.forEach(item => {
Matchtimelineevents.update({'id': item.id, 'orderId': item.orderId}, function() {
count++;
console.log('UPDATING: ' + item.id, item.orderId, count);
});
});
// Matchtimelineevents.bulkUpdate(items, null, function() {
// console.log(items);
// });
}
cb(null, count);
};
Matchtimelineevents.remoteMethod('reorder', {
accepts: {
arg: 'items',
type: 'array',
},
returns: {
arg: 'count',
type: 'number',
},
http: {'verb': 'patch', 'path': '/reorder'},
description: 'Reorder the items by orderId',
});
};
What is the best way to do that?

Try to use updateAll with where like this:
const updateAllToPromise = item => new Promise((resolve, reject) => {
Matchtimelineevents.updateAll({
where: { id: item.id },
}, {
orderId: item.orderId,
}, function (err) {
if (err) resolve(false);
else resolve(true);
});
});
Matchtimelineevents.reorder = (items, cb) => {
if (!Array.isArray(items)) cb(new Error('Items not is a Array object'));
else {
Promise.all(items.map(item => updateAllToPromise(item)))
.then(items => cb(null, items.filter(item => item).length))
.catch(cb)
}
};

Related

Aggregate method for each document resulting from find method in mongodb

I want to execute this aggregate query:
db.collection('mycoll').aggregate([
{
$search: {
index: 'default',
text: {
query: 'night',
path: {
wildcard: '*',
},
},
},
},
])
})
for each document resulting from my find method:
here is my find method:
app.get('/', (req, res) => {
db.collection('subs').find(
{ name: { $regex: 'dexter', $options: '$i' } },
{ projection: { _id: 0, content: 0 } }
)
.toArray((err, result) => {
if (err) {
throw new err();
}
res.json({
length: result.length,
body: { result },
});
});
});
I know I probably have to use forEach and create a function, but I couldn't find out what to put in this function, I assume ## Heading ##it should be something like that:
.find(
{ name: { $regex: 'dexter', $options: '$i' } },
{ projection: { _id: 0, content: 0 } }
).forEach(()=>{})
.toArray((err, result) => {
if (err) {
throw new err();
}
res.json({
length: result.length,
body: { result },
});
});
});
You can achieve this in several different ways, here is the simplest code sample I could produce:
app.get('/', async (req, res) => {
const result = await db.collection('subs').find(
{name: {$regex: 'dexter', $options: '$i'}},
{projection: {_id: 0, content: 0}}
).toArray();
const finalResults = await Promise.all(result.map(async (each) => {
each.textSearchResults = await db.collection('mycoll').aggregate([
{
$search: {
index: 'default',
text: { // decide what your query is based on each document
query: each.name,
path: {
wildcard: '*',
},
},
},
},
])
return each
}))
res.json({
length: result.length,
body: {result},
});
});

mongodb model contain changed on calling a function levelQuestion

Here is the code for schema
schema stores questions for a particular course and which contains chapter and there question
questions: [
{
Chapter: String,
chques: [
{
description: String,
questions: [
{
question: String,
options: [String],
answer: Number,
Explanation: String,
code: { type: String, default: null },
images: [{ type: String, default: null }],
level: String
}
]
}
]
}
],
Here is the code for route
Router.get(
"/queformock/:course_id",
passport.authenticate("jwt", { session: false }),
(req, res) => {
Courses.findOne({ _id: req.params.course_id })
.lean()
.exec()
.then(course => {
if (!course) res.status(404).json({ course: "course not found" });
else {
let coursepass = Object.assign({}, course);
console.log("coursepass1: ", coursepass); //before calling levelQuestion it output as expected
let level2 = levelQuestion(coursepass, "medium");
console.log("coursepass2: ", coursepass);
let level3 = levelQuestion(coursepass, "hard");
console.log("coursepass3: ", coursepass);
res.json(level2);
}
});
}
);
Here is the levelQuestion function code
function levelQuestion(coursed, type) {
let arr = [];
coursed.questions.forEach(item => {
item.chques.forEach(i => arr.unshift(i));
});
arr.forEach(item => {
_.remove(item.questions, i => {
return i.level !== type;
});
});
_.remove(arr, item => {
return item.questions == false;
});
return arr;
}
now the problem is on every function call of levelQuestion coursepass is changed...why??

Mongodb: When do we need to expire shopping carts?

I'm building an e-commerce website by ExpressJs + Mongodb and I'm stuck with this concern:
When do we need to expire the cart ( remove the cart and return the product to inventory ) technically ? Whenever user visit the cart? or should I need a cron job?
I've followed this article: https://www.infoq.com/articles/data-model-mongodb
Here's my cart model's implementation:
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CartItem = new Schema({
product: { type: Schema.Types.ObjectId, ref: 'Product' },
quantity: Number
});
const Cart = new Schema({
userSessionId: String,
status: {
type: String,
enum: [ 'active', 'completed', 'expiring', 'expired' ],
default: 'active'
},
items: [ CartItem ],
modifiedOn: { type: Date }
});
Cart.static({
summary: function(params, cb) {
this.aggregate([
{
$match: { userSessionId: params.userSessionId }
},
{
$unwind: {
path: '$items'
}
},
{
$lookup: {
from: 'products',
localField: 'items.product',
foreignField: '_id',
as: 'product'
}
},
{
$unwind: {
path: '$product',
preserveNullAndEmptyArrays: true
}
},
{
$group: {
_id: { userSessionId: '$userSessionId' },
count: { $sum: '$items.quantity' },
total: { $sum: { $multiply: [ '$product.price', '$items.quantity' ] } }
}
}
], (err, results) => cb(err, results[0]));
},
addProduct: function(params, cb, test) {
var d = new Date();
if (test) {
d.setMinutes(d.getMinutes() - 10);
}
this.findOneAndUpdate(
{ userSessionId: params.userSessionId },
{ $set: { modifiedOn: d } },
{ upsert: true, new: true }, (err, cart) => {
if (err) {
return cb(err);
}
const index = cart.items.findIndex((item) => {
return item.product.equals(params.productId);
});
if (index === -1) {
cart.items.push({
product: params.productId,
quantity: params.quantity
});
} else {
cart.items[index].quantity += parseFloat(params.quantity);
}
cart.save(cb);
});
},
updateQuantity: function(params, cb) {
this.findOneAndUpdate(
{ userSessionId: params.userSessionId },
{},
{ upsert: true, new: true }, (err, cart) => {
if (err) {
return cb(err);
}
const index = cart.items.findIndex((item) => {
return item.product.equals(params.productId);
});
if (index === -1) {
return cb(new Error('Can not find product in cart'));
}
cart.items[index].quantity = params.quantity;
cart.save(cb);
});
},
findItem: function(params, cb) {
this.findOne({ userSessionId: params.userSessionId }).exec((err, cart) => {
if (err) {
return cb(err);
}
const index = cart.items.findIndex((item) => {
return item.product.equals(params.productId);
});
if (index === -1) {
return cb(new Error('Can not find product in cart'));
}
cb(null, cart.items[index]);
});
},
removeProduct: function(params, cb) {
this.update(
{ userSessionId: params.userSessionId },
{
$pull: { items: { product: params.productId } },
$set: { modifiedOn: new Date() }
},
cb
);
},
getExpiredCarts: function(params, cb) {
var now = new Date();
if (typeof params.timeout !== 'number') {
return cb(new Error('timeout should be a number!'));
}
now.setMinutes(now.getMinutes() - params.timeout);
this.find(
{ modifiedOn: { $lte: now }, status: 'active' }
).exec(cb);
}
});
mongoose.model('Cart', Cart);
You should use some kind of distributed session to store the shopping cart!
I think you are looking for something like: https://www.youtube.com/watch?v=g32awc4HrLA
It uses expressjs-session and mongodb then you have a distributed cache and it will work with multiple instances of your application.

How to make querys when tou have many to many relationships between models?

i am trying to make a game. I need tu create a Match. I think the problem on this Way. The User create a Match. In a third table I save playerId and gameId. When another user join the match, I save again, playerId and gameId. Then, I make a query with player with gameId in common, and start the game.
first, One User may have many Games. second, One Match may have many Games. this is the Match model:
module.exports = {
attributes: {
name: {
type: 'string'
},
description: {
type: 'string'
},
game: {
collection: 'game',
via: 'gameId',
}
}
};
This is the User model:
var bcrypt = require('bcrypt');
module.exports = {
attributes: {
name: {
type:'string'
},
email: {
type: 'email',
required: true,
unique: true
},
password: {
type: 'string',
},
passwordConfirmation: {
type: 'string'
},
passwordEncrypted: {
type: 'string'
},
creator: {
collection: 'game',
via: 'playerId'
},
toJSON: function(){
var obj = this.toObject();
delete obj.password;
delete obj.passwordConfirmation;
delete obj._csrf;
return obj;
}
}, beforeCreate: function(values, next){
console.log("Acabo de entrar a eforeCreate");
var password = values.password;
var passwordConfirmation = values.passwordConfirmation;
if(!password || !passwordConfirmation || password != values.passwordConfirmation) {
var passwordDoesNotMatchError = [{
name: 'passwordDoesNotMatchError',
message: 'Las contraseñas deben coincidir'
}]
return next({
err: passwordDoesNotMatchError
});
}
require('bcrypt').hash(values.password, 10, function passwordEncrypted(err, EncryptedPassword){
values.EncryptedPassword = EncryptedPassword;
next();
});
}
};
This is the Game model:
module.exports = {
attributes: {
gameId: {
model: 'match'
},
playerId: {
model: 'user'
}
}
};
finally, this is my controller:
module.exports = {
createMatch: function(req,res){
var matchObj = {
name: req.param('name'),
description: req.param('description'),
}
Match.create(matchObj, function(err, match){
if(err){
console.log("el error fue: " + err);
return res.send(err);
} console.log("Entro en create");
return res.json(match);
})
var gameObj = {
gameId: 'aclaration: I dont know how do I get the match.id',
playerId: req.session.me
}
Game.create(gameObj,function(err,game){
console.log("entro a GameCreate");
if(err){
return res.send(err);
} return res.json(game);
})
}
};
I can create the Match, but Game.create send this error:
_http_outgoing.js:344 throw new Error('Can\'t set headers after they are sent.'); ^
Error: Can't set headers after they are sent.
Somebody can help me? probably, I have many errors. Thanks.
Couple of things here:
Having an explicit Game model is not required in Sails. It can manage it implicitly, unless you want to store more information than just gameId and userId. So, you can just do away with Game model.
Please refer for async programming: How do I return the response from an asynchronous call?
Below code should work for you. Hope it helps.
module.exports = {
createMatch: function(req, res) {
var matchObj = {
name: req.param('name'),
description: req.param('description'),
};
Match.create(matchObj, function(err, match) {
if (err) {
console.log("el error fue: " + err);
return res.send(err);
}
console.log("Entro en create");
var gameObj = {
gameId: match.id,
playerId: req.session.me
};
Game.create(gameObj, function(err, game) {
console.log("entro a GameCreate");
if (err) {
return res.send(err);
}
return res.json(game);
// return res.json(match);
});
});
}
};

Multiple chained promises is sailsjs

This is my first attempt at attempting to chain multiple finds together. The debug running shows that all the code executes correctly but there is a delay in receiving the users array back and therefore unable to present the data back.
The concept is a user may belong to multiple organizations, and there may be more than one user (other than the current user) that may belong to organizations. The function is trying to receive all users for all the organizations the current user belongs to.
getUserOrganizationsUsers: function (userId) {
var users = [];
sails.log.info('Getting the current users organizations [' + userId + ']');
return UserOrganization.find({ user_id: userId, deleted: null })
.populate('organization_id', { deleted: null })
.populate('user_id', { deleted: null })
.then(function (userorganization) {
return userorganization;
})
.then(function (userorgs) {
/* From all the organizations I want to get all the users from those organizations */
_.forEach(userorgs, function (userorg) {
UserOrganization.find({ organization_id: userorg.organization_id.id })
.populate('organization_id', { deleted: null })
.populate('user_id', { deleted: null })
.then(function (otherusrs) {
_.forEach(otherusrs, function (otherusr) {
sails.log.info('other userss each loop ');
var users = _.find(otherusrs, {id: otherusr.organization_id.id});
users.push(users);
})
})
});
return Q.when(employees);
})
},
Organization.js
module.exports = {
attributes: {
companyName: {
type: 'string',
required: true
},
Address: {
type: 'string'
},
ABN: {
type: 'string'
},
City: {
type: 'string'
},
contactNumber: {
type: 'string'
},
country: {
type: 'string'
},
icon: {
type: 'string'
},
users:
{ collection: 'userorganization',
via : 'user_id'
},
deleted: {
type: 'date',
defaultsTo: null
},
toJSON: function () {
var obj = this.toObject();
obj = _.pick(obj, Organization.publicFields);
return obj;
}
},
editableFields: [
'companyName',
'users'
// 'industries'
],
publicFields: [
'id',
'companyName',
'users'
],
};
UserOrganization.js
module.exports = {
attributes: {
organization_id: {
model : 'organization',
required: true
},
user_id: {
model: 'user',
required: true
},
organizationRole: {
type: 'string',
required: true
},
deleted: {
type: 'date',
defaultsTo: null
},
toJSON: function () {
var obj = this.toObject();
obj = _.pick(obj, UserOrganization.publicFields);
return obj;
}
},
editableFields: [
'organization_id',
'user_id',
'organizationRole',
],
publicFields: [
'id',
'organization_id',
'user_id',
'organizationRole'
],
};
and the user.js
var bcrypt = require('bcrypt-nodejs');
module.exports = {
attributes: {
email: {
type: 'email',
required: true,
unique: true
},
password: {
type: 'string',
required: true
},
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
verified: {
type: 'boolean',
defaultsTo: false
},
organizations:
{ collection: 'userorganization',
via : 'user_id'
}, deleted: {
type: 'date',
defaultsTo: null
},
fullName: function () {
return this.firstName + ' ' + this.lastName;
},
toJSON: function () {
var obj = this.toObject();
obj = _.pick(obj, User.publicFields);
return obj;
}
},
// TODO: Add initialFields
editableFields: [
'password',
'email',
'firstName',
'lastName',
'organizations'],
publicFields: [
'id',
'email',
'verified',
'firstName',
'lastName',
'fullName',
'organizations'
],
comparePassword: function (password, user, cb) {
bcrypt.compare(password, user.password, function (err, match) {
if(err) return cb(err);
cb(null, match);
})
},
beforeCreate: function (user, cb) {
bcrypt.genSalt(10, function (err, salt) {
bcrypt.hash(user.password, salt, function () {}, function (err, hash) {
if (err) {
sails.log.error(err);
return cb(err);
}
user.password = hash;
cb(null, user);
});
});
}
};
Okay, I think I understand what you're doing. It would be a lot simpler to have the User belong to an organization directly.
Anyways, if I understood your model structure correctly, something like this should work:
getUserOrganizationsUsers: function (userId) {
UserOrganization.find({ user_id: userId, deleted: null })
.then(function (userOrgs) {
// return array of organization IDs
return _.map(userOrgs, function(org){
return org.id;
});
})
.then(function (userOrgs) {
Organization.find(userOrgs)
.populate('users') // users is a collection of UserOrganization
.exec(function(err, orgs){ // lookup organizations
if(err) //handle error
else {
return _.flatten( // return basic array for next promise handler
_.map(orgs, function(org){ // for each organization
return _.map(org.users, function(user){ // return an array of user_ids
return user.user_id;
})
})
)
}
})
})
.then(function(allUserOrgs){
UserOrganization.find(allUserOrgs)
.populate('user_id')
.exec(function(err, userOrgsList){
return _.map(userOrgsList, function(user){
return user.user_id;
})
})
})
.then(function(users){
// users should be an array of all the users form allt he organizations that the current users belongs to
})
},