Meteor, ChartsJS and MongoDB - mongodb

I want to iterate through the MongoDB collection to get the chart labels but I get TypeError: undefined is not an object (evaluating 'teams[i].name') here is my code:
var teams = Teams.find();
var teamNames = [10];
for(i = 0; i < 10; i++)
{
teamNames.push(teams[i].name);
}
var chart = new Chart(canvas, {
type: 'bar',
data: {
labels: [teamNames]
....
Anyone any suggestions? I am running out of ideas.
Thank you in advance.

You can do this
var teamNames = Teams.find().map(
function(team){
return team.name;
}
)

teams must have a length of less than 10 items. If teams is [{name: "first"}], then teams[1] will return undefined and you will get that error. You can use:
for (let i = 0; i < teams.length; i++)
to solve this problem.
You can also map over the array to get specific properties:
labels: teams.map(team => team.name),

In Meteor, the Collection .find() function returns a cursor that you can then use to perform operations on collection items. In your case, you are treating the cursor as if it were an array which is incorrect. There are a few different ways that you can approach this.
1) Use .forEach() to iterate over the cursor.
var teamNames = [];
Teams.find().forEach(function (e) {
teamNames.push(e.name);
});
2) Use .fetch() to return all matching documents in an array, then iterate over that.
var teams = Teams.find().fetch();
var teamNames = [];
for(i = 0; i < teams.length; i++) {
teamNames.push(teams[i].name);
}
3) Use .map() to iterate over the collection calling the callback on all items and returning an array.
var teamNames = Teams.find().forEach(function (e) {
return e.name;
});

Related

Iterating Over an Array and Returning First Match from MongoDB using Mongoose

I have a collection of Users in my database with corresponding unique ids. I am writing a function that takes an array of ids as an argument, e.g:
["user_id1", "user_id2", "user_id3", "user_id4"].
I want my query to return the first and only the first match. i.e. Using the example above, if user_id2 and user_id4 were the only two matching users in the database, my result would only return user_id2. User ids that are not in the database are ignored.
My current approach is to use a while loop, but I wanted to see if there was a better solution provided by Mongoose.
Current Pseudo Code:
function findOneUser(userIdArr) {
let user = 0;
let returnedUser;
while(!returnedUser || user < userIdArr.length) {
let id = userIdArr[user];
user = await User.findByID(id);
user++;
}
}
try this.
Use promises and mongoose findOne method:
let argumentArr = ["user_id1", "user_id2", "user_id3", "user_id4"];
let getUser = new Promise(function(resolve, reject) {
for (let i = 0; i < argumentArr.length; i++) {
User.findOne({_id:argumentArr[i]}).then(user => {
if(Object.keys(user).length > 0){
resolve(user)
}
}).catch(err => {reject(err)})
}
});
getUser.then(
(user) => {console.log(user);},//expected results: user(:object)
(err) => {console.log(err);}
);
}

Meteor: Update object nested two arrays deep

