I'm making calls to a mongodb database - pulling data out... reading it, and then making further requests based on that data. Once all the data has been received, I wish to process it.
I've been using Q.promises library but don't know what I'm doing. I thought that q.all would only trigger once everything has completed? However, my processPlaylist function runs twice. I've commented the code below:
Thanks,
Rob
var PlaylistCollection = require('./models/playlist');
var AssetCollection = require('./models/asset');
var screenID = '############';
var playerData = [];
// array continaing playlistys which have been synced
var alreadySynced = [];
// Process our playlist once downloaded
var processPlaylist = function (playerData) {
console.log('----Processing Playerlist-----')
console.log(playerData);
// DO STUFF
}
// Get playlist by id. Return playlist Data
var getSubLists = function (id) {
return PlaylistCollection.findById(id);
}
// Get sub-playlist function
function getSubListRecursive(id) {
return getSubLists(id).then(function (playlist) {
// store all our returned playlist data into a playlist array
playerData.push(playlist)
// an Array to keep tabs on what we've already pulled down
alreadySynced.push(playlist.id)
// get all our playlist.resources, and only return those which are unique
var playlistResources = _.uniq(playlist.resources, 'rid');
// console.log('Playlist Resources: ', playlistResources)
// console.log(alreadySynced);
var sublists = _.pluck(_.filter(playlistResources, { 'type': 'playlist' }), 'rid');
// remove playlists which have already been synced. We don't want to pull them down twice
sublists = _.difference(sublists, alreadySynced);
// console.log('sublists: ', sublists)
// Get the next playlist and so on...
var dbops = sublists.map(function (sublist) {
// console.log(sublist)
return getSubListRecursive(sublist)
});
q.all(dbops).then(function () {
console.log('All Done - so process the playlist')
return processPlaylist(playerData);
});
})
}
// Trigger the whole process..... grab our first playlist / ScreenID
getSubListRecursive(screenID);
and I get the following output:
----Processing Playerlist-----
[ { _id: 554d1df16ce4c438f8e2225b,
title: 'list 1',
__v: 29,
daily: true,
endTime: '',
startTime: '',
resources:
[ { rid: '55650cebef204ab70302a4d9',
title: 'list 4',
type: 'playlist' },
{ rid: '554d1df16ce4c438f8e2225b',
title: 'list 1',
type: 'playlist' } ] },
{ _id: 55650cebef204ab70302a4d9,
title: 'list 4',
__v: 1,
daily: false,
endTime: '',
startTime: '',
resources:
[ { rid: '55650647ef204ab70302a4d8',
title: 'list 3',
type: 'playlist' } ] } ]
All Done - so process the playlist
----Processing Playerlist-----
[ { _id: 554d1df16ce4c438f8e2225b,
title: 'list 1',
__v: 29,
daily: true,
endTime: '',
startTime: '',
resources:
[ { rid: '55650cebef204ab70302a4d9',
title: 'list 4',
type: 'playlist' },
{ rid: '554d1df16ce4c438f8e2225b',
title: 'list 1',
type: 'playlist' } ] },
{ _id: 55650cebef204ab70302a4d9,
title: 'list 4',
__v: 1,
daily: false,
endTime: '',
startTime: '',
resources:
[ { rid: '55650647ef204ab70302a4d8',
title: 'list 3',
type: 'playlist' } ] },
{ _id: 55650647ef204ab70302a4d8,
title: 'list 3',
__v: 5,
daily: false,
endTime: '',
startTime: '',
resources:
[ { rid: '55650637ef204ab70302a4d7',
title: 'list 2',
type: 'playlist' },
{ rid: '554d1df16ce4c438f8e2225b',
title: 'list 1',
type: 'playlist' },
{ rid: '55650cebef204ab70302a4d9',
title: 'list 4',
type: 'playlist' } ] } ]
EDIT
There were a number of things wrong with what I wrote. I discussed it with a buddy of mine - who pointed out that getSubListRecursive is being invoked recursively several times so the q.all statement is being executed several times...
So I refactored...
// Get sub-playlist function
function getSubListRecursive(id) {
console.log(id)
return getSubLists(id).then(function (playlist) {
if (playlist) {
// store all our returned playlist data into a playlist array
playerData.push(playlist)
// an Array to keep tabs on what we've already pulled down
alreadySynced.push(playlist.id)
// get all our playlist.resources, and only return those which are unique
var playlistResources = _.uniq(playlist.resources, 'rid');
// console.log('Playlist Resources: ', playlistResources)
// console.log(alreadySynced);
var sublists = _.pluck(_.filter(playlistResources, { 'type': 'playlist' }), 'rid');
// remove playlists which have already been synced. We don't want to pull them down twice
sublists = _.difference(sublists, alreadySynced);
// console.log('sublists: ', sublists)
return sublists.map(function (sublist) {
// console.log(sublist)
if (sublists.length > 0) {
return getSubListRecursive(sublist)
} else {
return processPlaylist(playerData);
}
});
} else {
return processPlaylist(playerData);
}
});
}
this works. I'm basically using promises to control the flow here - which probably isn't the best way of doing it? I no longer use an all statement, and ultimately end up with array populated with all the playlist data - which I can manipulate in my processPlaylist function.
However, I've not marked the question as solved, as I'd really like to know how I can do this with Q.all (properly use promises)
Thanks,
Rob
I think you were just confused about when the entire process was finished. You need to wait until the entire recursive promise chain has resolved. I think you could use the original code with a slight change to where processPlaylist() is called:
var PlaylistCollection = require('./models/playlist');
var AssetCollection = require('./models/asset');
var screenID = '############';
var playerData = [];
// array continaing playlistys which have been synced
var alreadySynced = [];
// Process our playlist once downloaded
var processPlaylist = function (playerData) {
console.log('----Processing Playerlist-----')
console.log(playerData);
// DO STUFF
}
// Get playlist by id. Return playlist Data
var getSubLists = function (id) {
return PlaylistCollection.findById(id);
}
// Get sub-playlist function
function getSubListRecursive(id) {
return getSubLists(id).then(function (playlist) {
// store all our returned playlist data into a playlist array
playerData.push(playlist)
// an Array to keep tabs on what we've already pulled down
alreadySynced.push(playlist.id)
// get all our playlist.resources, and only return those which are unique
var playlistResources = _.uniq(playlist.resources, 'rid');
// console.log('Playlist Resources: ', playlistResources)
// console.log(alreadySynced);
var sublists = _.pluck(_.filter(playlistResources, { 'type': 'playlist' }), 'rid');
// remove playlists which have already been synced. We don't want to pull them down twice
sublists = _.difference(sublists, alreadySynced);
// console.log('sublists: ', sublists)
// Get the next playlist and so on...
var dbops = sublists.map(function (sublist) {
// console.log(sublist)
return getSubListRecursive(sublist)
});
return q.all(dbops);
});
}
// Trigger the whole process..... grab our first playlist / ScreenID
getSubListRecursive(screenID).then(function() {
console.log('All Done - so process the playlist')
return processPlaylist(playerData);
});
Related
I'm trying to make some comprobations on my API (Node.js + Mongodb)
I want to check if the proposerId is equal to the eventOrganizer. To do so
I'm sending this on the body:
{
"participantId": "6238a608170aff10d16ccd89",
"proposerId": "62385d8caee17d13a1762b39", // this guy id is also an organizer
"gender": "female",
"groupId": "623aea21fcfad83bcf8d5bc4"
}
in my PATCH controller to add a user I have this verification:
exports.addParticipantToEvent = async (req, res, next) => {
// Group Organizer constants
const eventId = req.params.eventId;
const groupId = req.body.groupId;
const proposerId = req.body.proposerId; // it can be an admin adding someone to the participants list
// Participant constants
const participantId = req.body.participantId;
const gender = req.body.gender;
// EVENT
// Does the event even exist?
const eventData = await Event.findById(eventId);
if (!eventData) {
return res.status(406).json({
code: 'EVENT_DOESNT_EXIST',
message: 'The event is not valid.',
});
}
console.log(eventData);
// What kind of users can participate in this event?
const allowedParticipants = eventData.allowedParticipants;
// whos the event organizer?
const eventOrganizer = eventData.organizer._id;
console.log('Organizer: ' + eventOrganizer);
console.log('Proposer: ' + proposerId);
console.log('Result: ' + proposerId === eventOrganizer);
try {
return res.status(200).json({
message: 'The participant can be added',
participantId: participantId,
gender: gender,
allowedParticipants: allowedParticipants,
});
} catch (err) {
return res.status(400).json({ message: err });
}
};
I want to verify is the proposerId is an admin or an organizer of the event, so I console.log the eventData entirely and later I console log all the constants I want to verify and the result, it gives me false all the time.
Maybe I need to specify better something?
{
location: { province: 'Barcelona' },
_id: new ObjectId("634ffee75990124926431e6f"),
title: 'Test open close 14',
sport: new ObjectId("622ce6ca672c3d4447676705"),
group: new ObjectId("623aea21fcfad83bcf8d5bc4"),
organizer: new ObjectId("62385d8caee17d13a1762b39"),
participants: [ new ObjectId("62385d8caee17d13a1762b39") ],
replacements: [],
invitations: [],
when: 2022-10-09T13:43:02.999Z,
open: true,
costPerParticipant: 4.4,
skill: 'novice',
allowedGender: 'female',
minParticipants: 2,
maxParticipants: 5,
visible: false,
externalLink: 'https://www.komoot.es/tour/731122050?ref=wta',
allowInvitations: true,
allowReplacements: false,
allowedParticipants: 'only-members',
createdAt: 2022-10-19T13:43:03.006Z,
updatedAt: 2022-10-19T13:43:03.006Z,
__v: 0
}
Organizer: 62385d8caee17d13a1762b39
Proposer: 62385d8caee17d13a1762b39
false
As you can see, both Organizer and proposer are same id, yet I get false.
After lurking some more, I have found that to validate mongos object ids with strings I need to use equals(). So now I have the solution.
I have this array list of objects.
var list = [{
'ID':1,
'name' : 'Vikas Yadav',
'mobile':8095638475,
'sent':false
},
{
'ID':2,
'name' : 'Rajat Shukla',
'mobile':7486903546,
'sent':false
},
{
'ID':3,
'name' : 'Munna Bhaiya',
'mobile':9056284550,
'sent':false
},
{
'ID':4,
'name' : 'Guddu Pandit',
'mobile':7780543209,
'sent':false
},
{
'ID':5,
'name' : 'Srivani Iyer',
'mobile':8880976501,
'sent':false
}];
Now I want to push two more datas in specific element of this array via forLoop as:
var timeAndOTPArray = {
"time" : new Date(),
"OTP": req.params.ran
}
I am retrieving the list data via cookies into one of the route.
Below is the code I am trying to push the element according to the matching condition.
var lists = req.cookies.list;
Object.keys(lists).forEach(function(item) {
if(req.params.ID == lists[item].ID){ //look for match with name
(lists[item]).push(timeAndOTPArray);
newAddedList.push(lists[item]);
console.log(item, lists[item]);
}
});
Perhaps it's not the correct way. Please help!
Wish you a happy and a prosperous Diwali.
Cheers!
You can use findIndex and append to update the object into list like this:
//List only with ID, easier to read the code
var list = [{'ID':1,},{'ID':2,}]
//your object
var timeAndOTPArray = {
"time" : new Date(),
"OTP": "otp"
}
//Index where object with ID == 2 is
var index = list.findIndex(obj => obj.ID == 2);
//Append the 'timeAndOTPArray' properties into the object itself
list[index] = {"time": timeAndOTPArray.time, "OTP":timeAndOTPArray.OTP, ...list[index]}
console.log(list)
I guess this will help
var lists = req.cookies.list;
Object.keys(lists).forEach(function(item) {
if(req.params.ID == lists[item].ID){ //look for match with ID
Object.keys(timeAndOTPArray).forEach(key=>{
lists[item][key]=timeAndOTPArray[key];
})
}
});
Good evening) I can advice you the best option is update with map
const listItems = [
{
ID: 1,
name: 'Vikas Yadav',
mobile: 8095638475,
sent: false,
},
{
ID: 2,
name: 'Rajat Shukla',
mobile: 7486903546,
sent: false,
},
{
ID: 3,
name: 'Munna Bhaiya',
mobile: 9056284550,
sent: false,
},
{
ID: 4,
name: 'Guddu Pandit',
mobile: 7780543209,
sent: false,
},
{
ID: 5,
name: 'Srivani Iyer',
mobile: 8880976501,
sent: false,
},
];
const paramId = 4;
const result = listItems.map((item) => {
if (paramId === item.ID) {
return {
...item,
time: new Date(),
OTP: 'smth',
};
}
return item;
});
console.log('result', result);
for appending, you can do this,
lists[index] = Object.assign(lists[index], timeAndOTPArray);
If you are using es6,
lists[index] = {...lists[index], timeAndOTPArray};
Here lists is an array of objects.
so lists[item] is an object, so you cant push an object to an object.
In your code timeAndOTPArray is an object.
In your lists object, initialize an empty array called timeAndOTPArray
var index = lists.findIndex(function(item){ return item.ID == req.params.ID});
lists[index].timeAndOTPArray.push(timeAndOTPArray);
I am trying to get the children of apostrophe pages to appear in my navigation object - however the _children array is always empty. My page does have child pages set up via the front end Pages UI.
My index.js for the lib/modules/apostrophe-pages module contains the following:
construct: function(self,options) {
// store the superclass method and call at the end
var superPageBeforeSend = self.pageBeforeSend;
self.pageBeforeSend = function(req, callback) {
// Query all pages with top_menu setting = true and add to menu collection
self.apos.pages.find(req, { top_menu: true }, {slug: 1, type: 1, _id: 1, title: 1})
.children(true)
.toArray(
function (err, docs) {
if (err) {
return callback(err);
}
req.data.navpages = docs;
return superPageBeforeSend(req, callback);
});
};
},
...
My top_menu attribute is set via apostrophe-custom-pages:
module.exports = {
beforeConstruct: function(self, options) {
options.addFields = [
{
name: 'subtitle',
label: 'Subtitle',
type: 'string'
},
{
name: 'css_class',
label: 'CSS Class',
type: 'string'
},
{
name: 'top_menu',
label: 'Include In Top Menu',
type: 'boolean'
}
].concat(options.addFields || []);
}
};
This gives me the pages I need with the top_menu setting.. but I want to get child pages too..
When debugging the code I can see that the docs._children array is present but is always empty, even though a page has child pages...
I have tried adding the following both to my app.js and to my index.js but it doesn't change the result:
filters: {
// Grab our ancestor pages, with two levels of subpages
ancestors: {
children: {
depth: 2
}
},
// We usually want children of the current page, too
children: true
}
How can I get my find() query to actually include the child pages?
Solved it..
I needed to add 'rank: 1, path: 1, level: 1' to the projection as per this page in the documentation: https://apostrophecms.org/docs/tutorials/howtos/children-and-joins.html#projections-and-children
I am stuck to save data in mongoDb. Here data is in array and i need to insert data if mongodb does not have. Please look code:-
var contactPersonData = [{
Name: 'Mr. Bah',
Organization: 'Ashima Limited - Point 2'
}, {
Name: 'Mr. Sel',
Organization: 'Ashima Limited - Point 2'
}, {
Name: 'Mr.ATEL',
Organization: 'Ashima Limited - Point 2'
}, {
Name: 'ANISH',
Organization: 'Ashima Limited - Point 2'
}, {
Name: 'sunny ji',
Organization: 'Ashima Limited - Point 2'
}, {
Name: 'ashish',
Organization: 'Ashima Limited - Point 2'
}]
console.log('filedata', contactPersonData);
var escapeData = [];
var tempArr = [];
function saveContact(personObj, mainCallback) {
var tempC = personObj['Organization'].trim();
var insertData = {};
Contact.findOne({ companyName: tempC })
.exec(function(err, contact) {
if (err)
return mainCallback(err);
console.log('find com', contact)
if (contact) {
//document exists
mainCallback(null, insertData);
} else {
var newContact = new Contact({ companyName: tempC, createdBy: '58ae5d18ba71d4056f30f7b1' });
newContact.save(function(err, contact) {
if (err)
return mainCallback(err);
console.log('new contact', contact)
insertData.contactId = contact._id;
insertData.name = personObj['Name'];
insertData.email = personObj['Email'];
insertData.contactNumber = { number: personObj['Phone'] };
insertData.designation = personObj['Designation'];
tempArr.push(insertData);
mainCallback(null, insertData);
})
}
});
}
async.map(contactPersonData, saveContact, function(err, result) {
console.log(err)
console.log(result)
},
function(err) {
if (err)
return next(err);
res.status(200).json({ unsaved: escapeData })
})
As per above code it has to insert six document instead of one. I think that above iteration not wait to complete previous one. So, the if condition is always false and else is executed.
Your saveContact() function is fine. The reason you get 6 documents instead of 1 document is that async.map() runs you code in parallel. All the 6 requests are made in parallel, not one after the another.
From the async.map() function's documentation -
Note, that since this function applies the iteratee to each item in parallel, there is no guarantee that the iteratee functions will complete in order.
As a result before the document is created in your database all queries are already run and all the 6 queries are not able to find that document since its still in the process of creation. Therefore your saveContact() method creates all the 6 documents.
If you run your code again then no more documents will be formed because by that time your document would be formed.
You should try running your code using async.mapSeries() to process your request serially. Just replace map() with mapSeries() in your above code. This way it will wait for one request to complete and then execute another and as a result only one document will be created. More on async.mapSeries() here.
You seem to be using async.map() wrong.
First, async.map() only has 3 parameters (i.e. coll, iteratee, and callback) so why do you have 4? In your case, coll is your contactPersonData, iteratee is your saveContact function, and callback is an anonymous function.
Second, the whole point of using async.map() is to create a new array. You are not using it that way and, instead, are using it more like an async.each().
Third, you probably should loop through the elements sequentially and not in parallel. Therefore, you should use async.mapSeries() instead of async.map().
Here's how I would revise/shorten your code:
var contactPersonData = [{
Name: 'Mr. Bah',
Organization: 'Ashima Limited - Point 2'
}, {
Name: 'Mr. Sel',
Organization: 'Ashima Limited - Point 2'
}, {
Name: 'Mr.ATEL',
Organization: 'Ashima Limited - Point 2'
}, {
Name: 'ANISH',
Organization: 'Ashima Limited - Point 2'
}, {
Name: 'sunny ji',
Organization: 'Ashima Limited - Point 2'
}, {
Name: 'ashish',
Organization: 'Ashima Limited - Point 2'
}];
function saveContact(personObj, mainCallback) {
var tempC = personObj.Organization.trim();
Contact.findOne({ companyName: tempC }, function (err, contact) {
if (err)
return mainCallback(err);
console.log('found contact', contact);
// document exists, so mark it as complete and pass the old item
if (contact)
return mainCallback(null, contact);
// document does not exist, so add it
contact = new Contact({ companyName: tempC, createdBy: '58ae5d18ba71d4056f30f7b1' });
contact.save(function (err, contact) {
if (err)
return mainCallback(err);
console.log('created new contact', contact)
// mark it as complete and pass a new/transformed item
mainCallback(null, {
contactId: contact._id,
name: personObj.Name,
email: personObj.Email, // ??
contactNumber: { number: personObj.Phone }, // ??
designation: personObj.Designation // ??
});
});
});
};
async.mapSeries(contactPersonData, saveContact, function (err, contacts) {
if (err)
return next(err);
// at this point, contacts will have an array of your old and new/transformed items
console.log('transformed contacts', contacts);
res.json({ unsaved: contacts });
});
In terms of ?? comments, it means you don't have these properties in your contactPersonData and would therefore be undefined.
I'm trying to fetch all user stories that belong to certain feature, but which have children.
Here's how I created query for that using rally-node.
async.map(features, function(feature, cb) {
self.restApi.query({
type: 'hierarchicalrequirement',
limit: Infinity,
order: 'Rank',
fetch: ['FormattedID', 'Name', 'Children'],
parent: feature.ObjectID,
query: queryUtils.where('DirectChildrenCount', '>', 0)
}, cb);
}, function(err, results) {
//user stories
});
And here's how my feature looks like:
{ _rallyAPIMajor: '2',
_rallyAPIMinor: '0',
_ref: 'https://rally1.rallydev.com/slm/webservice/v2.0/portfolioitem/feature/18846103932',
_refObjectUUID: 'c01d7f828-a6d6-4efc-8160-c0c19ad0fabc',
_objectVersion: '7',
_refObjectName: 'Dashboard Widgets',
ObjectID: 18836103932,
FormattedID: 'F1',
DirectChildrenCount: 2,
Name: 'Dashboard Widgets',
UserStories:
{ _rallyAPIMajor: '2',
_rallyAPIMinor: '0',
_ref: 'https://rally1.rallydev.com/slm/webservice/v2.0/PortfolioItem/Feature/18846103932/UserStories',
_type: 'HierarchicalRequirement',
Count: 2 },
_type: 'PortfolioItem/Feature' },
I'm new to rally, so any further help regardind documentation, etc, is really appreciated.
Here is a full example where Feature is queried and its UserStories collection is fetched and then hydrated.
v2.0 removed the ability to return child collections in the same response for performance reasons. Now fetching a collection will return an object with the count and the url from which to get the collection data. Separate request is needed to hydrate a collection.
This change is documented here
I do not see a question in your post, I am not sure what problem you encounter, but his code gets user stories based on feature, filtered by ('DirectChildrenCount', '>', 0)
var rally = require('rally'),
queryUtils = rally.util.query;
mySettings = {
apiKey: '_secret',
server: 'https://rally1.rallydev.com', //this is the default and may be omitted
requestOptions: {
headers: {
'X-RallyIntegrationName': 'My cool node.js program',
'X-RallyIntegrationVendor': 'My company',
'X-RallyIntegrationVersion': '1.0'
},
}
},
restApi = rally(mySettings);
function queryFeature() {
return restApi.query({
type: 'portfolioitem/feature',
fetch: ['FormattedID', 'Name', 'UserStories'],
query: queryUtils.where('FormattedID', '=', 'F7')
});
}
function queryChildren(result) {
return restApi.query({
ref: result.Results[0].UserStories,
limit: Infinity,
order: 'Rank',
fetch: ['FormattedID', 'Name'],
query: queryUtils.where('DirectChildrenCount', '>', 0)
});
}
function onSuccess(result) {
console.log('Success!', result);
}
function onError(errors) {
console.log('Failure!', errors);
}
queryFeature()
.then(queryChildren)
.then(onSuccess)
.fail(onError);