• Stars
    star
    202
  • Rank 186,691 (Top 4 %)
  • Language
    JavaScript
  • Created over 10 years ago
  • Updated over 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Node.js app for slicing and dicing ~100,000 models in <5ms with easy sorting, paginating, and filtering.

Snapshot.js

Node.js app for slicing and dicing paginated chunks of data with easy sorting and filtering.

Travis CI

ย 

NPM Version

Demo: http://snapshot.wildhoney.io/

Installing via npm

Snapshot is added to the npm registry, and can therefore be downloaded with npm install node-snapshot.

Dependencies

All dependencies can be installed with bower install and npm install, however the list is as follows:

Quick Start

Once a WebSocket connection has been successfully established, you're able to bootstrap Snapshot, passing in the WebSocket (Socket.IO) reference as a dependency.

var $snapshot = new Snapshot().bootstrap(socket).useDelta(false);

You then need to tell Snapshot which collection it's going to be creating snapshots of. You can pass in an optional string for the primaryKey โ€“ if you omit the primaryKey then the first key of the first model is used.

$snapshot.setCollection(collection, primaryKey);

(primaryKey is an optional parameter.)

Snapshot listens for three events natively, and these are handled automatically.

  • snapshot/:namespace/perPage โ€“ Set amount per page;
  • snapshot/:namespace/pageNumber โ€“ Set current page number;
  • snapshot/:namespace/sortBy โ€“ Set order column and ascending/descending;

As Snapshot supports multiple instances, a namespace is used to distinguish the events. If you don't explicitly specify a namespace in the instantiation then it will be default. Therefore all of your events will be: snapshot/default/perPage, snapshot/default/pageNumber and snapshot/default/sortBy.

(:namespace is the name you provided upon instantiation of Snapshot โ€“ if you didn't, then it's default.)

When the collection has been updated, Snapshot emits the snapshot/:namespace/contentUpdated event, passing through the snapshot as the first argument, and statistics relating to the request as the second argument.

For sorting by any given column, you can emit the snapshot/:namespace/sortBy event passing in the key and direction (ascending/descending). If you omit the direction property (or set its value to false) then Snapshot will cleverly invert the current sorting direction for you.

socket.emit('snapshot/:namespace/sortBy', property, 'descending');

Filtering

In addition to sorting and limiting, Snapshot also allows for the filtering of the collection. For this you can use the applyFilter method. Unfortunately you will need to read Crossfilter's API Reference before you begin filtering โ€“ or for simple filtering you can use Snapshot's primitive bundled filters.

socket.emit('filterByWord', text);

You can apply a filter however you like. It doesn't necessarily need to be applied via WebSockets, you could just as well use vanilla Node.js or Express.js. In our example though, we emit the filterByWord event to the Node.js server, and then we need to listen for that event.

socket.on('filterByWord', function(text) {

    $snapshot.applyFilter('word', function(dimension) {

        dimension.filterFunction(function(d) {
            var regExp = new RegExp(text, 'i');
            return d.match(regExp);
        });

    });

});

You essentially invoke the applyFilter on the snapshot object. Snapshot will pass in the dimension argument to your lambda function โ€“ this context is preserved. It's then entirely up to you to apply that dimension to the collection.

If you would like to clear a specific dimension, then you can use the clearFilter method โ€“ which takes the property name as its one and only argument.

$snapshot.clearFilter('word');

You can also clear every single filter by using the clearFilters method.

$snapshot.clearFilters();

Sometimes you may wish to clear certain filters before applying other filters, in which case you should utilise the composite filters approach by passing through the dimensions you wish to clear as well. You can then invoke the filterAll method on the dimension(s) you wish to clear.

$snapshot.applyFilter(['latitude', 'longitude'], function(latitudeDimension, longitudeDimension) {

    // Clear the latitude dimension.
    latitudeDimension.filterAll();

    // But then apply the longitude dimension.
    longitudeDimension.filterFunction(function(d) {
        return (d > 0);
    });

}, 'reduce');

Composite Filters

Before the release of 0.2.7 you could only apply filters one-at-a-time, which meant if you grouped filters together then the contentUpdated event would be emitted for each filter.

