excecute function upon document insertion - mongodb

I'm trying to find a way to achieve the following pseudo function on the server.The fields likesRecieved likesShown and likesMatch in exist in a document within the Posts collection.
I require this function to perform for every document in post collection by default. This is because Id like the function to do this...
1) find value(s) that exist in likesRecieved and likedShown fields.
2) insert these value(s) in likesMatch field.
3) remove values found in operation 1 from likesRecieved and likesShown
This is what I am essentially trying to do on the server...
likesRecieved: idA, idB, idE, idF, idL
likesShown: idE, idC, idF
..perform a function to result in the following...
likesRecieved: idA, idB, idL
likesShown: idC,
likesMatch: idE, idF
This is my code to find the ids in both arrays for one document only. The likeMatch helper returns the userIds that may exist in both 'likesRecieved' and 'likesShown' fields within a selected document in Posts collection. The resulting value(s) are then inserting into the likesMatch field.
likeMatch: function() {
var selectedPostId = Session.get('postId'); // _id of document in Post collection
var arrayOfLikeRecieved = Posts.find({_id: selectedPostId}, {fields: {LikesRecieved: 1}}).fetch();
var sumArrayRecieved = _.chain(arrayOfLikeRecieved).pluck('LikesRecieved').flatten().value();
var arrayOfLikeShown = Posts.find({_id: selectedPostId}, {fields: {LikesShown: 1}}).fetch();
var sumArrayShown = _.chain(arrayOfLikeShown).pluck('LikesShown').flatten().value();
var duplicates = _.intersection(sumArrayRecieved, sumArrayShown);
Meteor.call('insertDuplicateIntoMatchField', duplicates);
},

MongoDB doesn't have hooks like some other databases do, so there's no way to automatically have a function called when a document is inserted.
You have a couple of options, though. One way would be to have a hook in your application that runs just before inserting the document to run your function. This could be achieved in meteor by using a Collection.deny function.
If you would prefer to have the function be executed in mongodb, then you'll have to call the function manually. The problem is just how to know when the document was inserted or updated. Luckily, meteor allows you to observe changes to a cursor. You could use that to make a call out to the database and run a stored procedure (function) whenever a document gets updated.

Related

Meteor mongo get and set value

I am pretty new to Meteor and I'm just toying around to explore it at the moment.
I've got stuck with an issue trying to get and set a value in the mongo database, named Persons.
The MongoDB is named Persons and I've inserted two objects:
{name: "abcdef", coming: "false"}
If the user changes the state of a checkbox, that's attached to a name, a function is called that should find the value of the boolean coming.
What I think I got right so far, is that I can set the value like this?
var id = this._id;
Persons.update({_id: id}, {$set: {coming: true}});
But how can i get the value and make comparisons with it?
In meteor you use .find(selector).fetch() or .findOne(selector) on collections to get objects, where selector is a usual mongodb selector or just an id (string). After that you can pick out any fields using the usual . syntax of javascript.
Example:
var obj = Persons.findOne(id);
if (obj) { // the given id was found
console.log(obj.coming);
}

Need to remove ObjectID() from _id using Meteor

I am retrieving records from Mongo using Meteor. I use the {{_id}} placeholder in my meteor template to use the _id field of the record, but it adds this into my template....
ObjectID("54f27a1adfe0c11c824e04e9")
... and I would like just to have...
54f27a1adfe0c11c824e04e9
How do I do this?
Just add a global helper:
Template.registerHelper('formatId', function(data) {
return (data && data._str) || data;
});
You can also do it like this with ES6 syntax :
Template.registerHelper('formatId', (id) => (id && id._str) || id);
And use it in any template like this:
{{formatId _id}}
This will work for both mongo-style ObjectIds and meteor-style random strings.
Since your documents are using the MongoDB ID format rather than the default Meteor ID format (simply a randomly generated string), you will need to use the MongoDB ObjectId.toString() function described here. But unfortunately, this simply results in your ObjectID being printed out as a string like ObjectID("54f27a1adfe0c11c824e04e9").
I would recommend creating a document ID template helper that cleans your document IDs before including them in the template. Since this issue is most likely related to all of your documents in all of your collections, I would further suggest creating a global template helper. This can be done like so:
Template.registerHelper('cleanDocumentID', function(objectID) {
var objectIdString = objectID.toString();
var cleanedString = objectIDString.slice(objectIDString.indexOf('"') + 1, -2);
return cleanedString;
});
This template helper slices out just the actual object ID string from the string provided by the ObjectId.toString() function. You can use this template helper in your templates like so:
{{cleanDocumentID _id}}
I know that this is a lot messier than simply using the document ID in the template like {{_id}}, but it is necessary due to the fact that your documents have the MongoDB ObjectID-type document ID rather than the simple randomly generated string as used by Meteor by default.
If you would like to learn more about how to set your MongoDB collections to use randomly generated strings for document IDs and avoid this mess, check out this.
In Blaze templates, just add this {{_id.valueOf}}, and you will have a string value of the actual Object Id.
mongo is capable of storing many types including uuids and custom ones. the field is usually a self-describing object, comprised of the type and the id.
your record is using the default mongo format evidenced by the "ObjectId" prefix.
try ObjectId("507f191e810c19729de860ea").str

