Giter VIP home page Giter VIP logo

publish-counts's Introduction

Publish Counts

A package to help you publish the count of a cursor, in real time.

Publish-counts is designed for counting a small number of documents around an order of 100. Due to the real-time capability, this package should not be used to count all documents in large datasets. Maybe some, but not all. Otherwise, you will maximize your server's CPU usage as each client connects.

v1.x Breaking changes

This version is only compatible with Meteor 3.0 and higher.

Table of Contents

Installation

$ meteor add tmeasday:publish-counts

API

Counts.publish [server]

Counts.publish(subscription, counter-name, cursor, options)

Simply call Counts.publish within a publication, passing in a name and a cursor:

Example 1

JavaScript
Meteor.publish('publication', function() {
  Counts.publish(this, 'name-of-counter', Posts.find());
});
Coffeescript
Meteor.publish 'publication', ->
  Counts.publish this, 'name-of-counter', Posts.find()
  return undefined    # otherwise coffeescript returns a Counts.publish
                      # handle when Meteor expects a Mongo.Cursor object.

The Counts.publish function returns the observer handle that's used to maintain the counter. You can call its stop method in order to stop the observer from running.

Warning: Make sure you call collection.find() separately for Counts.publish and the Meteor.publish return value, otherwise you'll get empty documents on the client.

For more info regarding the options parameter, see Options.

Counts.get [client]

Counts.get(counter-name)

Once you've subscribed to 'publication' (Ex 1), you can call Counts.get('name-of-counter') to get the value of the counter, reactively.

This function will always return an integer, 0 is returned if the counter is neither published nor subscribed to.

Counts.has [client]

Counts.has(counter-name)

Returns true if a counter is both published and subscribed to, otherwise returns false. This function is reactive.

Useful for validating the existence of counters.

Counts.noWarnings [server]

Counts.noWarnings()

This function disables all development warnings on the server from publish-counts.

Not recommended for use by development teams, as warnings are meant to inform library users of potential conflicts, inefficiencies, etc in their use of publish-counts as a sanity check. Suppressing all warnings precludes this sanity check for future changes. See the noWarnings option for fine-grained warning suppression.

Options

noReady

If you publish a count within a publication that also returns cursor(s), you probably want to pass {noReady: true} as a final argument to ensure that the "data" publication sets the ready state. For example, the following publication sends down 10 posts, but allows us to see how many there are in total:

Meteor.publish('posts-with-count', function() {
  Counts.publish(this, 'posts', Posts.find(), { noReady: true });
  return Posts.find({}, { limit: 10 });
});

nonReactive

If you specify {nonReactive: true} the cursor won't be observed and only the initial count will be sent on initially subscribing. This is useful in some cases where reactivity is not desired, and can improve performance.

countFromField

countFromField allows you to specify a field to calculate the sum of its numbers across all documents. For example if we were to store page visits as numbers on a field called visits:

{ content: 'testing', visits: 100 },
{ content: 'a comment', visits: 50 }

We could then publish them like:

Meteor.publish('posts-visits-count', function() {
  Counts.publish(this, 'posts-visits', Posts.find(), { countFromField: 'visits' });
});

And calling Counts.get('posts-visits') returns 150

If the counter field is deeply nested, e.g.:

{ content: 'testing', stats: { visits: 100 } },
{ content: 'a comment', stats: { visits: 50 } }

Then use an accessor function instead like:

Meteor.publish('posts-visits-count', function() {
  Counts.publish(this, 'posts-visits',
    Posts.find({}, { fields: { _id: 1, 'stats.visits': 1 }}),
    { countFromField: function (doc) { return doc.stats.visits; } }
  );
});

Note that when using an accessor function, you must limit the fields fetched if desired, otherwise Counts will fetch entire documents as it updates the count.

countFromFieldLength

countFromFieldLength allows you to specify a field to calculate the sum of its length across all documents. For example if we were to store the userIds in an array on a field called likes:

{ content: 'testing', likes: ['6PNw4GQKMA8CLprZf', 'HKv4S7xQ52h6KsXQ7'] },
{ content: 'a comment', likes: ['PSmYXrxpwg276aPf5'] }

We could then publish them like:

Meteor.publish('posts-likes-count', function() {
  Counts.publish(this, 'posts-likes', Posts.find(), { countFromFieldLength: 'likes' });
});

If the counter field is deeply nested, e.g.:

{ content: 'testing', popularity: { likes: ['6PNw4GQKMA8CLprZf', 'HKv4S7xQ52h6KsXQ7'] } },
{ content: 'a comment', popularity: { likes: ['PSmYXrxpwg276aPf5'] } }

