Files
IdentityDB/src/queries/topics.ts

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();
}