├── .gitignore ├── .npm └── package │ ├── .gitignore │ ├── README │ └── npm-shrinkwrap.json ├── README.md ├── package.js ├── server.coffee └── versions.json /.gitignore: -------------------------------------------------------------------------------- 1 | .build* 2 | -------------------------------------------------------------------------------- /.npm/package/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npm/package/README: -------------------------------------------------------------------------------- 1 | This directory and the files immediately inside it are automatically generated 2 | when you change this package's NPM dependencies. Commit the files in this 3 | directory (npm-shrinkwrap.json, .gitignore, and this README) to source control 4 | so that others run the same versions of sub-dependencies. 5 | 6 | You should NOT check in the node_modules directory that Meteor automatically 7 | creates; if you are using git, the .gitignore file tells git to ignore it. 8 | -------------------------------------------------------------------------------- /.npm/package/npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "mongodb": { 4 | "version": "1.3.17", 5 | "dependencies": { 6 | "bson": { 7 | "version": "0.2.2" 8 | }, 9 | "kerberos": { 10 | "version": "0.0.3" 11 | } 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mongodb-server-aggregation 2 | ========================== 3 | 4 | Very simple implementation of some of mongodb aggregation framework functions for Meteor. 5 | 6 | **Mongodb-server-aggregation** is a fork of [mongodb-aggregation](https://github.com/jhoxray/meteor-mongo-extensions) 7 | that do not expose the aggregation framework to the client, being available only on server side. 8 | 9 | It extends `Collection` with 3 methods so far, **mapReduce**, **distinct** and **aggregate**, so that you can do: 10 | 11 | ```coffeescript 12 | col = new Meteor.Collection "name" 13 | 14 | if Meteor.isServer 15 | # mapReduce 16 | map = function() {emit(this.Region, this.Amount);} 17 | reduce = function(reg, am) { return Array.sum(am);}; 18 | 19 | col.mapReduce map, reduce, {out: "out_collection_name", verbose: true}, (err,res)-> 20 | console.dir res.stats # statistics object for running mapReduce 21 | 22 | # distinct 23 | result = col.distinct "Field Name" 24 | console.dir result 25 | 26 | #aggregate 27 | result = col.aggregate pipeline 28 | console.dir result 29 | ``` 30 | 31 | To install it, run: 32 | ```bash 33 | $ mrt add mongodb-server-aggregation 34 | ``` 35 | 36 | You can also perform Mongo's inline mapReduce query by not specifying 'out' option at all or by setting it 37 | to `out: {inline: 1}`. The result must fit within the maximum size of a BSON document (which is 16 megabytes by default). 38 | 39 | This package is MIT Licensed. Do whatever you like with it but any responsibility for doing so is your own. 40 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | summary: "Expose mongodb aggregation framework (mapReduce, aggregate and distinct), to SERVER side only.", 3 | version: '1.0.4', 4 | name: 'zvictor:mongodb-server-aggregation', 5 | git: 'https://github.com/zvictor/meteor-mongo-server.git' 6 | }); 7 | 8 | Npm.depends({mongodb: "1.3.17"}); 9 | 10 | Package.on_use(function (api, where) { 11 | if(api.versionsFrom !== undefined) api.versionsFrom('METEOR@0.9.1'); 12 | 13 | api.use('coffeescript', ['server']); 14 | api.use('underscore', ['server']); 15 | 16 | api.add_files('server.coffee', 'server'); 17 | }); 18 | -------------------------------------------------------------------------------- /server.coffee: -------------------------------------------------------------------------------- 1 | tl = TLog?.getLogger() 2 | #hacky advanced mongo definitions based on https://github.com/meteor/meteor/pull/644 3 | 4 | path = Npm.require("path") 5 | MongoDB = Npm.require("mongodb") 6 | Future = Npm.require(path.join("fibers", "future")) 7 | 8 | _dummyCollection_ = new Meteor.Collection '__dummy__' 9 | 10 | # Wrapper of the call to the db into a Future 11 | _futureWrapper = (collection, commandName, args)-> 12 | col = if (typeof collection) == "string" then _dummyCollection_ else collection 13 | collectionName = if (typeof collection) == "string" then collection else collection._name 14 | 15 | #tl?.debug "future Wrapper called for collection " + collectionName + " command: " + commandName + " args: " + args 16 | 17 | coll1 = col.find()._mongo.db.collection(collectionName) 18 | 19 | future = new Future 20 | cb = future.resolver() 21 | args = args.slice() 22 | args.push(cb) 23 | coll1[commandName].apply(coll1, args) 24 | result = future.wait() 25 | 26 | 27 | 28 | # Not really DRY, but have to return slightly different results from mapReduce as mongo method returns 29 | # a mongo collection, which we don't need here at all 30 | _callMapReduce = (collection, map, reduce, options)-> 31 | col = if (typeof collection) == "string" then _dummyCollection_ else collection 32 | collectionName = if (typeof collection) == "string" then collection else collection._name 33 | 34 | tl?.debug "callMapReduce called for collection " + collectionName + " map: " + map + " reduce: " + reduce + " options: #{JSON.stringify(options)}" 35 | 36 | coll1 = col.find()._mongo.db.collection(collectionName) 37 | 38 | future = new Future 39 | #cb = future.resolver() 40 | coll1.mapReduce map, reduce, options, (err,result,stats)-> 41 | #tl?.debug "Inside MapReduce callback now!" 42 | future.throw(err) if err 43 | if result.collectionName 44 | res = {collectionName: result.collectionName, stats: stats} 45 | else 46 | res = result 47 | future.return [true,res] 48 | 49 | result = future.wait() # 50 | #console.log "Result from the callMapReduce is: " 51 | #console.dir result[1] 52 | throw result[1] if !result[0] 53 | result[1] 54 | 55 | 56 | 57 | # Extending Collection on the server 58 | _.extend Meteor.Collection::, 59 | 60 | distinct: (key, query, options) -> 61 | #_collectionDistinct @_name, key, query, options 62 | _futureWrapper @_name, "distinct", [key, query, options] 63 | 64 | aggregate: (pipeline) -> 65 | _futureWrapper @_name, "aggregate", [pipeline] 66 | 67 | mapReduce: (map, reduce, options)-> 68 | options = options || {}; 69 | options.readPreference = "primary"; 70 | _callMapReduce @_name, map, reduce, options 71 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | [ 4 | "coffeescript", 5 | "1.0.4" 6 | ], 7 | [ 8 | "meteor", 9 | "1.1.2" 10 | ], 11 | [ 12 | "underscore", 13 | "1.0.1" 14 | ] 15 | ], 16 | "pluginDependencies": [], 17 | "toolVersion": "meteor-tool@1.0.34", 18 | "format": "1.0" 19 | } --------------------------------------------------------------------------------