Chats.findOne(id) returns nothing, but db.chats.findOne(ObjectId(id)) does - mongodb

In my meteor app, I want to publish a chatmate according to the chat object's members: basically there are two members, the one that is not the current user is the one I want to publish. Here's the code. (members is a list which always contains two different user ids)
Meteor.publish('chatmate', function(chat_id) {
var chat = Chats.findOne({_id:chat_id, members: this.userId});
var chatmate = undefined; // just in case... didn't know any better
if (chat) {
for (var i = 0; i < 2; i++) {
if (chat.members[i] !== this.userId)
chatmate = Meteor.users.find(chat.members[i], {fields: {'profile.name': 1}});
}
}
return chatmate;
});
(PS: yes, my publication is in the server folder)
My problem is: my first line of code returns undefined. But if I go on mongo and type db.chats.findOne(ObjectId('<mychatsid>')), I do get my record in the output, along with its members. Problem is, it seems I cannot use this useful ObjectId() method in Meteor!
What am I doing wrong here?

Did you insert these items directly into the mongo shell, or else mongorestore them from another database? By default, Meteor uses strings for _ids (although this can be changed by settings the idGeneration option to "MONGO" when the Collection is declared, as discussed here), whilst stand-alone MongoDB uses ObjectIds, which is what you've got here.
To cut a long story short though, you need to create a new ObjectID object and search based on that if this is the format your _ids are in:
Chats.findOne({ _id: new Meteor.Collection.ObjectID(chat_id), members: this.userId });

Related

Mongo db Collection find returns from the first entry not from last from client side

I am using mongodb and i am querying the database with some conditions which are working fine but the results are coming from the first entry to last where as i want to query from the last added entry to the collection in database
TaggedMessages.find({taggedList:{$elemMatch:{tagName:tagObj.tagValue}}}).fetch()
Meteor uses a custom wrapped version of Mongo.Collection and Mongo.Cursor in order to support reactivity out of the box. It also abstracts the Mongo query API to make it easier to work with.
This is why the native way of accessing elements from the end is not working here.
On the server
In order to use $natural correctly with Meteor you can to use the hint property as option (see the last property in the documentation) on the server:
const selector = {
taggedList:{ $elemMatch:{ tagName:tagObj.tagValue } }
}
const options = {
hint: { $natural : -1 }
}
TaggedMessages.find(selector, options).fetch()
Sidenote: If you ever need to access the "native" Mongo driver, you need to use rawCollection
On the client
On the client you have no real access to the Mongo Driver but to a seemingly similar API (called the minimongo package). There you won't have $natural available (maybe in the future), so you need to use sort with a descenging order:
const selector = {
taggedList:{ $elemMatch:{ tagName:tagObj.tagValue } }
}
const options = {
sort: { createdAt: -1 }
}
TaggedMessages.find(selector, options).fetch()

Meteor, mongodb - accessing an object inside an array

Alright, so I have a collection called Polls. Inside the Polls "table" there is an attribute called choiceObjects which is an array of objects. Each object inside this array has its own attributes. What I need to do is update one of the attributes there. Ill give you a screen shot so you can better visualise what Im talking about
As you can see the choice objects have attributes like body, country etc. There is another attribute called pollid which is set to optional and therefore you cant see it right now. I need to update this pollid attribute now that I have acess to the pollid
Polls.update(
{ _id: pollId },
{ "$set": { "choiceObjects": { pollid: pollId } } }
); //this is kind of what Im trying to do but this isnt right
Since then... I have further tried the following :
var selectedpoll = Polls.findOne(pollId);
console.log(selectedpoll);
//Polls.update( selectedpoll, {"$set"{'choiceObjects.$.pollId':pollId}},false, true );
but when i try that i get the error : the positional operator did not find the match needed from the query. unexpanded update: choiceObjects.$.pollId
If I understand your objective correctly, you want to update (or add) pollid to all objects in the choiceObjects array. Unfortunately $, $push, $addToSet only work with single elements AFAIK.
This might not be what you are looking for but one possible and very obvious way to approach this problem would be to update the entire array in the collection i.e.
var choiceObjects = Polls.findOne({_id: pollId}).choiceObjects;
for (var i = 0; i < choiceObjects.length; i++) {
choiceObjects[i].pollid = pollid;
}
Polls.update({_id: pollid}, {choiceObjects: choiceObjects});

