Mongoose Subdocument will not update - node.js

I am having trouble figuring out if I designed the schema correctly because I am receiving a 500 error when attempting to PATCH changes of the roles property from a profile. (Note: The 500 error just responds with an empty {}, so it isn't really informative)
Below is the profile schema:
var ProfileSchema = new Schema({
name: {
type: String,
required: true
},
roles: [{
application: {
type: Schema.Types.ObjectId,
required: true,
ref: 'Application'
},
role: {
type: String,
required: true,
enum: [ 'admin', 'author', 'publisher' ]
}
}]
});
Each profile has a role for an application, and when I send the request to the controller action 'update', it fails:
profile update controller:
// Updates an existing Profile in the DB
export function update(req, res) {
try {
if (req.body._id) {
delete req.body._id;
}
console.log('ENDPOINT HIT...');
console.log(`REQUEST PARAM ID: ${req.params.id}`);
console.log('REQUEST BODY:');
console.log(req.body);
console.log('ENTIRE REQUEST: ');
return Profile.findByIdAsync(req.params.id)
.then(handleEntityNotFound(res))
.then(saveUpdates(req.body))
.then(respondWithResult(res))
.catch(handleError(res));
} catch(ex) {
console.error('FAILED TO UPDATE PROFILE');
return handleError(res);
}
}
I made sure that the id and body was being sent properly, and I am hitting the end point.
This is an example of the request body JSON:
{
_id: 57e58ad2781fd340563e29ff,
__updated: Thu Oct 27 2016 15:41:12 GMT-0400 (EDT),
__created: Fri Sep 23 2016 16:04:34 GMT-0400 (EDT),
name: 'test',
__v: 11,
roles:[
{ application: 57b70937c4b9fe460a235375,
role: 'admin',
_id: 58125858a36bd76d8111ba16 },
{ application: 581b299f0145b48adf8f57bd,
role: 'publisher',
_id: 581b481898eefb19ed8a73ee }
]
}
When I try to find the Profile by Id, the promise chain goes straight to the catch(handleError(res)); part of the code and shows an empty object in my console.
My handle error function:
function handleError(res, statusCode) {
console.error('HANDLE PROFILE ERROR: ', statusCode);
statusCode = statusCode || 500;
return function(err) {
console.error('PROFILE ERROR:');
console.error(JSON.stringify(err, null, 2));
res.status(statusCode).send(err);
};
}
UPDATE
I am realizing the code is breaking when it hits my saveUpdates function (Note: I am using lodash):
function saveUpdates(updates) {
/// the code is fine here ///
return function(entity) {
/// once it enters in here, is where it begins to break ///
var updated = _.merge(entity, updates);
if(updated.roles.length != updates.roles.length) {
updated.roles = updates.roles;
}
for(var i in updates.roles) {
updated.roles.set(i, updates.roles[i]);
}
return updated.saveAsync()
.then(updated => {
return updated;
});
};
}

Lesson learned: Read Documentation properly.
Since I am using bluebird promises for this application, I forgot to use .spread() within my saveUpdates() callback function.
Solution:
function saveUpdates(updates) {
return function(entity) {
var updated = _.merge(entity, updates);
if(updated.roles.length != updates.roles.length) {
updated.roles = updates.roles;
}
for(var i in updates.roles) {
updated.roles.set(i, updates.roles[i]);
}
return updated.saveAsync()
// use `.spread()` and not `.then()` //
.spread(updated => {
return updated;
});
};
}
I want to thank the following SOA that led to this conclusion: https://stackoverflow.com/a/25799076/5994486
Also, here is the link to the bluebird documentation in case anyone was curious on .spread(): http://bluebirdjs.com/docs/api/spread.html

Related

Update document in MongoDB via NodeJS

So my knowledge of NodeJS and MongoDD are non-existent (just need to do a small code update for a friend) and I'm stuck.
Need to update a single document inside a collection via a unique id but can't seem to do it.
Here's the Model (I've trimmed it down and cut out all unnecessary data). I'm trying to update the field notes inside a transaction.
In short each entry in the given (an Agent) table will have a collection of multiple Transactions & Documents. I need to update a specific Transaction with the unique _id that is auto generated.
import { Schema, model } from 'mongoose';
interface Transaction {
first_name: string;
last_name: string;
type: string;
notes: string;
}
interface Agent {
org_id: number;
transactions: Array<Transaction>;
documents: Array<string>;
}
const transactionSchema = new Schema<Transaction>({
first_name: { type: String },
last_name: { type: String },
type: { type: String },
notes: String,
});
const transactionsSchema = new Schema<Agent>({
org_id: { type: Number },
transactions: [transactionSchema],
documents: [documentTypesSchema],
});
const AgentTransaction = model<Agent>(
'agent_transaction_table',
transactionsSchema
);
export default AgentTransaction;
Here's what I tried but didn't work (obviously), again I've trimmed out all unnecessary data. Just to clarify, the endpoint itself works, but the DB update does not.
import AgentTransaction from '../models/transaction'; // the above model
transaction.put('/notes', async (req, res) => {
const { org_id, transaction_id, notes } = req.body;
try {
const notesResult = await AgentTransaction.updateOne({
'transactions._id': transaction_id,
}, {
$set: {
'notes': notes
},
});
res
.status(200)
.json({ message: 'Updated', success: true, notesResult });
} catch (error) {
res.status(400).send(error);
}
});
So I figured it out. Maybe it'll help someone else as well.
const notesResult = await AgentTransaction.updateOne({
'transactions._id': { $in: [trunc2] },
}, {
$set: {
'transactions.$.notes': notes
},
});
The main issue was that the payload object needed to target the collection folder + the wildcard + the field, not just only the field.

