UNPKG

pouchdb-find

Version:
1,382 lines (1,314 loc) 113 kB
/* global sum */ 'use strict'; var testUtils = require('../test-utils'); var should = testUtils.should; module.exports = function tests(dbName, dbType, viewType, PouchDB) { var suiteName = 'test.mapreduce.js-' + viewType; describe(suiteName, function () { var Promise; var createView; if (viewType === 'persisted') { createView = function (db, viewObj) { var storableViewObj = { map : viewObj.map.toString() }; if (viewObj.reduce) { storableViewObj.reduce = viewObj.reduce.toString(); } return new Promise(function (resolve, reject) { db.put({ _id: '_design/theViewDoc', views: { 'theView' : storableViewObj } }, function (err) { if (err) { reject(err); } else { resolve('theViewDoc/theView'); } }); }); }; } else { createView = function (db, viewObj) { return new Promise(function (resolve) { setTimeout(function () { resolve(viewObj); }); }); }; } beforeEach(function () { Promise = PouchDB.utils.Promise; return new PouchDB(dbName).destroy(); }); afterEach(function () { return new PouchDB(dbName).destroy(); }); it("Test basic view", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.foo, doc); } }).then(function (view) { return db.bulkDocs({docs: [ {foo: 'bar'}, { _id: 'volatile', foo: 'baz' } ]}).then(function () { return db.get('volatile'); }).then(function (doc) { return db.remove(doc); }).then(function () { return db.query(view, {include_docs: true, reduce: false}); }).then(function (res) { res.rows.should.have.length(1, 'Dont include deleted documents'); res.total_rows.should.equal(1, 'Include total_rows property.'); res.rows.forEach(function (x) { should.exist(x.id); should.exist(x.key); should.exist(x.value); should.exist(x.value._rev); should.exist(x.doc); should.exist(x.doc._rev); }); }); }); }); }); it("Test basic view, no emitted value", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.foo); } }).then(function (view) { return db.bulkDocs({docs: [ {foo: 'bar'}, { _id: 'volatile', foo: 'baz' } ]}).then(function () { return db.get('volatile'); }).then(function (doc) { return db.remove(doc); }).then(function () { return db.query(view, {include_docs: true, reduce: false}); }).then(function (res) { res.rows.should.have.length(1, 'Dont include deleted documents'); res.total_rows.should.equal(1, 'Include total_rows property.'); res.rows.forEach(function (x) { should.exist(x.id); should.exist(x.key); should.equal(x.value, null); should.exist(x.doc); should.exist(x.doc._rev); }); }); }); }); }); if (dbType === 'local' && viewType === 'temp') { it("with a closure", function () { return new PouchDB(dbName).then(function (db) { return db.bulkDocs({docs: [ {foo: 'bar'}, { _id: 'volatile', foo: 'baz' } ]}).then(function () { var queryFun = (function (test) { return function (doc, emit) { if (doc._id === test) { emit(doc.foo); } }; }('volatile')); return db.query(queryFun, {reduce: false}); }); }).should.become({ total_rows: 1, offset: 0, rows: [ { id: 'volatile', key: 'baz', value: null } ] }); }); } if (viewType === 'temp') { it('Test simultaneous temp views', function () { return new PouchDB(dbName).then(function (db) { return db.put({_id: '0', foo: 1, bar: 2, baz: 3}).then(function () { return Promise.all(['foo', 'bar', 'baz'].map(function (key, i) { var fun = 'function(doc){emit(doc.' + key + ');}'; return db.query({map: fun}).then(function (res) { res.rows.should.deep.equal([{ id: '0', key: i + 1, value: null }]); }); })); }); }); }); it("Test passing just a function", function () { return new PouchDB(dbName).then(function (db) { return db.bulkDocs({docs: [ {foo: 'bar'}, { _id: 'volatile', foo: 'baz' } ]}).then(function () { return db.get('volatile'); }).then(function (doc) { return db.remove(doc); }).then(function () { return db.query(function (doc) { emit(doc.foo, doc); }, {include_docs: true, reduce: false}); }).then(function (res) { res.rows.should.have.length(1, 'Dont include deleted documents'); res.rows.forEach(function (x) { should.exist(x.id); should.exist(x.key); should.exist(x.value); should.exist(x.value._rev); should.exist(x.doc); should.exist(x.doc._rev); }); }); }); }); } it("Test opts.startkey/opts.endkey", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.key, doc); } }).then(function (queryFun) { return db.bulkDocs({docs: [ {key: 'key1'}, {key: 'key2'}, {key: 'key3'}, {key: 'key4'}, {key: 'key5'} ]}).then(function () { return db.query(queryFun, {reduce: false, startkey: 'key2'}); }).then(function (res) { res.rows.should.have.length(4, 'Startkey is inclusive'); return db.query(queryFun, {reduce: false, endkey: 'key3'}); }).then(function (res) { res.rows.should.have.length(3, 'Endkey is inclusive'); return db.query(queryFun, { reduce: false, startkey: 'key2', endkey: 'key3' }); }).then(function (res) { res.rows.should.have.length(2, 'Startkey and endkey together'); return db.query(queryFun, { reduce: false, startkey: 'key4', endkey: 'key4' }); }).then(function (res) { res.rows.should.have.length(1, 'Startkey=endkey'); }); }); }); }); it.skip("#4154 opts.start_key/opts.end_key are synonyms", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.key, doc); } }).then(function (queryFun) { return db.bulkDocs({docs: [ {key: 'key1'}, {key: 'key2'}, {key: 'key3'}, {key: 'key4'}, {key: 'key5'} ]}).then(function () { return db.query(queryFun, {reduce: false, start_key: 'key2'}); }).then(function (res) { res.rows.should.have.length(4, 'Startkey is inclusive'); return db.query(queryFun, {reduce: false, end_key: 'key3'}); }).then(function (res) { res.rows.should.have.length(3, 'Endkey is inclusive'); return db.query(queryFun, { reduce: false, start_key: 'key2', end_key: 'key3' }); }).then(function (res) { res.rows.should.have.length(2, 'Startkey and endkey together'); return db.query(queryFun, { reduce: false, start_key: 'key4', end_key: 'key4' }); }).then(function (res) { res.rows.should.have.length(1, 'Startkey=endkey'); }); }); }); }); //TODO: split this to their own tests within a describe block it("Test opts.inclusive_end = false", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.key, doc); } }).then(function (queryFun) { return db.bulkDocs({docs: [ {key: 'key1'}, {key: 'key2'}, {key: 'key3'}, {key: 'key4'}, {key: 'key4'}, {key: 'key5'} ]}).then(function () { return db.query(queryFun, { reduce: false, endkey: 'key4', inclusive_end: false }); }).then(function (resp) { resp.rows.should.have.length(3, 'endkey=key4 without ' + 'inclusive end'); resp.rows[0].key.should.equal('key1'); resp.rows[2].key.should.equal('key3'); }) .then(function () { return db.query(queryFun, { reduce: false, startkey: 'key3', endkey: 'key4', inclusive_end: false }); }).then(function (resp) { resp.rows.should.have.length(1, 'startkey=key3, endkey=key4 ' + 'without inclusive end'); resp.rows[0].key.should.equal('key3'); }).then(function () { return db.query(queryFun, { reduce: false, startkey: 'key4', endkey: 'key1', descending: true, inclusive_end: false }); }).then(function (resp) { resp.rows.should .have.length(4, 'startkey=key4, endkey=key1 descending without ' + 'inclusive end'); resp.rows[0].key.should.equal('key4'); }); }); }); }); it("Test opts.key", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.key, doc); } }).then(function (queryFun) { return db.bulkDocs({docs: [ {key: 'key1'}, {key: 'key2'}, {key: 'key3'}, {key: 'key3'} ]}).then(function () { return db.query(queryFun, {reduce: false, key: 'key2'}); }).then(function (res) { res.rows.should.have.length(1, 'Doc with key'); return db.query(queryFun, {reduce: false, key: 'key3'}); }).then(function (res) { res.rows.should.have.length(2, 'Multiple docs with key'); }); }); }); }); it("Test basic view collation", function () { var values = []; // special values sort before all other types values.push(null); values.push(false); values.push(true); // then numbers values.push(1); values.push(2); values.push(3.0); values.push(4); // then text, case sensitive // currently chrome uses ascii ordering and so wont handle caps properly values.push("a"); //values.push("A"); values.push("aa"); values.push("b"); //values.push("B"); values.push("ba"); values.push("bb"); // then arrays. compared element by element until different. // Longer arrays sort after their prefixes values.push(["a"]); values.push(["b"]); values.push(["b", "c"]); values.push(["b", "c", "a"]); values.push(["b", "d"]); values.push(["b", "d", "e"]); // then object, compares each key value in the list until different. // larger objects sort after their subset objects. values.push({a: 1}); values.push({a: 2}); values.push({b: 1}); values.push({b: 2}); values.push({b: 2, a: 1}); // Member order does matter for collation. // CouchDB preserves member order // but doesn't require that clients will. // (this test might fail if used with a js engine // that doesn't preserve order) values.push({b: 2, c: 2}); return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.foo); } }).then(function (queryFun) { var docs = values.map(function (x, i) { return {_id: (i).toString(), foo: x}; }); return db.bulkDocs({docs: docs}).then(function () { return db.query(queryFun, {reduce: false}); }).then(function (res) { res.rows.forEach(function (x, i) { JSON.stringify(x.key).should.equal(JSON.stringify(values[i]), 'keys collate'); }); return db.query(queryFun, {descending: true, reduce: false}); }).then(function (res) { res.rows.forEach(function (x, i) { JSON.stringify(x.key).should.equal(JSON.stringify( values[values.length - 1 - i]), 'keys collate descending'); }); }); }); }); }); it("Test joins", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { if (doc.doc_id) { emit(doc._id, {_id: doc.doc_id}); } } }).then(function (queryFun) { return db.bulkDocs({docs: [ {_id: 'mydoc', foo: 'bar'}, { doc_id: 'mydoc' } ]}).then(function () { return db.query(queryFun, {include_docs: true, reduce: false}); }).then(function (res) { should.exist(res.rows[0].doc); return res.rows[0].doc._id; }); }).should.become('mydoc', 'mydoc included'); }); }); it("No reduce function", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function () { emit('key', 'val'); } }).then(function (queryFun) { return db.post({foo: 'bar'}).then(function () { return db.query(queryFun); }); }); }).should.be.fulfilled; }); it("Query after db.close", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.foo, 'val'); } }).then(function (queryFun) { return db.put({_id: 'doc', foo: 'bar'}).then(function () { return db.query(queryFun); }).then(function (res) { res.rows.should.deep.equal([ { id: 'doc', key: 'bar', value: 'val' } ]); return db.close(); }).then(function () { db = new PouchDB(dbName); return db.query(queryFun).then(function (res) { res.rows.should.deep.equal([ { id: 'doc', key: 'bar', value: 'val' } ]); }); }); }); }); }); it("Built in _sum reduce function", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.val, 1); }, reduce: "_sum" }).then(function (queryFun) { return db.bulkDocs({ docs: [ { val: 'bar' }, { val: 'bar' }, { val: 'baz' } ] }).then(function () { return db.query(queryFun, {reduce: true, group_level: 999}); }).then(function (resp) { return resp.rows.map(function (row) { return row.value; }); }); }); }).should.become([2, 1]); }); it("Built in _count reduce function", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.val, doc.val); }, reduce: "_count" }).then(function (queryFun) { return db.bulkDocs({ docs: [ { val: 'bar' }, { val: 'bar' }, { val: 'baz' } ] }).then(function () { return db.query(queryFun, {reduce: true, group_level: 999}); }).then(function (resp) { return resp.rows.map(function (row) { return row.value; }); }); }); }).should.become([2, 1]); }); it("Built in _stats reduce function", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: "function(doc){emit(doc.val, 1);}", reduce: "_stats" }).then(function (queryFun) { return db.bulkDocs({ docs: [ { val: 'bar' }, { val: 'bar' }, { val: 'baz' } ] }).then(function () { return db.query(queryFun, {reduce: true, group_level: 999}); }).then(function (res) { return res.rows[0].value; }); }); }).should.become({ sum: 2, count: 2, min: 1, max: 1, sumsqr: 2 }); }); it("Built in _stats reduce function should throw an error with a promise", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: "function(doc){emit(doc.val, 'lala');}", reduce: "_stats" }).then(function (queryFun) { return db.bulkDocs({ docs: [ { val: 'bar' }, { val: 'bar' }, { val: 'baz' } ] }).then(function () { return db.query(queryFun, {reduce: true, group_level: 999}); }); }); }).should.be.rejected; }); it("Built in _sum reduce function should throw an error with a promise", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: "function(doc){emit(null, doc.val);}", reduce: "_sum" }).then(function (queryFun) { return db.bulkDocs({ docs: [ { val: 1 }, { val: 2 }, { val: 'baz' } ] }).then(function () { return db.query(queryFun, {reduce: true, group: true}); }); }); }).should.be.rejected; }); it("Built in _sum reduce function with num arrays should throw an error", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: "function(doc){emit(null, doc.val);}", reduce: "_sum" }).then(function (queryFun) { return db.bulkDocs({ docs: [ { val: [1, 2, 3] }, { val: 2 }, { val: ['baz']} ] }).then(function () { return db.query(queryFun, {reduce: true, group: true}); }); }); }).should.be.rejected; }); it("Built in _sum can be used with lists of numbers", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: "function(doc){emit(null, doc.val);}", reduce: "_sum" }).then(function (queryFun) { return db.bulkDocs({ docs: [ { _id: '1', val: 2 }, { _id: '2', val: [1, 2, 3, 4] }, { _id: '3', val: [3, 4] }, { _id: '4', val: 1 } ] }).then(function () { return db.query(queryFun, {reduce: true, group: true}); }).then(function (res) { res.should.deep.equal({rows : [{ key : null, value : [7, 6, 3, 4] }]}); }); }); }); }); if (viewType === 'temp') { it("No reduce function, passing just a function", function () { return new PouchDB(dbName).then(function (db) { return db.post({foo: 'bar'}).then(function () { var queryFun = function () { emit('key', 'val'); }; return db.query(queryFun); }); }).should.be.fulfilled; }); } it('Query result should include _conflicts', function () { var db2name = 'test2b' + Math.random(); var cleanup = function () { return new PouchDB(db2name).destroy(); }; var doc1 = {_id: '1', foo: 'bar'}; var doc2 = {_id: '1', foo: 'baz'}; return testUtils.fin(new PouchDB(dbName).then(function (db) { return new PouchDB(db2name).then(function (remote) { var replicate = testUtils.promisify(db.replicate.from, db.replicate); return db.post(doc1).then(function () { return remote.post(doc2); }).then(function () { return replicate(remote); }).then(function () { return db.query(function (doc) { if (doc._conflicts) { emit(doc._conflicts, null); } }, {include_docs : true, conflicts: true}); }).then(function (res) { should.exist(res.rows[0].doc._conflicts); return db.get(res.rows[0].doc._id, {conflicts: true}); }).then(function (res) { should.exist(res._conflicts); }); }); }), cleanup); }); /* jshint maxlen:false */ var icons = [ "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" ]; /* jshint maxlen:100 */ var iconDigests = [ "md5-Mf8m9ehZnCXC717bPkqkCA==", "md5-fdEZBYtnvr+nozYVDzzxpA==", "md5-ImDARszfC+GA3Cv9TVW4HA==", "md5-hBsgoz3ujHM4ioa72btwow==", "md5-jDUyV6ySnTVANn2qq3332g==" ]; var iconLengths = [1047, 789, 967, 527, 1108]; it('#190 Query works with attachments=true', function () { var db = new PouchDB(dbName); var docs = []; for (var i = 0; i < 5; i++) { docs.push({ _id: i.toString(), _attachments: { 'foo.png': { data: icons[i], content_type: 'image/png' } } }); } return db.bulkDocs(docs).then(function () { return createView(db, { map: function (doc) { emit(doc._id); } }); }).then(function (queryFun) { return db.query(queryFun, { include_docs: true, attachments: true }).then(function (res) { var attachments = res.rows.map(function (row) { var doc = row.doc; delete doc._attachments['foo.png'].revpos; return doc._attachments; }); attachments.should.deep.equal(icons.map(function (icon, i) { return { "foo.png": { "content_type": "image/png", "data": icon, "digest": iconDigests[i] } }; }), 'works with attachments=true'); return db.query(queryFun, {include_docs: true}); }).then(function (res) { var attachments = res.rows.map(function (row) { var doc = row.doc; delete doc._attachments['foo.png'].revpos; return doc._attachments['foo.png']; }); attachments.should.deep.equal(icons.map(function (icon, i) { return { "content_type": "image/png", stub: true, "digest": iconDigests[i], length: iconLengths[i] }; }), 'works with attachments=false'); return db.query(queryFun, {attachments: true}); }).then(function (res) { res.rows.should.have.length(5); res.rows.forEach(function (row) { should.not.exist(row.doc, 'ignored if include_docs=false'); }); }); }); }); it('#2858 Query works with attachments=true, binary=true 1', function () { var db = new PouchDB(dbName); var docs = []; for (var i = 0; i < 5; i++) { docs.push({ _id: i.toString(), _attachments: { 'foo.png': { data: icons[i], content_type: 'image/png' } } }); } return db.bulkDocs(docs).then(function () { return createView(db, { map: function (doc) { emit(doc._id); } }); }).then(function (queryFun) { return db.query(queryFun, { include_docs: true, attachments: true, binary: true }).then(function (res) { res.rows.forEach(function (row) { var doc = row.doc; Object.keys(doc._attachments).forEach(function (attName) { var att = doc._attachments[attName]; should.not.exist(att.stub); att.data.should.not.be.a('string'); }); }); }); }); }); it('#2858 Query works with attachments=true, binary=true 2', function () { var db = new PouchDB(dbName); var docs = []; for (var i = 0; i < 5; i++) { docs.push({ _id: i.toString() }); } return db.bulkDocs(docs).then(function () { return createView(db, { map: function (doc) { emit(doc._id); } }); }).then(function (queryFun) { return db.query(queryFun, { include_docs: true, attachments: true, binary: true }).then(function (res) { res.rows.forEach(function (row) { var doc = row.doc; should.not.exist(doc._attachments); }); }); }); }); it('#242 conflicts at the root level', function () { var db = new PouchDB(dbName); return db.bulkDocs([ { foo: '1', _id: 'foo', _rev: '1-w', _revisions: {start: 1, ids: ['w']} } ], {new_edits: false}).then(function () { return createView(db, { map: function (doc) { emit(doc.foo); } }).then(function (queryFun) { return db.query(queryFun).then(function (res) { res.rows[0].key.should.equal('1'); return db.bulkDocs([ { foo: '2', _id: 'foo', _rev: '1-x', _revisions: {start: 1, ids: ['x']} } ], {new_edits: false}).then(function () { return db.query(queryFun); }).then(function (res) { res.rows[0].key.should.equal('2'); return db.bulkDocs([ { foo: '3', _id: 'foo', _rev: '1-y', _deleted: true, _revisions: {start: 1, ids: ['y']} } ], {new_edits: false}); }).then(function () { return db.query(queryFun); }).then(function (res) { res.rows[0].key.should.equal('2'); }); }); }); }); }); it('#242 conflicts at the root+1 level', function () { var db = new PouchDB(dbName); return db.bulkDocs([ { foo: '2', _id: 'foo', _rev: '1-x', _revisions: {start: 1, ids: ['x']} }, { foo: '3', _id: 'foo', _rev: '2-y', _deleted: true, _revisions: {start: 2, ids: ['y', 'x']} } ], {new_edits: false}).then(function () { return createView(db, { map: function (doc) { emit(doc.foo); } }).then(function (queryFun) { return db.query(queryFun).then(function (res) { res.rows.length.should.equal(0); return db.bulkDocs([ { foo: '1', _id: 'foo', _rev: '1-w', _revisions: {start: 1, ids: ['w']} } ], {new_edits: false}).then(function () { return db.query(queryFun); }).then(function (res) { res.rows[0].key.should.equal('1'); return db.bulkDocs([ { foo: '4', _id: 'foo', _rev: '1-z', _revisions: {start: 1, ids: ['z']} } ], {new_edits: false}); }).then(function () { return db.query(queryFun); }).then(function (res) { res.rows[0].key.should.equal('4'); }); }); }); }); }); it('Views should include _conflicts', function () { var db2name = 'test2' + Math.random(); var cleanup = function () { return new PouchDB(db2name).destroy(); }; var doc1 = {_id: '1', foo: 'bar'}; var doc2 = {_id: '1', foo: 'baz'}; return testUtils.fin(new PouchDB(dbName).then(function (db) { return new PouchDB(db2name).then(function (remote) { return createView(db, { map : function (doc) { emit(doc._id, !!doc._conflicts); } }).then(function (queryFun) { var replicate = testUtils.promisify(db.replicate.from, db.replicate); return db.post(doc1).then(function () { return remote.post(doc2); }).then(function () { return replicate(remote); }).then(function () { return db.get(doc1._id, {conflicts: true}); }).then(function (res) { should.exist(res._conflicts); return db.query(queryFun); }).then(function (res) { res.rows[0].value.should.equal(true); }); }); }); }), cleanup); }); it("Test view querying with limit option", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map : function (doc) { if (doc.foo === 'bar') { emit(doc.foo); } } }).then(function (queryFun) { return db.bulkDocs({ docs: [ { foo: 'bar' }, { foo: 'bar' }, { foo: 'baz' } ] }).then(function () { return db.query(queryFun, { limit: 1 }); }).then(function (res) { res.total_rows.should.equal(2, 'Correctly returns total rows'); res.rows.should.have.length(1, 'Correctly limits returned rows'); }); }); }); }); it("Test view querying with group_level option and reduce", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.foo); }, reduce: '_count' }).then(function (queryFun) { return db.bulkDocs({ docs: [ { foo: ['foo', 'bar'] }, { foo: ['foo', 'bar'] }, { foo: ['foo', 'bar', 'baz'] }, { foo: ['baz'] }, { foo: ['baz', 'bar'] } ] }).then(function () { return db.query(queryFun, { group_level: 1, reduce: true}); }).then(function (res) { res.rows.should.have.length(2, 'Correctly group returned rows'); res.rows[0].key.should.deep.equal(['baz']); res.rows[0].value.should.equal(2); res.rows[1].key.should.deep.equal(['foo']); res.rows[1].value.should.equal(3); return db.query(queryFun, { group_level: 999, reduce: true}); }).then(function (res) { res.rows.should.have.length(4, 'Correctly group returned rows'); res.rows[2].key.should.deep.equal(['foo', 'bar']); res.rows[2].value.should.equal(2); return db.query(queryFun, { group_level: 0, reduce: true}); }).then(function (res) { res.rows.should.have.length(1, 'Correctly group returned rows'); res.rows[0].value.should.equal(5); }); }); }); }); it("Test view querying with invalid group_level options", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.foo); }, reduce: '_count' }).then(function (queryFun) { return db.query(queryFun, { group_level: -1, reduce: true }).then(function (res) { res.should.not.exist('expected error on invalid group_level'); }).catch(function (err) { err.status.should.equal(400); err.message.should.be.a('string'); return db.query(queryFun, { group_level: 'exact', reduce: true}); }).then(function (res) { res.should.not.exist('expected error on invalid group_level'); }).catch(function (err) { err.status.should.equal(400); err.message.should.be.a('string'); }); }); }); }); it("Test view querying with limit option and reduce", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.foo); }, reduce: '_count' }).then(function (queryFun) { return db.bulkDocs({ docs: [ { foo: 'bar' }, { foo: 'bar' }, { foo: 'baz' } ] }).then(function () { return db.query(queryFun, { limit: 1, group: true, reduce: true}); }).then(function (res) { res.rows.should.have.length(1, 'Correctly limits returned rows'); res.rows[0].key.should.equal('bar'); res.rows[0].value.should.equal(2); }); }); }); }); it('Test unsafe object usage (#244)', function () { var db = new PouchDB(dbName); return db.bulkDocs([ {_id: 'constructor'} ]).then(function (res) { var rev = res[0].rev; return createView(db, { map: function (doc) { emit(doc._id); } }).then(function (queryFun) { return db.query(queryFun, {include_docs: true}).then(function (res) { res.rows.should.deep.equal([ { "key": "constructor", "id": "constructor", "value": null, "doc": { "_id": "constructor", "_rev": rev } } ]); return db.bulkDocs([ {_id: 'constructor', _rev: rev} ]); }).then(function (res) { rev = res[0].rev; return db.query(queryFun, {include_docs: true}); }).then(function (res) { res.rows.should.deep.equal([ { "key": "constructor", "id": "constructor", "value": null, "doc": { "_id": "constructor", "_rev": rev } } ]); return db.bulkDocs([ {_id: 'constructor', _rev: rev, _deleted: true} ]); }).then(function (res) { rev = res[0].rev; return db.query(queryFun, {include_docs: true}); }).then(function (res) { res.rows.should.deep.equal([]); }); }); }); }); it("Test view querying with a skip option and reduce", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.foo); }, reduce: '_count' }).then(function (queryFun) { return db.bulkDocs({ docs: [ { foo: 'bar' }, { foo: 'bar' }, { foo: 'baz' } ] }).then(function () { return db.query(queryFun, {skip: 1, group: true, reduce: true}); }); }).then(function (res) { res.rows.should.have.length(1, 'Correctly limits returned rows'); res.rows[0].key.should.equal('baz'); res.rows[0].value.should.equal(1); }); }); }); it("Special document member _doc_id_rev should never leak outside", function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map : function (doc) { if (doc.foo === 'bar') { emit(doc.foo); } } }).then(function (queryFun) { return db.bulkDocs({ docs: [ { foo: 'bar' } ] }).then(function () { return db.query(queryFun, { include_docs: true }); }).then(function (res) { should.not.exist(res.rows[0].doc._doc_id_rev, '_doc_id_rev is leaking but should not'); }); }); }); }); it('multiple view creations and cleanups', function () { return new PouchDB(dbName).then(function (db) { var map = function (doc) { emit(doc.num); }; function createView(name) { var storableViewObj = { map: map.toString() }; return db.put({ _id: '_design/' + name, views: { theView: storableViewObj } }); } return db.bulkDocs({ docs: [ {_id: 'test1'} ] }).then(function () { function sequence(name) { return createView(name).then(function () { return db.query(name + '/theView').then(function () { return db.viewCleanup(); }); }); } var attempts = []; var numAttempts = 10; for (var i = 0; i < numAttempts; i++) { attempts.push(sequence('test' + i)); } return Promise.all(attempts).then(function () { var keys = []; for (var i = 0; i < numAttempts; i++) { keys.push('_design/test' + i); } return db.allDocs({keys : keys, include_docs : true}); }).then(function (res) { var docs = res.rows.map(function (row) { row.doc._deleted = true; return row.doc; }); return db.bulkDocs({docs : docs}); }).then(function () { return db.viewCleanup(); }).then(function (res) { res.ok.should.equal(true); }); }); }); }); it('If reduce function returns 0, resulting value should not be null', function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.foo); }, reduce: function () { return 0; } }).then(function (queryFun) { return db.bulkDocs({ docs: [ { foo: 'bar' } ] }).then(function () { return db.query(queryFun).then(function (data) { should.exist(data.rows[0].value); }); }); }); }); }); it('Testing skip with a view', function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map : function (doc) { emit(doc.foo); } }).then(function (queryFun) { return db.bulkDocs({ docs: [ { foo: 'bar' }, { foo: 'baz' }, { foo: 'baf' } ] }).then(function () { return db.query(queryFun, {skip: 1}); }).then(function (data) { data.rows.should.have.length(2); data.offset.should.equal(1); data.total_rows.should.equal(3); }); }); }); }); it('Map documents on 0/null/undefined/empty string', function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map : function (doc) { emit(doc.num); } }).then(function (mapFunction) { var docs = [ {_id: '0', num: 0}, {_id: '1', num: 1}, {_id: 'undef' /* num is undefined */}, {_id: 'null', num: null}, {_id: 'empty', num: ''}, {_id: 'nan', num: NaN}, {_id: 'inf', num: Infinity}, {_id: 'neginf', num: -Infinity} ]; return db.bulkDocs({docs: docs}).then(function () { return db.query(mapFunction, {key: 0}); }).then(function (data) { data.rows.should.have.length(1); data.rows[0].id.should.equal('0'); return db.query(mapFunction, {key: ''}); }).then(function (data) { data.rows.should.have.length(1); data.rows[0].id.should.equal('empty'); return db.query(mapFunction, {key: undefined}); }).then(function (data) { data.rows.should.have.length(8); // everything // keys that should all resolve to null var emptyKeys = [null, NaN, Infinity, -Infinity]; return Promise.all(emptyKeys.map(function (emptyKey) { return db.query(mapFunction, {key: emptyKey}).then(function (data) { data.rows.map(function (row) { return row.id; }).should.deep.equal(['inf', 'nan', 'neginf', 'null', 'undef']); }); })); }); }); }); }); it('Testing query with keys', function () { return new PouchDB(dbName).then(function (db) { return createView(db, { map: function (doc) { emit(doc.field); } }).then(function (queryFun) { var opts = {include_docs: true}; return db.bulkDocs({ docs: [ {_id: 'doc_0', field: 0}, {_id: 'doc_1', field: 1}, {_id: 'doc_2', field: 2}, {_id: 'doc_empty', field: ''}, {_id: 'doc_null', field: null}, {_id: 'doc_undefined' /* field undefined */}, {_id: 'doc_foo', field: 'foo'} ] }).then(function () { return db.query(queryFun, opts); }).then(function (data) { data.rows.should.have.length(7, 'returns all docs'); opts.keys = []; return db.query(queryFun, opts); }).then(function (data) { data.rows.should.hav