Skip to content

Commit

Permalink
Add findByIndex and PipeSingle.exists
Browse files Browse the repository at this point in the history
  • Loading branch information
etienne-dldc committed Feb 13, 2022
1 parent 6aa2e78 commit 6acae9c
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 9 deletions.
49 changes: 49 additions & 0 deletions src/DatabaseTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type QueriesCache = {
countAll: DB.Statement | null;
};

type IndexQueriesCache<IndexName extends string> = { [K in IndexName]?: DB.Statement | undefined };

export type DatabaseTableAny = DatabaseTable<
string | number | symbol,
any,
Expand Down Expand Up @@ -44,6 +46,7 @@ export class DatabaseTable<
findByKey: null,
countAll: null,
};
private readonly indexQueriesCache: IndexQueriesCache<Indexes[number]['name']> = {};

constructor(name: Name, schema: SchemaAny, getDb: () => DB.Database) {
this.name = name;
Expand All @@ -68,6 +71,38 @@ export class DatabaseTable<
return this.cache[name] as any;
}

private getIndexStatement<IndexName extends Indexes[number]['name']>(
name: IndexName,
create: () => DB.Statement
): DB.Statement {
const current = this.indexQueriesCache[name];
if (current === undefined) {
const query = create();
this.indexQueriesCache[name] = query;
return query;
}
return current;
}

private getFindByIndexQuery<IndexName extends Indexes[number]['name']>(
index: IndexName
): DB.Statement {
return this.getIndexStatement(index, (): DB.Statement => {
const db = this.getDb();
const key = this.sqlTable.column('key');
const data = this.sqlTable.column('data');
const indexColumn = this.sqlTable.column(index);
const query = sql.SelectStmt.print(
sql.SelectStmt.create({
columns: [key, data],
from: this.sqlTable,
where: sql.Expr.eq(indexColumn, sql.Param.createAnonymous()),
})
);
return db.prepare(query);
});
}

private getDeleteByKeyQuery(): DB.Statement {
return this.getStatement('deleteByKey', (): DB.Statement => {
const db = this.getDb();
Expand Down Expand Up @@ -274,6 +309,20 @@ export class DatabaseTable<
);
}

findByIndex<IndexName extends Indexes[number]['name']>(
index: IndexName,
value: Extract<Indexes[number], { name: IndexName }>['_value']
): PipeCollection<Key, Data> {
const indexConfig = notNil(this.tableConfig.indexes.find((i) => i.name === index));
const preparedQuery = this.getFindByIndexQuery(index);
const valueSerialized = sql.Value.serialize(indexConfig.value, value, indexConfig.name);
const iter = preparedQuery.iterate(valueSerialized);
return new PipeCollection(
traverserFromRowIterator<Key, string, Data>(iter, (data) => this.restore(data)),
this.pipeParent
);
}

all(): PipeCollection<Key, Data> {
const iter = this.getSelectAllQuery().iterate();
return new PipeCollection(
Expand Down
41 changes: 32 additions & 9 deletions src/Pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ export class PipeSingleReadonly<Key, Data, Maybe extends boolean> {
return new PipeSingle<Key, Data, true>(null, this.parent);
}

/**
* Make sure the Pipe contains a value
* Throw otherwise
*/
exists(): PipeSingle<Key, Data, false> {
if (this.internal === null) {
throw new Error(`.exists() called on empty PipeSingle`);
}
return this as any;
}

value(): Maybe extends true ? Data | null : Data {
return (this.internal?.data ?? null) as any;
}
Expand Down Expand Up @@ -73,9 +84,9 @@ export class PipeSingle<Key, Data, Maybe extends boolean> extends PipeSingleRead
}

/**
* Throw if the entry does not exists
* Update the value, throw if the entry does not exists
*/
update(update: Data | ((item: Data) => Data)): PipeSingle<Key, Data, Maybe> {
update(update: Data | ((item: Data) => Data)): PipeSingle<Key, Data, false> {
if (this.internal === null) {
throw new Error('Cannot update a non-existing entry');
}
Expand All @@ -84,14 +95,17 @@ export class PipeSingle<Key, Data, Maybe extends boolean> extends PipeSingleRead
return this as any;
}
const { updatedKey } = this.parent.updateByKey(this.internal.key, updated);
return new PipeSingle<Key, Data, Maybe>({ key: updatedKey, data: updated }, this.parent);
return new PipeSingle<Key, Data, false>({ key: updatedKey, data: updated }, this.parent);
}

/**
* Like .update except that it will not throw if the entry does not exists
*/
updateIfExists(update: Data | ((item: Data) => Data)): PipeSingle<Key, Data, Maybe> {
if (this.internal === null) {
return this as any;
}
return this.update(update);
return this.update(update) as any;
}

/**
Expand Down Expand Up @@ -119,6 +133,7 @@ export class PipeCollectionReadonly<Key, Data> {

/**
* Get all data then re-emit them
* We use this when we delete items since we need to keep values around
*/
cache(): PipeCollection<Key, Data> {
const all = this.entriesArray();
Expand All @@ -131,7 +146,7 @@ export class PipeCollectionReadonly<Key, Data> {
}

/**
* Get all data then re-emit them
* Same as .cache but returns a readonly collection
*/
cacheReadonly(): PipeCollectionReadonly<Key, Data> {
const all = this.entriesArray();
Expand Down Expand Up @@ -176,7 +191,9 @@ export class PipeCollectionReadonly<Key, Data> {
}, this.parent);
}

// throw if more count is not one
/**
* throw if count is not one
*/
one(): PipeSingle<Key, Data, false> {
const first = this.traverser();
if (first === null) {
Expand All @@ -189,7 +206,9 @@ export class PipeCollectionReadonly<Key, Data> {
return new PipeSingle<Key, Data, false>(first, this.parent);
}

// throw if count > 1
/**
* throw if count > 1
*/
maybeOne(): PipeSingle<Key, Data, true> {
const first = this.traverser();
if (first === null) {
Expand All @@ -202,7 +221,9 @@ export class PipeCollectionReadonly<Key, Data> {
return new PipeSingle<Key, Data, true>(first, this.parent);
}

// throw if count < 1
/**
* throw if count < 1
*/
first(): PipeSingle<Key, Data, false> {
const first = this.traverser();
if (first === null) {
Expand All @@ -211,7 +232,9 @@ export class PipeCollectionReadonly<Key, Data> {
return new PipeSingle<Key, Data, false>(first, this.parent);
}

// never throw
/**
* never throw
*/
maybeFirst(): PipeSingle<Key, Data, true> {
const first = this.traverser();
if (first === null) {
Expand Down

0 comments on commit 6acae9c

Please sign in to comment.