Iterating over data from mongoose in node - node.js

I have this fiddle that illustrates what I want to do and it works as intended:
https://jsfiddle.net/mbLwsnak/
All it does is loop through an array of objects, and adds a key value pair.
I have this same code in my node app, and it doesn't work. The new pair is not added to the objects, even though the code is practically the same, so I figure it must be the context.
Schema.find()
.then(function(currencydata){
var data = currencydata;
data.forEach(function(obj){
obj.thing = "Thing";
})
console.log(data);
mdata.currencydata = data;
})
.then(function(result){
res.render('index', mdata);
})
So the only difference I can think of that matters is that this is all inside an asynchronous function. I even tried putting the log inside a setTimeout in case it was something to do with being async, but no change.
Edit:
currencydata is an array of objects returned by the mongoose schema. It looks like this:
[{
country: 'Canada',
currency: 'CAD',
cost: 5.98,
exchangerate: 1.33,
value: -10.9
}, {
country: 'United States',
currency: 'USD',
cost: 5.06,
exchangerate: 1,
value: 0
}, {
country: 'India',
currency: 'INR',
cost: 170,
exchangerate: 68.33,
value: -50.8
}]

Use lean() to convert a mongoose document into a plain javascript
object.
Then inside the first then() you need to return a promise so that the
next then() will get the returned value.
Inside the promise object, the resolve and reject functions, when called, resolve or reject the promise, respectively. Promise Reference
Schema.find()
.lean()
.then((currencydata, err) => {
return new Promise((resolve, reject) => {
currencydata.forEach(data => {
data.thing = 'Thing';
});
resolve(currencydata);
});
})
.then((newdata, err) => {
res.render('index', newdata);
});

Related

Can't acces specific values in my node.js mongoose Model (only the Object)

I have been working at this for the past 4 hours. I would therefore like some help. I want to access the specific values in my database, eg. as response.data.values.imglink although when adding imglink in console.log() I get undefined. I can get the general object but not the specifik values.
I have defined my Song Schema as:
const songSchema = new Schema({
values: [{
imglink: {
type: String
},
id: {
type: String
},
spotify: {
type: String,
},
soundCloud: {
type: String,
},
youtube: {
type: String,
},
appleMusic: {
type: String,
}}
],
}, {
timestamps: true,
})
As you can see values is an array of objects. People with a similiar problem on here hadn't included the correct values in their Schema, so maybe that's my problem? Although to me it looks correct. I then GET the values in my database. The JSON object usually looks something like this:
[
{
"_id": "5ffbba4dc47e847a79c9c68f",
"values": [
{
"_id": "5ffbba4dc47e847a79c9c690",
"imglink": "imagelink",
"id": "id",
"soundCloud": "soundcloudvalue",
"youtube": "youtubevalue",
"appleMusic": "applemusicvalue",
"spotify": "spotifyvalue"
}
]
}
]
I call it by this function, which is supposed to print out the individual values:
const getAllSongs = () => {
axios.get('http://localhost:5000/songs/'+id)
.then(function (response) {
console.log(response); // returns an object
console.log(response.data.values.imglink); // returns an object
})
.catch(function (error) {
// handle error
console.log(error);
})
}
I have an Express route object that allows me to access a song by it's id as GET http://localhost:5000/songs/id in the VS-code HTTP client (similiar to postman):
router.get(`/:id`, function(req, res) {
return Song.find(
{"values.id": req.params.id}
).then(function(song) {
// return orders when resolved
res.send(song);
console.log(id);
res.json('works yesss');
})
.catch(function (err) {
// handle error
res.status(400).json('Error: '+err)
})
});
Here are some popular solutions I have tried:
Wrapping response in JSON.stringify() doesn't work.
toObject() and toJSON() don't work either as they aren't defined when I use them.
the _doc hack doesn't work either.
I have tried looking at the Schema which is where I think the problem is. The POST-request adds the right data, the GET-request goes through I just can't acces the specific values.
I hope you have the time to help, thanks. I will be extremely grateful. And of course let me know if you have any questions.
the result of find() is a Array so to access the desired key, if length of result Array is one, to access the desired key is response.data[0].values[0].imglink.
note: the values key is a array of obejct
If the array size is more than one, you want to see the result, you can use map()
if it's not worked, using lean() like this
router.get(`/:id`, function(req, res) {
return Song.find(
{"values.id": req.params.id}
).lean().then(function(song) {
// return orders when resolved
res.send(song);
console.log(song[0].values[0].imglink); // type of song is array of object [] and values is array
res.json('works yesss');
})
.catch(function (err) {
// handle error
res.status(400).json('Error: '+err)
})
});