Build a reactive publication with additional fields in each document

I want to make a publication with several additional fields, but I don't want to either use Collection.aggregate and lose my publication updates when the collection change (so I can't just use self.added in it either).
I plan to use Cursor.observeChanges in order to achieve that. I have two major constraints:
I don't want to publish all the documents fields
I want to use some of the unpublished fields to create new ones. For example, I have a field item where I store an array of item _id. I don't want to publish it, but I want to publish a item_count field with the length of my field array
Here comes the approach:
I plan to chain find queries. I never did that so I wonder if it possible. The general (simplified) query structure would be like this: http://jsfiddle.net/Billybobbonnet/1cgrqouj/ (I cant get the code properly displayed here)
Based on the count example in Meteor documentation, I store my query in a variable handle in order to stop the changes notification if a client unsubscribes:
self.onStop(function () {
handle.stop();
});
I add a flag initializing = true; before my query and I set it to true just before calling self.ready();. I use this flag to change my itemCount variable only if it is the publication is initialized. So basically, I change my switch like that:
switch (field) {
case "item"
if (!initializing)
itemCount = raw_document.item.length;
break;
default:
}
I wanted to check that this approach is good and possible before committing into big changes in my code. Can someone confirm me if this is the right way to go?
It's relatively easy to keep fields private even if they are part of the database query. The last argument to self.added is the object being passed to the client, so you can strip/modify/delete fields you are sending to the client.
Here's a modified version of your fiddle. This should do what you are asking for. (To be honest I'm not sure why you had anything chained after the observeChanges function in your fiddle, so maybe I'm misunderstanding you, but looking at the rest of your question this should be it. Sorry if I got it wrong.)
var self = this;
// Modify the document we are sending to the client.
function filter(doc) {
var length = doc.item.length;
// White list the fields you want to publish.
var docToPublish = _.pick(doc, [
'someOtherField'
]);
// Add your custom fields.
docToPublish.itemLength = length;
return docToPublish;
}
var handle = myCollection.find({}, {fields: {item:1, someOtherField:1}})
// Use observe since it gives us the the old and new document when something is changing.
// If this becomes a performance issue then consider using observeChanges,
// but its usually a lot simpler to use observe in cases like this.
.observe({
added: function(doc) {
self.added("myCollection", doc._id, filter(doc));
},
changed: function(newDocument, oldDocument)
// When the item count is changing, send update to client.
if (newDocument.item.length !== oldDocument.item.length)
self.changed("myCollection", newDocument._id, filter(newDocument));
},
removed: function(doc) {
self.removed("myCollection", doc._id);
});
self.ready();
self.onStop(function () {
handle.stop();
});
To solve your first problem, you need to tell MongoDB what fields it should return in the cursor. Leave out the fields you don't want:
MyCollection.find({}, {fields: {'a_field':1}});
Solving your second problem is also pretty easy, I would suggest using the collection helpers packages. You could accomplish this easily, like so:
// Add calculated fields to MyCollection.
MyCollection.helpers({
item_count: function() {
return this.items.length;
}
});
This will be run before an object is added to a cursor, and will create properties on the returned objects that are calculated dynamically, not stored in MongoDB.

Mongo - exclude entries in one collection from another find()

