118 lines
3.6 KiB
TypeScript
118 lines
3.6 KiB
TypeScript
import type { Kysely, Transaction } from 'kysely';
|
|
|
|
import type { IdentityDatabaseSchema } from '../types/database';
|
|
import type { TopicAliasRecord, TopicRecord } from '../types/domain';
|
|
|
|
export type DatabaseExecutor = Kysely<IdentityDatabaseSchema> | Transaction<IdentityDatabaseSchema>;
|
|
|
|
export interface ConnectedTopicRow extends TopicRecord {
|
|
shared_fact_count: number;
|
|
}
|
|
|
|
export async function findTopicRowByNormalizedName(
|
|
executor: DatabaseExecutor,
|
|
normalizedName: string,
|
|
): Promise<TopicRecord | undefined> {
|
|
return executor
|
|
.selectFrom('topics')
|
|
.selectAll()
|
|
.where('normalized_name', '=', normalizedName)
|
|
.executeTakeFirst();
|
|
}
|
|
|
|
export async function findTopicRowByNormalizedAlias(
|
|
executor: DatabaseExecutor,
|
|
normalizedAlias: string,
|
|
): Promise<TopicRecord | undefined> {
|
|
return executor
|
|
.selectFrom('topic_aliases')
|
|
.innerJoin('topics', 'topics.id', 'topic_aliases.topic_id')
|
|
.selectAll('topics')
|
|
.where('topic_aliases.normalized_alias', '=', normalizedAlias)
|
|
.executeTakeFirst();
|
|
}
|
|
|
|
export async function findTopicRowByNameOrAlias(
|
|
executor: DatabaseExecutor,
|
|
normalizedName: string,
|
|
): Promise<TopicRecord | undefined> {
|
|
const directMatch = await findTopicRowByNormalizedName(executor, normalizedName);
|
|
if (directMatch) {
|
|
return directMatch;
|
|
}
|
|
|
|
return findTopicRowByNormalizedAlias(executor, normalizedName);
|
|
}
|
|
|
|
export async function listTopicAliasRowsForTopicId(
|
|
executor: DatabaseExecutor,
|
|
topicId: string,
|
|
): Promise<TopicAliasRecord[]> {
|
|
return executor
|
|
.selectFrom('topic_aliases')
|
|
.selectAll()
|
|
.where('topic_id', '=', topicId)
|
|
.orderBy('is_primary', 'desc')
|
|
.orderBy('normalized_alias', 'asc')
|
|
.execute();
|
|
}
|
|
|
|
export async function listTopicRows(
|
|
executor: DatabaseExecutor,
|
|
limit?: number,
|
|
): Promise<TopicRecord[]> {
|
|
let query = executor.selectFrom('topics').selectAll().orderBy('normalized_name', 'asc');
|
|
|
|
if (limit !== undefined) {
|
|
query = query.limit(limit);
|
|
}
|
|
|
|
return query.execute();
|
|
}
|
|
|
|
export async function findConnectedTopicRows(
|
|
executor: DatabaseExecutor,
|
|
topicId: string,
|
|
): Promise<ConnectedTopicRow[]> {
|
|
return executor
|
|
.selectFrom('fact_topics as source_link')
|
|
.innerJoin('fact_topics as related_link', 'related_link.fact_id', 'source_link.fact_id')
|
|
.innerJoin('topics', 'topics.id', 'related_link.topic_id')
|
|
.selectAll('topics')
|
|
.select((eb) => eb.fn.count<number>('related_link.fact_id').as('shared_fact_count'))
|
|
.where('source_link.topic_id', '=', topicId)
|
|
.whereRef('related_link.topic_id', '!=', 'source_link.topic_id')
|
|
.groupBy('topics.id')
|
|
.orderBy('shared_fact_count', 'desc')
|
|
.orderBy('topics.normalized_name', 'asc')
|
|
.execute() as Promise<ConnectedTopicRow[]>;
|
|
}
|
|
|
|
export async function findChildTopicRows(
|
|
executor: DatabaseExecutor,
|
|
parentTopicId: string,
|
|
): Promise<TopicRecord[]> {
|
|
return executor
|
|
.selectFrom('topic_relations')
|
|
.innerJoin('topics', 'topics.id', 'topic_relations.child_topic_id')
|
|
.selectAll('topics')
|
|
.where('topic_relations.parent_topic_id', '=', parentTopicId)
|
|
.where('topic_relations.relation', '=', 'parent_of')
|
|
.orderBy('topics.normalized_name', 'asc')
|
|
.execute();
|
|
}
|
|
|
|
export async function findParentTopicRows(
|
|
executor: DatabaseExecutor,
|
|
childTopicId: string,
|
|
): Promise<TopicRecord[]> {
|
|
return executor
|
|
.selectFrom('topic_relations')
|
|
.innerJoin('topics', 'topics.id', 'topic_relations.parent_topic_id')
|
|
.selectAll('topics')
|
|
.where('topic_relations.child_topic_id', '=', childTopicId)
|
|
.where('topic_relations.relation', '=', 'parent_of')
|
|
.orderBy('topics.normalized_name', 'asc')
|
|
.execute();
|
|
}
|