minimongo
Version:
Client-side mongo database with server sync over http
200 lines (170 loc) • 4.92 kB
text/typescript
// TODO: This file was created by bulk-decaffeinate.
// Sanity-check the conversion and remove this comment.
let MemoryDb
import _ from "lodash"
import { createUid, processFind } from "./utils"
import { compileSort } from "./selector"
export default MemoryDb = class MemoryDb {
constructor(options: any, success: any) {
this.collections = {}
if (success) {
success(this)
}
}
addCollection(name: any, success: any, error: any) {
const collection = new Collection(name)
this[name] = collection
this.collections[name] = collection
if (success != null) {
return success()
}
}
removeCollection(name: any, success: any, error: any) {
delete this[name]
delete this.collections[name]
if (success != null) {
return success()
}
}
}
// Stores data in memory
class Collection {
constructor(name: any) {
this.name = name
this.items = {}
this.upserts = {} // Pending upserts by _id. Still in items
this.removes = {} // Pending removes by _id. No longer in items
}
find(selector: any, options: any) {
return {
fetch: (success: any, error: any) => {
return this._findFetch(selector, options, success, error)
}
}
}
findOne(selector: any, options: any, success: any, error: any) {
if (_.isFunction(options)) {
;[options, success, error] = [{}, options, success]
}
return this.find(selector, options).fetch(function (results: any) {
if (success != null) {
return success(results.length > 0 ? results[0] : null)
}
}, error)
}
_findFetch(selector: any, options: any, success: any, error: any) {
if (success != null) {
return success(processFind(this.items, selector, options))
}
}
upsert(doc: any, success: any, error: any) {
// Handle both single and multiple upsert
let items = doc
if (!_.isArray(items)) {
items = [items]
}
for (let item of items) {
if (!item._id) {
item._id = createUid()
}
// Replace/add
this.items[item._id] = item
this.upserts[item._id] = item
}
if (success) {
return success(doc)
}
}
remove(id: any, success: any, error: any) {
if (_.has(this.items, id)) {
this.removes[id] = this.items[id]
delete this.items[id]
delete this.upserts[id]
} else {
this.removes[id] = { _id: id }
}
if (success != null) {
return success()
}
}
cache(docs: any, selector: any, options: any, success: any, error: any) {
// Add all non-local that are not upserted or removed
let sort: any
for (let doc of docs) {
this.cacheOne(doc)
}
const docsMap = _.fromPairs(_.zip(_.map(docs, "_id"), docs))
if (options.sort) {
sort = compileSort(options.sort)
}
// Perform query, removing rows missing in docs from local db
return this.find(selector, options).fetch((results: any) => {
for (let result of results) {
if (!docsMap[result._id] && !_.has(this.upserts, result._id)) {
// If past end on sorted limited, ignore
if (options.sort && options.limit && docs.length === options.limit) {
if (sort(result, _.last(docs)) >= 0) {
continue
}
}
delete this.items[result._id]
}
}
if (success != null) {
return success()
}
}, error)
}
pendingUpserts(success: any) {
return success(_.values(this.upserts))
}
pendingRemoves(success: any) {
return success(_.map(this.removes, "_id"))
}
resolveUpsert(doc: any, success: any) {
// Handle both single and multiple upsert
let items = doc
if (!_.isArray(items)) {
items = [items]
}
for (let item of items) {
if (this.upserts[item._id]) {
// Only safely remove upsert if doc is unchanged
if (_.isEqual(item, this.upserts[item._id])) {
delete this.upserts[item._id]
}
}
}
if (success != null) {
return success()
}
}
resolveRemove(id: any, success: any) {
delete this.removes[id]
if (success != null) {
return success()
}
}
// Add but do not overwrite or record as upsert
seed(doc: any, success: any) {
if (!_.has(this.items, doc._id) && !_.has(this.removes, doc._id)) {
this.items[doc._id] = doc
}
if (success != null) {
return success()
}
}
// Add but do not overwrite upserts or removes
cacheOne(doc: any, success: any) {
if (!_.has(this.upserts, doc._id) && !_.has(this.removes, doc._id)) {
const existing = this.items[doc._id]
// If _rev present, make sure that not overwritten by lower _rev
if (!existing || !doc._rev || !existing._rev || doc._rev >= existing._rev) {
this.items[doc._id] = doc
}
}
if (success != null) {
return success()
}
}
}