{"id":15013358,"url":"https://github.com/jcbernack/meteor-reactive-aggregate","last_synced_at":"2025-10-30T01:33:20.267Z","repository":{"id":43676472,"uuid":"48398101","full_name":"JcBernack/meteor-reactive-aggregate","owner":"JcBernack","description":"Reactively publish aggregations with Meteor.","archived":false,"fork":false,"pushed_at":"2022-02-24T15:40:39.000Z","size":19,"stargazers_count":98,"open_issues_count":9,"forks_count":29,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-12T04:44:08.263Z","etag":null,"topics":["aggregate","meteor","reactive"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JcBernack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-12-21T22:45:10.000Z","updated_at":"2025-02-14T21:31:00.000Z","dependencies_parsed_at":"2022-08-23T05:10:34.577Z","dependency_job_id":null,"html_url":"https://github.com/JcBernack/meteor-reactive-aggregate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/JcBernack/meteor-reactive-aggregate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JcBernack%2Fmeteor-reactive-aggregate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JcBernack%2Fmeteor-reactive-aggregate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JcBernack%2Fmeteor-reactive-aggregate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JcBernack%2Fmeteor-reactive-aggregate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JcBernack","download_url":"https://codeload.github.com/JcBernack/meteor-reactive-aggregate/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JcBernack%2Fmeteor-reactive-aggregate/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263410769,"owners_count":23462298,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aggregate","meteor","reactive"],"created_at":"2024-09-24T19:44:10.768Z","updated_at":"2025-10-30T01:33:15.220Z","avatar_url":"https://github.com/JcBernack.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# meteor-reactive-aggregate\n\nReactively publish aggregations.\n\n    meteor add jcbernack:reactive-aggregate\n\nThis helper can be used to reactively publish the results of an aggregation.\n\n## Usage\n    ReactiveAggregate(subscription, collection, pipeline[, options])\n\n- `subscription` should always be `this` in a publication.\n- `collection` is the Mongo.Collection instance to query.\n- `pipeline` is the aggregation pipeline to execute.\n- `options` provides further options:\n  - `observeSelector` can be given to improve efficiency. This selector is used for observing the reactive collections. If you wish to have different selectors for multiple reactive collections, use `lookupCollections` options.\n  - `observeOptions` can be given to limit fields, further improving efficiency. Ideally used to limit fields on your query.\n  - `delay` (default: `250`) the time (in milliseconds) between re-runs caused by changes in any reactive collections in the aggregation.\n  - `lookupCollections` is keyed by your `$lookup.from` collection name(s). it takes `observeSelector` and `observeOptions` parameters. see example below.\n  If none is given any change to the collection will cause the aggregation to be reevaluated.\n\n### Examples\n  `options` applied to the **default** collection\n  ```\n  const options = {\n      observeSelector: {\n          bookId: { $exists: true },\n      },\n      observeOptions: {\n          limit: 10,\n          sort: { createdAt: -1 },\n      }\n  };\n  ```\n  `options` applied to all **$lookup** reactive collections\n  ```\n  const options = {\n      lookupCollections: {\n          'books': {\n             observeSelector: {\n                'releaseDate': { $gte: new Date('2010-01-01') }\n             },\n             observeOptions: {\n                 limit: 10,\n                 sort: { createdAt: -1 },\n             }\n          }\n      }\n  };\n  ```\n\n  - `clientCollection` defaults to `collection._name` but can be overriden to sent the results to a different client-side collection.\n\n\n## Multiple collections observe\nBy default, any collection instances passed into the aggregation pipeline as a `Mongo.Collection` instance will be reactive. If you wish to opt out of reactivity for a collection in your pipeline, simply pass the `Collection._name` as a string.\n\n### Example\nAll collections reactive:\n```\nconst pipeline = [{\n    $lookup: {\n        from: Books,\n        localField: 'bookId',\n        foreignField: '_id',\n        as: 'books',\n    },\n    ...\n    $lookup: {\n        from: Authors,\n        localField: 'authorId',\n        foreignField: '_id',\n        as: 'authors',\n    },\n    ...\n}];\n```\n\nOnly `Books` collection is reactive:\n```\nconst pipeline = [{\n    $lookup: {\n        from: Books,\n        localField: 'bookId',\n        foreignField: '_id',\n        as: 'books',\n    },\n    ...\n    $lookup: {\n        from: Authors._name,\n        localField: 'authorId',\n        foreignField: '_id',\n        as: 'authors',\n    },\n    ...\n}];\n```\n\n## Quick Example\n\nA publication for one of the\n[examples](https://docs.mongodb.org/v3.0/reference/operator/aggregation/group/#group-documents-by-author)\nin the MongoDB docs would look like this:\n```\nimport { ReactiveAggregate } from 'meteor/jcbernack:reactive-aggregate';\n\nMeteor.publish(\"booksByAuthor\", function () {\n    ReactiveAggregate(this, Books, [{\n    $group: {\n        _id: \"$author\",\n        books: { $push: \"$$ROOT\" }\n    }\n    }]);\n});\n```\n\n## Extended Example\n\nDefine the parent collection you want to run an aggregation on. Let's say:\n```\nReports = new Meteor.Collection('Reports');\n```\n\n.. in a location where all your other collections are defined, say `lib/collections.js`\n\nNext, prepare to publish the aggregation on the `Reports` collection into another client-side-only collection we'll call, `clientReport`.\n\nCreate the `clientReport` in the client side (its needed only for client use). This  collection will be the destination in which the aggregation will be put into upon completion.\n\nNow you publish the aggregation on the server:\n```\nimport { ReactiveAggregate } from 'meteor/jcbernack:reactive-aggregate';\n\nMeteor.publish(\"reportTotals\", function() {\n    // Remember, ReactiveAggregate doesn't return anything\n    ReactiveAggregate(this, Reports, [{\n        // assuming our Reports collection have the fields: hours, books\n        $group: {\n            '_id': this.userId,\n            'hours': {\n            // In this case, we're running summation.\n                $sum: '$hours'\n            },\n            'books': {\n                $sum: 'books'\n            }\n        }\n    }, {\n        $project: {\n            // an id can be added here, but when omitted,\n            // it is created automatically on the fly for you\n            hours: '$hours',\n            books: '$books'\n        } // Send the aggregation to the 'clientReport' collection available for client use\n    }], { clientCollection: \"clientReport\" });\n});\n```\n\nWe therefore need to subscribe to the above Publish.\n```\nMeteor.subscribe(\"reportTotals\");\n```\n\nThen in our Template helper:\n```\nTemplate.statsBrief.helpers({\n    reportTotals: function() {\n        console.log(\"I'm working\");\n        return clientReport.find();\n    },\n});\n```\n\nFinally, your template:\n```\n{{#each reportTotals}}Total Hours: {{hours}} \u003cbr/\u003eTotal Books: {{books}}{{/each}}\n```\n\nYour aggregated values will therefore be available in client-side and behave reactively just as you'd expect.\n\nEnjoy aggregating `reactively`!\n\n## Multiple collections observe example\n```\nimport { ReactiveAggregate } from 'meteor/jcbernack:reactive-aggregate';\n\nMeteor.publish(\"booksByAuthor\", function () {\n    ReactiveAggregate(this, Books, [{\n    $group: {\n        _id: \"$author\",\n        books: { $push: \"$$ROOT\" }\n    }\n    }], {\n    observeSelector: {\n        `${Books._name}`: {\n            authorId: { $exists: true },\n        }\n    }, // for Books\n        // for Authors get default: {}\n    // observeOptions: {} \u003c- default: all reactive collections get no query options\n    );\n});\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcbernack%2Fmeteor-reactive-aggregate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjcbernack%2Fmeteor-reactive-aggregate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcbernack%2Fmeteor-reactive-aggregate/lists"}