{"id":16939276,"url":"https://github.com/a8m/doqmentdb","last_synced_at":"2025-06-23T00:03:53.427Z","repository":{"id":57215242,"uuid":"26730430","full_name":"a8m/doqmentdb","owner":"a8m","description":"A Promise-Based DocumentDB ODM Client for NodeJS","archived":false,"fork":false,"pushed_at":"2017-11-26T20:03:48.000Z","size":171,"stargazers_count":51,"open_issues_count":15,"forks_count":18,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-06-09T01:49:03.228Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/a8m.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"license.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-11-16T22:30:49.000Z","updated_at":"2025-02-28T22:39:07.000Z","dependencies_parsed_at":"2022-09-07T12:13:00.098Z","dependency_job_id":null,"html_url":"https://github.com/a8m/doqmentdb","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/a8m/doqmentdb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a8m%2Fdoqmentdb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a8m%2Fdoqmentdb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a8m%2Fdoqmentdb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a8m%2Fdoqmentdb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/a8m","download_url":"https://codeload.github.com/a8m/doqmentdb/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a8m%2Fdoqmentdb/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261386707,"owners_count":23150866,"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":[],"created_at":"2024-10-13T21:04:12.579Z","updated_at":"2025-06-23T00:03:48.410Z","avatar_url":"https://github.com/a8m.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## DoQmentDB - A Promise-Based DocumentDB Client\n[![NPM version][npm-image]][npm-url]\n[![Build status][travis-image]][travis-url]\n[![Test coverage][coveralls-image]][coveralls-url]\n[![Dependency Status][david-image]][david-url]\n[![License][license-image]][license-url]\n[![Downloads][downloads-image]][downloads-url]\n\u003e DoQmentDB is a tiny layer that provides the simplicity of MongoDB for DocumentDB users(support schema, hooks/middleware, atomic-transactions, udf and more).\n\n## Table of contents:\n- [![Gitter][gitter-image]][gitter-url]\n- [Get Started](#get-started)\n- [Examples](#examples-1)\n- [Changelog](#changelog)\n- [Database](#database)\n  - [create](#create)\n  - [insert](#create)\n  - [getDatabase](#getdatabase)\n  - [find](#find)\n  - [findById](#findbyid)\n  - [findOrCreate](#findorcreate)\n  - [remove](#remove)\n  - [use](#use)\n- [Collection](#collection)\n  - [create](#create-1)\n  - [insert](#create-1)\n  - [createOrUpdate](#createorupdate)\n  - [upsert](#createorupdate)\n  - [getCollection](#getcollection)\n  - [find](#find-1)\n  - [findOne](#findone)\n  - [findById](#findbyid-1)\n  - [findAndRemove](#findandremove)\n  - [findOneAndRemove](#findoneandremove)\n  - [findAndModify](#findandmodify)\n  - [findOneAndModify](#findoneandmodify)\n  - [findOrCreate](#findorcreate-1)\n  - [update](#findandmodify)\n  - [updateOne](#findoneandmodify)\n- [Queries](#queries)\n- [Operations](#operations)\n- [Schema](#schema)\n- [Atomic Transactions](#atomic-transactions)\n- [Middleware](#middleware)\n  - [pre](#pre)\n  - [post](#post)\n\n# Get Started\n**(1)** You can install **DoQmentDB** using 2 different methods:  \n- clone \u0026 [build](#developing) this repository\n- via **[npm](https://www.npmjs.org/)**: by running `$ npm install doqmentdb` from your terminal\n\n**(2)** Add to your project:  \n```js\nvar DoqmentDB = require('doqmentdb');\n```\n**(3)** Start Playing with DoqmentDB:\n```js\nvar DoQmentDB  = require('doqmentdb');\n// Create DocumentDB connection\nvar connection = new (require('documentdb').DocumentClient)(HOST, OPTIONS);\n// Pass connection and database-name, if `test` does not exist it will create one.\nvar db = new DoQmentDB(connection, 'test');\n// Create a CollectionManager instance, if `users` does not exist it will create one.\nvar users = db.use('users');\n// Using schema\nusers.schema(model);\n// Add hooks(see `users` full example)\nusers.pre('save', function(next) {\n  var doc = this;\n  doc.createdAt = new Date().toString();\n  next();\n});\n\n// Each http function returns a `Promise` with two specific methods: success and error.\nusers.create({ name: '6534' })\n  .then(console.log);\n\nusers.findById(1)\n  .then(console.log);\n\nusers.findAndRemove({ isAdmin: false })\n  .then(console.log);\n```\n# Database\nCreate a DatabaseManager by passing `connection` and `databaseName`.\n```js\nvar DoQmentDB  = require('doqmentdb');\n// Create DocumentDB connection\nvar connection = new (require('documentdb').DocumentClient)(HOST, OPTIONS);\n// if `test` does not exist it will create one\nvar db = new DoQmentDB(connection, 'test');\n```\n## create\nGet name and crete new collection in the used db.  \n**Usage:** `db.create(string)`  \n**Aliases:** `insert`  \n**Returns:** `Object`\n```js\ndb.create('users')\n  .then(console.log);\n```\n## getDatabase\nReturn the used database.  \n**Usage:** `db.getDatabase()`\n```js\ndb.getDatabase()\n  .then(console.log);\n```\n## find\nfind collection by given object params.  \n**Note:** to return all documents, omit params argument or pass an empty object({}).  \n**Usage:** `db.find(object[optional])`  \n**Returns:** `Array`\n```js\ndb.find()\n  .then(console.log); // Return all collections\n\ndb.find({ id: 'users' })\n  .then(console.log); // Return collections where id equal to `users`\n```\n## findById\nfind collection by given `string` id.  \n**Usage:** `db.findById(string)`  \n**Returns:** `Object`\n```js\ndb.findById('users')\n  .then(console.log);\n```\n## findOrCreate\nget object properties, search for collection, if it does not exist create one.  \n**Usage:** `db.findOrCreate(object)`  \n**Returns:** `Object`\n```js\ndb.findOrCreate({ name: 'users', id: '#1' })\n  .then(console.log);\n```\n## remove\nget collection id as a `String`, if it exists - remove it and return `undefined`, else return `false`.  \n**Usage:** `db.remove(string)`  \n**Returns:** `undefined` or `Boolean`\n```js\ndb.remove('test')\n  .then(console.log);\n```\n## use\nget collection name and return `CollectionManager` instance.  \n**Note:** if the given `collection` does not exist it will create one.  \n**Usage:** `var coll = db.use(string);`  \n**Returns:** `object` instanceof `CollectionManager`\n```js\nvar users = db.use('users'); // This operation is not async\n```\n# Collection\nCreate a CollectionManager by passing to `.use` function a collection name.\n```js\nvar users = db.use('users');\nconsole.log(users.constructor.name); // Collection\n```\n## create\nget object properties, and create new document under the used collection.  \n**Usage:** `users.create(object)`  \n**Aliases:** `insert`  \n**Returns:** `Object`\n```js\nusers.create({ name: 'Ariel', admin: true })\n  .then(console.log); // { name: 'Ariel', admin: true, id: '8...31', _self: ... }\n```\n## createOrUpdate\ncreate a new document under the current collection, or update an existing document with the same id.  \n**Usage:** `users.createOrUpdate(object)`  \n**Aliases:** `upsert`  \n**Returns:** `Object`\n```js\nusers.upsert({ id: 'my_user_id', admin: true })\n  .then(console.log); // { id: 'my_user_id', admin: true, _self: ... }\n```\n## getCollection\nreturn the used collection.  \n**Usage:** `users.getCollection()`\n```js\nusers.getCollection()\n  .then(console.log);\n```\n## find\nget object properties and return an array of results.  \n**Usage:** `users.find(object)`  \n**Note:** to return all collections, omit params argument or pass an empty object({}).  \n**Returns:** `Array`\n```js\nusers.find({ active: true })\n  .then(console.log);\n```\n## findOne\nget object properties and return the first matching result.  \n**Usage:** `users.findOne(object)`  \n**Returns:** `Object`\n```js\nusers.findOne({ active: true, name: 'Bar' })\n  .then(console.log);\n```\n## findById\nfind document by giving a `string` id.  \n**Usage:** `users.findById(string)`  \n**Returns:** `Object`\n```js\nusers.findById('53...3')\n  .then(console.log);\n```\n## findAndRemove\nget object properties to search, find the equivalents and remove them.  \n**Usage:** `users.findAndRemove(object)`  \n**Returns:** `Array`  \n**Note:** if you want atomic-transactions support(**i.e:** do things concurrently, **e.g:** distributed system),\nyou need use this method prefix with `$` sign.\n```js\nusers.findAndRemove({ name: 'Ariel' })\n  .then(console.log);\n\n// Using stored procedure\nusers.$findAndRemove({ name: 'Ariel' })\n  .then(console.log);\n\n// Remove all users\nusers.findAndRemove({})\n  .then(console.log);\n```\n## findOneAndRemove\nget object properties, and remove the first matching result.  \n**Usage:** `users.findOneAndRemove(object)`  \n**Returns:** `undefined` or `Boolean`  \n**Note:** if you want atomic-transactions support(**i.e:** do things concurrently, **e.g:** distributed system),\nyou need use this method prefix with `$` sign.\n```js\nusers.findOneAndRemove({ name: 'Ariel', admin: true })\n  .then(console.log);\n\n// Using stored procedure\nusers.$findOneAndRemove({ name: 'Ariel', admin: true })\n  .then(console.log);\n```\n## findAndModify\nget object properties to search, find the equivalents and modify them(`extend` operation).  \n**Usage:** `users.findAndModify(object, extend)`  \n**Aliases:** `update`  \n**Returns:** `Array`  \n**Note:** if you want atomic-transactions support(**i.e:** do things concurrently, **e.g:** distributed system),\nyou need use this method prefix with `$` sign.\n```js\nusers.update({ name: 'Ariel', admin: true }, { admin: false })\n  .then(console.log);\n\n// Push 'a' and 'b' to `list` field(do it concurrently)\n['a', 'b'].forEach(function(ch) {\n  users.$update({}, { list: { $push: ch } });\n});\n```\n## findOneAndModify\nget object properties and modify(`extend` operation) the first matching.  \n**Usage:** `users.findOneAndModify(object, extend)`  \n**Aliases:** `updateOne`  \n**Returns:** `Object`  \n**Note:** if you want atomic-transactions support(**i.e:** do things concurrently, **e.g:** distributed system),\nyou need use this method prefix with `$` sign.\n```js\nusers.findOneAndModify({ admin: false }, { admin: true })\n  .then(console.log);\n\n// Using stored procedure\nusers.$findOneAndModify({ admin: false }, { admin: true })\n  .then(console.log);\n```\n## findOrCreate\nget object properties, search for document, if it not exist create one.  \n**Usage:** `users.findOrCreate(object)`    \n**Returns:** `Object`  \n**Note:** if you want atomic-transactions support(**i.e:** do things concurrently, **e.g:** distributed system),\nyou need use this method prefix with `$` sign.\n```js\nusers.findOrCreate({ admin: false, name: 'Ariel' })\n  .then(console.log);\n\n// Using stored procedure\nusers.$findOrCreate({ admin: false, name: 'Ariel' })\n  .then(console.log);\n```\n\n# Queries\n### Operators\n* Logical \u0026 Conjunctive:\n  * `$or` OR\n  * `$and` AND\n  * `$not` NOT\n  * `$nor` NOT(... OR ...)\n* Comparison:\n  * `$gt` \u003e\n  * `$gte` \u003e=\n  * `$lt` \u003c\n  * `$lte` \u003c=\n  * `$ne` \u003c\u003e or !=\n* UDF:\n  * `$in` like `Array.prototype.some(...)`\n  * `$all` like `Array.prototype.every(...)`\n  * `$type` `typeof value`\n  * `$regex` `new RegExp(...).test(value)`\n  * `$size` test `array.length`\n\n### Examples\n```js\nusers.find({ a: 1, b: 2, c: '3' })\n// ... r WHERE r.a=1 AND r.b=2 AND r.c=\"3\"\n\nusers.find({ $or: [{ a: 2, b: 3}, { c: 3 }] })\n// ... r WHERE ((r.a=2 AND r.b=3) OR r.c=3)\n\nusers.find({ $not: { a: 1, b: 2, c: 3 } })\n// ... r WHERE NOT(r.a=1 AND r.b=2 AND r.c=3)\n\nusers.find({ $nor: [ { a: 1 }, { b: 3 }]})\n// ... r WHERE NOT(r.a=1 OR r.b=3)\n\nusers.find({ $nor: [ { a: 1, b: 1 }, { c: 3 } ] })\n// ... r WHERE NOT((r.a=1 AND r.b=1) OR r.c=3)\n\nusers.find({ $not: { name: { $gt: 3 }, age: 12 } })\n// ... r WHERE NOT(r.name \u003e 3 AND r.age=12)\n\nusers.find({ $not: { name: { $ne: 'bar' } } })\n// ... r WHERE NOT(r.name \u003c\u003e \"bar\")\n\nusers.find({ $or: [\n        { name: { $ne: 'Ariel' } },\n        { age: { $lte: 26 } },\n        { $and: [\n          { isAdmin: { $ne: false } },\n          { isUser: { $ne: false } }\n        ]}\n      ]})\n// ... r WHERE r.name \u003c\u003e \"Ariel\" OR r.age \u003c= 26 OR (r.isAdmin \u003c\u003e false AND r.isUser \u003c\u003e false)\n\nusers.find({ coins: { $in: 2 } })\n// ... r WHERE inUDF(r.coins, 2)\n\nusers.find({ $not: { age: { $type: 'number' } } })\n// ... r WHERE NOT(typeUDF(r.age, \"number\"))\n```\n\n# Operations\nWhen using one of the update operations(**e.g:** `.update()`, `.findAndModify()`, etc...), you could use the built-in `prototype` functions(based on the type) prefixed with the `$` sign.  \n**Usage:** `users.update({ ... }, { keyName: { $method: value } })`  \n**Note:** value could be single or array of arguments.\n```js\n// Find all, and push 2 to `arr` field\nusers.update({}, { arr: { $push: 2 } });\n\n// Suffix all users `name` with #\nusers.update({}, { name: { $concat: '#' } });\n\n// Trim the name from `foo` to `o`\nusers.update({  name: 'foo' }, { name: { $substr: [1,1] } });\n```\n\n# Schema\nManage your documents with schema.  \n**fields:**\n* `type`\n  *  ***required***\n  *  used for type comparing, (e.g: `String`, `Boolean`, `Number`, etc..).\n* `default`\n  * ***optional***\n  * value fallback\n* `regex`\n  * ***optional***\n  * regex validation, (e.g: email validation - `/^[a-zA-Z0-9@:%_\\+.~#?\u0026//=|/d]{10,}$/`).\n* `error`\n  * ***optional***\n  * return message to fields that fail in the validation phase(`regex`/`type`). see: [example](https://github.com/a8m/doqmentdb/tree/master/example/odm)\n* `expose`\n  * ***optional***\n  * `expose` by default is `true`, unless you set it to `false`, it's means that all the `find` operations returns the documents without exposing this fields. see: [example](https://github.com/a8m/doqmentdb/blob/master/example/users/model/users/schema.js#L42)\n\n**Example using schema:**  \nschema: `model.js`\n```js\nmodule.exports = {\n  /**\n   * @field name\n   * @default no default value\n   */\n  name: {\n    type: String,\n    'default': ''\n  },\n\n  /**\n   * @field email\n   * @default no default value\n   * @regex email, min-length = 10\n   */\n  email: {\n    type: String,\n    'default': '',\n    regex: /^[a-zA-Z0-9@:%_\\+.~#?\u0026//=|/d]{10,}$/,\n    error: '`email` must be type string, valid email address, and least 10 chars',\n    expose: true\n  },\n\n  /**\n   * @field password\n   * @default no default value\n   * @regex password\n   */\n  password: {\n    type: String,\n    'default': '',\n    regex: /^.*(?=.{8,})(?=.*[a-zA-Z])(?=.*\\d)(?=.*[!#$%\u0026? \"]).*$/,\n    error: '`password` must be type string, contain 8 chars and at least one number, ' +\n      'one letter and one unique character such as !#$%\u0026? \"',\n    expose: false\n  },\n\n  /**\n   * @field isAdmin\n   * @default false\n   */\n  isAdmin: {\n    type: Boolean,\n    'default': false\n  }\n};\n```\nusing schema(`model.js`)\n```js\nvar DoQmentDB  = require('doqmentdb');          \nvar model      = require('./model');            // Get model/schema\nvar connection = new (require('documentdb')     // Create DocumentDB connection\n.DocumentClient)(CONFIG.HOST, CONFIG.OPTIONS);\n\nvar db = new DoQmentDB(connection, CONFIG.DB);  // Create DBManager 'test'\nvar users = db.use('users');                    // Create CollectionManager 'users'\nusers.schema(model);                            // Using schema\n\nusers.create({ password: 'Ar2!as_s'})\n  .then(console.log)\n  .catch(console.log);\n  /*\n   [Error:\n   `email` must be type string, valid email address, and least 10 chars\n   ]\n   */\n\nusers.create({ name: 'Ariel', email: 'ariel.com', password: 'Ar2!as_s'})\n  .then(console.log)\n  .catch(console.log);\n/*\n [Error:\n `email` must be type string, valid email address, and least 10 chars\n ]\n */\n\nusers.create({ name: 'Ariel', email: 'a8m@gm.com', password: 'Ar2!as_s'})\n  .then(console.log)\n  .catch(console.log);\n/*\n {\n   name: 'Ariel',\n   email: 'a8m@gm.com',\n   password: 'Ar2!as_s',\n   id: '2eb7...c0',\n    ...\n }\n */\nusers.find({})\n  .then(console.log);\n/*\n Get all documents but without exposing fields(i.e: omit `password` field)\n */\n```\nsee: [How to architect your models](https://github.com/a8m/doqmentdb/tree/master/example/users/model)\n\n# Middleware\nMiddleware/Hooks are executed at the document level(`create`/`save`/`insert`, `update`, `remove/delete`).  \nThere are two types of middleware, **pre** and **post**.  \n\n## pre\n**Usage:** `users.pre(operation, callback)`  \n**Note:** `pre` middleware are executed one after another, when each middleware calls next.  \n**Example:**\n```js\nusers.pre('save', function(next) {\n  var doc = this;\n  doc.createdAt = new Date().toString();\n  next();\n}, function(next) {\n  var doc = this;\n  doc.updatedAt = new Date().toString();\n  next();\n});\n\n// Do something async\nusers.pre('save', function(next) {\n  var doc = this;\n  bcrypt.genSalt(10, function(err, salt) {\n    bcrypt.hash(doc.password, salt, function(err, hash) {\n      doc.password = hash;\n      next();\n    });\n  });\n});\n// ##Note: the order is important, this example order:\n// `createdAt()`, `updatedAT()`, `hash/bcrypt()`, and then the `.create` operation will called\n```\n## post\n**Usage:** `users.post(operation, callback)`  \n**Note:** `post` middleware are executed in parallel.  \n**Example:**\n```js\nusers.post('save', function(doc) {\n  logger(new Date(), doc, 'saved!')\n});\n```\n# Atomic Transactions\nSince **v0.2.6** DoQmentDB supports atomic-transactions using a built-in sporcs(i.e: stored procedures) to handle concurrently well.  \n**Note:** To perform some operation this way, you should prefix it with `$`.  \n**Read More:** [DocumentDB - Atomic Transactions](https://github.com/Azure/azure-content/blob/master/articles/documentdb-programming.md#introduction)\n```js\n// Lets take some example of `consuming` from two differents\n// Service-Bus queues and update the same `model`/`document`\n//\n// Note: This also could happen in a distributed system, when two operations happens in parallel\n\n// We have a `stores` collection that holds the `sales` and the `users`\n// fields per `store`(a Document)\n// We are using the `atomic` version of `update`, because we don't want to lose data\nsbs.receiveQueueMessage('sales', function(msg) {\n  stores.$update({ id: msg.id }, { sales: { $push: msg.sale } });\n  // Polling again...\n});\n\nsbs.receiveQueueMessage('users', function(msg) {\n  stores.$update({ id: msg.id }, { users: { $push: msg.user } });\n  // ...\n});\n```\n\n# Examples\n* [Koa DocumentDB Example](https://github.com/a8m/koa-documentdb-example) - A users CRUD application using Koa and DocumentDB.\n* [Express DocumentDB Example](https://github.com/a8m/doqmentdb/tree/master/example/users) - Express application using DocumentDB.\n\n# Changelog\n## 0.2.9\n* Schema Fix- issue #26\n\n## 0.2.8\n* Add aliases: `updateOne` and `$updateOne`(the conccurent one)\n* refactor the built-in stored procedure(`findAndModify`)\n\n## 0.2.6\nSince **0.2.6** DoQmentDB support atomic transactions using `DocumentDB stored procedures`.  \nMethods that support:\n * `update`/`findAndModify`\n * `findOneAndModify`\n * `findOrCreate`\n * `findAndRemove`\n * `findOneAndRemove`\n\nIf you want to use one of this methods, you should use them prefix with `$` sign.\n```js\n// Push 'a' and 'b' to `list` field(do it concurrently)\n['a', 'b'].forEach(function(ch) {\n  users.$update({}, { list: { $push: ch } });\n});\n```\n\n[npm-image]: https://img.shields.io/npm/v/doqmentdb.svg?style=flat-square\n[npm-url]: https://npmjs.org/package/doqmentdb\n[travis-image]: https://img.shields.io/travis/a8m/doqmentdb.svg?style=flat-square\n[travis-url]: https://travis-ci.org/a8m/doqmentdb\n[coveralls-image]: https://img.shields.io/coveralls/a8m/doqmentdb.svg?style=flat-square\n[coveralls-url]: https://coveralls.io/r/a8m/doqmentdb\n[david-image]: http://img.shields.io/david/a8m/doqmentdb.svg?style=flat-square\n[david-url]: https://david-dm.org/a8m/doqmentdb\n[license-image]: http://img.shields.io/npm/l/doqmentdb.svg?style=flat-square\n[license-url]: LICENSE\n[downloads-image]: http://img.shields.io/npm/dm/doqmentdb.svg?style=flat-square\n[downloads-url]: https://npmjs.org/package/doqmentdb\n[gitter-image]: https://badges.gitter.im/Join%20Chat.svg\n[gitter-url]: https://gitter.im/a8m/doqmentdb?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa8m%2Fdoqmentdb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fa8m%2Fdoqmentdb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa8m%2Fdoqmentdb/lists"}