From 970230da068570385a03483dec0559049f3ac458 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Fri, 24 Mar 2017 16:20:31 +0100 Subject: [PATCH] Add support for conditionals in DB queries --- src/query.ts | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/query.ts b/src/query.ts index 30633b0af..89d4133b3 100644 --- a/src/query.ts +++ b/src/query.ts @@ -76,10 +76,87 @@ export interface QueryStream { map(f: (x:T) => S): QueryStream; flatMap(f: (x: T) => S[]): QueryStream; toArray(): Promise; + first(): QueryValue; then(onfulfill: any, onreject: any): any; } + +/** + * Query result that consists of at most one value. + */ +export interface QueryValue { + map(f: (x: T) => S): QueryValue; + cond(f: (x: T) => boolean, onTrue: (r: QueryRoot) => R, onFalse: (r: QueryRoot) => R): Promise; +} + + +abstract class BaseQueryValue implements QueryValue { + root: QueryRoot; + + constructor(root: QueryRoot) { + this.root = root; + } + + map(f: (x: T) => S): QueryValue { + return new MapQueryValue(this, f); + } + + cond(f: (x: T) => boolean, onTrue: (r: QueryRoot) => R, onFalse: (r: QueryRoot) => R): Promise { + return new Promise((resolve, reject) => { + this.subscribeOne((v, tx) => { + if (f(v)) { + onTrue(this.root); + } else { + onFalse(this.root); + } + }); + resolve(); + }); + } + + abstract subscribeOne(f: SubscribeOneFn): void; +} + +class FirstQueryValue extends BaseQueryValue { + gotValue = false; + s: QueryStreamBase; + constructor(stream: QueryStreamBase) { + super(stream.root); + this.s = stream; + } + + subscribeOne(f: SubscribeOneFn): void { + this.s.subscribe((isDone, value, tx) => { + if (this.gotValue) { + return; + } + if (isDone) { + f(undefined, tx); + } else { + f(value, tx); + } + this.gotValue = true; + }); + } +} + +class MapQueryValue extends BaseQueryValue { + mapFn: (x: T) => S; + v: BaseQueryValue; + + constructor(v: BaseQueryValue, mapFn: (x: T) => S) { + super(v.root); + this.v = v; + this.mapFn = mapFn; + } + + subscribeOne(f: SubscribeOneFn): void { + this.v.subscribeOne((v, tx) => this.mapFn(v)); + } +} + + export let AbortTransaction = Symbol("abort_transaction"); /** @@ -112,6 +189,10 @@ abstract class QueryStreamBase implements QueryStream, PromiseLike { this.root = root; } + first(): QueryValue { + return new FirstQueryValue(this); + } + then(onfulfilled: (value: void) => R | PromiseLike, onrejected: (reason: any) => R | PromiseLike): PromiseLike { return this.root.then(onfulfilled, onrejected); } @@ -183,6 +264,7 @@ abstract class QueryStreamBase implements QueryStream, PromiseLike { type FilterFn = (e: any) => boolean; type SubscribeFn = (done: boolean, value: any, tx: IDBTransaction) => void; +type SubscribeOneFn = (value: any, tx: IDBTransaction) => void; interface FlatMapFn { (v: T): T[];