Meteor: how to extract id of first item from a collection - meteor

I have a Meteor collection and want to get the id of the first item. The query has a limit 1 clause and I just want the id of the first item if there is one. I can't figure out how to code it.
Here is my query.
var myGames = Games.find(
{
game_minutes: {$gt: MinutesSinceMidnightNow},
court_id: court,
game_date: {$gt: lastMidnight}
},
{
sort: { "game_minutes": 1},
limit: 1
});
Then, if I iterate over the results, my app goes off to the races.
myGames.forEach(function (game) {
gameId = game._id;
console.log('gameId: ' + gameId );
});
I know the query is working and currently returning a record but I can't figure out how to access the record.

find() returns a cursor. Instead of using forEach use fetch which will fetch all data from the cursor to an array. So in your case:
var id = myGames.fetch()[0]._id;
(when you are certain that the data is there. otherwise check if array is not empty)

Related

Retrieve id using multiple value From Firebase

I want to retrieve item id and get it to variable using items other value. according to this image ("get id where Brand="Manchee" and ItemName="Cream Cracker" and SubCategory="100g" ")
how write this function
Here you can try this solution.I think you wanted to query the database so you can fire query on your database based on your variable value.Just pass the key and value in this query and you can have all the data reside in the table which falls within this query.
var ref = firebase.database().ref("items");
ref.orderByChild("itemName").equalTo('Manchee').once("value", (items : any)=> {
console.log(items.key);
console.log(items.val());
let itemArray: any = [];
items.forEach((item) => {
itemArray.push({
key: item.key,
});
});
this.items = itemArray;
console.log("Prescription Data: ", itemArray);
});

Meteor: how to find record's position in the collection

I'm working on a cursor-based pagination and I need to find record's position in the collection by its _id. Is it possible to do so, using mongo query only (or several queries), and not fetching all the records and counting because that is inefficient in case a collection contains millions of records as well as using createdBy field, because few records can be created at the same time.
The _ids are strings so I cannot just do something like
n = myCollection.find({ _id: { $lte : 12345}}).count() ;
By default, the Meteor .find() query sorts records in their natural order. This is not based on _id, but rather the position of the records on disk.
Instead, you need to explicitly define the sort parameter if you want records to be sorted by _id. Thus your query should look something like this:
// pageNo = the page number
// pageSize = number of records per page
records = myCollection.find({}, {
sort: { '_id' : 1 }, // Sort by _id in ascending order
limit: pageSize // Only return pageSize elements
skip: pageSize*pageNo // Skip the elements fetched on previous pages.
});

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.

How to publish a view/transform of a collection in Meteor?

