npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

doqmentdb

v0.3.3

Published

A Promise-Based DocumentDB ODM Client for NodeJS

Downloads

64

Readme

##DoQmentDB - A Promise-Based DocumentDB Client NPM version Build status Test coverage Dependency Status License Downloads

DoQmentDB is a tiny layer that provides the simplicity of MongoDB for DocumentDB users(support schema, hooks/middleware, atomic-transactions, udf and more).

##Table of contents:

#Get Started (1) You can install DoQmentDB using 2 different methods:

  • clone & build this repository
  • via npm: by running $ npm install doqmentdb from your terminal

(2) Add to your project:

var DoqmentDB = require('doqmentdb');

(3) Start Playing with DoqmentDB:

var DoQmentDB  = require('doqmentdb');
// Create DocumentDB connection
var connection = new (require('documentdb').DocumentClient)(HOST, OPTIONS);
// Pass connection and database-name, if `test` is not exist it will create one.
var db = new DoQmentDB(connection, 'test');
// Create a CollectionManager instance, if `users` is not exist it will create one.
var users = db.use('users');
// Using schema
users.schema(model);
// Add hooks(see `users` full example)
users.pre('save', function(next) {
  var doc = this;
  doc.createdAt = new Date().toString();
  next();
});

// Each http function returns a `Promise` with two specific methods: success and error.
users.create({ name: '6534' })
  .then(console.log);

users.findById(1)
  .then(console.log);

users.findAndRemove({ isAdmin: false })
  .then(console.log);

#Database Create a DatabaseManager by passing connection and databaseName.

var DoQmentDB  = require('doqmentdb');
// Create DocumentDB connection
var connection = new (require('documentdb').DocumentClient)(HOST, OPTIONS);
// if `test` is not exist it will create one
var db = new DoQmentDB(connection, 'test');

##create Get name and crete new collection in the used db.
Usage: db.create(string)
Aliases: insert
Returns: Object

db.create('users')
  .then(console.log);

##getDatabase Return the used database.
Usage: db.getDatabase()

db.getDatabase()
  .then(console.log);

##find find collection by given object params.
Note: to return all documents, omit params argument or pass an empty object({}).
Usage: db.find(object[optional])
Returns: Array

db.find()
  .then(console.log); // Return all collections

db.find({ id: 'users' })
  .then(console.log); // Return collections where id equal to `users`

##findById find collection by given string id.
Usage: db.findById(string)
Returns: Object

db.findById('users')
  .then(console.log);

##findOrCreate get object properties, search for collection, if it not exist create one.
Usage: db.findOrCreate(object)
Returns: Object

db.findOrCreate({ name: 'users', id: '#1' })
  .then(console.log);

##remove get collection id as a String, if it exist - remove it and return undefined, else return false.
Usage: db.remove(string)
Returns: undefined or Boolean

db.remove('test')
  .then(console.log);

##use get collection name and return CollectionManager instance.
Note: if the given collection is not exist it will create one.
Usage: var coll = db.use(string);
Returns: object instanceof CollectionManager

var users = db.use('users'); // This operation is not async

#Collection Create a CollectionManager by passing to .use function a collection name.

var users = db.use('users');
console.log(users.constructor.name); // Collection

##create get object properties, and create new document under the used collection.
Usage: users.create(object)
Aliases: insert
Returns: Object

users.create({ name: 'Ariel', admin: true })
  .then(console.log); // { name: 'Ariel', admin: true, id: '8...31', _self: ... }

##createOrUpdate create a new document under the current collection, or update an existing document with the same id.
Usage: users.createOrUpdate(object)
Aliases: upsert
Returns: Object

users.upsert({ id: 'my_user_id', admin: true })
  .then(console.log); // { id: 'my_user_id', admin: true, _self: ... }

##getCollection return the used collection.
Usage: users.getCollection()

users.getCollection()
  .then(console.log);

##find get object properties and return array of results.
Usage: users.find(object)
Note: to return all collections, omit params argument or pass an empty object({}).
Returns: Array

users.find({ active: true })
  .then(console.log);

##findOne get object properties and return the first matching result.
Usage: users.findOne(object)
Returns: Object

users.findOne({ active: true, name: 'Bar' })
  .then(console.log);

##findById find document by giving a string id.
Usage: users.findById(string)
Returns: Object

users.findById('53...3')
  .then(console.log);

##findAndRemove get object properties to search, find the equivalents and remove them.
Usage: users.findAndRemove(object)
Returns: Array
Note: if you want support atomic-transactions(i.e: do things concurrently, e.g: distributed system), you need use this method prefix with $ sign.

users.findAndRemove({ name: 'Ariel' })
  .then(console.log);

// Using stored procedure
users.$findAndRemove({ name: 'Ariel' })
  .then(console.log);

// Remove all users
users.findAndRemove({})
  .then(console.log);