Find the collection name from document._id in meteor (mongodb)

From the looks of the syntax for handling mongodb related things in meteor it seems that you always need to know the collection's name to update, insert, remove or anything to the document.
What I am wondering is if it's possible to get the collection's name from the _id field of a document in meteor.
Meaning if you have a document with the _id equal to TNTco3bHzoSFMXKJT. Now knowing the _id of the document you want to find which collection the document is located in. Is this possible through meteor's implementation of mongodb or vanilla mongodb?
As taken from the official docs:
idGeneration String
The method of generating the _id fields of new documents in this collection. Possible values:
'STRING': random strings
'MONGO': random Meteor.Collection.ObjectID values
The default id generation technique is 'STRING'.
Your best option would be to insert records within a pseudo transaction where the second step is to take the id and collection name to feed it into a reference collection. Then, you can do your lookups from that.
It would be pretty costly, though to construct your find's but might be a pattern worthwhile exploring if you are building an app where your users will be creating arbitrary data patterns.
You could accomplish this by doing a findOne on all of the collections:
var collectionById = function(id) {
return _.find(_.keys(this), function(name) {
if (this[name] instanceof Meteor.Collection) {
if (this[name].findOne(id)) {
return true;
}
}
});
};
I tested this on both the client and the server and it seemed to work when run in the global context.

How do you access attributes of an object queried from Mongo in Meteor

I'm pretty new to mongo in the Meteor.js framework. Here, I've queried a MongoDB object using it's ID. I'm trying to access an attribute "mana" but it returns me undefined.
var skill = Skills.find({_id:Session.get("selected_skill")});
alert(skill);//This returns me "undefined"
Skills.update(Session.get("selected_skill"), {$inc: {mana: 1}});
Could you enlighten me on the requirements of accessing attributes in mongo for meteor? Thanks
find method returns a cursor, not object nor array. To access object, you need to either fetch it from the cursor
var skill = Skills.find(Session.get('selected_skill')).fetch()[0];
or get it directly with findOne:
var skill = Skills.findOne(Session.get('selected_skill'));
Then you may use it just as any other js object:
console.log(skill.mana);
skill._cache = {cooldown: true};
Keep in mind that on client-side, collection methods like find are non-blocking. They return whatever Meteor has in cache, not necessarily what is in the server-side db. That's why you should always use them in a reactive context, or ensure that all data has been fetched before execution (don't worry about the latter until you're fluent with Meteor, start with the first way).
Also, you need to keep in mind that because of this, findOne and find.fetch may return null / empty array, even when the corresponding element is in db (but hasn't been yet cached). If you don't take that into account in your reactive functions, you'll run into errors.
Template.article.slug = function() {
var article = Articles.findOne(current_article);
if(!article) return '';
return slugify(article.title);
};
If we didn't escape from the function with if(!article), the expression article.title would raise an error in the first computation, as article would be undefined (assuming it wasn't cached earlier).
When you want to update the database from client side, you can alter only one item by a time, and you must refer to the item by its _id. This is due to security reasons. Your line for this was ok:
Skills.update(Session.get('selected_skill'), {$inc: {mana: 1}});
alert() is a function that returns undefined no matter what you feed it.
alert(42); // -> undefined
Generally, it's far better to debug with console.log than with alert.

Mongo find unique results

What's the easiest way to get all the documents from a collection that are unique based on a single field.
I know I can use db.collections.distrinct to get an array of all the distinct values of a field, but I want to get the first (or really any one) document for every distinct value of one field.
e.g. if the database contained:
{number:1, data:'Test 1'}
{number:1, data:'This is something else'}
{number:2, data:'I'm bad at examples'}
{number:3, data:'I guess there\'s room for one more'}
it would return (based on number being unique:
{number:1, data:'Test 1'}
{number:2, data:'I'm bad at examples'}
{number:3, data:'I guess there\'s room for one more'}
Edit: I should add that the server is running Mongo 2.0.8 so no aggregation and there's more results than group will support.
Update to 2.4 and use aggregation :)
When you really need to stick to the old version of MongoDB due to too much red tape involved, you could use MapReduce.
In MapReduce, the map function transforms each document of the collection into a new document and a distinctive key. The reduce function is used to merge documents with the same distincitve key into one.
Your map function would emit your documents as-is and with the number-field as unique key. It would look like this:
var mapFunction = function(document) {
emit(document.number, document);
}
Your reduce-function receives arrays of documents with the same key, and is supposed to somehow turn them into one document. In this case it would just discard all but the first document with the same key:
var reduceFunction = function(key, documents) {
return documents[0];
}
Unfortunately, MapReduce has some problems. It can't use indexes, so at least two javascript functions are executed for every single document in the collections (it can be limited by pre-excluding some documents with the query-argument to the mapReduce command). When you have a large collection, this can take a while. You also can't fully control how the docments created by MapReduce are formed. They always have two fields, _id with the key and value with the document you returned for the key.
MapReduce is also hard to debug an troubleshoot.
tl;dr: Update to 2.4