A plugin that adds a JSON-based map/reduce view interface to PouchDB and CouchDB.
Here is an example:
const PouchDB = require('pouchdb')
PouchDB.plugin(require('pouchdb-jsonviews'))
const db = new PouchDB('your_cool_project')
// add a jsonview with this convenience method.
// it just creates a design doc: `_design/my_doc_name`.
// and in it, creates a view: `my_view_name`.
// the map function is templated using the jsonview.
await db.addJsonView('my_doc_name', 'my_view_name', {
map: {
key: [
// [date, username, time] for a daily digest timeline
{ access: 'created_at', transform: 'date' },
'username',
{ access: 'created_at', transform: 'time' }
],
value: 'status'
},
reduce: '_count'
})
// then you can just query it like any other view!
const { rows } = await db.query('my_doc_name/my_view_name', {
group: true,
group_level: 2
})
console.log(rows)
> {
> "rows": [
> {
> "key": ['YYYY-MM-DD', 'garbados'],
> "value": 12
> },
> ...
> ]
> }
Although Mango is a powerful query language in its own right -- and PouchDB-JsonViews is not intended as a competitor or replacement! -- it does not allow you to use a reduce function. This forces certain classes of queries to occur in-memory, on-demand, page by page, which is simply not tenable for certain scales of data.
However, the alternative of writing JavaScript views lends itself to the worse of our tendencies as programmers: to inefficient mutations of data that are impossible to examine or debug. These can create showstopping problems as datasets scale, as the size of indices may explode out of control due to errors and oversights. It is unwise to permit ourselves so much room for failure, grateful for it though we may be from time to time.
Usually, a view just emits a certain number of keys and a value, and maybe specifies a reduce function. PouchDB-JsonViews uses a simple access syntax to specify document attributes for keys and values, and exposes a small number of transforms which convert a value in some way (such as generating a time or date string from a timestamp, with date
and time
). In this way, much as Mango simplifies queries with selectors, this plugin does the same with what it calls JsonViews.
Install this plugin in your JavaScript project with NPM:
$ npm i pouchdb-jsonviews
Then attach it to PouchDB like so:
const PouchDB = require('pouchdb')
PouchDB.plugin(require('pouchdb-jsonviews'))
This plugin adds some new methods to PouchDB instances:
.interpretJsonView(jsonview)
.addView(ddocName, viewName, view)
.putView(ddocName, viewName, view)
.addJsonView(ddocName, viewName, jsonview)
.putJsonView(ddocName, viewName, jsonview)
-
.removeView(ddocName, viewName)
.
These common parameters are defined as such:
-
ddocName
: The part of a design document's_id
that follows_design/
. For example, the ddocName of_design/hello-world
ishello-world
. -
viewName
: The name of a view. -
view
: A normal JavaScript view. -
jsonview
: An object describing a JsonView, which this plugin can compile to a JavaScript view for the purpose of querying. See Usage, JsonViews for more information.
A convenience method that returns the JavaScript view compiled from a given jsonview. Used internally, but might be of interest to the curious user.
A convenience method that adds a view to a design document, creating it if necessary. If a view already exists by this name, the method will throw an error.
Returns as though you had run db.put()
.
A convenience method that adds a view to a design document, creating that document if necessary. If a view already exists by this name, the view will be overwritten.
Returns as though you had run db.put()
.
Compiles a JsonView to a JavaScript view and adds it to a design document, using db.addView
under the hood.
Compiles a JsonView to a JavaScript view and adds it to a design document, using db.putView
under the hood.
Removes a view from a design document.
Returns as though you had run db.put()
.
A JsonView is a JavaScript object with two properties:
-
map
: A description of the map function to use. -
map.key
: An access parameter or an array of access parameters, which are interpreted and emitted as a key or keys. -
map.value
: An access parameter, which is interpreted and emitted as a value.
An access parameter is a string or object that describes how to access and interpret a property on a document. In its simplest form, it is the string value of that property's key. The access parameter to access doc.foo
would be 'foo'
.
You can access nested properties using dots as separators. For example, you can access doc.foo.bar
with 'foo.bar'
.
An access parameter may also be an object. In its object form, it may have these properties:
-
access
: A string access parameter, such as'foo'
or'foo.bar'
. -
transform
: The string name of a transform helper, such asdate
ortime
. -
splay
: When used with an array, emits one row per entry in the array. Otherwise, the whole array is emitted in just one row. For example, for a news website, you might emit one row per tag per article, rather than emitting the whole list of tags in one row per article. -
flatten
: When used with an array,flatten: true
flattens the array into each row's keys rather than emitting the whole array as a single key. -
emit
: An access parameter withemit: true
will only emit a row if the document contains the accessed property. For example,{ access: 'some_field', emit: true }
will only emit rows if thesome_field
property exists and is truthy. Properties accessed this way are not emitted as part of the key.emit
does nothing when used to calculate a value. -
equals
: Used withemit: true
, this property allows you to specify a value to which the accessed property should be compared. For example,{ access: 'type', emit: true, equals: 'entry' }
will only emit rows if the propertytype
equals'entry'
. -
invert
: Used withemit: true
, this property inverts the accessed property so that rows are emitted only if it is falsy. Can be used withequals
to indicate that something should not be equal to a given value.
These are the currently available transforms:
-
date
: Converts a Unix timestamp or otherDate
initialization parameter(s) into an array of the form[year, month, day]
. -
year
: Converts a Unix timestamp or otherDate
initialization parameter(s) into a N-length string, representing the timestamp's year. -
month
: Converts a Unix timestamp or otherDate
initialization parameter(s) into a 2-length string, representing the timestamp's month in the year. -
year-month
: Converts a Unix timestamp or otherDate
initialization parameter(s) into an array of the form[year, month]
. -
day
: Converts a Unix timestamp or otherDate
initialization parameter(s) into a 2-length string, representing the timestamp's day in the month. -
time
: Converts a Unix timestamp or otherDate
initialization parameter(s) into an array of the form[hour, minute, second, millisecond]
. -
hour
: Converts a Unix timestamp or otherDate
initialization parameter(s) into a 2-length string, representing the timestamp's hour in 24-hour time. -
minute
: Converts a Unix timestamp or otherDate
initialization parameter(s) into a 2-length string, representing the timestamp's minute in a 60-minute hour. -
datetime
: Converts a Unix timestamp or otherDate
initialization parameter(s) into an array of the form[year, month, day, hour, minute, second, millisecond]
. -
words
: Split a text field on whitespace and quotations, into an array of strings. Useful withsplay
.
Note that because the datetime utilities use an ISO representation of dates internally, all times use UTC+0 as their timezone and all years are Gregorian.
If you encounter a bug or would like to request a feature, please file an issue!
If you are submitting a patch, please be a good neighbor and include tests!
To hack on this project locally, first get its source and install its dependencies:
$ git clone git@github.com:garbados/pouchdb-jsonviews.git
$ cd pouchdb-jsonviews
$ npm i
Then you can run the test suite:
$ npm test