##findOneAndRemove get object properties, and remove the first matching result.
Usage: users.findOneAndRemove(object)
Returns: undefined or Boolean
Note: if you want support atomic-transactions(i.e: do things concurrently, e.g: distributed system), you need use this method prefix with $ sign.

users.findOneAndRemove({ name: 'Ariel', admin: true })
  .then(console.log);

// Using stored procedure
users.$findOneAndRemove({ name: 'Ariel', admin: true })
  .then(console.log);

##findAndModify get object properties to search, find the equivalents and modify them(extend operation).
Usage: users.findAndModify(object, extend)
Aliases: update
Returns: Array
Note: if you want support atomic-transactions(i.e: do things concurrently, e.g: distributed system), you need use this method prefix with $ sign.

users.update({ name: 'Ariel', admin: true }, { admin: false })
  .then(console.log);

// Push 'a' and 'b' to `list` field(do it concurrently)
['a', 'b'].forEach(function(ch) {
  users.$update({}, { list: { $push: ch } });
});

##findOneAndModify get object properties and modify(extend operation) the first matching.
Usage: users.findOneAndModify(object, extend)
Aliases: updateOne
Returns: Object
Note: if you want support atomic-transactions(i.e: do things concurrently, e.g: distributed system), you need use this method prefix with $ sign.

users.findOneAndModify({ admin: false }, { admin: true })
  .then(console.log);

// Using stored procedure
users.$findOneAndModify({ admin: false }, { admin: true })
  .then(console.log);

##findOrCreate get object properties, search for document, if it not exist create one.
Usage: users.findOrCreate(object)
Returns: Object
Note: if you want support atomic-transactions(i.e: do things concurrently, e.g: distributed system), you need use this method prefix with $ sign.

users.findOrCreate({ admin: false, name: 'Ariel' })
  .then(console.log);

// Using stored procedure
users.$findOrCreate({ admin: false, name: 'Ariel' })
  .then(console.log);

#Queries ###Operators

  • Logical & Conjunctive:
    • $or OR
    • $and AND
    • $not NOT
    • $nor NOT(... OR ...)
  • Comparison:
    • $gt >
    • $gte >=
    • $lt <
    • $lte <=
    • $ne <> or !=
  • UDF:
    • $in like Array.prototype.some(...)
    • $all like Array.prototype.every(...)
    • $type typeof value
    • $regex new RegExp(...).test(value)
    • $size test array.length

###Examples

users.find({ a: 1, b: 2, c: '3' })
// ... r WHERE r.a=1 AND r.b=2 AND r.c="3"

users.find({ $or: [{ a: 2, b: 3}, { c: 3 }] })
// ... r WHERE ((r.a=2 AND r.b=3) OR r.c=3)

users.find({ $not: { a: 1, b: 2, c: 3 } })
// ... r WHERE NOT(r.a=1 AND r.b=2 AND r.c=3)

users.find({ $nor: [ { a: 1 }, { b: 3 }]})
// ... r WHERE NOT(r.a=1 OR r.b=3)

users.find({ $nor: [ { a: 1, b: 1 }, { c: 3 } ] })
// ... r WHERE NOT((r.a=1 AND r.b=1) OR r.c=3)

users.find({ $not: { name: { $gt: 3 }, age: 12 } })
// ... r WHERE NOT(r.name > 3 AND r.age=12)

users.find({ $not: { name: { $ne: 'bar' } } })
// ... r WHERE NOT(r.name <> "bar")

users.find({ $or: [
        { name: { $ne: 'Ariel' } },
        { age: { $lte: 26 } },
        { $and: [
          { isAdmin: { $ne: false } },
          { isUser: { $ne: false } }
        ]}
      ]})
// ... r WHERE r.name <> "Ariel" OR r.age <= 26 OR (r.isAdmin <> false AND r.isUser <> false)

users.find({ coins: { $in: 2 } })
// ... r WHERE inUDF(r.coins, 2)

users.find({ $not: { age: { $type: 'number' } } })
// ... r WHERE NOT(typeUDF(r.age, "number"))

#Operations When using one of the update operations(e.g: .update(), .findAndModify(), etc...), you could use the build-in prototype functions(based on the type) prefixing with $ sign.
Usage: users.update({ ... }, { keyName: { $method: value } })
Note: value could be single or array of arguments.

// Find all, and push 2 to `arr` field
users.update({}, { arr: { $push: 2 } });

// Suffix all users `name` with #
users.update({}, { name: { $concat: '#' } });

// Trim the name from `foo` to `o`
users.update({  name: 'foo' }, { name: { $substr: [1,1] } });