Then use an accessor function instead like:

Meteor.publish('posts-likes-count', function() {
  Counts.publish(this, 'posts-likes',
    Posts.find({}, { fields: { _id: 1, 'popularity.likes': 1 }}),
    { countFromFieldLength: function (doc) { return doc.popularity.likes; } }
  );
});

Note that when using an accessor function, you must limit the fields fetched if desired, otherwise Counts will fetch entire documents as it updates the count.

noWarnings

Pass the option, noWarnings: true, to Counts.publish to disable its warnings in a development environment.

Each call to Counts.publish may print warnings to the console to inform developers of non-fatal conflicts with publish-counts. In some situations, a developer may intentionally invoke Counts.publish in a way that generates a warnings. Use this option to disable warnings for a particular invocation of Counts.publish.

This fine-grained method of warning suppression is recommended for development teams that rely on warnings with respect to future changes.

Template helpers

To easily show counter values within your templates, use the getPublishedCount or hasPublishedCount template helper.

Example:

<p>There are {{getPublishedCount 'posts'}} posts.</p>
<p>
  {{#if hasPublishedCount 'posts'}}
    There are {{getPublishedCount 'posts'}} posts.
  {{else}}
    The number of posts is loading...
  {{/if}}
</p>

Notes

Observer handle leak testing

The package includes a test that checks the number of observer handles opened and closed (to check for memory leaks). You need to run the enable-publication-tests-0.7.0.1 branch of percolatestudio/meteor to run it however.

Why doesn't this library count directly in Mongo? or...

Why does my MongoDB connection time-out with large (1000+) datasets?

This package is designed primarily for correctness, not performance. That's why it's aimed at counting smaller datasets and keeping the count instantly up to date.

To achieve perfect correctness in Meteor data layer, we use a database observer to know immediately if a relevant change has occurred. This approach does not necessarily scale to larger datasets, as the observer needs to cache the entire matching dataset (amongst other reasons).

Counting large datasets in this manner is suspected to cause database connections to time out (see #86).

An alternative approach would be to take a .count() of the relevant cursor (or perform an aggregation in more complex use cases), and poll it regularly to update the count. Bulletproof Meteor provides a proof of concept of this approach in their bullet-counter example.

We'd love to see someone publish a package for this use case! If you do end up making such a package, let us know and we'll link it here.

Scalable Count Packages

Compatibility with Meteor < 1.3

Publish-counts 0.8.0 introduces an explicit dependency on the underscore js library which may be incompatible with versions of Meteor below 1.3. Please upgrade Meteor to the latest version or, if you cannot, continue to use publish-counts 0.7.3.

Frequently Asked Questions

More information can be found in the FAQ Section of the issue tracker.

License

MIT. (c) Percolate Studio

publish-counts was developed as part of the Verso project.

publish-counts's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

publish-counts's Issues

Need help working it correctly

Publications

Meteor.publish('counts', function () {
  Counts.publish(this, 'notificationCount',
    Notifications.find({'to._id': this.userId}),
    { countFromField: 'read' },
    { noReady: true });
});

HTML

{{getPublishedCount 'notificationCount'}}

Router Subscription

Router.configure({
  subscriptions: function () {
    return [
      Meteor.subscribe('counts')
    ]
  }
});

I have the following code to retrieve the notifications counts of a user that is NOT READ yet (read: false).

I am pretty sure I have to do more from here, but it seems to be not working yet.

Is there anything I can do to make this work?

Right now it functions in a weird manner that I can't seem to grasp a hold a pattern of.

Split the client / server into 2 files for performance

Firstly, thanks for taking the hassle out of publishing counts, it's a great service to the meteor community.

I'm assuming that because the publish-counts.js file is being loaded on both client and server, that all of its code is minified and shipped to the client. I realise it's not much, but the client only part looks like <20% of the total filesize. If it was 2 files, then I'm assuming meteor would only minify and ship the client part of the code to the browser, instead of the whole file.

A minor optimisation for sure, but probably relatively quick to implement. I'd be happy to submit a pull request if you're open to merging it.

Augment cursor to limit fields

It would be nice to somehow augment the cursor so only the _id field is observed.

e.g: Posts.find({}, {fields: {_id: true}})

countFromField returning number of objects instead of sum of fields.

Publish Method:

Meteor.publish('student-points-earned', function (courseId) {
  Counts.publish(this, 'points-earned', AssignmentBox.find({ 'student._id': this.userId, 'status': 'graded', 'course': courseId }), { countFromField: 'pointsEarned' });
});

subscription:

$scope.$meteorSubscribe('student-points-earned', this.courseId).then(() => {
  this.totalEarnedPoints = $scope.$meteorObject(Counts, 'points-earned', false);
  console.log(this.totalEarnedPoints);
})

Re-subscription with different parameters doesn't update count

Hi,

I have a filter object attached to my user, which determines the query to run against my collection. Additionally I have pagination support with a limit (load-more scenario):

My publication:

Meteor.publish('filtercustomer', function(limit) {
    if(this.userId) {
        console.log('re-subscribing');
        var query = getCustomerFilter(this.userId);
        Counts.publish(this, 'total-engagements', customers.find(query.filter));
        return customers.find(query.filter, {
            sort: query.sort,
            limit: parseInt(limit)
        });
    }
});

As I modify my filter object via the UI (by applying new filters or removing old filters) I re-subscribe to this publication (I see the text "re-subscribing" on the server console whenever I change the filter). In the UI I also see a different list of entries (according to the selected filters).

The only thing that doesn't change is the "Total" at the bottom of my list, which is also used to determine if the "Show More" link is displayed. The helper showing the total uses Count.get('total-engagements').

I've tried using a Session variable as well, but for some reason the Counts.publish doesn't seem to update my counter, which I retrieve via Counts.get.

Did something go wrong with 0.3.5?

Hi, no idea if and how this could be. But starting tonight (after update) my Client won't run (filled with Meteor is undefined, Template is undefined, etc. in the console).

If I remove publish-counts it works again. If i re-add it. It breaks again. Re-installed Meteor, all packages, etc. But still the same behaviour.

When manually adding the package after removing, it says "adding 0.3.5".

Cheers.

p.s. force downgrade to 0.3.4 and it works.

[Future] Support `applySkipLimit` or some way to respect skip/limit

As of right now, Meteor doesn't support cursor.find({}, {skip: 5, limit: 5}).count({applySkipLimit: true}) all queries can be assumed to return the full number of matches documents without regards to the skip / limit.

So either:

When: meteor/meteor#1201 is resolved, publish-counts should support applySkipLimit.

Or if it turns out that applySkipLimit won't be supported we should figure out some workaround.

In the meantime it might be worth it to make note of this in the README.md.

Cheers

Provide api to stop observing query

It would be nice to have an api to stop a reactive observe.
This is useful e.g. when I publish counts for all documents of an outer query
and documents get removed from that query.

Maybe an example does explain more:

I want to publish the comment count for all posts in a query.
When a post gets removed, I want to stop the publishCount for that post.

I could provide a PR if that is appreciated.

big collections redux

I need this to watch a big collection, but I don't want to actually publish the big collection to the client. I just need the count of the collection to be published, and if that count changes, then I need the change to be reactive and sent to the client.

Currently when I fire this up on a large collection, it just crashes my server after showing me the initial count.

How to count documents based on user created documents createdBy field?

Hello,

Posting this as separate issue as requested by @boxofrox .
I have a use case for counting the documents by a user createdBy field, to count all the the user (branch users) documents based on the user company.

I've managed to achieved the counts of documents per company per branch based on user roles with the help of @boxofrox :) Thanks for that!

My requirement is something like this, if the role is HQ company hq(head office), it should display the document count of its branches.
branchcount

What you see on the total enrollments column is actually the total number of documents the company has. I don't know how achieve this requirement, based on @tmeasday , it is not possible at this time.

Do you have any workaround for this?

bad count with accessor functions when doc updates remove then re-add the count field

Steps to reproduce:

  1. create a countByField* with accessor function. e.g. {countByField: function (doc) { return doc.a; }}
  2. add a document. e.g. Posts.insert({_id: 123, a: 5})
  3. update the document without a count field: e.g. Posts.update(123, {})
  4. update the document with a count field: e.g. Posts.update(123, {a: 6})
  5. note that Counts.get() did not add 6.

PR to follow.

Thank you!

I swear, I was just implementing similar code in my app and checked atmosphere two hours ago for something... Of course I finish up and see this now!

Thanks for sharing this guys.

Return 0 when counter doesn't exists destroys reactivity

Hey awesome packge.

I'm sorry to say the check in 35a0eba, destroys the reactivity for the template helper.

Also I don't see how it should return 0 if the counter doesn't exists? It should be throwing and error saying that the counter doesn't exist or something.

However if you really want it to return 0 for non existent counters then the check needs to be updated.

return count && count.count || 0; // This is not good enough

If count.count returns 0, the count is actually 0, then it will return the static number 0 instead.
Which for existing counters breaks reactivity. Because Count.get isn't a reactive getter.

countFromField returns collection count rather than the sum of field values

trying to return the collection count, and the sum of a field on each doc.

Counts.publish(this, 'clips', Media.find({
    $and: [
      {type: 'clip'},
      {publishedStatus: 'published'}
    ]
  }, {
    fields: {
      _id: 1,
      'stats': 1
    }
  }));
  Counts.publish(this, 'responses', Media.find({
    $and: [
      {type: 'response'},
      {publishedStatus: 'published'}
    ]
  },{
    fields: {
      _id: 1,
      'stats': 1
    }
  }));

  Counts.publish(this, 'votesDown', Media.find({
    'stats.votesDown': { $exists: true }
  }, {
    fields: {
      _id: 1,
      'stats.votesDown': 1
    }
  },{
    countFromField: function (doc) {
      return doc.stats.votesDown;
    }
  }));

This only return s the total count for the collection.

Small readme typo

In the example for countFromFieldLength you show:

Meteor.publish('posts-likes-count', function() {
    Counts.publish(this, 'posts-likes', Posts.find(), { countFromFieldLength: 'likes' });
});

Then say:

And calling Counts.get('posts-likes-count') returns 3

But I think you meant:

And calling Counts.get('posts-likes') returns 3

Just double checking

Counts.get() should be working for SSR

I'm using React.JS with publish-counts and it's awesome. One problem I have however is I can't have server-rendering because Counts.get() will only work on the client.

There is probably something we could do to make it works on both the client and the server. What do you think?

options.countFromFieldLength documentation?

Hello there!

Could you guys elaborate a little about this options.countFromFieldLength we can see in the source file? It makes the code a bit more complicated, and I can't guess the usage for it.

Thanks!

Return counts broken down by attribute from collection

Hey Percolate Team -

Is it possible to return the counts from publish-counts broken down by an attributes from the collection? I have an "events" collection with a user_id and Type and I would like to know the count of docs in the collection broken down by both. In sql terms select count(*) from events group by user_id, type.

Is this possible with publish-counts?

picture of desired-ish output
screen shot 2015-08-23 at 12 24 12 pm

Big collections

Hey,

I've done exactly what you doing in your package, but made my own code.
One thing I've noticed is the memory usage on big collections. Memory goes to space if you have a lot of docs and I believe it's because of how observe works without the oplog.
I think it should be nice to warn people about it in README file.

Just it! Cya

countFromField with tranform doesn't work

I'm trying yo use countFromField to publish the number of roles from all the users but this doesn't work. Does publish counts ignore collection transform?

Counts.publish(this, 'membersCount',
        Meteor.users.find({}, { fields: { _id: 1, roles: 1 },transform:function(doc){
            var keys = _.keys(doc.roles);
            doc.rolesCount = keys.length;
            return doc;
        }}),
        {
            countFromField:'rolesCount'
        }
    );

I've also tried using a function in countFromField and returning there the count and doesn't work too. I've added logs in both functions and never fire. Any idea?

Counts is getting the same number of comments for each post

Lets say im showing a list of posts with each corresponding number of comments. For some reason i get the same number of comments for each post when ever i add or remove comments. This is happening when i do return Counts.get("comments-count") but it works fine if i do return Comments.find({postId: postId}).count();
This is my code so far:

post_item.html

<template name="postItem">
  <li>{{name}} <span class="pull-right"> {{commentsCount}}</span></li>
</template>

post_item.js

Template.postItem.onCreated(function() {
  return this.subscribe('comments', this.data._id);
});

Template.postItem.helpers({
  commentsCount: function() {
    return Counts.get('comments-count'); // This works: Comments.find({postId: this._id}).count(); 
  }
});

comments.js

Meteor.publish('comments', function(postId) {
  Counts.publish(this, 'comments-count', Comments.find({postId: postId}));
  return Comments.find({postId: postId});
});

Feature - support count unique

It would be nice and useful to support a count of uniques across a specified field in the collection documents (or to be able to provide a uniqueId function that accepts a document and returns its unique ID for the sake of this count).

Is this BTW perhaps already supported?...

Example:

Counts.publish(this, 'unique-name-of-counter', 
    Posts.find(), 
    {uniqueField: 'referrerId'});

// or: 

Counts.publish(this, 'unique-name-of-counter', 
    Posts.find(), 
    {uniqueFunction: function(post){
      return post.referrerId + '-' + post.userId;
    });

High CPU

From an article a few weeks ago:
https://kadira.io/blog/meteor/success-stories-meteor-live-query-monitoring

Counting With Publish Count

A few of our users had some sudden CPU spikes, and they were able to find it with our live query monitoring support.

This is the "Live Queries" tab for one such user:

Kadira Live Queries tab

What can you learn from this set of graphs? Have a look at it.

His app's CPU usage spikes to near 100% suddenly when new users use his app. (SubRate also increased at that time, so that's why we said he was getting new users visiting.)
If you look at the "Fetched Documents" tab, you can identify the issue.
The publication counters fetched a lot of documents to Meteor. He was using publish-counts inside that publication.
That was the reason for the high CPU usage by his app. Here's some more information about it.

To count a Mongo query reactively, publish-counts fetches all the documents that match that query to Meteor.(But it won't send them to the client.) This works fine for a small number of documents, but as the document count increases, you will face issues like this.

So, it is always a better idea to count inside the DB.

So why don't we fix this package so that it gets Mongo to count instead of fetching all the docs itself?

Collection.find( with $gt ) count change and no refresh well

Counts.publish(this, 'position', Clicks.find({count:{$gt:countLimit}}),{ noReady: true });

I 'm trying to publish this count, but the number return well at first. When I change the Clicks collection, number increment 1, without sense. And when the count it should be to change, it does not happen.

Cursor values get emptied when passed to Counts.publish()

I'm not sure why, but when I save my search cursor to a variable (so that I can use it twice) and pass it into Counts.publish() and then use it again to return the values to the client, the cursor sent to the client has _id fields only. As you can see in the code below, the search specifies which fields are to be returned to the client, and this works just fine if the Cursor.publish() line is commented out. But with that line active the fields{} parameter seems to be overridden.

I did find a workaround (below in case it benefits others) but the workaround is a little less elegant and I wanted to ask if I was doing something wrong.

Is this the best way to do it?

    Meteor.publish("search", function (searchObj, limit, skip) {

        //build a search object that looks like {lastName: "Smith", firstName: "Jim"} (but with regex to fix case sensitivity)
        searchParameters = Meteor.call("buildSearchParameters", searchObj);

        if(!limit){var limit = 30} //in case the client code forgot to set one, or to prevent malicious overload
        if(!skip){var skip = 0}

        cursor = Contacts.find(
            searchParameters   //object built by buildSearchParameters method
            , 
            {
                limit: limit,
                skip: skip,
                fields: 
                {
                    lastName: 1, 
                    firstName: 1, 
                    middleName: 1,
                    streetNumber: 1,
                    streetName: 1,
                    city: 1,
                    zipCode: 1 
                }
            }
        ); 

        //use tmeasday:publish-counts package to publish the total count from this cursor
        //{noReady: true} allows the cursor to determine the limit rather than this counter (see docs)
        Counts.publish(this, 'totalSearchResultsCounter', cursor);
        return cursor;

Workaround: Instead of using cursor in the Counts.publish() function, I just do a new Contacts.find and bring down the same searchParameters variable that I was already doing above. This method requires that I save all of my search parameters into a separate object to avoid duplication (DRY).

                ...
        Counts.publish(this, 'totalSearchResultsCounter', Contacts.find(searchParameters);
        return cursor;

Thanks in advance, and thanks for this handy package !

How can restart Count?

Hey!
I have some publication function, like this:

Meteor.reactivePublish('postsCount', function() {
  if (this.userId) {
    var user = Meteor.users.findOne({
      _id: this.userId
    }, {
      reactive: true
    });

    if (!!user.team) {
      var team = Teams.findOne({
        _id: user.team
      });

      if (team && team.owner === user._id) {
        Counts.publish(this, 'postsCounter', Posts.find({
          $or: [
            {
              owner: user.team,
              shared: true,
              archive: false
            }, {
              owner: user.team,
              shared: false,
              archive: false
            }, {
              owner: this.userId,
              shared: false,
              archive: false
            }
          ]
        }));
      }

      Counts.publish(this, 'postsCounter', Posts.find({
        $or: [
          {
            owner: user.team,
            shared: true,
            archive: false
          }, {
            owner: this.userId,
            shared: false,
            archive: false
          }
        ]
      }));
    }

    Counts.publish(this, 'postsCounter', Posts.find({
      owner: this.userId,
      shared: false,
      archive: false
    }));
  }

  this.ready();
});

renaming or removing field 'team' from user record does not trigger update event for counter

Update 20150920: reformat and add syntax highlighting --boxofrox

Search speed greatly reduced when using Counts.publish()

First, thanks for creating this package, I was accomplishing some of this with custom method calls but this offers many handy features. I also find it much easier to use than the many others out there that seek to help with counts and paging.

The one downside is that it seems to add a lot of lag to my searches and it's doing so in a blocking (synchronous) fashion. I don't really care if the total count takes a second or two to respond, if it can run in the background and just update the template when it's ready. But holding up the rest of the app is a bummer. Is there's a way to get this to run asynchronously?

Example:
The following code executes and returns/displays 30 records to browser almost instantly (at most 5ms after button click)

    Meteor.publish("search", function (searchParameters) {
        cursor = Contacts.find(searchParameters); 
        //with Counts disabled, results on screen feel instant. 
        //counts = Counts.publish(this, 'totalSearchResultsCounter', Contacts.find(searchParameters));
        return cursor;   
    });

The same code with Counts.publish() added is taking between .5 and 2 full seconds before the first signs of life are hitting the browser, and then things drop in sequentially (the total count from Counts.publish (y), then the count of records (x) on page (showing x of y records), then the records themselves)

    Meteor.publish("search", function (searchParameters) {
        cursor = Contacts.find(searchParameters); 
        //with Counts enabled, results have a lot of lag because it's blocking everything else form running. 
        counts = Counts.publish(this, 'totalSearchResultsCounter', Contacts.find(searchParameters));
        return cursor;   
    });

Any suggestions on how to improve? Thanks again!

Publish function can only return a Cursor or an array of Cursors

I can not create a simple subscription:

Meteor.publish "requests_count", () ->
    res = Counts.publish(this, "requests_count", Requests.find())
    console.log(res)
    res

I get in log:

I20141102-12:31:33.564(3)? { stop: [Function] }
I20141102-12:31:33.606(3)? Exception from sub DBaRbdKhTHwShqwJT Error: Publish function can only return a Cursor or an array of Cursors
I20141102-12:31:33.607(3)?     at _.extend._runHandler (packages/ddp/livedata_server.js:1011)
I20141102-12:31:33.607(3)?     at _.extend._startSubscription (packages/ddp/livedata_server.js:769)
I20141102-12:31:33.608(3)?     at _.extend.protocol_handlers.sub (packages/ddp/livedata_server.js:582)
I20141102-12:31:33.608(3)?     at packages/ddp/livedata_server.js:546

ps Can I use many Counts.publish into one Meteor.publish with different names?

Computations that depend on counters are not reactive.

Hi,
I'm not sure my problem is an issue or just a bad understanding of how meteor works, but I have a problem using counts. I posted a question on stackoverflow about my problem in case you want to have a look there (http://stackoverflow.com/questions/30263378/how-to-make-a-function-that-uses-counters-to-compute-values-reactively). I'll copy the same here :

Here is what I have :

  • On the server :

    Meteor.publish('counters', function() {
        Counts.publish(this, 'searches-thisWeek', UserActions.find({
            $and: [
                { action: SEARCHES_REGEX },
                { date : { $gte : moment().startOf('week').toDate()} },
                { date : { $lt : moment().toDate()} }
            ]
        }));
    
        Counts.publish(this, 'searches-lastWeek', UserActions.find({
            $and: [
                { action: SEARCHES_REGEX },
                { date : { $gte :  moment().subtract(1, 'week').startOf('week').toDate()} },
                { date : { $lt :  moment().subtract(1, 'week').endOf('week').toDate()} }
            ]
        }));
    });
    
  • On the client :

    Template.dashboard.helpers({
        nbSearchesThisWeek : function() {
            var variation = Counts.get('searches-thisWeek') - Counts.get('searches-lastWeek');
            return {
                currentValue : Counts.get('searches-thisWeek'),
                orientation : {
                    sign: (variation > 0) ? '+' : '',
                    class: (variation > 0) ? 'up' : 'down'
                },
                variation : variation
            };
        }
    });
    
  • In my template I have a :

    {{getPublishedCount 'searches'}}
    

    This one updates fine. It just gets my "searches" counter and updates anytime the counter changes.

However, I have a template that uses my nbSearchesThisWeek helper, and that one executes fine at startup but never updates when any of the dependent counters change.

So my question is, how and where do I make my nbSearchesThisWeek helper react to the changes on the dependent counters values ? I'm very confused when I read the documentation about Deps, Tracking, and ReactiveVars... I didn't really understand where those things should be used to make my use case work...

Thanks for your help.

Philippe

Undefined object exception when using local collections.

In #35, @jjman505 writes:

So I tried the example from @boxofrox, with minor changes here:
https://gist.github.com/jjman505/edd91510eeb8b27d9853

I get the following error:

Exception from sub pendingReview id CCtvre3cA73bFozdb TypeError: Cannot read property 'options' of undefined
     at Object.Counts.publish (packages/tmeasday:publish-counts/publish-counts.js:42:1)
     at [object Object]._handler (app/server/publications.js:51:16)
     at maybeAuditArgumentChecks (packages/ddp/livedata_server.js:1617:1)
     at [object Object]._.extend._runHandler (packages/ddp/livedata_server.js:950:1)
     at [object Object]._.extend._startSubscription (packages/ddp/livedata_server.js:769:1)
     at [object Object]._.extend.protocol_handlers.sub (packages/ddp/livedata_server.js:582:1)
     at packages/ddp/livedata_server.js:546:1

A local collection is defined as new Mongo.Collection(null) (see Meteor Docs).

Apparently local collections don't define a _cursorDescription property on their cursors.

Suggested fix: skip field specifier optimization if _cursorDescription is undefined.

PR to follow after consensus.

CC @tmeasday, @dburles

Counts.get() delivers zero even though MongoDb console and server show count > 0

Autopublish package is removed. Here's the code in counts.html:

if (Meteor.isServer) {
    Meteor.publish('jobCompleted', function() {
        Counts.publish(this, 'numJobCompleted', myJobs.find({status: {$eq: "completed"}}));
    });

    Meteor.publish('atDNA', function() {
        Counts.publish(this, 'numAtDNA', Gedmatches.find({ atTotalCm: {$exists:true} }));
    });
}

if (Meteor.isClient) {
    Meteor.subscribe('jobCompleted');
     console.log('Number of jobs completed (client): ', Counts.get('numJobCompleted'));

    Meteor.subscribe('atDNA');
    console.log('Number of atDNA matches (client): ', Counts.get('numAtDNA'));
}

Output in the browser console is:

Number of jobs completed (client): 0
Number of atDNA matches (client): 0

Whereas the same query:

console.log('Number of jobs completed (server): ' + myJobs.find({status: {$eq: "completed"}}).count());
console.log('Number of atDNA matches (server): ' + Gedmatches.find({ atTotalCm: {$exists:true} }).count());

executed on the server (and in Mongodb shell) gives:

Number of jobs completed (server): 207
Number of atDNA matches (server): 51

I'm not sure what I'm doing wrong, tried to copy everything from the documentation. FYI - A normal find with a pub for all records (without further filter) find all records and works.

Thanks in advance for your help!

support numbers for countFromFieldLength

Hi Guys,

would you like to support countFromFieldLength on numbers? I just have a usecase where it would be useful. Or is there an approach already? I have a field visits:Numberinside of a collection which I would like to count.
Thanks for looking into this
Cheers Dominic

Document need for unique count names

I noticed in #43 that @boxofrox mentions the need to publish counts with a unique name. I didn't notice that in the docs (forgive me if I overlooked it). It didn't occur to me after reading the README that the counts would require a unique name per client to be unique per client. If that is the case, would be nice to mention it (or make it bolder if it's already there) in the docs.

Happy to submit a PR if you'd like one.

countFromField broken in Meteor 1.1.0.2

Meteor 1.1.0.2 does not provide the fields argument for the remove callback of cursor.observeChanges.
According to the Meteor documentation:

callbacks may have the following functions as properties:

changed(id, fields)
The document identified by id has changed. fields contains the changed fields with their new values. If a field was removed from the document then it will be present in fields with a value of undefined.

removed(id)
The document identified by id was removed from the result set.

This breaks a hard requirement for countFromField as there is no way to recover the field count lost by the document removal event.

As a result, Line 52 will always throw a TypeError. In this example, 'count' is my countFromField value.

Exception in queued task: TypeError: Cannot read property 'count' of undefined
     at observers.removed (packages/tmeasday:publish-counts/publish-counts.js:52:1)
     ...

Unfortunately, we can't fall back to the changed callback as it only returns undefined values for the removed fields instead of the previous value. I think this only leaves maybe three solutions for publish-counts moving forward:

  1. appeal to MDG to (restore?) the fields argument to the remove callback of cursor.observeChanges, or
  2. use cursor.observe instead which provides the old document as an argument to its removed callback, or
  3. cache doc ids with counts so publish-counts always knows the count of every document observed (not recommended).

I prefer (2) assuming no existing features are broken by the change.

Here's a sample test case to demonstrate the problem.

$ cd /tmp
$ meteor create testunit
$ cd testunit
$ cat <EOF > testunit.js
if (Meteor.isServer) {
  Meteor.startup(function () {
    var Test = new Meteor.Collection('test');

    Test.find({}).observeChanges({
      removed: function (id, fields) {
        if (undefined === fields)
          throw new Error('fields is undefined');
        if (null === fields)
          throw new Error('fields is null');

        console.log('fields is good', fields);
      },
    });

    Test.insert({
      a: {
        b: 1,
        c: 2,
      },
      d: 10,
    });

    Test.remove({});
  });
}
EOF
$ meteor
[[[[[ /tmp/testunit ]]]]]

=> Started proxy.
=> Started MongoDB.
=> Started your app.

=> App running at: http://localhost:3000/
Exception in queued task: Error: fields is undefined
     at Test.find.observeChanges.removed (testunit/testunit:8:17)
     at packages/mongo/observe_multiplex.js:183:1
     at Array.forEach (native)
     at Function._.each._.forEach (packages/underscore/underscore.js:105:1)
     at Object.task (packages/mongo/observe_multiplex.js:177:1)
     at [object Object]._.extend._run (packages/meteor/fiber_helpers.js:147:1)
     at packages/meteor/fiber_helpers.js:125:1
^C
$

Upon running this test, I observe the "fields is undefined" error is thrown and reported in the console.

Will see about submitting a PR for this as I find time.

Renamed publishCount

As per today's discussion with MDG, let's move the publishCount function into the Counts namespace on the server, called e.g Counts.publish. @tmeasday - any objections?

Broken in 0.5.1 when using Partitioner

Hi,

Today i updated Meteor to the latest version.
I am using Partitioner in my project. It is using _groupId property in documents to allow grouping data. After the update, counts are started to fail. I get these messages in the console:

W20150804-14:13:37.211(2)? (STDERR) publish-counts: unused fields removed from cursor fields option. { _groupId: 0 }
W20150804-14:13:37.212(2)? (STDERR) publish-counts: unused fields removed from cursor fields option. { _groupId: 0 }
W20150804-14:13:37.221(2)? (STDERR) publish-counts: unused fields removed from cursor fields option. { _groupId: 0 }
W20150804-14:13:37.225(2)? (STDERR) publish-counts: unused fields removed from cursor fields option. { _groupId: 0 }
W20150804-14:13:37.235(2)? (STDERR) publish-counts: unused fields detected in cursor fields option { _groupId: 0 }
I20150804-14:13:37.290(2)? Exception from sub calculateTasksTime id 85639iS5gCWLAshhy MinimongoError: You cannot currently mix including and excluding fields.
I20150804-14:13:37.290(2)?     at MinimongoError (packages/minimongo/minimongo.js:53:1)
I20150804-14:13:37.290(2)?     at packages/minimongo/projection.js:75:1
I20150804-14:13:37.290(2)?     at Array.forEach (native)
I20150804-14:13:37.290(2)?     at Function._.each._.forEach (packages/underscore/underscore.js:105:1)
I20150804-14:13:37.290(2)?     at projectionDetails (packages/minimongo/projection.js:69:1)
I20150804-14:13:37.290(2)?     at Function.LocalCollection._compileProjection (packages/minimongo/projection.js:12:1)
I20150804-14:13:37.291(2)?     at new OplogObserveDriver (packages/mongo/oplog_observe_driver.js:87:1)
I20150804-14:13:37.291(2)?     at [object Object].MongoConnection._observeChanges (packages/mongo/mongo_driver.js:1164:1)
I20150804-14:13:37.291(2)?     at [object Object].mongoConnectionProto._observeChanges (packages/meteorhacks:kadira/lib/hijack/wrap_observers.js:61:1)
I20150804-14:13:37.291(2)?     at [object Object].Cursor.observeChanges (packages/mongo/mongo_driver.js:854:1)
I20150804-14:13:37.377(2)? Kadira: successfully authenticated
I20150804-14:14:10.291(2)? Kadira: completed instrumenting the app
I20150804-14:14:10.322(2) (synced-cron-server.js:52) SyncedCron: scheduled "Send reminder to users for weekly review on Friday at 3 PM" next run @Fri Aug 07 2015 15:00:00 GMT+0200 (CEST)
=> Meteor server restarted
W20150804-14:14:10.509(2)? (STDERR) publish-counts: unused fields removed from cursor fields option. { _groupId: 0 }
W20150804-14:14:10.514(2)? (STDERR) publish-counts: unused fields removed from cursor fields option. { _groupId: 0 }
W20150804-14:14:10.523(2)? (STDERR) publish-counts: unused fields removed from cursor fields option. { _groupId: 0 }
W20150804-14:14:10.525(2)? (STDERR) publish-counts: unused fields removed from cursor fields option. { _groupId: 0 }

I noticed that only fields required for counting are fetched from database, and other are omitted. I totally support that, but it breaks in this situation. After i downgrade it to 0.4.0 everything got back to normal.

I suggest adding a property to options (4th parameter) that will allow passing in fields that should not be omitted. For example:

  var tasks = Task.find({user: user._id});
  Counts.publish(this, 'calculateTasksTime', tasks, {
    countFromField: 'time', 
    fields: ['_groupId']
  });

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.