marsdb
Version:
MarsDB is a lightweight client-side MongoDB-like database, Promise based, written in ES6
237 lines (200 loc) • 9.02 kB
Markdown
<div style="text-align:center"><img src="https://static.studytime.me/marsdb.png" /></div>
[MarsDB](https://github.com/c58/marsdb)
=========
[](https://travis-ci.org/c58/marsdb)
[](https://www.npmjs.com/package/marsdb)
[](https://coveralls.io/github/c58/marsdb?branch=master)
[](https://david-dm.org/c58/marsdb)
[](https://www.bithound.io/github/c58/marsdb)
[](https://gitter.im/c58/marsdb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[](https://github.com/c58/marsdb)
MarsDB is a lightweight client-side database.
It's based on a Meteor's **minimongo** matching/modifying implementation. It's carefully written on **ES6**, have a **Promise based** interface and may be backed with any storage implementation ([see plugins](https://github.com/c58/marsdb#plugins)). It's also supports **observable** cursors.
MarsDB supports any kind of find/update/remove operations that Meteor's minimongo does. So, go to the Meteor docs for supported query/modifier operations.
You can use it in any JS environment (Browser, Electron, NW.js, Node.js).
## Features
* **Promise based API**
* **Carefully written on ES6**
* **Very very flexible** – just take a look to the [plugins section](https://github.com/c58/marsdb#plugins)
* **Supports many of MongoDB query/modify operations** – thanks to a Meteor's minimongo
* **Flexible pipeline** – map, reduce, custom sorting function, filtering. All with a sexy JS interface (no ugly mongo's aggregation language)
* **Persistence API** – all collections can be stored (and restored) with any kind of storage (in-memory, LocalStorage, LevelUP, etc)
* **Observable queries** - live queries just like in Meteor, but with simplier interface
* **Reactive joins** – out of the box
## Bindings
* [React](https://github.com/c58/marsdb-react)
* [AngularJS 1.x](https://github.com/c58/marsdb-angular)
## Plugins
* In-memory storage (built-in default)
* [LocalForage storage](https://github.com/c58/marsdb-localforage) – fastest in-browser storage (InexedDB, WebSQL and fallback to localStorage)
* [LocalStorage storage](https://github.com/c58/marsdb-localstorage) – not recommended, better prefer LocalForage
* [LevelUP storage](https://github.com/c58/marsdb-levelup) – lightweight server-less Node.js storage
* [MongoDB wrapper](https://github.com/c58/marsdb-mongo) – use MarsDB for comfortable work with MongoDB
* [Validation via Mongoose](https://github.com/c58/marsdb-validation) – validate objects with Mongoose
## Meteor compatible client/server
Sometimes you can't use Meteor infrastructure. Maybe you need to build a custom client. Maybe you need to build a custom server with express and other modules. In meteor it can be done with a ton of hack. But the only reason why it's so ugly to do a simple things is because Meteor forces you to use their infrastructure. I'm trying to solve this issue with DDP client/server modules, based on MarsDB.
* [DDP client](https://github.com/c58/marsdb-sync-client)
* [DDP server](https://github.com/c58/marsdb-sync-server)
## Examples
### Using within non-ES6 environment
The `./dist` folder contains already compiled to a ES5 code, but some polyfills needed. For using in a browser you must to include `marsdb.polyfills.js` before `marsdb.min.js`. In node.js you need to `require('marsdb/polyfills')`.
It sets in a window/global: Promise, Set and Symbol.
### Create a collection
```javascript
import Collection from 'marsdb';
import LocalForageManager from 'marsdb-localforage';
// Default storage is in-memory
// Setup different storage managers
// (all documents will be save in a browser cache)
Collection.defaultStorageManager(LocalForageManager);
// Create collection wit new default storage
const users = new Collection('users');
```
### Create an in-memory collection
```javascript
import Collection from 'marsdb';
import LocalStorageManager from 'marsdb-localstorage';
// Set some defaults and create collection
Collection.defaultStorageManager(LocalStorageManager);
const users = new Collection('users');
// But it may be useful to create in-memory
// collection without defined defaults
// (for example to save some session state)
const session = new Collection('session', {inMemory: true});
```
### Find documents
```javascript
const posts = new Collection('posts');
posts.find({author: 'Bob'})
.project({author: 1})
.sort(['createdAt'])
.then(docs => {
// do something with docs
});
```
### Find with pipeline (map, reduce, filter)
An order of pipeline methods invokation is important. Next pipeline operation gives as argument a result of a previous operation.
```javascript
const posts = new Collection('posts');
// Get number of all comments in the DB
posts.find()
.limit(10)
.sortFunc((a, b) => a - b + 10)
.filter(doc => Matsh.sqrt(doc.comment.length) > 1.5)
.map(doc => doc.comments.length)
.reduce((acum, val) => acum + val)
.then(result => {
// result is a number of all comments
// in all found posts
});
// Result is `undefined` because posts
// is not exists and additional processing
// is not ran (thanks to `.ifNotEmpty()`)
posts.find({author: 'not_existing_name'})
.aggregate(docs => docs[0])
.ifNotEmpty()
.aggregate(user => user.name)
```
### Find with observing changes
Observable cursor returned by a `find` and `findOne` methods of a collection. Updates of the cursor is batched and debounced (default batch size is `20` and debounce time is `1000 / 15` ms). You can change the paramters by `batchSize` and `debounce` methods of an observable cursor (methods is chained).
```javascript
const posts = new Collection('posts');
const stopper = posts.find({tags: {$in: ['marsdb', 'is', 'awesome']}})
.observe(docs => {
// invoked on every result change
// (on initial result too)
stopper.stop(); // stops observing
}).then(docs => {
// invoked once on initial result
// (after `observer` callback)
});
```
### Find with joins
```javascript
const users = new Collection('users');
const posts = new Collection('posts');
posts.find()
.join(doc => {
// Return a Promise for waiting of the result.
return users.findOne(doc.authorId).then(user => {
doc.authorObj = user;
// any return is ignored
});
})
.join(doc => {
// For reactive join you must invoke `observe` instead `then`
// That's it!
return users.findOne(doc.authorId).observe(user => {
doc.authorObj = user;
});
})
.join((doc, updated) => {
// Also any other “join” mutations supported
doc.another = _cached_data_by_post[doc._id];
// Manually update a joined parameter and propagate
// update event from current cursor to a root
// (`observe` callback invoked)
setTimeout(() => {
doc.another = 'some another user';
updated();
}, 10);
})
// Or just pass join spec object for fast joining
// (only one `find` will be produced for all posts)
.join({ authorId: users }) // posts[i].authorId will be user object
.observe((posts) => {
// do something with posts with authors
// invoked any time when posts changed
// (and when observed joins changed too)
})
```
### Inserting
```javascript
const posts = new Collection('posts');
posts.insert({text: 'MarsDB is awesome'}).then(docId => {
// Invoked after persisting document
})
posts.insertAll(
{text: 'MarsDB'},
{text: 'is'},
{text: 'awesome'}
).then(docsIds => {
// invoked when all documents inserted
});
```
### Updating
```javascript
const posts = new Collection('posts');
posts.update(
{authorId: {$in: [1, 2, 3]}},
{$set: {text: 'noop'}}
).then(result => {
console.log(result.modified) // count of modified docs
console.log(result.updated) // array of updated docs
console.log(result.original) // array of original docs
});
// Upsert (insert when nothing found)
posts.update(
{authorId: "123"},
{$set: {text: 'noop'}},
{upsert: true}
).then(result => {
// { authorId: "123", text: 'noop', _id: '...' }
});
```
### Removing
```javascript
const posts = new Collection('posts');
posts.remove({authorId: {$in: [1,2,3]}})
.then(removedDocs => {
// do something with removed documents array
});
```
## Roadmap
* Indexes support for some kind of simple requests {a: '^b'}, {a: {$lt: 9}}
* Documentation
## Contributing
I'm waiting for your pull requests and issues.
Don't forget to execute `gulp lint` before requesting. Accepted only requests without errors.
## License
See [License](LICENSE)