#Schema Manage your documents with schema.
fields:

  • type
    • required
    • used for type comparing, (e.g: String, Boolean, Number, etc..).
  • default
    • optional
    • value fallback
  • regex
    • optional
    • regex validation, (e.g: email validation - /^[a-zA-Z0-9@:%_\+.~#?&//=|/d]{10,}$/).
  • error
    • optional
    • return message to fields that fail in the validation phase(regex/type). see: example
  • expose
    • optional
    • 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

Example using schema:
schema: model.js

module.exports = {
  /**
   * @field name
   * @default no default value
   */
  name: {
    type: String,
    'default': ''
  },

  /**
   * @field email
   * @default no default value
   * @regex email, min-length = 10
   */
  email: {
    type: String,
    'default': '',
    regex: /^[a-zA-Z0-9@:%_\+.~#?&//=|/d]{10,}$/,
    error: '`email` must be type string, valid email address, and least 10 chars',
    expose: true
  },

  /**
   * @field password
   * @default no default value
   * @regex password
   */
  password: {
    type: String,
    'default': '',
    regex: /^.*(?=.{8,})(?=.*[a-zA-Z])(?=.*\d)(?=.*[!#$%&? "]).*$/,
    error: '`password` must be type string, contain 8 chars and at least one number, ' +
      'one letter and one unique character such as !#$%&? "',
    expose: false
  },

  /**
   * @field isAdmin
   * @default false
   */
  isAdmin: {
    type: Boolean,
    'default': false
  }
};

using schema(model.js)

var DoQmentDB  = require('doqmentdb');          
var model      = require('./model');            // Get model/schema
var connection = new (require('documentdb')     // Create DocumentDB connection
.DocumentClient)(CONFIG.HOST, CONFIG.OPTIONS);

var db = new DoQmentDB(connection, CONFIG.DB);  // Create DBManager 'test'
var users = db.use('users');                    // Create CollectionManager 'users'
users.schema(model);                            // Using schema

users.create({ password: 'Ar2!as_s'})
  .then(console.log)
  .catch(console.log);
  /*
   [Error:
   `email` must be type string, valid email address, and least 10 chars
   ]
   */

users.create({ name: 'Ariel', email: 'ariel.com', password: 'Ar2!as_s'})
  .then(console.log)
  .catch(console.log);
/*
 [Error:
 `email` must be type string, valid email address, and least 10 chars
 ]
 */

users.create({ name: 'Ariel', email: '[email protected]', password: 'Ar2!as_s'})
  .then(console.log)
  .catch(console.log);
/*
 {
   name: 'Ariel',
   email: '[email protected]',
   password: 'Ar2!as_s',
   id: '2eb7...c0',
    ...
 }
 */
users.find({})
  .then(console.log);
/*
 Get all documents but without exposing fields(i.e: omit `password` field)
 */

see: How to architect your models

#Middleware Middleware/Hooks are executed at the document level(create/save/insert, update, remove/delete).
There are two types of middleware, pre and post.

##pre Usage: users.pre(operation, callback)
Note: pre middleware are executed one after another, when each middleware calls next.
Example:

users.pre('save', function(next) {
  var doc = this;
  doc.createdAt = new Date().toString();
  next();
}, function(next) {
  var doc = this;
  doc.updatedAt = new Date().toString();
  next();
});

// Do something async
users.pre('save', function(next) {
  var doc = this;
  bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash(doc.password, salt, function(err, hash) {
      doc.password = hash;
      next();
    });
  });
});
// ##Note: the order is importatnt, this example order:
// `createdAt()`, `updatedAT()`, `hash/bcrypt()`, and then the `.create` operation will called

##post Usage: users.post(operation, callback)
Note: post middleware are executed in parallel.
Example:

users.post('save', function(doc) {
  logger(new Date(), doc, 'saved!')
});

#Atomic Transactions Since v0.2.6 DoQmentDB supports atomic-transactions using a built-in sporcs(i.e: stored procedures) to handle concurrently well.
Note: To perform some operation this way, you should prefix it with $.
Read More: DocumentDB - Atomic Transactions

// Lets take some example of `consuming` from two differents
// Service-Bus queues and update the same `model`/`document`
//
// Note: This also could happen in a distributed system, when two operations happens in parallel

// We have a `stores` collection that holds the `sales` and the `users`
// fields per `store`(a Document)
// We are using the `atomic` version of `update`, because we don't want to lose data
sbs.receiveQueueMessage('sales', function(msg) {
  stores.$update({ id: msg.id }, { sales: { $push: msg.sale } });
  // Polling again...
});

sbs.receiveQueueMessage('users', function(msg) {
  stores.$update({ id: msg.id }, { users: { $push: msg.user } });
  // ...
});

#Examples

#Changelog ##0.2.9

  • Schema Fix- issue #26

##0.2.8

  • Add aliases: updateOne and $updateOne(the conccurent one)
  • refactor the built-in stored procedure(findAndModify)

##0.2.6 Since 0.2.6 DoQmentDB support atomic transactions using DocumentDB stored procedures.
Methods that support:

  • update/findAndModify
  • findOneAndModify
  • findOrCreate
  • findAndRemove
  • findOneAndRemove

If you want to use one of this methods, you should use them prefix with $ sign.

// Push 'a' and 'b' to `list` field(do it concurrently)
['a', 'b'].forEach(function(ch) {
  users.$update({}, { list: { $push: ch } });
});