Mongoose - how to fetch specific number and message ID

I'm trying to make the bot basically edit the message of any specific case mentioned for example if i do -case 5 test it will look for case 5 and it's message. So far when i do it, it basically changes the recent case number message, instead of the one i want it to change. like if i do case 5 test and the latest case is #9, it will change 9 instead of 5.
This is how i send the message:
Modlog.findOneAndUpdate({ guildID: msg.channel.guild.id }, { $inc: { 'caseID': 1 } }, { new: true }, async function (err, doc) {
if (err) throw err;
if (!doc) return;
if (doc.modLog.enabled) {
if (msg.channel.guild.channels.get(doc.modLog.channelID)) {
let m = await msg.channel.guild.channels.get(doc.modLog.channelID).createMessage({
embed: {
title: `${action} | Case #${doc.caseID}`,
color: colour,
fields: [
{
name: 'User',
value: user,
inline: true
},
{
name: 'Moderator',
value: moderator ? moderator : 'No issuer.',
inline: true
},
{
name: 'Reason',
value: reason ? reason : 'No reason.'
}
]
}
});
doc.messageID = m.id;
doc.type = action;
doc.caseID = doc.caseID;
//doc.caseID = m.id
doc.moderatorID = moderator,
doc.targetID = user
doc.save();
}
}
})
that is how i send my message. And you can see i'm storing the things so when someone changes a specific case's reason, for example: case 5 spamming, i would want it to look for caseID 5, and then edit the message through it's ID. but i'm not sure how am i doing it wrong. I'm trying to make each case store it's own message ID and i would really appreciate any help. This is what i use to look for the case and edit's reason.
Modlog.findOne({ guildID: msg.guildID }, async (err, doc) => {
if (err) throw err;
if (!doc.modLog.enabled) return msg.channel.createMessage(`Modlog is not enabled in this server! ${this.emoji.cross}`);
if (isNaN(Number(caseID))) return msg.channel.createMessage(`Case \`#${caseID}\` was not a number! ${this.emoji.cross}`);
if (doc.caseID === undefined) return msg.channel.createMessage(`Couldn\'t find case \`#${caseID}\`! ${this.emoji.cross}`);
const moderator = this.bot.users.get(doc.moderatorID) || {
username: 'Unknown User',
discriminator: '0000'
}
const target = this.bot.users.get(doc.targetID) || {
username: 'Unknown User',
discriminator: '0000'
}
let embed = {
title: `${doc.type} | Case #${doc.caseID}`,
fields: [
{
name: 'User',
value: `${target.username}#${target.discriminator} (${target.id})`,
inline: true
},
{
name: 'Moderator',
value: `${moderator.username}#${moderator.discriminator} (${moderator.id})`,
inline: true
},
{
name: 'Reason',
value: reason
}
]
};
try {
await this.bot.editMessage(doc.modLog.channelID, doc.messageID, { embed: embed });
await msg.channel.createMessage(`Case **#${caseID}** has been updated. ${this.emoji.tick}`);
} catch (e) {
await msg.channel.createMessage(`I\'m unable to edit that case or it has been deleted. ${this.emoji.cross}`);
}
});```
Solution: Search for Case ID
It seems you didn't look for the case ID, and only looked for the guild's ID in the filter parameter.
Modlog.findOneAndUpdate({ guildID: msg.channel.guild.id }, { ... }, { ... }, ... {
...
}
In your code, only guildID was passed into the filter parameter. This causes Mongoose to look for the most recently initialized document for the server. For your case, you should also pass caseID into the filter parameter.
Modlog.findOneAndUpdate({ guildID: msg.channel.guild.id, caseID: caseIDArg }, { ... }, { ... }, ... {
...
}
Replace caseIDArg with your supposed caseID argument in the message's content. For example, args[1] or however you programmed your argument handler to work.
Hope this helped to answer your question!

how to fetch _id data in loopback

My Database is arangodb. What i want to do is : I have data like this:
_id:authModel/1209597
_key:1209597
{
email: 'abc#gmail.com',
password: 'password',
subuser_ids:[ '811289', '1209611' ],
id: '1209597'
}
_id:authModel/811289
_key:811289
{
email: 'pqr#gmail.com',
password: 'password',
id: '811289'
}
actually i need to fetch data which is the ids in subuser_ids array, ie the subuser_ids contain 2 ids. I need to fetch the data that the subuser_id hold. suppose subuser_id is "811289" that is _id="811289" i need to fetch that data. am using arangodb and loopback. also i write an remote method to accesss that data. What i have is :
model.js
var server = require("../../server/server");
module.exports = function (Authmodel) {
Authmodel.on("attached", function () {
Authmodel.getApp(function (err, app) {
Authmodel.getSubUsers = function (params, cb) {
var result = [];
params.forEach(function (id) {
app.models.authModel.findOne({ "_id": id }, function (err, r) {
console.log("err", err);
console.log("r", r);
result.push(r);
});
})
cb(null, result);
};
});
});
Authmodel.remoteMethod('getSubUsers', {
accepts: { arg: 'params', type: 'array' },
returns: { arg: 'result', type: 'array' }
});
};
i got the log in the result console but that data is not correct.
How can i solve this issue? i need to fetch all subuser_ids data. any help will really appreciable and helpfull.

ElasticSearch TypeError: Request path contains unescaped characters

I am writing server code that listens for search requests on the firebase database and return the search result. This is the code that indexes the data from Firebase Database to Elastic Search. Is this the correct way to index data?
var firebase = require('firebase');
var ElasticClient = require('elasticsearchclient');
// listen for changes to Firebase data
var client = new ElasticClient({host:'localhost',port:9200});
firebase.initializeApp({
serviceAccount: {
...
},
databaseURL: "https://searchserver.firebaseio.com/"
})
var db = firebase.database();
var ref = db.ref("realtime");
ref.on('child_added', upsert);
ref.on('child_changed', upsert);
ref.on('child_removed', remove);
function upsert(snap) {
client.index({
index: 'firebase',
type: 'users',
id: snap.key,
body: snap.val()
}, function (err, response) {
if (err) {
console.log("error indexing " + error)
}
});
}
function remove(snap) {
client.delete({
index: 'firebase',
type: 'users',
id: snap.key
}, function (error, response) {
if (error) {
console.log("Error deleting user : " + error);
}
});
}
This is the error message.
TypeError: Request path contains unescaped characters.
at new ClientRequest (_http_client.js:50:11)
at Object.exports.request (http.js:31:10)
at ElasticSearchCall.exec (C:\Users\jojo\Documents\readouts-search\readouts-server\node_modules\elasticsearchclient\lib\elasticsearchclient\calls\elasticSearchCall.js:45:26)
at ElasticSearchClient.createCall (C:\Users\jojo\Documents\readouts-search\readouts-server\node_modules\elasticsearchclient\lib\elasticsearchclient\elasticSearchClient.js:28:14)
at ElasticSearchClient.index (C:\Users\jojo\Documents\readouts-search\readouts-server\node_modules\elasticsearchclient\lib\elasticsearchclient\calls\core.js:37:17)
at upsert (C:\Users\jojo\Documents\readouts-search\readouts-server\readouts-server.js:28:12)
at C:\Users\jojo\Documents\readouts-search\readouts-server\node_modules\firebase\database-node.js:146:375
at Qb (C:\Users\jojo\Documents\readouts-search\readouts-server\node_modules\firebase\database-node.js:43:165)
at nc (C:\Users\jojo\Documents\readouts-search\readouts-server\node_modules\firebase\database-node.js:31:216)
at oc (C:\Users\jojo\Documents\readouts-search\readouts-server\node_modules\firebase\database-node.js:30:701)
Value of snap.val()
{ '5Nyu7Px7vNYGizJqlyGHhAfnvwu2':
{ allOwnerVideos: 'none',
allRatedVideos: 'none',
email: 'iron#man.com',
favorite_category:
{ fifth: 'Business',
first: 'Sports',
fourth: 'Biography',
second: 'Sci-Fi',
seventh: 'Action and Adventure',
sixth: 'Art',
third: 'Satire' },
name: 'iron',
personalmessage: 'man',
profileimage: 'https://firebasestorage.googleapis.com/v0/b/readoutsa75e0.appspot.com/o/ProfilePictures%2F_5Nyu7Px7vNYGizJqlyGHhAfnvwu2.jpg?alt=media&token=743dbd6a-985c-4481-9d63-4ed30e8703a9' },
'6bt3EqdWyfQ3ksEdy9BTyYvQK4F3'

Why is req./body.param through a GET request undefined?

my issue is that req.body.param is undefined but body itself returns the wanted json from the requested source.
This means my iteration wont work too because i need to access req.body.data.length or the count of the id param available.
Thats also why i set num of iterations to 5 to test the output.
Project is created via the express-generator(body-parser is installed).
This is what i get returned
[
{
__v: 0,
id: "0",
appid: 730,
updated_at: "2016-06-05T16:52:39.742Z",
_id: "575458d71d1c41f414e3b29a",
created_at: "2016-06-05T16:52:39.731Z"
},
{
__v: 0,
id: "1",
appid: 730,
updated_at: "2016-06-05T16:52:39.749Z",
_id: "575458d71d1c41f414e3b29b",
created_at: "2016-06-05T16:52:39.737Z"
}, ...
]
This is how the requested api json body looks like:
{
data: [
{
id: 1,
product_name: "Product1",
app: appName,
quantity: 137360,
price: 0.05,
updated_at: "2016-06-04 23:02:03",
median_week: 0.04,
median_month: 0.05,
},
{
id:2,
...,
}
.....
{
id:142640,
...,
}
}
My Function
router.get('/', function(req, res) {
request.get('url/api/items/all/', function (error, response, body) {
console.log(options);
console.log('###########\n '+ body + '\n');
console.log('error '+error);
console.log('req:body '+req.body.data);
if (error && response.statusCode !== 200) {
// request could not be completed
return res.send(400, {message: 'Something went wrong'});
} else {
// get the count
//console.log('body.id.length: '+req.body.id.length);
//console.log('body.data.length: '+req.body.data.length);
//console.log('data.length: '+data.length);
var iterator = 5;//req.body.data.length; //|| req.body.num_items;
// iterate number of times
async.times(iterator, function(number, next){
console.log('iterating'+ number);
var newItem = Item({
id: number,
market_hash_name: req.body.market_hash_name,
appid: '730',
quantity: req.body.quantity,
price: req.body.price,
median_week: req.body.median_week,
median_month: req.body.median_month,
average_median_week: req.body.average_median_week,
trend_week: req.body.trend_week,
trend_month: req.body.trend_month,
total_sold_week: req.body.total_sold_week,
total_sold_month: req.body.total_sold_month,
updated_at: req.body.updated_at
})
// save andt
// call next (which is callback)
newItem.save(next);
}, function(timesErr, timesResult){
// if something failed, return error
// even if 1 item failed to save, it will return error
if(timesErr) {
return res.send(400, {message: 'Something went wrong'});
}
// send the list of saved items;
// or whatever you want
res.status(200).send(timesResult);
});
}
});
});
This Problem is referred to this issue
Try this code
var app = require('express')();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.get.....
This question is old. For anyone having the same problem here is the Solution. You have to parse body content to json after that you´ll be able to access the param´s. like so const content = JSON.parse(body)

Resources