Knex Inserting Same Record Twice when Query Executed Asynchronously

In my /api/v1/chats POST route I make an async call to user_chat_queries.addUserChat for each user_id passed in the request body. The idea is that if lots of user_id's come in I don't want to have each insertion await, instead I'd like to to dispatch all the insertions asynchronously so I do:
Asynchronous Route handler (https://github.com/caseysiebel/lang-exchange/blob/master/src/server/routes/chats.js#L57):
await Promise.all(user_ids.map((user_id) => {
return user_chat_queries.addUserChat(user_id, chat.id)
}));
As opposed to,
Synchronously:
for (let user_id of user_ids) {
await user_chat_queries.addUserChat(user_id, chat.id)
}
And in the user_chat_queries (https://github.com/caseysiebel/lang-exchange/blob/master/src/server/db/queries/user_chat.js#L5):
addUserChat: ( async (user_id, chat_id) => {
const user_chat = await userChats
.insert({ user_id, chat_id })
.returning('*')
const data = await db('user_chat').select('*')
return user_chat;
}),
Now the route is accessed from my test file: (https://github.com/caseysiebel/lang-exchange/blob/master/test/routes.chats.test.js#L83)
it('should add 2 user_chats', (done) => {
chai.request(server)
.post('/api/v1/chats')
.send({
created_at: Date.now(),
user_ids: [ 2, 4 ]
})
.end((err, res) => {
should.not.exist(err);
res.status.should.equal(201);
res.type.should.equal('application/json');
res.body.status.should.eql('success');
const chat = res.body.data;
chat.should.include.keys('id', 'created_at');
knex('user_chat')
.select('*')
.then((data) => console.log('data', data))
done();
});
});
The log shows that the { user_id: 4, chat_id: 3 } in inserted in the user_chat table twice.
The expected result (and the result when executed synchronously) is one { user_id: 2, chat_id: 3 } record and one { user_id: 4, chat_id: 3 } record are inserted into the user_chat table.
I can't track down what is causing this. It seems to me that addUserChat should insert a record made from the inputs it is passed each time, regardless of when it resolves.
Full code base: https://github.com/caseysiebel/lang-exchange
Debugging console output: https://gist.github.com/caseysiebel/262997efdd6467c72304ee783dadd9af#file-console-L5
you need to use mapSeries type mechanism (Which is not available on default Promise API). So, you may need to use other 3rd party library like bluebird.
more details bluebird mapSeries
You don't show what userChats is in your code examples, but it looks like you may have a global instance of knex that you reuse across different queries, such as:
const userChats = knex('user_chats')
This is not safe because each query building operation mutates the query builder. You may get away with it if you're running in series but when you run in parallel, both your addUserChat calls are using the same userChats query builder at the same time and weird stuff happens.
Rule of thumb: call knex() once for every query you build.

How to use bluebird to chain consecutive callback functions such that they both have on catch block

I have two callback functions from mongoose that I would like to chain using bluebird's then
My first callback function uses then successfully.
User.findOne().distinct(('Friends.id'), {id: req.body.myId}, {Friends: {$elemMatch: { gender: req.body.gender}}})
.then(function(IDs){
var results = //////some computation
})
.catch(function(error)) {
}
I just can't get the syntax right to chain the second callback function so that it shares the first callback function's catch method. In this case, I cannot use Promise.all because the second callback function is dependent on the first callback function's results. Anyway the second callback function is the following:
User.find({Friends: { $not: { $elemMatch: { id: req.body.myId }}}, id: {$in: results}}, function(err, users){
})
You can chain two promises like this.
User.findOne().distinct(('Friends.id'), {id: req.body.myId}, {Friends: {$elemMatch: { gender: req.body.gender}}})
.then(function(IDs){
var results = //////some computation
// second promise
return User.find({Friends: { $not: { $elemMatch: { id: req.body.myId }}}, id: {$in: results}})
})
.then(function(friends) {
// do something with the result of the second query
})
.catch(function(error)) {
}

Knex.js: Create table and insert data

Given that I have a Knex.js script like this:
exports.up = function(knex, Promise) {
return knex.schema.createTable('persons', function(table) {
table.increments('id').primary();
table.string('name').notNullable();
});
};
which currently creates a table.
How do I add subsequent insert statements to this script?
What I want to do is add a line like this (or similar):
knex.insert({id: 1, name: 'Test'}).into('persons')
I'm not sure I understand how this promise-based approach works. Am I supposed to write another script with insert statements? Or can I somehow append them to my existing script?
Unfortunately, I don't find any complete example of create + insert in Knex.js documentation.
The then method returns a Promise, which you can use to implement insertion after you have created the table. For example:
exports.up = function (knex, Promise) {
return Promise.all([
knex.schema.createTableIfNotExists("payment_paypal_status", function (table) {
table.increments(); // integer id
// name
table.string('name');
//description
table.string('description');
}).then(function () {
return knex("payment_paypal_status").insert([
{name: "A", description: "A"},
{name: "B", description: "BB"},
{name: "C", description: "CCC"},
{name: "D", description: "DDDD"}
]);
}
),
]);
};
exports.down = function (knex, Promise) {
return Promise.all([
knex.schema.dropTableIfExists("payment_paypal_status")
]);
};
With modern Javascript's await/async keywords, you could do it like this:
exports.up = async function(knex) {
await knex.schema.createTable('persons', function(table) {
table.increments('id').primary();
table.string('name').notNullable();
});
// You could replace "return" by "await" here if you wish.
return knex.insert({id: 1, name: 'Test'}).into('persons');
};
It's basically the same, except using await/async instead of then.
The then method returns a Promise, which you can use to implement insertion after you have created the table. For example:
exports.up = (knex) => {
return knex.schema
.createTable("payment_paypal_status", (table) => {
table.increments()
table.string("name")
table.string("description")
})
.then(() =>
knex("payment_paypal_status").insert([
{name: "A", description: "A"},
{name: "B", description: "BB"},
{name: "C", description: "CCC"},
{name: "D", description: "DDDD"},
])
)
}
exports.down = (knex) => {
return knex.schema.dropTableIfExists("payment_paypal_status")
}
PS.:
Since .createTableIfNotExists actually just generates plain "CREATE TABLE IF NOT EXIST..." query it will not work correctly if there are any alter table queries generated for columns afterwards. To not break old migrations this function is left untouched for now, but it should not be used when writing new code and it is removed from documentation.
I used the Fareed Alnamrouti example/code and follow the suggestion to discard Promise.All by Jonny Rathbone

Mongoose : Queries through an array, return a promise of an array

I am trying to iterate through opts and make the same query on each element, I want in return a promise of an array containing the results of the queries. How can I do that ?
I tried for the moment with only the first item
//opts is an array of objects
function getRecapOfCampaign (campaignId, opts) {
var p_votes = Models.BSVote
.find({
created: {
$gte: opts[0].fromDate,
$lt: opts[0].toDate
}
})
.where('campaign').equals(campaignId)
.count()
.exec();
return p_votes;
}
There are a bunch of promise libs out there you could use for this or you could use the native JS Promise implementation on the newer releases of Node. In particular the Promise.all method.
Promise.all([ARRAY_OR_PROMISES]).then(values => {
console.log(values); // [ARRAY_OF_RESULTS_FROM_PROMISES]
});
Using Promise.all along with map should give you exactly what you want:
//opts is an array of objects
function getRecapOfCampaign(campaignId, opts) {
return Promise.all(opts.map(function(opt) {
return Models.BSVote.find({
created: {
$gte: opt.fromDate,
$lt: opt.toDate
}
})
.where('campaign').equals(campaignId)
.count()
.exec();
}));
}
Note: with Promise.all there is no limit on the number of promises concurrently running. If that is a concern you can either roll your own or use one of the many promise libs that have methods for this.

Resources