I have a collection with a key called fields, which is an array of JSON objects. Those objects can have options which is another array of JSON objects. I’m trying to update one of the options by optionId. I tried this but it doesn't work.
Projects.update({
'fields.options._id': optionId
}, {
$set: {
`fields.$.options.$.title`: title
}
}
This does find the correct Project document, but doesn't update it.
You can use the $ operator for single level arrays only. Use of array1.$.array2.$.key is not supported.
However, if you are aware of the exact index of the element to be updated within the array, you can update like so:
Projects.update({
'fields.options._id': optionId
}, {
$set: {
`fields.0.options.1.title`: title
}
}
This is one way to update:
Projects.find({"fields.options._id":optionId}).forEach(function(record) {
var match = false;
// iterate fields array
for(var i=0; i< record.fields.length; i++){
// iterate options array
for(var j=0; j<record.fields[i].options.length; j++){
if(record.fields[i].options[j]._id == optionsID){
record.fields[i].options[j].title = title;
match = true;
// break;
}
}
}
if (match === true) Projects.update( { 'fields.options._id': optionId }, record );
});
Source

Error when inserting Documents with for Loop

When I try to add some Documents to a Collection, exactly 1 of 4 times I get an Error.
for (var i = 0; i < 50; i=i+1){
db.SampleOrder.insert(
{
"SampleId": NumberInt(i),
"PuckId": NumberInt(i)
});
}
Error:
Picture of the Error
Does anybody know why this doesn't work?
I use Robomongo Robo 3T 1.1.1.
you can use insertMany instead of insert to insert multiple document
like:
var docs = [];
for (var i = 0; i < 50; i=i+1){
docs.push({
"SampleId": NumberInt(i),
"PuckId": NumberInt(i)
});
}
db.SampleOrder.insertMany(docs);

Mongo - Replace references with embedded documents

I have a Collection with a nested attribute that is an array of ObjectId References. These refer to documents in another Collection.
I'd like to replace these references with the documents themselves, i.e. embed those documents where the references are now. I've tried with and without the .snapshot() option. This may be caused because I'm updating a document while in a loop on that doc, and .snapshot() isn't available at that level.
My mongo-fu is low and I'm stuck on a call stack error. How can I do this?
Example code:
db.CollWithReferences.find({}).snapshot().forEach( function(document) {
var doc_id = document._id;
document.GroupsOfStuff.forEach( function(Group) {
var docsToEmbed= db.CollOfThingsToEmbed.find({ _id: { $in: Group.ArrayOfReferenceObjectIds }});
db.CollWithReferences.update({"_id": ObjectId(doc_id) },
{$set: {"Group.ArrayOfReferenceObjectIds ":docsToEmbed}} )
});
});
Gives this error:
{
"message" : "Maximum call stack size exceeded",
"stack" : "RangeError: Maximum call stack size exceeded" +
....}
I figure this is happening for one of two reasons. Either you are running out of memory by executing two queries in a for loop, or the update operation is being executed before the find operation has finished.
Either way, it is not a good idea to execute too many queries in a for loop as it can lead to this type of error.
I can't be sure if this will fix your problem as I don't know how many documents are in your collections, but it may work if you first get all documents from the CollWithReferences collection, then all you need from the CollOfThingsToEmbed collection. Then build a map of an _id from the CollOfThingsToEmbed collection to the actual document that corresponds to that. You can then loop through each document you got from the CollWithReferences collection, and mutate the groupsOfStuff array by accessing each ArrayOfReferenceObjectIds array and setting the ObjectId to the value that you have in the map you already built up, which will be the whole document. Then just update that document by setting GroupsOfSuff to its mutated value.
The following JavaScript code will do this (it could be organised better to have no logic in the global scope etc.):
var references = db.CollWithReferences.find({});
function getReferenceIds(references) {
var referenceIds = [];
for (var i = 0; i < references.length; i++) {
var group = references[i].GroupsOfStuff;
for (let j = 0; j < group.ArrayOfReferenceObjectIds; j++) {
referenceIds.push(group.ArrayOfReferenceObjectIds[j]);
}
}
return referenceIds;
}
function buildIdMap(docs) {
var map = {};
for (var i = 0; i < docs.length; i++) {
map[docs[i]._id.toString()] = docs[i];
}
return map;
}
var referenceIds = getReferenceIds(references);
var docsToEmbed = db.CollOfThingsToEmbed.find({_id: {$in: referenceIds}});
var idMap = buildIdMap(docsToEmbed);
for (var i = 0; i < references.length; i++) {
var groups = references[i].GroupsOfStuff;
for (var j = 0; j < groups.length; j++) {
refs = groups[j].ArrayOfReferenceObjectIds;
refs.forEach(function(ref) {
ref = idMap[ref.toString()];
});
}
db.CollWithReferences.update({
_id: ObjectId(ref._id)
}, {
$set: {GroupsOfStuff: groups}
});
}
It would be better if it was possible to just do one bulk update, but as each document needs to be updated differently, this is not possible.

How to iterate a mongo query through a for loop

What I'm trying to achieve is the following which doesn't work:
var names = ["MATT", "GABE", "SAM"];
var students = [];
for (var i = 0; i < 3; i++) {
students[i] = Programs.find({ CampYear: 2016, 'Teachers.Week1.Sunday': names[i] }).fetch();
}
I would expect that it would return an array of student names for each iteration, but I keep getting an empty array when the array should have names.
If I remove the for loop and just do:
students[0] = Programs.find({ CampYear: 2016, 'Teachers.Week1.Sunday': listOfSundayTeacherNames[2] }).fetch();
It will return the student name(s) expected. Is a for-loop the right way to do this?
Figured it out. For whatever reason the for-loop won't work, but using a .map will.
var names = _.map(names, function(num){ return Programs.find({ CampYear: 2016), 'Teachers.Week1.Sunday': num }).fetch(); });
A much better approach would be to use the $in operator with your query:
var students = Programs.find({
'CampYear': 2016,
'Teachers.Week1.Sunday': { '$in': names }
}).fetch();