{"id":23256663,"url":"https://github.com/aliatech/loopback-mongo-aggregate-mixin","last_synced_at":"2025-07-12T09:34:27.484Z","repository":{"id":34240606,"uuid":"171855356","full_name":"aliatech/loopback-mongo-aggregate-mixin","owner":"aliatech","description":"Loopback mixin to query MongoDB aggregation pipeline and build the instances from results","archived":false,"fork":false,"pushed_at":"2022-12-30T16:38:33.000Z","size":1016,"stargazers_count":8,"open_issues_count":17,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-01T01:05:20.145Z","etag":null,"topics":["aggregate","aggregation","loopback","mixin","mongodb","node","orm","pipeline"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aliatech.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-21T10:58:58.000Z","updated_at":"2022-09-30T16:11:50.000Z","dependencies_parsed_at":"2023-01-15T05:45:28.872Z","dependency_job_id":null,"html_url":"https://github.com/aliatech/loopback-mongo-aggregate-mixin","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/aliatech/loopback-mongo-aggregate-mixin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aliatech%2Floopback-mongo-aggregate-mixin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aliatech%2Floopback-mongo-aggregate-mixin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aliatech%2Floopback-mongo-aggregate-mixin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aliatech%2Floopback-mongo-aggregate-mixin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aliatech","download_url":"https://codeload.github.com/aliatech/loopback-mongo-aggregate-mixin/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aliatech%2Floopback-mongo-aggregate-mixin/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264972153,"owners_count":23691375,"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","aggregation","loopback","mixin","mongodb","node","orm","pipeline"],"created_at":"2024-12-19T12:18:41.830Z","updated_at":"2025-07-12T09:34:27.117Z","avatar_url":"https://github.com/aliatech.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Loopback Aggregate mixin for MongoDB\n\n[![Build Status](https://travis-ci.org/aliatech/loopback-mongo-aggregate-mixin.svg?branch=master)](https://travis-ci.org/aliatech/loopback-mongo-aggregate-mixin)\n[![Coverage Status](https://coveralls.io/repos/github/aliatech/loopback-mongo-aggregate-mixin/badge.svg?branch=master)](https://coveralls.io/github/aliatech/loopback-mongo-aggregate-mixin?branch=master)\n[![npm version](https://img.shields.io/npm/v/@aliatech/loopback-mongo-aggregate-mixin.svg?color=blue)](https://www.npmjs.com/package/@aliatech/loopback-mongo-aggregate-mixin)\n\u003c!--![npm total downloads](https://img.shields.io/npm/dt/@aliatech/loopback-mongo-aggregate-mixin.svg?color=9cf)--\u003e\n\nGive models the ability to query native **MongoDB aggregates** and **build instances** from results.\n\n**Highlights**\n\n* Accepts both Loopback filter's features and pipeline stages, it will merge in a single parsed pipeline to aggregate.  \n* Accepts relations' fields within the root where, it will be handled as $lookup stages.\n* Refactor the logic from Loopback which is responsible for building the model instances and take advantage of it.\n* Supports both callbacks and promises.\n\nThis Loopback mixin is intended to be used together with MongoDB connector.\nWorks for Loopback 2 and 3.\n\n## How to install\n\nInstall the package through NPM\n\n```bash\nnpm i -S @aliatech/loopback-mongo-aggregate-mixin\n```\n\nInstall the package through Yarn\n\n```bash\nyarn add --prod @aliatech/loopback-mongo-distinct-mixin\n```\n\n## Basic configuration\n\nInclude the mixin in `server/model-config.json`. Example for Loopback 3:\n\n```json\n{\n  \"_meta\": {\n    \"sources\": [\n      \"loopback/common/models\",\n      \"loopback/server/models\",\n      \"../common/models\",\n      \"./models\"\n    ],\n    \"mixins\": [\n      \"loopback/common/mixins\",\n      \"../node_modules/@aliatech/loopback-mongo-aggregate-mixin/lib\",\n      \"../common/mixins\"\n    ]\n  }\n}\n```\n\nEnable the mixin in your model definition, ie `person.json`.\n\n```json\n{\n  \"name\": \"Person\",\n  \"properties\": {\n    \"name\": \"string\"\n  },\n  \"mixins\": {\n    \"Aggregate\": true\n  }\n}\n```\n\n## Usage\n\nInvoke `aggregate` method passing either:\n\n* A regular Loopback filter (where, fields, include, order, skip, limit)\n* An aggregate pipeline\n* A combination of both\n\n### Basic example\n\n\u003e Find a random sample of 3 persons born after 1980:\n\n```js\napp.models.Person.aggregate({\n  where: {birthDate: {gt: new Date('1980')}},\n  aggregate: [{$sample: {size: 3}}],\n}, (err, persons) =\u003e {\n  if (err) return next(err);\n  // persons are Person model instances\n});\n```\n\n### Find where relation properties\n\nRelation properties can be specified in the \"where\" criteria using dot notation.\n$lookup stages will be automatically generated to reach those relations and filter the root documents by such criteria.\nit works like a \"LEFT JOIN\" feature, however it's still necessary to add the \"include\" filter\nif you require the relation to be hydrated.\n\n\u003e Example: Bring persons who are part of a team in which there is some person who is born after 2001\n\n```js\napp.models.Person.aggregate({\n  where: {'team.persons.birthDate': {$gt: new Date('2001')}},\n}, (err, persons) =\u003e {\n  if (err) return next(err);\n  // persons are Person model instances\n});\n```\n\nNote: It works for hasOne, belongsTo and hasMany. Filtering by embedded properties is not affected and continues to work as usual.\n\n### Do not build instances\n\nSome queries are intended to retrieve data that can not be transformed into model instances.\n`aggregate` method will attempt to build instances by default, but this behavior can be disabled\npassing an options object `{build: false}` as second argument.\n\n\u003e Example: Bring count of persons by company\n\n```js\napp.models.Person.aggregate({\n  aggregate: [{\n    $group: {\n      _id: '$companyId',\n      total: {$sum: 1},\n    },\n  }],\n}, {build: false}, (err, groups) =\u003e {\n  if (err) return done(err);\n  // Each group should be a plain object with just 'id' and 'total' attributes  \n});\n```\n\n### Build instances on demand\n\nThe aggregate result often needs some processing before building the model instances.\nIt's possible to postpone the build phase until the models' data are resolved. \n\n\u003e Example: Bring the persons count together with a specific page\n\n```js\nPerson.aggregate([{\n  group: {\n    _id: null,\n    total: {$sum: 1},\n    objects: {$push: '$$ROOT'},\n  },\n}, {\n  project: {\n    total: 1,\n    items: {$slice: ['$objects', pageStart, pageLength]},\n  },\n}], {buildLater: true}, (err, [data, build]) =\u003e {\n  if (err) return next(err);\n  // data is a plain structure {total, items} where items is an array of documents, not model instances. \n  build(data.items, (err, persons) =\u003e {\n    if (err) return next(err);\n    // now you got persons as Person model instances\n  });\n});\n``` \n\n* In this case, model documents are not brought as root result,\nso we could disable the automatic building by just passing the option `{build: false}`,\nbut in this case, what we really need is the option `{buildLater: true}`.\n* The difference is that `buildLater` will provide us a build function (together with native documents) to invoke by our hand .\nPerson instances will be finally obtained by calling such function passing `data.items`.\n* Build on demand feature it's available as a model static method `Model.buildResult`.\n\nNote: Pipeline array can be directly passed as argument. Also stage names can obviate \"$\" character.\n\n### GeoNear example\n\nCombine regular \"where\" with $geoNear stage.\n$geoNear will be moved to the pipeline head as MongoDB requires.\n\n```js\napp.models.Company.aggregate({\n  where: {sector: 'Software'},\n  aggregate: [{\n    $geoNear: {\n      near: {type: 'Point', coordinates: [-0.076132, 51.508530]},\n      distanceField: 'distance',\n      maxDistance: 5000, // 5Km.\n      spherical: true,\n    },\n  }],\n}, (err, companies) =\u003e {\n  if (err) return done(err);\n  // companies are Company model instances\n});\n```\n\n### Promise support\n\nMethods `aggregae` and `buildResult` support either callback or promise usage.\nAll the examples above are made with callbacks.\nBelow it's shown how it's made with promise style.\n\n\u003e Example: Find a random sample of 3 persons born after 1980:\n\n```js\napp.models.Person.aggregate({\n  where: {birthDate: {gt: new Date('1980')}},\n  aggregate: [{$sample: {size: 3}}],\n}).then((persons) =\u003e {\n  // persons are Person model instances\n}).catch((err) =\u003e {\n  // handle an error\n});\n```\n\n\u003e Same example using await\n\n```js\ntry{\n  const persons = await app.models.Person.aggregate({\n    where: {birthDate: {gt: new Date('1980')}},\n    aggregate: [{$sample: {size: 3}}],\n  });\n} catch(err) {\n  // handle an error\n}\n```\n\n\n## Advanced configuration\n\nEnable the mixin passing an options object instead of just true.\n\n**Available options:**\n\n| Option                | Type      | Required  | Description                                                                                                                                                                                                                                                                                           |\n| --------------------- | ----------| --------- | ----------------- |\n| mongodbArgs           | object    | optional  | Set defaults for MongoDB aggregate command options (default `{}`). Check the [official documentation](https://docs.mongodb.com/manual/reference/command/aggregate/ \"Link to documentation\") |\n| build                 | boolean   | optional  | Whether to automatically build model instances from aggregate results by default. (default `true`) |\n| buildOptions          | object    | optional  | Set defaults for building process options (default `{notify: true}`) |\n| buildOptions.notify   | boolean   | optional  | Whether to notify model operation hooks on build by default (default `true`) |\n\nAny of these options can be replaced on the fly with the following syntax:\n\n```js\napp.models.Person(filter, options, callback);\n```\n\nThe `options` argument will be timely merged with the defaults for a single call. \n\n### Example: Allow MongoDB to use disk\n\nThis is a MongoDB aggregate command option that prevent memory issues on large queries.\nIt can be enabled by default as follows:\n\n```json\n{\n  \"name\": \"Person\",\n  \"properties\": {\n    \"name\": \"string\"\n  },\n  \"mixins\": {\n    \"Aggregate\": {\n      \"mongodbArgs\": {\n        \"allowDiskUse\": true\n      }\n    }\n  }\n}\n```\n\nOr just enable the option on the fly for a single call:\n\n```js\napp.models.Person(filter, {mongodbArgs: {allowDiskUse: true}}, callback);\n```\n\n# Debug\n\nPrepend DEBUG environment when running server or tests to display what pipelines are being sent to MongoDB:\n\n```bash\nDEBUG=loopback:mixins:aggregate node . # Run server with debug\n```\n\n# Testing\n\nInstall develop dependences\n\n````bash\nnpm i -D # If you use NPM\nyarn install # If you use Yarn\n````\n\nExecute tests\n\n````bash\nnpm test # Without coverage check\nnpm run test-with-coverage # With coverage check\n````\n\n# Credits\n\nInspired by [https://github.com/BoLaMN/loopback-mongo-aggregate-mixin](https://github.com/BoLaMN/loopback-mongo-aggregate-mixin \"Github's repository\")\n\nDeveloped by\n[Juan Costa](https://github.com/Akeri \"Github's profile\")\nfor\n[ALIA Technologies](https://github.com/aliatech \"Github's profile\")\n\n[\u003cimg src=\"http://alialabs.com/images/logos/logo-full-big.png\" alt=\"ALIA Technologies\" height=100/\u003e](http://alialabs.com \"Go to ALIA Technologies' website\")\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faliatech%2Floopback-mongo-aggregate-mixin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faliatech%2Floopback-mongo-aggregate-mixin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faliatech%2Floopback-mongo-aggregate-mixin/lists"}