I have a local collection that logs when a user has viewed an entry. It stores the ID of the entry and the time it was viewed:
viewedDate = new Date();
notifications.insert({
'viewed': this.data._id,
'viewedDate': viewedDate
});
I want to exclude any of the 'viewed' ids in this collection from another find() (I basically want to count how many entries haven't been viewed)
How can I use the results of a notifications.find() to exclude results from another find()? If I assign the notifications.find() to a variable, it returns all kinds of stuff as an object.
edit
OK, so if I use fetch() I can restrict what comes back - can I do something with this in a find()?
myNotes = notifications.find({}, {fields: {'viewedDate' :0, _id:0}}).fetch();
This returns
[
Object
viewed: "HqYcCma3qKseHALyv"
__proto__: Object
]
Thanks to some invaluable help from garilla_ in the Meteor IRC, I got it working, solution as follows:
myViewedOffers = notifications.find({},{fields: {'viewed':1}}).fetch();
myViewedArray = myViewedOffers.map(function(viewed){return viewed.viewed});
offerCount = Offers.find({_id: {$nin: myViewedArray}}).count();

Finding an Embedded Document by a specific property in Mongoose, Node.js, MongodDB

For this app, I'm using Node.js, MongoDB, Mongoose & Express
So I have a Param Object that contains an array of Pivots, and I want to read certain data from the pivots as outlined below
---in models.js-------------------------
var Pivot = new Schema({
value : String
, destination : String
, counter : Number
});
var Param = new Schema({
title : String
, desc : String
, pivots : [Pivot]
});
------------- in main.js --------------
var Param = db.model('Param');
app.get('/:title/:value', function(req, res){
Param.findOne({"title":req.param('title')}, function(err, record){
console.log(record.pivots);
record.pivots.find({"value":req.param('value')}, function(err, m_pivot){
pivot.counter++;
res.redirect(m_pivot.destination);
});
record.save();
});
});
I know that the code works until console.log(record.pivots), since i got a doc collection with the right pivot documents inside.
However, there does not seem to be a find method to let me match an embedded document by the 'value' property defined in the schema. Is it possible to search through this array of embedded documents using .find() or .findOne() , and if not, is there some easy way to access it through mongoose?
varunsrin,
This should do it
app.get('/:title/:value', function(req, res) {
Param.findOne({'pivots.value': req.param('value'), "title":req.param('title')}},
function(err, record) {
record.pivot.counter++;
res.redirect(m_pivot.destination);
record.save();
});
});
Note the pluralization of the query to match the field name in your schema
You can querying using embedded document properties like this:
{'pivot.value': req.param('value')}}
Update in response to comment:
app.get('/:title/:value', function(req, res) {
Param.findOne({'pivot.value': req.param('value'), "title":req.param('title')}},
function(err, record) {
record.pivot.counter++;
res.redirect(m_pivot.destination);
record.save();
});
});
I solved it temporarily using a simple for loop to parse the object array as follows:
for (var i=0; i <record.pivots.length; i++){
if (record.pivots[i].value == req.param('value')){
res.redirect(record.pivots.destination);
}
}
However, I still think that Mongoose must have a simpler way of interacting with embedded documents - and this loop is somewhat slow, especially when the number of embedded documents grows large.
If anyone has any suggestions for a faster way to search this object array either in js or with a mongoose function, please post below.
the biggest problem with this is that if your req has some fields empty (that should act as wildcard), you will not find anything since mongo tries to match empty params as well, so searching for {"user":"bob", "color":""} is not the same as {"user":"bob", "color":"red"} or {"user":"bob"}. this means that you have to first create a query object and filter out any unused parameters before you pass it in, and if you create a query object, you can no longer do something like "user.name=..." because mongo interperets this as an error since it does not first resolve the object literal into a string.
Any ideas on this problem?
ps. You'd think it would be easy enough to make an object like:
user.name="bob"; user.color:"green"; user.signup.time="12342561"
and then just use user as a query object :/
I think you are looking for the "$in" keyword?
As in:
{a: {$in: [10, "hello"]}}
source: MongoDB Queries CheatSheet