Meteor - Publish just the count for a collection
Asked Answered
A

3

15

Is it possible to publish just the count for a collection to the user? I want to display the total count on the homepage, but not pass all the data to the user. This is what I tried but it does not work:

Meteor.publish('task-count', function () {
    return Tasks.find().count();
});

this.route('home', { 
    path: '/',
    waitOn: function () {
        return Meteor.subscribe('task-count');
    }
});

When I try this I get an endless loading animation.

Alcalde answered 14/1, 2015 at 16:40 Comment(1)
is those Tasks belong to users or anything ? Why i am asking is that you can maintain that count inside users collection iteself and get it from thereTerriterrible
A
19

Meteor.publish functions should return cursors, but here you're returning directly a Number which is the total count of documents in your Tasks collection.

Counting documents in Meteor is a surprisingly more difficult task than it appears if you want to do it the proper way : using a solution both elegant and effective.

The package ros:publish-counts (a fork of tmeasday:publish-counts) provides accurate counts for small collections (100-1000) or "nearly accurate" counts for larger collections (tens of thousands) using the fastCount option.

You can use it this way :

// server-side publish (small collection)
Meteor.publish("tasks-count",function(){
  Counts.publish(this,"tasks-count",Tasks.find());
});

// server-side publish (large collection)
Meteor.publish("tasks-count",function(){
  Counts.publish(this,"tasks-count",Tasks.find(), {fastCount: true});
});

// client-side use
Template.myTemplate.helpers({
  tasksCount:function(){
    return Counts.get("tasks-count");
  }
});

You'll get a client-side reactive count as well as a server-side reasonably performant implementation.

This problem is discussed in a (paid) bullet proof Meteor lesson which is a recommended reading : https://bulletproofmeteor.com/

Anatolic answered 14/1, 2015 at 16:56 Comment(0)
H
8

I would use a Meteor.call

Client:

 var count; /// Global Client Variable

 Meteor.startup(function () {
    Meteor.call("count", function (error, result) {
      count = result;
    })
 });

return count in some helper

Server:

Meteor.methods({
   count: function () {
     return Tasks.find().count();
   }
})

*Note this solution would not be reactive. However if reactivity is desired it can be incorporated.

Humpy answered 14/1, 2015 at 16:54 Comment(1)
is there a reactive approach?Daughtry
T
1

This is an old question, but I hope my answer might help others who need this info as I did.

I sometimes need some miscellaneous but reactive data to display indicators in the UI and documents count is a good example.

  1. Create a reusable (exported) client-side only collection that won't be imported on the server (to avoid creating unnecessary database collection). Note the name passed as argument ("misc" here).
import { Mongo } from "meteor/mongo";

const Misc = new Mongo.Collection("misc");

export default Misc;
  1. Create a publication on the server that accepts docId and the name of the key where the count will be saved (with a default value). The collection name to publish to is the one used to create the client only collection ("misc"). The docId value does not matter much, it just needs to be unique among all Misc docs to avoid conflicts. See Meteor docs for details about the publication behavior.
import { Meteor } from "meteor/meteor";
import { check } from "meteor/check";
import { Shifts } from "../../collections";

const COLL_NAME = "misc";

/* Publish the number of shifts that need revision in a 'misc' collection
 * to a document specified as `docId` and optionally to a specified `key`. */
Meteor.publish("shiftsToReviseCount", function({ docId, key = "count" }) {
  check(docId, String);
  check(key, String);

  let initialized = false;
  let count = 0;

  const observer = Shifts.find(
    { needsRevision: true },
    { fields: { _id: 1 } }
  ).observeChanges({
    added: () => {
      count += 1;

      if (initialized) {
        this.changed(COLL_NAME, docId, { [key]: count });
      }
    },

    removed: () => {
      count -= 1;
      this.changed(COLL_NAME, docId, { [key]: count });
    },
  });

  if (!initialized) {
    this.added(COLL_NAME, docId, { [key]: count });
    initialized = true;
  }

  this.ready();

  this.onStop(() => {
    observer.stop();
  });
});
  1. On the client, import the collection, decide of a docId string (can be saved in a constant), subscribe to the publication and fetch the appropriate document. Voilà!
import { Meteor } from "meteor/meteor";
import { withTracker } from "meteor/react-meteor-data";
import Misc from "/collections/client/Misc";

const REVISION_COUNT_ID = "REVISION_COUNT_ID";

export default withTracker(() => {
  Meteor.subscribe("shiftsToReviseCount", {
    docId: REVISION_COUNT_ID,
  }).ready();

  const { count } = Misc.findOne(REVISION_COUNT_ID) || {};

  return { count };
});
Thrasher answered 24/9, 2019 at 16:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.