{"id":13804160,"url":"https://github.com/quavedev/collections","last_synced_at":"2025-05-13T17:31:33.452Z","repository":{"id":55135589,"uuid":"254858443","full_name":"quavedev/collections","owner":"quavedev","description":"Utility package to create Meteor collections in a standard way","archived":true,"fork":false,"pushed_at":"2024-04-05T18:47:38.000Z","size":148,"stargazers_count":24,"open_issues_count":0,"forks_count":3,"subscribers_count":16,"default_branch":"master","last_synced_at":"2024-11-18T20:50:16.948Z","etag":null,"topics":["collections","meteor","meteor-package","mongodb"],"latest_commit_sha":null,"homepage":null,"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/quavedev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-11T12:01:27.000Z","updated_at":"2024-04-05T18:47:54.000Z","dependencies_parsed_at":"2023-11-16T05:29:36.760Z","dependency_job_id":"640befa2-3350-443a-82f0-32f17db7cb7d","html_url":"https://github.com/quavedev/collections","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quavedev%2Fcollections","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quavedev%2Fcollections/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quavedev%2Fcollections/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quavedev%2Fcollections/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quavedev","download_url":"https://codeload.github.com/quavedev/collections/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253993043,"owners_count":21996223,"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":["collections","meteor","meteor-package","mongodb"],"created_at":"2024-08-04T01:00:42.910Z","updated_at":"2025-05-13T17:31:33.148Z","avatar_url":"https://github.com/quavedev.png","language":"JavaScript","funding_links":[],"categories":["Packages","Collections"],"sub_categories":[],"readme":"# quave:collections\n\n### MIGRATED TO https://github.com/quavedev/meteor-packages/tree/main/collections\n\n`quave:collections` is a Meteor package that allows you to create your collections in a standard way.\n\nFeatures\n\n- Schemas\n- Types\n- Helpers\n- Hooks\n- Composers\n\n## Why\n\nEvery application that connects to databases usually need the following features:\n\n- A way to access object instances when they come from the database: helpers\n- Provide new methods to collections: collection\n- Add a few hooks to react to changes in different collections: hooks\n- Map some types to avoid manual conversion all the time: types\n- Valid the data before persisting: schemas\n- Centralize behaviors: composers\n\nMeteor has packages for almost all these use cases but it's not easy to find the best in each case and also how to use them together, that is why we have created this package.\n\nWe offer here a standard way for you to create your collections by configuring all these features in a function call `createCollection` using a bunch of options in a declarative way and without using Javascript classes.\n\nWe also allow you to extend your `Meteor.users` collection in the same way as any other collection.\n\nWe believe we are not reinventing the wheel in this package but what we are doing is like putting together the wheels in the vehicle :).\n\n## Installation\n\n```sh\nmeteor add quave:collections\n```\n\n### Optional installations\n\nTo use Type or Hooks options you need to install [meteor-collection-hooks](https://github.com/Meteor-Community-Packages/meteor-collection-hooks)\n\n```sh\nmeteor add matb33:collection-hooks\n```\n\nTo use Schema options you need to install [meteor-collection2](https://github.com/Meteor-Community-Packages/meteor-collection2)\n\n```sh\nmeteor add aldeed:collection2\nmeteor npm install simpl-schema\n```\n\nTo use Helpers options you need to install [meteor-collection-helpers](https://github.com/dburles/meteor-collection-helpers)\n\n```sh\nmeteor add dburles:collection-helpers\n```\n\nCheck the documentation of each package to learn how to use them.\n\n## Usage\n\n### Methods\n\nExample applying `collection` property:\n\n```javascript\nimport { createCollection } from 'meteor/quave:collections';\n\nexport const AddressesCollection = createCollection({\n  name: 'addresses',\n  collection: {\n    save(addressParam) {\n      const address = { ...addressParam };\n\n      if (address._id) {\n        this.update(address._id, { $set: { ...address } });\n        return address._id;\n      }\n      delete address._id;\n      return this.insert({ ...address });\n    },\n  },\n});\n```\n\n### Schema\n\nExample applying `SimpleSchema`:\n\n```javascript\nimport { createCollection } from 'meteor/quave:collections';\n\nimport SimpleSchema from 'simpl-schema';\n\nconst PlayerSchema = new SimpleSchema({\n  name: {\n    type: String,\n  },\n  age: {\n    type: SimpleSchema.Integer,\n  },\n});\n\nexport const PlayersCollection = createCollection({\n  name: 'players',\n  schema: PlayerSchema,\n});\n```\n\n### Composers\n\nExample creating a way to paginate the fetch of data using `composers`\n\n```javascript\nimport { createCollection } from 'meteor/quave:collections';\n\nconst LIMIT = 7;\nexport const paginable = collection =\u003e\n  Object.assign({}, collection, {\n    getPaginated({ selector, options = {}, paginationAction }) {\n      const { skip, limit } = paginationAction || { skip: 0, limit: LIMIT };\n      const items = this.find(selector, {\n        ...options,\n        skip,\n        limit,\n      }).fetch();\n      const total = this.find(selector).count();\n      const nextSkip = skip + limit;\n      const previousSkip = skip - limit;\n\n      return {\n        items,\n        pagination: {\n          total,\n          totalPages: parseInt(total / limit, 10) + (total % limit \u003e 0 ? 1 : 0),\n          currentPage:\n            parseInt(skip / limit, 10) + (skip % limit \u003e 0 ? 1 : 0) + 1,\n          ...(nextSkip \u003c total ? { next: { skip: nextSkip, limit } } : {}),\n          ...(previousSkip \u003e= 0\n            ? { previous: { skip: previousSkip, limit } }\n            : {}),\n        },\n      };\n    },\n  });\n\nexport const StoresCollection = createCollection({\n  name: 'stores',\n  composers: [paginable],\n});\n\n// This probably will come from the client, using Methods, REST, or GraphQL\n// const paginationAction = {skip: XXX, limit: YYY};\n\nconst { items, pagination } = StoresCollection.getPaginated({\n  selector: {\n    ...(search ? { name: { $regex: search, $options: 'i' } } : {}),\n  },\n  options: { sort: { updatedAt: -1 } },\n  paginationAction,\n});\n```\n\nA different example, a little bit more complex, overriding a few methods of the original collection in order to implement a soft removal (this example only works in `quave:collections@1.1.0` or later).\n\n```javascript\nimport { createCollection } from 'meteor/quave:collections';\n\nconst toSelector = (filter) =\u003e {\n  if (typeof filter === 'string') {\n    return { _id: filter };\n  }\n  return filter;\n};\n\nconst filterOptions = (options = {}) =\u003e {\n  if (options.ignoreSoftRemoved) {\n    return {};\n  }\n  return { isRemoved: { $ne: true } };\n};\n\nexport const softRemoval = (collection) =\u003e {\n  const originalFind = collection.find.bind(collection);\n  const originalFindOne = collection.findOne.bind(collection);\n  const originalUpdate = collection.update.bind(collection);\n  const originalRemove = collection.remove.bind(collection);\n  return Object.assign({}, collection, {\n    find(selector, options) {\n      return originalFind(\n        {\n          ...toSelector(selector),\n          ...filterOptions(options),\n        },\n        options\n      );\n    },\n    findOne(selector, options) {\n      return originalFindOne(\n        {\n          ...toSelector(selector),\n          ...filterOptions(options),\n        },\n        options\n      );\n    },\n    remove(selector, options = {}) {\n      if (options.hardRemove) {\n        return originalRemove(selector);\n      }\n      return originalUpdate(\n        {\n          ...toSelector(selector),\n        },\n        {\n          $set: {\n            ...(options.$set || {}),\n            isRemoved: true,\n            removedAt: new Date(),\n          },\n        },\n        { multi: true }\n      );\n    },\n  });\n};\nexport const SourcesCollection = createCollection({\n  name: 'sources',\n  composers: [softRemoval],\n});\n\n// usage example\nSourcesCollection.remove({ _id: 'KEFemSmeZ9EpNfkcH' }); // this will use the soft removal, it means, this setting `isRemoved` to true\nSourcesCollection.remove({ _id: 'KEFemSmeZ9EpNfkcH' }, { hardRemove: true }); // this will remove in the database\n\n```\n\n### Options\nSecond argument for the default [collections constructor](https://docs.meteor.com/api/collections.html#Mongo-Collection).\nExample defining a transform function.\n```javascript\nconst transform = doc =\u003e ({\n  ...doc,\n  get user() {\n    return Meteor.users.findOne(this.userId);\n  },\n});\n\nexport const PlayersCollection = createCollection({\n  name: 'players',\n  schema,\n  options: {\n    transform,\n  },\n});\n```\n\n### Meteor.users\n\nExtending Meteor.users, also using `collection`, `helpers`, `composers`, `apply`.\n\nYou can use all these options also with `name` instead of `instance`.\n\n```javascript\nimport {createCollection} from 'meteor/quave:collections';\n\nexport const UsersCollection = createCollection({\n  instance: Meteor.users,\n  schema: UserTypeDef,\n  collection: {\n    isAdmin(userId) {\n      const user = userId \u0026\u0026 this.findOne(userId, {fields: {profiles: 1}});\n      return (\n        user \u0026\u0026 user.profiles \u0026\u0026 user.profiles.includes(UserProfile.ADMIN.name)\n      );\n    },\n  },\n  helpers: {\n    toPaymentGatewayJson() {\n      return {\n        country: 'us',\n        external_id: this._id,\n        name: this.name,\n        type: 'individual',\n        email: this.email,\n      };\n    },\n  },\n  composers: [paginable],\n  apply(coll) {\n    coll.after.insert(userAfterInsert(coll), {fetchPrevious: false});\n    coll.after.update(userAfterUpdate);\n  },\n});\n```\n\n## Limitations\n\n- You can't apply `type` and `typeFields` when you inform an instance of a MongoDB collection, usually you only use an instance for `Meteor.users`. In this case I would recommend you to don't add fields with custom types to the users documents.\n\n- If you want to use your objects from the database also in the client but you don't use your whole collection in client (you are not using Mini Mongo) you need to instantiate your type also in the client, you can do this importing your type and calling `register`. This is important to register it as an EJSON type.\n\n### License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquavedev%2Fcollections","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fquavedev%2Fcollections","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquavedev%2Fcollections/lists"}