How to publish document ID? - meteor

I am trying to access document _id in the client
Here is how I am publishing it:
Meteor.publish('events', function () {
return Requests.find({}, {fields: {"_id": 1, 'start': 1, 'title': 1, 'reasons': 1}});
});
and in the client when I try to console.log() the id, I get "_fc4" printed to the console
Here is my code (in the client):
eventClick: function(event, jsEvent, view) {
console.log(event._id);
}
event is the documents returned. I get the correct value when printing event.title, event.start, or event.reasons to the console. But I get _fc4 when trying event._id.
My question now is, how to publish the _id correctly?
NOTE: Tried to change "_id" with _id and received the same results in the console

I believe your printing the event id in the calendar, not your document in the database.
make sure you set the event id to your document id before adding it to the calendar

Related

Google Insert Events Duplicating In Google Calendar By Clicking Insert Button again and again

I have used a button for inserting events but when i click button again it inserts again and created a duplicate copies of events. Is there any way to only insert latest events.
{
var request;
for (var j = 0; j < this.state.syncEvent.length; j++) {
console.log("J loop", this.state.syncEvent[j]);
request = function (resource) {
return gapi.client.calendar.events.insert({
'calendarId': 'primary',
'eventId': resource
});
}(this.state.syncEvent[j]);
request.execute(function (resp: any) {
console.log(resp);
});
}
}
If you are inserting an event with a predefined event id - check first if an event with this id already exists
First of all, there are some problems with your code.
Have a look at the Javasript sample in the documentation:
The correct syntax would be:
var request = gapi.client.calendar.events.insert({
'calendarId': 'primary',
'resource': event
});
whereby event is the event resource of type
var event = {
...
'start': {
'dateTime': '2015-05-28T09:00:00-07:00',
'timeZone': 'America/Los_Angeles'
},
'end': {
'dateTime': '2015-05-28T17:00:00-07:00',
'timeZone': 'America/Los_Angeles'
},
'id': SPECIFY_HERE_THE_EVENT_ID,
...
};
The event resource must contain the Required Properties end and start, in addition you specify additional properties mentioned in the documentaiton of the method like e.g. summary or id.
Now, since from your code snippet one can deduct that you pass the event id to your function - before inserting the event, you can check with the method Events: get either an event with the given id already exists.
Alternatively, you can also use the method Events: list to retrieve the already existing events in your calendar. Thereby you can use the query parameter q to filter by e.g. summary or you can query by specifying the paramters timeMax and timeMin - depending on your use case.
All you need to do is to implement a conditional statement to create a new event only if Events: get or Events: list did not return an already existing event with the specified parameters.

How can I read a value from a mongodb cursor inside the same publication where the cursor was generated

I have a meteor publication in which I was trying to use .findOne() to access the fields from the mongodb. But as I cannot return .findOne() from a meteor publish, I am now using just .find(). The problem is that it returns a cursor instead of a document so I am not able to read the values inside that cursor in my publish function.
Below is my mongodb query
var question = Question.find({ "_id": quesId },
{ fields: {"pages": 1, "settings.name": 1, "status": 1 }},
{limit: 1});
And I want to use the value of pages that I get from the above query inside the same publish function
you can set an observer on your cursor and get a hook into the results. i do that frequently to transform the result.
e.g. (i am making the assumption that the publish name is "questions")
let cursor = Question.find({ "_id": quesId }, { "pages": 1, "settings.name": 1, "status": 1 }, {limit: 1});
let handle = cursor.observeChanges({
added: (id, fields) => {
// look at fields
this.added('questions', id, fields);
},
changed: (id, fields) => {
// look at fields
this.changed('questions', id, fields);
},
removed: (id) => {
this.removed('questions', id);
}
});
this.ready();
this.onStop(() => {
handle.stop();
});
this will give you a hook into the records that are added at the initial publish, and changed later. call ready() when your publication is ready to be published (you can see the this.ready() in the sample).
As far as I understand your question, you want to access the single document in the cursor without doing a redundant findOne() in your code. You can transform the result into an array with .fetch() on the cursor, get the first entry with [0] and then directly get the pages attribute.
const question = Question.find(quesId,
{ fields: { "pages": 1, "settings.name": 1, "status": 1 }});
const pages = question.fetch()[0].pages;
Note also that when searching on _id you don't have to specify {_id: quesId} in your filter, you can directly use the value you want to search on as the filter parameter, mongo assumes that you're searching on _id. Furthermore the {limit: 1} is redundant since you're searching on a scalar _id value and so you're guaranteed that the cursor length will be one.

userId gets into the document magically