However, 0.2.7 introduced the concept of composite filters, which are nothing more than groups of filters which can be applied together, and have the event emitted once all filters have been applied. In order to create a composite filter, simply pass in an array of properties to filter on to the applyFilter method โ€“ the callback in the second argument will be the dimension of each property.

$snapshot.applyFilter(['latitude', 'longitude'], function(latitudeDimension, longitudeDimension) {

    // Filter to ensure the latitude is above 50, and longitude above 0.

    latitudeDimension.filterFunction(function(d) {
        return (d > 50);
    });

    longitudeDimension.filterFunction(function(d) {
        return (d > 0);
    });

    // Event will now be fired to reflect the current collection state.

}, 'reduce');

Bundled Filters

In light of Crossfilter's learning curve, Snapshot ships with a handful of bundled filters for common filtering techniques. These can all be invoked by emitting an event with a corresponding value.

  • snapshot/:namespace/fuzzyFilter {String}
  • snapshot/:namespace/exactFilter {String}
  • snapshot/:namespace/rangeFilter {Array}
  • snapshot/:namespace/regExpFilter {String} {String}
  • snapshot/:namespace/inArrayFilter {String} {Array}
  • snapshot/:namespace/notInArrayFilter {String} {Array}
  • snapshot/:namespace/clearFilter {String}
socket.emit('snapshot/default/fuzzyFilter', 'word', 'abc');
socket.emit('snapshot/default/exactFilter', 'word', 'kittens');
socket.emit('snapshot/default/rangeFilter', 'word', [12, Infinity]);
socket.emit('snapshot/default/regExpFilter', 'word', '[a-z0-9]+', 'ig');
socket.emit('snapshot/default/inArrayFilter', 'word', ['firstWord', 'secondWord']);
socket.emit('snapshot/default/notInArrayFilter', 'word', ['thirdWord']);
socket.emit('snapshot/default/clearFilter', 'word');

Each bundled filter expects the event name (snapshot/default/fuzzyFilter), the key (word), and value (abc).

Filtering Types

By default when you apply a filter, the previous filter will be cleared which is mostly likely the behaviour you're looking for. However, what if the user clicks red, and then clicks blue? Wouldn't it be nice if we could filter by both red and blue? In that case you're looking for the third argument of the applyFilter method.

$snapshot.applyFilter('word', ..., 'reduce');
  • afresh โ€“ filtering cleared before each filter;
  • reduce โ€“ filtering applied on current collection;

Ranges

Since the browser does not download every model into the browser, it's impossible to determine what the minimum/maximum for any given key is without loading all models. Snapshot therefore allows you to specify which columns you wish to generate minimum/maximum ranges for.

Please be careful with these as too many may noticeably slow down your Snapshots.

The following would specify that you wish to retrieve the ranges for the id property on every content change.

$snapshot.setRanges(['id']);

When the content changes you can access the range with stats.ranges.id.min and stats.ranges.id.max.

Multiple Instances

When instantiating Snapshot you should pass in the namespace for the current collection โ€“ that way you could create a new instance of Snapshot with a unique collection of models.

var $dogs       = new Snapshot('dogs').bootstrap(socket).useDelta(false);
var $cats       = new Snapshot('cats').bootstrap(socket).useDelta(false);
var $rabbits    = new Snapshot('rabbits').bootstrap(socket).useDelta(false);

In the above example Snapshot will have 9 events to listen to (3 events * 3 snapshots):

  • snapshot/dogs/perPage, snapshot/dogs/pageNumber, snapshot/dogs/sortBy
  • snapshot/cats/perPage, snapshot/cats/pageNumber, snapshot/cats/sortBy
  • snapshot/rabbits/perPage, snapshot/rabbits/pageNumber, snapshot/rabbits/sortBy

And it will emit 3 events:

  • snapshot/dogs/contentUpdated
  • snapshot/cats/contentUpdated
  • snapshot/rabbits/contentUpdated

If you don't create a namespace then the namespace will be set to default.

Delta Updates

There may be instances where sending delta updates is preferable to re-sending whole models. Snapshot supports the providing of delta updates โ€“ essentially, any models that have already been transmitted across the wire will not be sent again in their entirety; instead only their primary ID is sent.

var $snapshot = new Snapshot().bootstrap(socket).useDelta(true);

Once you've enabled delta updates using useDelta(true) as part of the bootstrap process, Snapshot will keep a history of transmitted models. It's crucial that you set the appropriate primary ID when invoking setCollection, otherwise a default primary key will be assumed.