I have made a collection
var Words = new Meteor.Collection("words");
and published it:
Meteor.publish("words", function() {
return Words.find();
});
so that I can access it on the client. Problem is, this collection is going to get very large and I just want to publish a transform of it. For example, let's say I want to publish a summary called "num words by length", which is an array of ints, where the index is the length of a word and the item is the number of words of that length. So
wordsByLength[5] = 12;
means that there are 12 words of length 5. In SQL terms, it's a simple GROUP BY/COUNT over the original data set. I'm trying to make a template on the client that will say something like
You have N words of length X
for each length. My question boils down to "I have my data in form A, and I want to publish a transformed version, B".
UPDATE You can transform a collection on the server like this:
Words = new Mongo.Collection("collection_name");
Meteor.publish("yourRecordSet", function() {
//Transform function
var transform = function(doc) {
doc.date = new Date();
return doc;
}
var self = this;
var observer = Words.find().observe({
added: function (document) {
self.added('collection_name', document._id, transform(document));
},
changed: function (newDocument, oldDocument) {
self.changed('collection_name', oldDocument._id, transform(newDocument));
},
removed: function (oldDocument) {
self.removed('collection_name', oldDocument._id);
}
});
self.onStop(function () {
observer.stop();
});
self.ready();
});
To wrap transformations mentioned in other answers, you could use the package I developed, meteor-middleware. It provides a nice pluggable API for this. So instead of just providing a transform, you can stack them one on another. This allows for code reuse, permissions checks (like removing or aggregating fields based on permissions), etc. So you could create a class which allows you to aggregate documents in the way you want.
But for your particular case you might want to look into MongoDB aggregation pipeline. If there is really a lot of words you probably do not want to transfer all of them from the MongoDB server to the Meteor server side. On the other hand, aggregation pipeline lacks the reactivity you might want to have. So that published documents change counts as words come in and go.
To address that you could use another package I developed, PeerDB. It allows you to specify triggers which would be reactively called as data changes, and stored in the database. Then you could simply use normal publishing to send counts to the client. The downside is that all users should be interested in the same collection. It works globally, not per user. But if you are interested in counts of words per whole collection, you could do something like (in CoffeesScript):
class WordCounts extends Document
#Meta
name: 'WordCounts'
class Words extends Document
#Meta
name: 'Words'
triggers: =>
countWords: #Trigger ['word'], (newDocument, oldDocument) ->
# Document has been removed.
if not newDocument._id
WordCounts.update
length: oldDocument.word.length
,
$inc:
count: -1
# Document has been added.
else if not oldDocument._id
WordCounts.update
length: newDocument.word.length
,
$inc:
count: 1
# Word length has changed.
else if newDocument.word.length isnt oldDocument.word.length
WordCounts.update
length: oldDocument.word.length
,
$inc:
count: -1
WordCounts.update
length: newDocument.word.length
,
$inc:
count: 1
And then you could simply publish WordCounts documents:
Meteor.publish 'counts', ->
WordCounts.documents.find()
You could assemble the counts by going through each document in Words, (cursor for each)
var countingCursor = Words.find({});
var wordCounts = {};
countingCursor.forEach(function (word) {
wordCounts[word.length].count += 1;
wordCounts[word.length].words = wordCounts[word.length].words || []
wordCounts[word.length].words.push(word);
});
create a local collection,
var counts = new Meteor.Collection('local-counts-collection', {connection: null});
and insert your answers
var key, value;
for (key in wordCounts) {
value = object[key];
counts.insert({
length: key,
count: value.count,
members: value.words
});
}
Counts is now a collection, just not stored in Mongo.
Not tested!

Meteor.js: having multiple subscriptions on one collection enforce storage results in one local collection(Workarounds?)

Is there a way to store subscriptions of the same server collection in a different minimongo collection?
If not is there any best practice to work around?
I do have a summary table having 50k datasets with a lot of details in the documents.
// Server
var collection = new Meteor.Collection("collection");
Meteor.publish("detail", function (id) {
return collection.find({_id: id});
});
// A pager that does not include the data (fields:{data:0})
Meteor.publish("master", function (filter, sort, skip, limit) {
return collection.find({name: new RegExp("^" + filter + "|\\s" + filter, "i")}, {limit: limit, skip: skip, sort: options, fields: {data: 0}});
});
// Client
var collection = new Meteor.Collection("collection");
Deps.autorun(function () {
Meteor.subscribe("master",
Session.get("search"),
Session.get("sort"),
Session.get("skip"),
Session.get("limit")
);
Meteor.subscribe("detail", Session.get("selection"));
});
Problem above: both subscriptions are feed into the same collection.
This does not work well if the results of the finds are stored in the same local collection.
Having a local collection with the name of the subscription/publish would be great.
// Client
var detail = new Meteor.Collection("detail"),
master = new Meteor.Collection("master");
Any Ideas?
If you want your client side collection to have a different name from the server side collection you can not just return a collection cursor. This can be done in the publish function though like this:
Meteor.publish("details", function (id) { //details here matches the subscribe request
var self = this;
self.added( "details", id, collection.findOne({_id: id}); //details here tells the client which collection holds the data
self.ready();
});
This will not be reactive but can be made that way by using observe as in the counts by room example at http://docs.meteor.com which is explained in detail here How does the messages-count example in Meteor docs work?.
While this answers your question of how to get a specific name for a collection without having that collection on the server. I think you probably get what you want more easily with a publish function more like this:
Meteor.publish("master", function (filter, sort, skip, limit, id) {
return [
collection.find({name: new RegExp("^" + filter + "|\\s" + filter, "i")}, {limit: limit, skip: skip, sort: options, fields: {data: 0}})
, collection.find( id , {fields: {data: 1}} )
];
});
Then subscribe on client:
Deps.autorun(function () {
Meteor.subscribe("master",
Session.get("search"),
Session.get("sort"),
Session.get("skip"),
Session.get("limit"),
Session.get("selection")
);
});
Then even though all your data is in one collection you can have a reactive cursor to your selected id with the data included. Query from the client like this:
collection.find( Session.get("selection") );

Resources