Giter VIP home page Giter VIP logo

minq's Introduction

minq

fluent queries for mongodb using promises

Circle CI js-standard-style

installation

$ npm install minq

usage example

var Minq = require('minq')

Minq.connect(connectionString).then(function (db) {})

db.from('foo')
  .where({name: /John/i})
  .select(['name', 'email', 'homeAddress.zipCode'])
  .limit(1000)
// => Promise<Array>

db.from('foo')
  .skip(20)
  .limit(50)
  .sort('name')
// => Promise<Array>

As a bonus, convenience collection accessors are added to the db object:

db.foo
// equivalent to db.from('foo')

To return a scalar (single) value:

db.foo.first().then(function (aFoo) {
  console.log(aFoo
})

We can end a query by .pipeing to a stream:

db.foo.pipe(process.stdout)

Or we can enumerate over a query in a streaming fashion, with a promise to indicate when the whole stream is done (including processing other tasks):

db.spiders
  .where({scary: false})
  .forEach(function (spider) {
    return db.spiders
      .byId(spider._id)
      .update({$set:{scary: true}})
  })
  .then(function () {
    console.log('Fixed some misinformation about some spiders!')
  })

Other commands:

db.foo
  .where({email: /lol\.com$/)
  .count()
// => Promise<Number>

db.foo
  .insert({name: 'Melissa', email: '[email protected]'})
// => Promise

db.foo
  .upsert({_id: 15, name: 'Cian'})
// => Promise

tools

good to know

Minq queries are contstructed starting with a db and collection, then by adding various options and constraints.

Minq queries implement the Promises/A+ interface - that is, you get the asynchronous value of the query results by calling the .then() method.

Read queries can also be treated as a Node.js stream, using the .pipe() method. If an error occurs when building or executing the query, it will be sent as a stream error. Streaming is useful when dealing with a large number of query results.

api reference

  • Minq

    • .connect()
    • #ready
    • #disconnect
    • #from()
  • Query

    • #clone()
    • #from()
    • #where()
    • #not()
    • #select()
    • #limit()
    • #skip()
    • #sort()
    • #options()
    • #first()
    • #firstOrDefault()
    • #byId()
    • #byIds()
    • #count()
    • #exists()
    • #aggregate()
    • #assert()
    • #expect()
    • #then()
    • #pipe()
    • #forEach()
    • #insert()
    • #update()
    • #findAndModify()
    • #modifyAndFind()
    • #pull()
    • #upsert()
    • #remove()
    • #removeAll()

Uses jsig notation. Object.Function denotes a function available on the constructor. Object#Method denotes a method on a particular instance of Object.

new Minq(MinqStore?) => instanceof Minq

MinqStore is an interface for Minq storage engines. By default, uses MongoDb node native driver. You can supply your own for other use cases, like testing or memory-backed stores. Currently the best documentation for MinqStore interface is in the mongodb.js and test/mongodb.test.js files.

Minq.connect(connectionString: String) => Promise<instanceof Minq>)

This is the recommended way to create and work with Minq in most cases. A factory function to create a new MongoDb connection and wrap it in a Minq object. connectionString should be a MongoDb uri

Example:

var Minq = require('minq')

Minq.connect('mongodb://localhost/mydb').then(function (minq) {
  // we now have an active db connection
  ...
})

Once the db connection is established, the Minq instance has property getters for each collection, similar to the db object in the MongDb shell:

minq.myCollection.first()

// is equivalent to

minq.from('myCollection').first()

Minq#from(collectionName : String) => instanceof Query

Begins a new Query builder object in the context of the current active MongoStore. This is the recommended way to create queries.

Minq#store : MinqStore

Accessor property for the current active MinqStore.

new Query(store : MongoStore) => instanceof Query

The recommended way to create a Query is Minq#from() (see above) Creates a new query in the context of store.

Querys are the basic API for interacting with a database in Minq. The syntax is meant to by fluent (chained), and the names of the commands are inspired by SQL.

Partial Query objects can be passed around to different parts of a program. For example, one function might add a where clause, and another might handle the select projection, while a third might deal with sort or skip.

Querys are executed when they produce a side effect (in the case of a write query, like update) or their value is accessed. Consider:

var Minq = require('minq')
Minq.connect('mongodb://localhost/db').then(function (minq) {
  var query = minq.from('users')
                  .where({email: '[email protected]'})
                  .first()
  // the query has not yet been evaluated
  query.then(function (user) {
    // when the query is accessed via `then`, it forces evaluation of the query
    ...
  })
})

Read queries will always return a set of results, unless .first() is specified.

Result sets can be used as a Promise using then, a ReadableStream using pipe, or by enumerating each result using forEach. See the documentation for those methods.

Query#clone() => instanceof Query

Deep clones a query object (most useful before the query has been executed!)

Query#from(collectionName: String) => Query

Sets the collection name.

Query#where(query: Object) => Query

query is a mongodb query object, with standard $ operators

where can be called multiple times to add multiple query terms. Mongodb joins them with logical AND, see $and.

Query#not(property : String) => Query

Adds to the where clause of a query, checking that a value is not JavaScript falsy.

Equivalent to where({'property': {$nin: [undefined, null, false, 0]}}).

Query#select(fields: Object|Array<String>) => Query

fields is a mongodb projection object, with keys corresponding to the fields of the document you want to return. fields can also be an array of strings indicating the property names.

Example:

query.select(['a.b','c','d'])

// is equivalent to

query.select({
  'a.b': true,
  'c': true,
  'd': true
})

Query#options(opts: Object) => Query

configure any additional options, for example {multi: true}

Query#sort(by: Object|Array) => Query

by is a mongodb sort order option. alias: Query#orderBy

Query#limit(number: Number) => Query

number is a Number for the maximum number of documents you want to return. alias: Query#take

Query#skip(number: Number) => Query

number is a Number for the number of documents which otherwise match the query that you want to skip in the result

Query#first() => Query

Specifies that a scalar (single document) return value is expected, rather than a set of results. The query will error if no document is found.

Query#firstOrDefault(default: Object) => Query

Specifies that a scalar (single document) return value is expected, rather than a set of results. If no matching document is found, the default parameter is used as the result.

Query.then(onFulfilled : Function, onRejected : Function) => Promise

Forces query evaluation. Conforms to the Promises/A+ interface. Fulfilled with the result set as an Array (or a single object if .first() is specified)

Query.pipe(WritableStream) => Stream

Forces query evaluation. Conforms to the node ReadableStream interface. Emits an object for each document in the result set. Only suitable for read queries.

Query#forEach(iterator: (Object) => Promise?) => Promise

Forces query evaluation. Only suitable for read queries. Streams the results of a query. If iterator returns a promise, will await each of the promises, for example if performing batch updates.

Returns a void Promise to rejoin program execution once all results have been iterated.

Example:

minq.from('users')
  .where({'canEmail': true})
  .select(['name','email'])
  .forEach(function (user) {
    // the hypothetical sendEmail function returns a promise
    return sendEmail({
      to: user.email,
      subject: 'Hi ' + user.name,
      body: 'Please ignore, just a test'
    })
  })
  .then(function () {
    // once all of the promises in the iterator have been fulfilled,
    // this function will be called
    console.log('done sending emails')
  }, function (err) {
    // if there is an error, this function will be called instead
    console.log('an unexpected error occured:', err)
  })

The implementation of forEach uses charybdis - please refer to that module's documentation for more info.

Query#insert(doc: Object) => Promise<Object>

Insert a document collection. The promise is the inserted object, including _id if assigned by db.

Query#update(changes: Object) => Promise<Number>

Update documents in a collection with changes, a mongodb setter or unsetter. Use with Query.where or include _id on the changes object. The promise is the count of updated documents.

Query#upsert(setter: Object) => Promise<Number>

Create or update a document in a collection with setter, a mongodb setter. The promise is the count of updated documents.

Query#remove() => Promise<Number>

Remove documents matching a where query. The promise is the number of documents removed. Rejected if no where query is specified.

Query#removeAll() => Promise<Number>

Remove all documents in a collection. The promise is the number of documents removed.

running the tests

$ npm install
$ npm test

As well, integration tests:

$ sudo mongod
$ npm run test-integration

contributors

js-standard-style

jden [email protected]

license

MIT. (c) MMXIII jden [email protected]. See LICENSE.md.

minq's People

Contributors

junosuarez avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

minq's Issues

Pipelining

Eg byId -> byIds rewrite
Experiment
Hinting? Automatic?

expect

add a Query#expect method to throw a NotFoundError if the actual number of documents returned is less than the number expected.

If used in conjunction with Query#byIds, Query#byId, Query#one, and Query#deferredOne, default to the appropriate number.

Current behavior is it will return null or an empty array.

caching

add a Query#cache() method to trigger use of a caching plugin, if available.

can use in conjunction with Query#byId and Query#byIds. Possibly formalize this in a post-processing step so other chained custom query functions can use it.

cache plugin should have interface:{
get:
put: or set: (support either, prefer put)
del:
}

fix collection names when db name contains non alpha characters

getCollectionNames should return an array of strings with collection names, not including the db name. if the db name contains, eg, a -, the collection name is returned including the db name, eg:

['test-db.foos','test-db.bars'] should be ['foos','bars']

generic defer/thunk

Rather than implementing, eg, Query#deferOne for each finalizer, have a Query#defer() function which alters the behavior of the finalizers.

publish v1.1.0 to npm

@jden i upgraded the underlying mongo driver to the last stable 1.x version; can you check it out and if it looks good pub to npm.

Make lazy

Return Query instances which are lazy promises, to allow returning partial queries which can be further modified, restricted, etc.

Add Query#clone() method

Callable any time before the query is finalized,, create a new Query object with the same parameters (db, collection, query, options, etc)

forEach, when used in a script, throws "Object #<Object> has no method 'forEach'"

using minq 1, rc 8; ubuntu 14.04; node 0.10.26; npm 1.4.3

setup

var connstr = process.env.DB_CONN_STRING;
var minq = require('minq');

minq.connect(connstr).then(function (db) {
  console.log('connected: ' + connstr);

  return db
    .from('CollectionName')
    .forEach(function (data) {
       console.log(data);
    });
})
.then(function () {
  console.log('done');
});

expected

a log of each item in the collection

> {}
> {}
...

actual

TypeError: Object #<Object> has no method 'forEach'
    at /home/test.js:23:15
    at Writable.stream._write (/home/node_modules/minq/node_modules/charybdis/index.js:51:14)
    at doWrite (_stream_writable.js:226:10)
    at writeOrBuffer (_stream_writable.js:216:5)
    at Writable.write (_stream_writable.js:183:11)
    at Stream.ondata (stream.js:51:26)
    at Stream.EventEmitter.emit (events.js:95:17)
    at drain (/home/node_modules/minq/node_modules/through/index.js:36:16)
    at Stream.stream.queue.stream.push (/home/node_modules/minq/node_modules/through/index.js:45:5)
    at Stream.<anonymous> (/home/node_modules/minq/mongodb.js:85:50)

notes

interestingly, if you add a select(['_id']) it works fine; however if you add any other element to the select, it fails with the same error

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.