Skip to content

Commit

Permalink
release 0.2.0, added paginate, sort mixed orders
Browse files Browse the repository at this point in the history
  • Loading branch information
colinskow committed May 26, 2016
1 parent 15c9b73 commit f8be4e7
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 64 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ For more information see `examples/tutorial.js`.

## API

LiveFind uses the exact same API as [`pouchdb-find`](https://github.com/nolanlawson/pouchdb-find) with the addition of the `aggregate` option. For details, see the [`pouchdb-find` documentation](https://github.com/nolanlawson/pouchdb-find) as well as [Cloudant Query](https://docs.cloudant.com/cloudant_query.html).
LiveFind uses the exact same API as [`pouchdb-find`](https://github.com/nolanlawson/pouchdb-find) with the addition of the `aggregate` option. Also LiveFind has none of the sort restrictions of PouchDB Find. You can sort by any list of fields, and even with mixed sort directions. (Just make sure the `fields` options doesn't remove fields needed to sort.) For details, see the [`pouchdb-find` documentation](https://github.com/nolanlawson/pouchdb-find) as well as [Cloudant Query](https://docs.cloudant.com/cloudant_query.html).

#### `db.liveFind(request)`

Expand All @@ -119,6 +119,7 @@ LiveFind uses the exact same API as [`pouchdb-find`](https://github.com/nolanlaw
* `liveFeed.then(...)` hooks into the `pouchdb-find` promise and resolves when the initial query is complete
* `liveFeed.catch(...)` hooks into the `pouchdb-find` promise and catches any errors with the initial query
* `liveFeed.sort(list)` a convenience function to sort any list in place by the `sort` order you provided. (This will mutate the Array.)
* `liveFeed.paginate(options)` updates the pagination and sorting of the aggregate list and immediately returns the updated list. Available options are `sort`, `skip`, and `limit`.

#### Events

Expand Down Expand Up @@ -155,4 +156,5 @@ liveFeed.on('update', function(update, aggregate) {

## Release History

* **0.2.0** (2016-05-25) - Added pagination and the ability to sort by any list of fields with mixed directions
* **0.1.0** (2016-05-19) - Initial Release
101 changes: 83 additions & 18 deletions dist/live-find.browser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ describe('PouchDB LiveFind', function() {

var previous;
var db = createDB('liveFindTest');
var feed1, feed2, feed3, feed4, feed5, feed6;
var watcher1, watcher2, watcher3, watcher4, watcher5, watcher6;
var feed1, feed2, feed3, feed4, feed5, feed6, feed7;
var watcher1, watcher2, watcher3, watcher4, watcher5, watcher6, watcher7;

before(function() {
previous = db.createIndex({
Expand Down Expand Up @@ -315,7 +315,7 @@ describe('PouchDB LiveFind', function() {
return watcher4.fetchListUpdates(2);
})
.then(function(aggregates) {
expect(aggregates).to.deep.equal([ ['mario'], ['dk', 'mario'] ]);
expect(aggregates[1]).to.deep.equal(['mario', 'dk']);
return Promise.all([
watcher4.fetchListUpdates(2),
db.bulkDocs(smashers2)
Expand All @@ -325,8 +325,8 @@ describe('PouchDB LiveFind', function() {
// Testing add to list
var aggregates = result[0];
expect(aggregates).to.deep.equal([
[ 'dk', 'luigi', 'mario' ],
[ 'dk', 'luigi', 'mario', 'yoshi' ] ]
[ 'mario', 'luigi', 'dk' ],
[ 'yoshi', 'mario', 'luigi', 'dk' ] ]
);
return db.get('luigi');
})
Expand All @@ -339,7 +339,7 @@ describe('PouchDB LiveFind', function() {
})
.then(function(result) {
var aggregates = result[0];
expect(aggregates).to.deep.equal([ [ 'dk', 'mario', 'yoshi' ] ]);
expect(aggregates).to.deep.equal([ [ 'yoshi', 'mario', 'dk' ] ]);
return db.get('yoshi');
})
.then(function(yoshi) {
Expand All @@ -352,7 +352,7 @@ describe('PouchDB LiveFind', function() {
})
.then(function(result) {
var aggregates = result[0];
expect(aggregates).to.deep.equal([ [ 'yoshi', 'dk', 'mario' ] ]);
expect(aggregates).to.deep.equal([ [ 'mario', 'dk', 'yoshi' ] ]);
feed4.cancel();
});
});
Expand Down Expand Up @@ -408,27 +408,88 @@ describe('PouchDB LiveFind', function() {
return watcher6.fetchListUpdates(6);
})
.then(function(aggregates) {
expect(aggregates).to.deep.equal([
[],
[],
[ 'link' ],
[ 'pikachu', 'link' ],
[ 'puff', 'pikachu', 'link' ],
[ 'mario', 'puff', 'pikachu' ] ]);
expect(aggregates[5]).to.deep.equal(['puff', 'mario', 'dk']);
});
});

it('should have a working custom sort function', function() {
return previous
.then(function() {
var sorted = feed6.sort(smashers1).map(function(item) {
return item._id;
});
expect(sorted).to.deep.equal([ 'falcon', 'dk', 'mario', 'puff', 'pikachu', 'link' ]);
var sorted = feed6.sort(smashers1).map(mapById);
expect(sorted).to.deep.equal([ 'link', 'pikachu', 'puff', 'mario', 'dk', 'falcon' ]);
});
});

it('paginate should set a different sort, skip and limit', function() {
return previous
.then(function() {
var result1 = feed6.paginate({
sort: [{series: 'desc'}, {name: 'desc'}],
skip: 0,
limit: 0
}).map(mapById);
expect(result1).to.deep.equal(['link', 'pikachu', 'puff', 'mario', 'dk', 'falcon']);
var result2 = feed6.paginate({
sort: [{series: 'asc'}, {name: 'asc'}],
skip: 0,
limit: 0
}).map(mapById);
expect(result2).to.deep.equal(['falcon', 'dk', 'mario', 'puff', 'pikachu', 'link']);
var result3 = feed6.paginate({
skip: 2,
limit: 3
}).map(mapById);
expect(result3).to.deep.equal(['mario', 'puff', 'pikachu']);
// Now we'll make sure that updates keep the new pagination
return Promise.all( [
watcher6.fetchListUpdates(1),
db.put({ name: 'Bulbasaur', _id: 'bulb', series: 'Pokemon', debut: 1996 })
]);
})
.then(function(results) {
var update = results[0][0];
expect(update).to.deep.equal(['mario', 'bulb', 'puff']);
feed6.cancel();
});
});

it('should sort fields with mixed directions', function() {
var sortDocs = [
{_id: 'apple', letter: 'a', word: 'Apple'},
{_id: 'axel', letter: 'a', word: 'Axel'},
{_id: 'abby', letter: 'a', word: 'Abby'},
{_id: 'bat', letter: 'b', word: 'Bat'},
{_id: 'bone', letter: 'b', word: 'Bone'},
{_id: 'better', letter: 'b', word: 'Better'}
];
return previous
.then(function() {
return db.destroy();
})
.then(function() {
db = createDB('liveFindTest');
return db.createIndex({
index: {fields: ['letter', 'word']}
});
})
.then(function() {
return db.bulkDocs(sortDocs);
})
.then(function() {
feed7 = db.liveFind({
selector: {letter: {$gt: null}},
sort: [{letter: 'desc'}, {word: 'asc'}],
aggregate: true
});
watcher7 = new UpdateWatcher(feed7);
return watcher7.fetchListUpdates(6);
})
.then(function(results) {
expect(results[5]).to.deep.equal(['bat', 'better', 'bone', 'abby', 'apple', 'axel']);
feed7.cancel();
});
});

it('should throw an error if pouchdb-find is not loaded', function() {
return previous
.then(function() {
Expand All @@ -452,6 +513,10 @@ function createDB(name) {
}
return new PouchDB(name, {adapter: 'memory'});
}

function mapById(item) {
return item._id;
}
},{"../lib/index":undefined,"./watcher":3,"chai":undefined,"memdown":undefined,"pouchdb":undefined,"pouchdb-find":undefined}],3:[function(require,module,exports){
'use strict';
var defer = require("promise-defer");
Expand Down
70 changes: 54 additions & 16 deletions dist/pouchdb.live-find.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var utils = require('pouchdb-find/lib/utils');
var localUtils = require('pouchdb-find/lib/adapters/local/utils');
var getUserFields = localUtils.getUserFields;
var getKey = localUtils.getKey;
var getValue = localUtils.getValue;
var parseField = localUtils.parseField;

exports.memoryFilter = function(docs, requestDef) {
Expand All @@ -30,15 +31,25 @@ exports.createFieldSorter = function(sort) {
});
}

var directions = sort.map(function(sorting) {
if (typeof sorting !== 'string' &&
getValue(sorting) === 'desc') {
return -1;
}
return 1;
});

return function (aRow, bRow) {
var aFieldValues = getFieldValuesAsArray(aRow);
var bFieldValues = getFieldValuesAsArray(bRow);
var collation = collate(aFieldValues, bFieldValues);
if (collation !== 0) {
return collation;
for(var i=0, len=directions.length; i<len; i++) {
var collation = collate(aFieldValues[i], bFieldValues[i]);
if (collation !== 0) {
return collation * directions[i];
}
}
// this is what mango seems to do
return utils.compare(aRow._id, bRow._id);
return utils.compare(aRow._id, bRow._id) * directions[0];
};
};

Expand All @@ -60,6 +71,7 @@ var helpers = require('./helpers');
var collate = require('pouchdb-collate').collate;
var utils = require('pouchdb-find/lib/utils');
var localUtils = require('pouchdb-find/lib/adapters/local/utils');
var getValue = localUtils.getValue;
var massageSelector = localUtils.massageSelector;
var massageSort = localUtils.massageSort;

Expand Down Expand Up @@ -95,23 +107,19 @@ function liveFind(requestDef) {
if(requestDef.selector) {
selector = massageSelector(requestDef.selector);
}
var sort;
var sort, sortFn;
if(requestDef.sort) {
sort = massageSort(requestDef.sort);
sortFn = helpers.createFieldSorter(sort);
}
var skip = requestDef.skip || 0;
var limit = requestDef.limit;
var skip = parseInt(requestDef.skip, 10) || 0;
var limit = parseInt(requestDef.limit, 10) || 0;
var findRequest = {
selector: selector,
sort: sort,
// sort: sort,
fields: fields
};

var sortFn;
if(sort) {
sortFn = helpers.createFieldSorter(sort);
}

var ready = db.find(findRequest)
.then(function(results) {
results.docs.forEach(function(doc) {
Expand Down Expand Up @@ -151,10 +159,10 @@ function liveFind(requestDef) {
if(!sort) {
return list;
}
return list.sort(sortFn);
return sortList(list);
};


emitter.paginate = paginate;

function changeHandler(change) {
ready.then(function() {
Expand Down Expand Up @@ -282,10 +290,23 @@ function liveFind(requestDef) {
emitter.emit('update', { action: 'UPDATE', id: id, rev: rev, doc: doc }, list);
}

/* function sortList(list) {
list = list.sort(sortFn);
if (typeof sort[0] !== 'string' &&
getValue(sort[0]) === 'desc') {
list = list.reverse();
}
return list;
} */

function sortList(list) {
return list.sort(sortFn);
}

// Applies sort, skip, and limit to a list
function formatList(list) {
if(sort) {
list = list.sort(sortFn);
list = sortList(list);
}
if(skip || limit) {
if(limit) {
Expand All @@ -297,6 +318,23 @@ function liveFind(requestDef) {
return list;
}

function paginate(options) {
if(!aggregate || !options || typeof options !== 'object') {
return;
}
if(options.skip != null) {
skip = parseInt(options.skip, 10) || 0;
}
if(options.limit != null) {
limit = parseInt(options.limit, 10) || 0;
}
if(options.sort && options.sort instanceof Array) {
sort = massageSort(options.sort);
sortFn = helpers.createFieldSorter(sort);
}
return formatList(docList);
}

return emitter;

}
Expand Down
4 changes: 2 additions & 2 deletions dist/pouchdb.live-find.min.js

Large diffs are not rendered by default.

29 changes: 22 additions & 7 deletions examples/tutorial.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ db.createIndex({
})

/* Outputs:
ADD mario
New List: [ 'Mario' ]
ADD dk
New List: [ 'Donkey Kong', 'Mario' ]
New List: [ 'Donkey Kong' ]
ADD mario
New List: [ 'Mario', 'Donkey Kong' ]
Initial query complete.
*/

Expand All @@ -80,7 +80,7 @@ db.createIndex({

/* Outputs:
ADD wario
New List: [ 'Donkey Kong', 'Mario', 'Wario' ]
New List: [ 'Wario', 'Mario', 'Donkey Kong' ]
*/

// Update a doc
Expand All @@ -94,7 +94,7 @@ db.createIndex({

/* Outputs:
UPDATE mario
New List: [ 'Baby Mario', 'Donkey Kong', 'Wario' ]
New List: [ 'Wario', 'Donkey Kong', 'Baby Mario' ]
*/

// When a doc no longer matches your query it is removed
Expand All @@ -105,6 +105,21 @@ db.createIndex({
return db.put(dk);
})

/* Outputs:
REMOVE dk
New List: [ 'Wario', 'Baby Mario' ]
*/

// You can use the paginate function to change the sort order, skip and limit on the fly
.then(function() {
setTimeout(function() {
var newList = liveFeed.paginate({
sort: [{name: 'asc'}]
});
refreshUI(newList);
}, 10);
})

/* Outputs:
REMOVE dk
New List: [ 'Baby Mario', 'Wario' ]
Expand All @@ -119,10 +134,10 @@ db.createIndex({

// When you are done listening to updates cancel the listener to save resources
.then(function() {
// setTimeout prevents the feed from cancelling before it is finished
// setTimeout prevents the feed from cancelling before it is finished processing updates
setTimeout(function() {
liveFeed.cancel();
});
}, 25);
});
// Outputs: "LiveFind cancelled."

Expand Down
Loading

0 comments on commit f8be4e7

Please sign in to comment.