Meteor - Cannot Access All Read Operations - mongodb

I'm new to Meteor. I've been stuck on this problem for a while. I can successfully adds items to a collection and look at them fully in the console. However, I cannot access all of the read operations in my .js file.
That is, I can use .find() and .findOne() with empty parameters. But when I try to add .sort or an argument I get an error telling me the object is undefined.
Autopublish is turned on, so I'm not sure what the problem is. These calls are being made directly in the client.
This returns something--
Template.showcards.events({
"click .play-card": function () {
alert(Rounds.find());
}
})
And this returns nothing--
Template.showcards.events({
"click .play-card": function () {
alert(Rounds.find().sort({player1: -1}));
}
})
Sorry for the newbie question. Thanks in advance.

Meteor's collection API works a bit differently from the mongo shell's API, which is understandably confusing for new users. You'll need to do this:
Template.showcards.events({
'click .play-card': function() {
var sortedCards = Rounds.find({}, {sort: {player1: -1}}).fetch();
console.log(sortedCards);
}
});
See this for more details. Also note that logging a cursor (the result of a find) probably isn't what you want. If you want to see the contents of the documents, you need to fetch them.

Rounds.find().sort({player1: -1}) returns a cursor, so you will want to do this:
Rounds.find().sort({player1: -1}).fetch();
Note that this returns an Array of document objects. So you would do something more like this:
docs = Rounds.find().sort({player1: -1}).fetch();
alert(docs[0]);

Related

How do I create an array for an updateOne() call in mongoc (C libarary for Mongodb)?

I am completely mystified (and supremely frustrated). How do I create this call using the mongoc library?
I have the following doc structure in the collection
{_id: myOID,
subsriptions: {
newProducts: true,
newBlogPosts: true,
pressReleases: true,
}
}
I want to remove one of the subscriptions, for example, the user no longer wants to receive press releases from me.
This works in the mongo shell. Now I need to do it in C code
updateOne({_id: myOID}, [{'$unset': 'subscriptions.pressReleases'}], {})
Note how the update parameter in the Mongo shell is an anonymous array. I need to do that for the bson passed in as the update parameter in the mongoc_collection_update_one() API call.
The C code for updateOne is
mongo_status = mongoc_collection_update_one (mongo_collection,
mongo_query,
mongo_update,
NULL, /* No Opts to pass in */
NULL, /* no reply wanted */
&mongo_error);
Also note that in the aggregate() API, this is done with
{"pipeline" : [{'$unset': 'elists.lunch' }] }
Neither the updateOne() shell function nor the mongoc_collection_update_one() API call accept that, they want just the array.
How do I create the bson to use as the second parameter for mongoc_collection_update_one() API call?
Joe's answer works and I am able to accomplish what I need to do.
The $unset update operator takes an object, just like $set.
updateOne({_id: myOID},{'$unset':{'subscriptions.pressReleases': true}})
OR perhaps even better
updateOne({_id: myOID},{'$unset':{'subscriptions.pressReleases': {'$exists': true}}})
which will remove the subscription flag no matter what the value is for that field.
Doing it this way does not require an anonymous array (which I still don't know how to create).

call custom python function on every document in a collection Mongo DB

I want to call a custom python function on some existing attribute of every document in the entire collection and store the result as a new key-value pair in that (same) document. May I know if there's any way to do that (since each call is independent of others) ?
I noticed cursor.forEach but can't it be done just using python efficiently ?
A simple example would be to split the string in text and store the no. of words as a new attribute.
def split_count(text):
# some complex preprocessing...
return len(text.split())
# Need something like this...
db.collection.update_many({}, {'$set': {"split": split_count('$text') }}, upsert=True)
But it seems like setting a new attribute in a document based on the value of another attribute in the same document is not possible this way yet. This post is old but the issues seem to be still open.
I found a way to call any custom python function on a collection using parallel_scan in PyMongo.
def process_text(cursor):
for row in cursor.batch_size(200):
# Any complex preprocessing here...
split_text = row['text'].split()
db.collection.update_one({'_id': row['_id']},
{'$set': {'split_text': split_text,
'num_words': len(split_text) }},
upsert=True)
def preprocess(num_threads=4):
# Get up to max 'num_threads' cursors.
cursors = db.collection.parallel_scan(num_threads)
threads = [threading.Thread(target=process_text, args=(cursor,)) for cursor in cursors]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
This is not really faster than cursor.forEach (but not that slow either), but it helps me execute any arbitrarily complex python code and save the results from within Python itself.
Also if I have an array of ints in one of the attributes, doing cursor.forEach converts them to floats which I don't want. So I preferred this way.
But I would be glad to know if there're any better ways than this :)
It is quite unlikely that it will ever be efficient to do this kind of thing in python. This is because the document would have to make a round trip and go through the python function on the client machine.
In your example code, you are passing the result of a function to a mongodb update query, which won't work. You can't run any python code inside mongodb queries on the db server.
As the answer to you linked question suggests, this type of action has to be performed in the mongo shell. e.g:
db.collection.find().snapshot().forEach(
function (elem) {
splitLength = elem.text.split(" ").length
db.collection.update(
{
_id: elem._id
},
{
$set: {
split: splitLength
}
}
);
}
);

Meteor/MongoDB limiting the result

I am trying to find all documents and publish at most 5 from the results.
Following this section of the MongoDB doc, I am trying to do this:
Meteor.publish('teams', function () {
return Teams.find().limit(5);
});
Yet, in the server console, I get an exception:
Exception from sub teams id Pm6jKL8Sv3FSDSTfM TypeError: Object [object Object] has no method 'limit'
The following works fine:
Meteor.publish('teams', function () {
return Teams.find({}, {limit:5});
});
Why does the second way work, rather than the first? And where can I find documentation for it?
Meteor's collection API is somewhat different from that of the mongo API. find takes up to two parameters: a selector object, and an options object. options allows you to specify such things as sort, skip, limit and fields, in addition to the meteor-specific reactive and transform.

In MongoDB, how can I retrieve an item and then add to an embedded document within it?

I'm trying to do something similar to the following
http://mongoosejs.com/docs/embedded-documents.html
However instead of new BlogPost I'm trying to retrieve then add as follows
function addComment(id, comment, callback) {
Post.findOne(id, function(err, post) {
post.comments.push(comment);
});
}
I'm getting
TypeError: Cannot call method 'call' of undefined
What am I doing wrong? I left out the saving code for simplification it is crashing even without trying to save.
Assuming that the id parameter is an ObjectId or string (and not a query object), you should be calling findById instead of findOne.

How to compare 2 mongodb collections?

Im trying to 'compare' all documents between 2 collections, which will return true only and if only all documents inside 2 collections are exactly equal.
I've been searching for the methods on the collection, but couldnt find one that can do this.
I experimented something like these in the mongo shell, but not working as i expected :
db.test1 == db.test2
or
db.test1.to_json() == db.test2.to_json()
Please share your thoughts ! Thank you.
You can try using mongodb eval combined with your custom equals function, something like this.
Your methods don't work because in the first case you are comparing object references, which are not the same. In the second case, there is no guarantee that to_json will generate the same string even for the objects that are the same.
Instead, try something like this:
var compareCollections = function(){
db.test1.find().forEach(function(obj1){
db.test2.find({/*if you know some properties, you can put them here...if don't, leave this empty*/}).forEach(function(obj2){
var equals = function(o1, o2){
// here goes some compare code...modified from the SO link you have in the answer.
};
if(equals(ob1, obj2)){
// Do what you want to do
}
});
});
};
db.eval(compareCollections);
With db.eval you ensure that code will be executed on the database server side, without fetching collections to the client.