$snapshot.setCollection([{ id: 1 }, { id: 2 }, { id: 3 }], 'id');

Note: You can suppress the initial event when invoking setCollection by passing true as the third argument.

Since unique models will only ever be transmitted once, it's imperative that you keep a history of all models from the snapshot/:namespace/contentUpdated event, and then to utilise those from your local cache when you come across a delta model.

Delta models are nothing more than the primary key of the model, which will help you lookup the model from your own collection cache. Therefore to detect a delta model, simply use something like Number.isFinite (or Underscore's _.isNumber) on the returned collection.

socket.on('snapshot/:namespace/contentUpdated', function(models, stats) {

    _.forEach(models, function(model) {

        if (_.isNumber(model)) {
            // Delta model!
            return;
        }

        // ...

    });

});

Example

Snapshot comes bundled with an example to get you started.

  • Navigate to example/server and run node default.js;
  • Open example/client/index.html in your browser;

Architecture

Below is a simple diagram of how Snapshot works. It demonstrates how the snapshot/:namespace/pageNumber event operates โ€“ which is also the same way other native Snapshot events function. It also demonstrates the flow of custom filters.

Snapshot Architecture

  • Browser establishes a WebSocket connection to Node.js โ€“ models are added;
  • Browser emits snapshot/:namespace/pageNumber event with data (example);
  • Snapshot along with Crossfilter updates the collection snapshot;
  • Snapshot emits snapshot/:namespace/contentUpdated event with the updated collection;
  • Browser emits a custom event (customFilterApplied) with the data;
  • Node.js listens for the customFilterApplied event and then interacts with Snapshot;
  • Snapshot emits the snapshot/:namespace/contentUpdated event with the updated filter applied;

Testing

Unit Testing

Grunt is a prerequisite to run the Mocha tests, which is installed when you run npm install. Afterwards all of Snapshot's unit tests can be run with the grunt test command from the terminal.

Cucumber

Snapshot also comes bundled with a handful of Cucumber tests.

  • cd tests/cucumber
  • bundle install
  • bundle exec cucumber

Philosophy

Loading a large collection of models into the browser is slow and unnecessary, instead Snapshot uses WebSockets to serve snapshots of those models to the browser when requested. It retains the state of the models, and so if a filter is changed, or the page number incremented, it will modify the snapshot only for that client.

Snapshot is also tremendously fast because of its use of Socket.io and Crossfilter. Snapshot listens for events to change the state of the collection of models, and fires another event to let the client know the snapshot was updated. Crossfilter allows Snapshot to quickly slice and dice models โ€“ in the example, slicing and dicing takes 0-1 milliseconds for 1,000 models.

Since Snapshot uses Node.js, the browser support is that of Socket.io, which essentially means Snapshot supports Internet Explorer 5.5+.

Example

  • Browser connects to Snapshot on Node.js server;
  • Snapshot emits snapshot/default/contentUpdated with first page's 50 models;
  • Browser increments the page number;
  • Snapshot emits snapshot/default/contentUpdated with second page's 50 models;
  • Browser applies filter to select only red items;
  • Snapshot emits snapshot/default/contentUpdated to supply second page's red models;
  • Browser sorts the models by their colour;
  • Snapshot emits snapshot/default/contentUpdated to supply second page's red models ordered globally by colour;

Caching

You may wish to cache the collection loaded into Snapshot โ€“ for this we recommend something like RedisCache.

More Repositories

1

ReactShadow

๐Ÿ”ฐ Utilise Shadow DOM in React with all the benefits of style encapsulation.
JavaScript
1,262
star
2

Leaflet.FreeDraw

๐ŸŒ FreeDraw allows the free-hand drawing of shapes on your Leaflet.js map layer โ€“ providing an intuitive and familiar UX for creating geospatial boundaries similar to Zoopla and others. Included out-of-the-box is the concaving of polygons, polygon merging and simplifying, as well as the ability to add edges and modify existing shapes.
JavaScript
526
star
3

Maple.js

Maple.js is a React webcomponents based framework mixing ES6 with Custom Elements, HTML Imports and Shadow DOM. It has in-built support for SASS and JSX, including a Gulp task for vulcanizing your project.
JavaScript
431
star
4

Magento-on-Angular

Angular.js application using Magento as the backend API
PHP
361
star
5

Legofy

Legofy your images with retina support using SVG.
JavaScript
299
star
6

ngDroplet

Angular.js HTML5 file uploading with drag & drop and image/file preview.
JavaScript
286
star
7

Switzerland

๐Ÿ‡จ๐Ÿ‡ญSwitzerland takes a functional approach to Web Components by applying middleware to your components. Supports Redux, attribute mutations, CSS variables, React-esque setState/state, etcโ€ฆ out-of-the-box, along with Shadow DOM for style encapsulation and Custom Elements for interoperability.
TypeScript
270
star
8

ngVideo

Modularised ~13KB HTML5 audio/video implementation using Angular.js
JavaScript
227
star
9

Keo

Plain functions for a more functional Deku approach to creating stateless React components, with functional goodies such as compose, memoize, etc... for free.
JavaScript
227
star
10

Standalone

Create framework agnostic components that are truly reusable and interoperable with all the benefits of the React ecosystem โ€“ using the HTML5 custom elements API to extend HTML's vocabulary.
JavaScript
206
star
11

EmberDroplet

Ember.js HTML5 file uploading with drag & drop and image/file preview.
JavaScript
198
star
12

EmberSockets

Socket.io (WebSockets) integrated with Ember.js' observer pattern.
JavaScript
136
star
13

Dory

Dory is a responsive, universal, GitHub collaborated blogging platform built on React and powered by Express. By combining awesome features such as automatic RSS generation, HTML5 offline support, push notifications, with a powerful development environment using hot reloading, SASS and Markdown, Dory allows developers to quickly dive into the depths of blogging.
JavaScript
128
star
14

Interpose

Apply stylesheet variables to your React components for use in your stylesheets. Interpose reduces the clutter of React components by bridging the gap between JS and CSS without resorting to complicating your components with CSS logic.
JavaScript
125
star
15

Mocktail

๐Ÿน Mock all of your ES6 module components with Mocktail using dependency injection.
JavaScript
103
star
16

ngCrossfilter

Usual Angular.js style filtering and sorting with a twist of Crossfilter for improved performance.
JavaScript
87
star
17

ngContextMenu

Handcraft your very own context menus for a richer UX!
JavaScript
80
star
18

gulp-processhtml

Process html files at build time to modify them depending on the release environment
JavaScript
77
star
19

L.Pather

Branching from Leaflet.FreeDraw, L.Pather is a freehand polyline creator that simplifies the polyline for mutability.
JavaScript
69
star
20

Freelancer

๐Ÿ‘” An implementation of on-the-fly defined WebWorkers that are created inline using data URIs, rather than separate physical files โ€” for the benefit of all humanity.
JavaScript
66
star
21

Amelie

HTML5 audio visualiser experiment using D3 with a curious Amelie theme.
JavaScript
57
star
22

ngRangeSlider

Multi-handle range slider utilising the native HTML5 input range elements.
JavaScript
54
star
23

EmberCrossfilter

Instead of using Ember DataStore, EmberCrossfilter provides a basic architecture for creating Ember models with Crossfilter; which allows for much quicker sorting and filtering.
JavaScript
48
star
24

DetectFont

Detect which font your system has cherry-picked from font-family.
JavaScript
40
star
25

CloudConvert

Easy-to-use Node.js implementation of the CloudConvert API.
JavaScript
38
star
26

Redecorate

Simple module for reducing immutable nested properties in Redux applications.
JavaScript
27
star
27

ngPourOver

PourOver wrapper for Angular.js with super-quick filtering and sorting.
JavaScript
27
star
28

ReactDelayed

Small component for delaying the mounting and unmounting of a child component for CSS animation purposes.
JavaScript
21
star
29

ngImgur

Take your favourite cat picture, and upload it to Imgur.com w/ Angular!
JavaScript
17
star
30

RedisCache

Simple Node.js based Redis cache for storing large collections of data.
JavaScript
16
star
31

ngRoundabout

Three-dimensional HTML5 carousel implemented in Angular.js.
JavaScript
14
star
32

ReactCrossfilter

Crossfilter.js implemented as a mixin for ultra-fast filtering and sorting techniques baked into React.js components.
JavaScript
13
star
33

ngDonut

Lightweight and extensible Angular implementation of the D3 donut graph.
JavaScript
11
star
34

Readlint

๐Ÿ“™Lint all of the code examples in your README documentation using shared configs.
JavaScript
10
star
35

ngObelisk

Easily use Obelisk to create animations using Angular.js.
JavaScript
10
star
36

ngTeleport

Move a section of the DOM and inherit the scope of the target node.
JavaScript
9
star
37

Webmonkey

๐Ÿ™Š Robust and versatile headless monkey testing for the modern web with reproducible steps, error alerts, strategy sharing and many other good things.
JavaScript
9
star
38

Formv

๐Ÿ—ณReact form validation using the validation native to all recent browsers. Also includes support for handling API validation messages, success messages, memoized and nested form state, and super easy styling.
JavaScript
9
star
39

Taskfile

๐Ÿ“ฆ Yet another attempt at a simple task runner for npm with parallelisation support using bash commands via YAML.
JavaScript
8
star
40

EmberRickshaw

Rickshaw faรงade for Ember which allows automatic redrawing of graphs using Ember's observer pattern.
JavaScript
7
star
41

Memoria

Extensible form storage for memorising user inputs with local storage. Never lose your form data ever again!
JavaScript
7
star
42

EarthApp

Three.js w/ Angular implementation of planet earth using SketchUp for the models.
JavaScript
7
star
43

Koi

Koi is a time aware interactive bird written in THREE.js with voice recognition, and an artificial IQ.
JavaScript
6
star
44

Pellucid

Experimental module using HTML5's Custom Elements that creates a crystalline blurred background.
JavaScript
6
star
45

Catwalk

Intuitive and fast relational CRUD interface for modelling relationships using vanilla objects written in ES6.
JavaScript
6
star
46

Mapleify

Mapleify is a build tool for Maple.js; it uses Polymer's vulcanize to process HTML imports with a little twist of Maple specific logic for HTML Import paths, and the processing of SASS/JSX documents.
JavaScript
6
star
47

Bi-cycle

Bi-cycle assists in making infinite carousels and sliders by handling the index logic for you.
JavaScript
5
star
48

Openroulette

Openroulette is a Chatroulette implementation built using the WebRTC component.
JavaScript
4
star
49

Vik

CLI Semver autoincrement with major, minor, and patch.
JavaScript
4
star
50

Draught

Drawing tool in ES6 for creating diagrams using D3 with an extensible event driven architecture.
JavaScript
4
star
51

Maducer

An experimental map-reduce concurrency over web workers using shared array buffer for handling large datasets.
JavaScript
3
star
52

OrderlyQueue

Implementation of a promise-based FIFO queuing system using ES2017 async generators.
JavaScript
3
star
53

AnsibleAlexa

Development deployment using Vagrant w/ Ansible provisioner.
PHP
3
star
54

Canvas-Background

A super useful function for applying a background colour to a canvas element before invoking toDataURL.
JavaScript
3
star
55

MayBee

Safe chaining of object properties and functions using ES2015 Proxy.
JavaScript
3
star
56

Angularise

Deferred compilation of Angular.js templates for applications that render HTML from asynchronous processes.
JavaScript
3
star
57

PointerEvents

Allow the emulation of pointer events for browsers without native support, such as Internet Explorer.
JavaScript
3
star
58

Instamap

โœˆ๏ธ Towards the end of 2016, Instagram removed the Photo Maps feature. Instagram says it was unused, but we remember! Instamap brings it back for good, open-source and ad-free.
JavaScript
3
star
59

mundus-meus

Leaflet.js & Angular.js mapping tool for finding entities based on your location.
JavaScript
2
star
60

Typified

๐Ÿ—ผAn experimental implementation of first class functional types using pure ES at runtime, inspired by Haskell, PureScript and Idris.
JavaScript
2
star
61

UMLApp

JavaScript
2
star
62

UploadButton

UploadButton is a tiny module for a custom stylable upload button. Using HTML5's Custom Element API and Shadow DOM for encapsulation.
JavaScript
2
star
63

Lenin

Diagram tool using D3 providing a set of common functions for easy integration and extensibility.
JavaScript
2
star
64

Needle.js

Angular.js style dependency injection using reflection.
JavaScript
2
star
65

Biutiful

๐ŸŒฟ Biutiful transform ES imports into browser usable ECMAScript imports.
JavaScript
2
star
66

Shift.js

Use the shift key to select a range of elements, such as checkboxes.
JavaScript
2
star
67

Tail.cat

Modern e-mail client and SMTP/POP server using MongoDB, Ruby, Angular
JavaScript
2
star
68

Banter.js

Angular based real-time app for communicating with customers via IRC. Individual customers connect to a common IRC channel, giving staff members the ability to see all customers' messages, with the ability to respond directly and individually.
JavaScript
2
star
69

Tessellate

Small vanilla JavaScript module for gracefully removing floating elements from the page.
JavaScript
2
star
70

PolymerDroplet

Polymer.js adaptation of my popular EmberDroplet module for Ember.js.
JavaScript
2
star
71

DjangoExceptions

Handle and parse Django REST Framework validation messages with aplomb.
JavaScript
2
star
72

Moggy

Miniature ~2kb library that brings immutability to existing prototype functions employing the principle of least astonishment.
JavaScript
2
star
73

NodeURLImports

Transform browser URL imports into Node compatible import/require statements using local dependencies.
JavaScript
2
star
74

Viewport

Determine how much of an element is visible in the viewport.
JavaScript
1
star
75

Tdo

Terminal based todo app for managing today's tasks with gentle reminders
Rust
1
star
76

Memor

Use memoization for Rust functions to increase performance
Rust
1
star
77

ReduxLocal

Redux helper for maintaining pseudo-local state in a single tree.
JavaScript
1
star
78

Tidal.js

Socket.io benchmarking with realtime web-based statistics.
JavaScript
1
star
79

StickyRice

๐Ÿš React implementation to allow the natural position sticky behaviour in table headers and other elements
JavaScript
1
star
80

Kiwi.js

DRY interface for Angular.js tests in Jasmine/Karma.
JavaScript
1
star
81

Workex

1
star
82

Funkel

Simple and lightweight functional toolset inspired by Clojure using import.
JavaScript
1
star
83

Hylian

Quick and easy doubly and singly linked immutable list implementation that allows for inserting, removing and shifting.
JavaScript
1
star
84

DeveloperInk

Facilitates the use of ZURB's Ink for developers using the CLI with SASS, Email Testing, Compilation, etc... ๐Ÿ™
CSS
1
star
85

Mareos

MapReduce over WebSockets using Goroutines.
Go
1
star
86

WeakTree

๐ŸŒฒWeakMap implementation that allows for composite keys in a tree formation.
JavaScript
1
star
87

ava-webcomponents

Utility middleware for testing web components in AVA via Puppeteer.
JavaScript
1
star
88

SetOrder

Tiny module for sorting by a set order, using a custom sort function for omitting explicits.
JavaScript
1
star
89

Doogle

Node.js app for taking HTML snapshots of JavaScript pages to make your dynamic apps Google crawlable.
JavaScript
1
star
90

Paramo

๐ŸŒตSwiss-army knife of stringifying, parsing and manipulating URL parameters by applying types to the parameters.
JavaScript
1
star
91

Regrowth

๐Ÿ”ฌRegrowth is a monstrous laboratory experiment in container queries brought to life.
JavaScript
1
star
92

Termtodo

Todo app for the terminal for keeping reminders for later
Rust
1
star
93

Honey.js

Simple JavaScript library with auto-updating templates.
JavaScript
1
star
94

ReactAutolist

Browser native implementation of autocomplete using the datalist element.
JavaScript
1
star
95

Relayed

Convenient Node.js app for circumventing CORS issues when developing on localhost.
JavaScript
1
star
96

Cinematic.js

Experimental module for video sequence scrolling with zero dependencies.
JavaScript
1
star
97

gulp-envy

Gulp plugin for transferring your chosen environment variables to objects, with module loader, globals, angular, and other strategies.
1
star
98

redux-nest

Redux middleware for wrapping store in a Proxy to help with complex nested states.
JavaScript
1
star
99

Async

Yet another simple Promises/A+ compliant async flow control using ES6 generators.
JavaScript
1
star
100

TravelMap

๐ŸŒ Pin visited locations on a simple-to-use map, as well as future places you'd love to see. Then share with the world!
Python
1
star