mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
✨ feat(database): added listMemories method (#11806)
This commit is contained in:
@@ -844,6 +844,7 @@ export class UserMemoryModel {
|
||||
};
|
||||
};
|
||||
|
||||
// TODO(@nekomeowww): should use L & R generic for helping to determine what result type to return
|
||||
queryMemories = async (params: QueryUserMemoriesParams = {}): Promise<GetMemoriesResult> => {
|
||||
const {
|
||||
categories,
|
||||
@@ -1344,6 +1345,196 @@ export class UserMemoryModel {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* NOTICE: internal service / backend / data aggregation use only, for most of the UI related use-cases,
|
||||
* please use `queryMemories` method instead.
|
||||
*/
|
||||
listMemories = async <
|
||||
L extends LayersEnum,
|
||||
R extends UserMemoryDetail = L extends LayersEnum.Context
|
||||
? UserMemoryDetail & { context: UserMemoryContext; layer: LayersEnum.Context }
|
||||
: L extends LayersEnum.Experience
|
||||
? UserMemoryDetail & { experience: UserMemoryExperience; layer: LayersEnum.Experience }
|
||||
: L extends LayersEnum.Identity
|
||||
? UserMemoryDetail & { identity: UserMemoryIdentity; layer: LayersEnum.Identity }
|
||||
: L extends LayersEnum.Preference
|
||||
? UserMemoryDetail & { layer: LayersEnum.Preference; preference: UserMemoryPreference }
|
||||
: L extends LayersEnum.Activity
|
||||
? UserMemoryDetail & { activity: UserMemoryActivity; layer: LayersEnum.Activity }
|
||||
: never,
|
||||
>(params: {
|
||||
layer?: L;
|
||||
order?: 'asc' | 'desc';
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}): Promise<R[]> => {
|
||||
const { layer = LayersEnum.Context, order = 'desc', page = 1, pageSize = 20 } = params;
|
||||
|
||||
const normalizedPage = Math.max(1, page ?? 1);
|
||||
const normalizedPageSize = Math.min(Math.max(pageSize ?? 20, 1), 100);
|
||||
const offset = (normalizedPage - 1) * normalizedPageSize;
|
||||
const applyOrder = order === 'asc' ? asc : desc;
|
||||
|
||||
const baseSelection = selectNonVectorColumns(userMemories);
|
||||
const contextSelection = selectNonVectorColumns(userMemoriesContexts);
|
||||
const experienceSelection = selectNonVectorColumns(userMemoriesExperiences);
|
||||
const identitySelection = selectNonVectorColumns(userMemoriesIdentities);
|
||||
const preferenceSelection = selectNonVectorColumns(userMemoriesPreferences);
|
||||
// TODO(@nekomeowww): activity
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
const activitySelection = selectNonVectorColumns(userMemoriesActivities);
|
||||
|
||||
const baseConditions: Array<SQL | undefined> = [
|
||||
eq(userMemories.userId, this.userId),
|
||||
eq(userMemories.memoryLayer, layer),
|
||||
];
|
||||
const baseWhere = baseConditions.filter(Boolean) as SQL[];
|
||||
|
||||
const buildOrderBy = (updatedAtColumn: AnyColumn | SQL, createdAtColumn: AnyColumn | SQL) =>
|
||||
[applyOrder(updatedAtColumn), applyOrder(createdAtColumn)].filter(
|
||||
(item): item is SQL => item !== undefined,
|
||||
);
|
||||
|
||||
switch (layer) {
|
||||
case LayersEnum.Context: {
|
||||
const orderByClauses = buildOrderBy(
|
||||
userMemoriesContexts.updatedAt,
|
||||
userMemoriesContexts.createdAt,
|
||||
);
|
||||
const joinCondition = and(
|
||||
eq(userMemories.userId, userMemoriesContexts.userId),
|
||||
sql<boolean>`
|
||||
COALESCE(${userMemoriesContexts.userMemoryIds}, '[]'::jsonb) ? (${userMemories.id})::text
|
||||
`,
|
||||
);
|
||||
|
||||
const contextFilters: Array<SQL | undefined> = [
|
||||
baseWhere.length ? and(...baseWhere) : undefined,
|
||||
];
|
||||
const contextWhere = contextFilters.filter(Boolean) as SQL[];
|
||||
|
||||
const rows = await this.db
|
||||
.select({
|
||||
context: contextSelection,
|
||||
memory: baseSelection,
|
||||
})
|
||||
.from(userMemories)
|
||||
.innerJoin(userMemoriesContexts, joinCondition)
|
||||
.where(contextWhere.length ? and(...contextWhere) : undefined)
|
||||
.orderBy(...orderByClauses)
|
||||
.limit(normalizedPageSize)
|
||||
.offset(offset);
|
||||
|
||||
return rows.map((row) => ({
|
||||
context: row.context,
|
||||
layer: LayersEnum.Context,
|
||||
memory: row.memory,
|
||||
})) as unknown as R[];
|
||||
}
|
||||
case LayersEnum.Experience: {
|
||||
const orderByClauses = buildOrderBy(
|
||||
userMemoriesExperiences.updatedAt,
|
||||
userMemoriesExperiences.createdAt,
|
||||
);
|
||||
const joinCondition = and(
|
||||
eq(userMemories.id, userMemoriesExperiences.userMemoryId),
|
||||
eq(userMemoriesExperiences.userId, this.userId),
|
||||
);
|
||||
|
||||
const experienceFilters: Array<SQL | undefined> = [
|
||||
baseWhere.length ? and(...baseWhere) : undefined,
|
||||
];
|
||||
const experienceWhere = experienceFilters.filter(Boolean) as SQL[];
|
||||
|
||||
const rows = await this.db
|
||||
.select({
|
||||
experience: experienceSelection,
|
||||
memory: baseSelection,
|
||||
})
|
||||
.from(userMemories)
|
||||
.innerJoin(userMemoriesExperiences, joinCondition)
|
||||
.where(experienceWhere.length ? and(...experienceWhere) : undefined)
|
||||
.orderBy(...orderByClauses)
|
||||
.limit(normalizedPageSize)
|
||||
.offset(offset);
|
||||
|
||||
return rows.map((row) => ({
|
||||
experience: row.experience,
|
||||
layer: LayersEnum.Experience,
|
||||
memory: row.memory,
|
||||
})) as unknown as R[];
|
||||
}
|
||||
case LayersEnum.Identity: {
|
||||
const orderByClauses = buildOrderBy(
|
||||
userMemoriesIdentities.updatedAt,
|
||||
userMemoriesIdentities.createdAt,
|
||||
);
|
||||
const joinCondition = and(
|
||||
eq(userMemories.id, userMemoriesIdentities.userMemoryId),
|
||||
eq(userMemoriesIdentities.userId, this.userId),
|
||||
);
|
||||
|
||||
const identityFilters: Array<SQL | undefined> = [
|
||||
baseWhere.length ? and(...baseWhere) : undefined,
|
||||
];
|
||||
const identityWhere = identityFilters.filter(Boolean) as SQL[];
|
||||
|
||||
const rows = await this.db
|
||||
.select({
|
||||
identity: identitySelection,
|
||||
memory: baseSelection,
|
||||
})
|
||||
.from(userMemories)
|
||||
.innerJoin(userMemoriesIdentities, joinCondition)
|
||||
.where(identityWhere.length ? and(...identityWhere) : undefined)
|
||||
.orderBy(...orderByClauses)
|
||||
.limit(normalizedPageSize)
|
||||
.offset(offset);
|
||||
|
||||
return rows.map((row) => ({
|
||||
identity: row.identity,
|
||||
layer: LayersEnum.Identity,
|
||||
memory: row.memory,
|
||||
})) as unknown as R[];
|
||||
}
|
||||
case LayersEnum.Preference: {
|
||||
const orderByClauses = buildOrderBy(
|
||||
userMemoriesPreferences.updatedAt,
|
||||
userMemoriesPreferences.createdAt,
|
||||
);
|
||||
const joinCondition = and(
|
||||
eq(userMemories.id, userMemoriesPreferences.userMemoryId),
|
||||
eq(userMemoriesPreferences.userId, this.userId),
|
||||
);
|
||||
|
||||
const preferenceFilters: Array<SQL | undefined> = [
|
||||
baseWhere.length ? and(...baseWhere) : undefined,
|
||||
];
|
||||
const preferenceWhere = preferenceFilters.filter(Boolean) as SQL[];
|
||||
|
||||
const rows = await this.db
|
||||
.select({
|
||||
memory: baseSelection,
|
||||
preference: preferenceSelection,
|
||||
})
|
||||
.from(userMemories)
|
||||
.innerJoin(userMemoriesPreferences, joinCondition)
|
||||
.where(preferenceWhere.length ? and(...preferenceWhere) : undefined)
|
||||
.orderBy(...orderByClauses)
|
||||
.limit(normalizedPageSize)
|
||||
.offset(offset);
|
||||
|
||||
return rows.map((row) => ({
|
||||
layer: LayersEnum.Preference,
|
||||
memory: row.memory,
|
||||
preference: row.preference,
|
||||
})) as unknown as R[];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
getMemoryDetail = async (
|
||||
params: GetMemoryDetailParams,
|
||||
): Promise<UserMemoryDetail | undefined> => {
|
||||
@@ -2318,10 +2509,7 @@ export class UserMemoryModel {
|
||||
.where(eq(userMemoriesIdentities.userId, this.userId))
|
||||
.orderBy(desc(userMemoriesIdentities.capturedAt));
|
||||
|
||||
return res as Array<{
|
||||
identity: typeof userMemoriesIdentities.$inferSelect;
|
||||
memory: typeof userMemories.$inferSelect;
|
||||
}>;
|
||||
return res;
|
||||
};
|
||||
|
||||
getIdentitiesByType = async (type: string): Promise<UserMemoryIdentityWithoutVectors[]> => {
|
||||
|
||||
Reference in New Issue
Block a user