This Meteor code is working fine, but I would like to ask if it is the way Meteor does things or it is a un predictable side effect that may change under some condition later.
The things is that when I do
DisplayCol.insert({action: 'task1', element: 'p', value: value_variable});
Meteor also inserts the correct userId (using 2 different browsers logged in as 2 different users) which I did not explicitly included in the document.
The above line of code is inside a server side function which is called from Meteor method.
here is the relevant information;
//lib/collection.js
DisplayCol = new Mongo.Collection('displayCol');
//server.js
Meteor.publish('displayCol', function () {
return DisplayCol.find({userId: this.userId});
});
DisplayCol.before.insert(function (userId, doc) {
doc.userId = userId;
});
In the docs of Collection hooks > Additional notes > second bulleted paragraph says:
userId is available to find and findOne queries that were invoked within a publish function.
But this is a collection.insert. So should I explicitly include the userId in the document or let the collection hook do its hidden magic? Thanks
No, there is no hidden magic in that code, your before hook is inserting the userId field in the document.
When you do an insert like this,
DisplayCol.insert({action: 'task1', element: 'p', value: value_variable});
the doc that your are inserting is { action: 'task1', element: 'p', value: value_variable }
Because, you have this hook,
DisplayCol.before.insert(function (userId, doc) {
doc.userId = userId;
});
it changes the doc before inserting into collection. So the above hook will change your doc to {action: 'task1', element: 'p', value: value_variable, userId: 'actual-user-id' }
This is the expected behaviour.
Regarding your other point in the question,
userId is available to find and findOne queries that were invoked
within a publish function.
Previously userId parameter in the find and findOne returns null, so user needs to pass userId as a parameter as mentioned in this comment. Additional notes mentions that the hack is not required any more. It has nothing to do with inserting userId field into the collection document.
To have a quick test, remove the DisplayCol.before.insert hook above, you will not see userId field in the newly inserted documents.
UPDATE
Just to clarify your doubt further, from the 4th point in the docs that you provided
It is quite normal for userId to sometimes be unavailable to hook
callbacks in some circumstances. For example, if an update is fired
from the server with no user context, the server certainly won't be
able to provide any particular userId.
which means that if the document is inserted or updated on the server, there will be no user associated with the server, in that case, userId will return null.
Also you can check the source code yourself here. Check the CollectionHooks.getUserId method, it uses Meteor.userId() to get the userId.
CollectionHooks.getUserId = function getUserId() {
var userId;
if (Meteor.isClient) {
Tracker.nonreactive(function () {
userId = Meteor.userId && Meteor.userId(); // <------- It uses Meteor.userId() to get the current user's id
});
}
if (Meteor.isServer) {
try {
// Will throw an error unless within method call.
// Attempt to recover gracefully by catching:
userId = Meteor.userId && Meteor.userId(); // <------- It uses Meteor.userId() to get the current user's id
} catch (e) {}
if (!userId) {
// Get the userId if we are in a publish function.
userId = publishUserId.get();
}
}
return userId;
};

How can I get other users' profiles details in meteor

I have got problem accessing the user profile details of the users other then the current user.
The goal is to display a little footer under a each of the posts in the kind of blog entries list . Footer should consist of the post and author details (like date, username etc.).
Blog entry is identified by authors' _id but the point is that I can not access
Meteor.users.find({_id : authorId});
Resulting cursor seems to be the same as Meteor.user (not 'users') and consists of one only document, and is valid for the current user ID only. For others, like authors ID, I can only get an empty collection.
The question is, if is there any way, other then next Meteor.users subscription to get authors profile (like username profile.nick etc) ???
Update: You can Publish Composite package if you want to get blog entry and user details in a single subscription. See the following sample code and edit as per your collection schemas,
Meteor.publishComposite('blogEntries', function (blogEntryIds) {
return [{
find: function() {
return BlogEntries.find({ courseId: { $in: blogEntryIds }});
// you can also do -> return BlogEntries.find();
// or -> return BlogEntries.find({ courseId: blogEntryId });
},
children: [{
find: function(blogEntry) {
return Meteor.users.find({
id: blogEntry.authorId
}, {
fields: {
"profile": 1,
"emails": 1
}
});
}
}}
}]
});
End of update
You need to publish Meteor.users from the server to be able to use it on client. accounts package will publish current user, that's why you are only seeing current user's information.
In a file in server folder or in Meteor.isServer if block do something like this
//authorIds = ["authorId1", "authorId2];
Meteor.publish('authors', function (authorIds) {
return Meteor.users.find({ _id : { $in: authorIds }});
});
or
Meteor.publish('author', function (authorId) {
return Meteor.users.find({ _id : authorId });
});
Then on client side subscribe to this publication, in template's onCreated function, with something like this
Meteor.subscribe('author', authorId); //or Meteor.subscribe('author', authorIds);
or
template.subscribe('author', authorId); //or template.subscribe('author', authorIds);
If you want to show only username (or a few other fields), you can save them in post document along with authorId. For example:
post:{
...
authorId: someValue,
authorName: someValue
}
You can use them in your templates as a field of a post.
If you have too many fields which you do not want to embed in post document, (so you want to keep only authorId), you can use publish-composite when you make your posts publication. (See example 1)
You do not need to publish all your users and their profiles.

Using Pub/Sub, how do I add additional fields if I previously subscribed to the collection with a limited set of fields?

If I have a subscription that does
Meteor.publish("mypublish", function (status) {
var foundOnServer = MyCollection.find({"status": status}, {fields: {_id: 1, status: 1, "name": 1}});
return foundOnServer;
});
and I'm displaying those records in a table, then I want to let the user click on one of them to bring up the full detail of the record, and now I want to display some more fields that weren't previously published to the client, like maybe address, city, state, etc... how do I code things so that I can get the new fields picked up and put into the local minimongo collection, considering the record is already in the client. Doing a Find just returns the document already in the client, without the extra fields I want.
EDIT: I was not aware of the DDP limitation in that once you subscribe to a field in a nested structure, you are unable to then add more fields from that level of the nested structure. https://github.com/meteor/meteor/issues/998
Write another publish function for the appropriate subset and return more/all of the the fields. For example:
Meteor.publish('singlePost', function(postId) {
check(postId, String);
return Posts.find(postId);
});
When you click on a post, you can subscribe to singlePost which will send all of the fields for that post. The key insight is the documents will be merged on the client.
So if the client had {_id: 'x', message: 'hello'} already in minimongo, and you later publish {_id: 'x', color: 'blue'}, the client will then have {_id: 'x', message: 'hello', color: 'blue'} in its database.

Resources