UNPKG

minimongo

Version:

Client-side mongo database with server sync over http

82 lines (75 loc) 3.43 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.decodeResponse = exports.encodeResponse = exports.encodeRequest = void 0; const lodash_1 = __importDefault(require("lodash")); const js_sha1_1 = __importDefault(require("js-sha1")); const selector_1 = require("./selector"); /* Quickfind protocol allows sending information about which rows are already present locally to minimize network traffic. Protocal has 3 phases: encodeRequest: Done on client. Summarize which rows are already present locally by sharding and then hashing _id:_rev| encodeResponse: Done on server. Given complete server list and results of encodeRequest, create list of changes, sharded by first two characters of _id decodeResponse: Done on client. Given encoded response and local list, recreate complete list from server. Interaction of sort, limit and fields: - fields present: _rev might be missing. Do not use quickfind - limit with no sort: This gives unstable results. Do not use quickfind - sort: final rows need to be re-sorted. Since fields not present, is possible. - no sort, no limit: always sort by _id */ // Characters to shard by of _id const shardLength = 2; // Given an array of client rows, create a summary of which rows are present function encodeRequest(clientRows) { // Index by shard clientRows = lodash_1.default.groupBy(clientRows, (row) => row._id.substr(0, shardLength)); // Hash each one const request = lodash_1.default.mapValues(clientRows, (rows) => hashRows(rows)); return request; } exports.encodeRequest = encodeRequest; // Given an array of rows on the server and an encoded request, create encoded response function encodeResponse(serverRows, encodedRequest) { // Index by shard serverRows = lodash_1.default.groupBy(serverRows, (row) => row._id.substr(0, shardLength)); // Include any that are in encoded request but not present for (let key in encodedRequest) { const value = encodedRequest[key]; if (!serverRows[key]) { serverRows[key] = []; } } // Only keep ones where different from encoded request const response = lodash_1.default.pickBy(serverRows, (rows, key) => hashRows(rows) !== encodedRequest[key]); return response; } exports.encodeResponse = encodeResponse; // Given encoded response and array of client rows, create array of server rows function decodeResponse(encodedResponse, clientRows, sort) { // Index by shard clientRows = lodash_1.default.groupBy(clientRows, (row) => row._id.substr(0, shardLength)); // Overwrite with response let serverRows = lodash_1.default.extend(clientRows, encodedResponse); // Flatten serverRows = lodash_1.default.flatten(lodash_1.default.values(serverRows)); // Sort if (sort) { serverRows.sort((0, selector_1.compileSort)(sort)); } else { serverRows = lodash_1.default.sortBy(serverRows, "_id"); } return serverRows; } exports.decodeResponse = decodeResponse; function hashRows(rows) { const hash = js_sha1_1.default.create(); for (let row of lodash_1.default.sortBy(rows, "_id")) { hash.update(row._id + ":" + (row._rev || "") + "|"); } // 80 bits is enough for uniqueness return hash.hex().substr(0, 20); }