Skip to content

Commit

Permalink
feat(next/api): has one through pointer relation
Browse files Browse the repository at this point in the history
  • Loading branch information
sdjdd committed Apr 7, 2024
1 parent 07cd71a commit fe55aad
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 7 deletions.
6 changes: 3 additions & 3 deletions next/api/src/model/Ticket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
pointerId,
pointerIds,
pointTo,
hasManyThroughPointer,
hasManyThroughPointerArray,
hasOneThroughPointer,
serialize,
} from '@/orm';
import { TicketUpdater, UpdateOptions } from '@/ticket/TicketUpdater';
Expand Down Expand Up @@ -210,8 +210,8 @@ export class Ticket extends Model {
@field()
privateTags?: Tag[];

@hasManyThroughPointer(() => Notification)
notifications?: Notification[];
@hasOneThroughPointer(() => Notification)
notification?: Notification;

@pointerId(() => Ticket)
parentId?: string;
Expand Down
19 changes: 19 additions & 0 deletions next/api/src/orm/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,25 @@ export function hasManyThroughRelation(getRelatedModel: ModelGetter, relatedKey?
};
}

export function hasOneThroughPointer(
getRelatedModel: ModelGetter,
foreignPointerKey?: string,
foreignKeyField?: string
) {
return (target: Model, field: string) => {
const model = target.constructor as typeof Model;
foreignPointerKey ??= lowercaseFirstChar(model.getClassName());
model.setRelation({
type: RelationType.HasOneThroughPointer,
model,
field,
getRelatedModel,
foreignPointerKey,
foreignKeyField: foreignKeyField ?? foreignPointerKey + 'Id',
});
};
}

function serializeDecoratorFactory(config?: Partial<Omit<SerializedField, 'key'>>) {
return (target: Model, key: string) => {
const model = target.constructor as typeof Model;
Expand Down
41 changes: 41 additions & 0 deletions next/api/src/orm/preloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
HasManyThroughPointer,
HasManyThroughPointerArray,
HasManyThroughRelation,
HasOneThroughPointer,
RelationName,
RelationType,
} from './relation';
Expand Down Expand Up @@ -258,6 +259,44 @@ class HasManyThroughRelationPreloader {
}
}

class HasOneThroughPointerPreloader implements Preloader {
queryModifier?: (query: QueryBuilder<any>) => void;

constructor(private relation: HasOneThroughPointer) {}

async load(items: Item[], options?: AuthOptions) {
if (items.length === 0) {
return;
}

const { model, field, getRelatedModel, foreignPointerKey, foreignKeyField } = this.relation;

const itemsChunks = _(items)
.uniqBy((item) => item.id)
.chunk(50)
.value();

const relatedItemsChunks: Item[][] = [];

for (const items of itemsChunks) {
const query = getRelatedModel().queryBuilder();
this.queryModifier?.(query);
const pointers = items.map((item) => model.ptr(item.id));
query.where(foreignPointerKey, 'in', pointers);
const relatedItems = await query.find(options);
relatedItemsChunks.push(relatedItems);
}

const relatedItemsGroups = _(relatedItemsChunks).flatten().groupBy(foreignKeyField).value();
for (const item of items) {
const relatedItems = relatedItemsGroups[item.id];
if (relatedItems) {
item[field] = relatedItems[0];
}
}
}
}

export function preloaderFactory<M extends typeof Model, N extends RelationName<M>>(
model: M,
name: N
Expand All @@ -281,5 +320,7 @@ export function preloaderFactory<M extends typeof Model, N extends RelationName<
return new HasManyThroughPointerArrayPreloader(relation);
case RelationType.HasManyThroughRelation:
return new HasManyThroughRelationPreloader(relation);
case RelationType.HasOneThroughPointer:
return new HasOneThroughPointerPreloader(relation);
}
}
13 changes: 12 additions & 1 deletion next/api/src/orm/relation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export enum RelationType {
HasManyThroughIdArray,
HasManyThroughPointerArray,
HasManyThroughRelation,
HasOneThroughPointer,
}

export interface BelongsTo {
Expand Down Expand Up @@ -79,11 +80,21 @@ export interface HasManyThroughRelation {
relatedKey: string;
}

export interface HasOneThroughPointer {
type: RelationType.HasOneThroughPointer;
model: typeof Model;
field: string;
getRelatedModel: ModelGetter;
foreignPointerKey: string;
foreignKeyField: string;
}

export type Relation =
| BelongsTo
| BelongsToThroughPointer
| HasMany
| HasManyThroughPointer
| HasManyThroughIdArray
| HasManyThroughPointerArray
| HasManyThroughRelation;
| HasManyThroughRelation
| HasOneThroughPointer;
3 changes: 1 addition & 2 deletions next/api/src/response/ticket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ export class TicketListItemResponse extends BaseTicketResponse {
toJSON(options?: TicketResponseOptions) {
return {
...super.toJSON(options),
// 因为限定 notification.user 为当前用户,所以 notifications 最多只有一个元素
unreadCount: this.ticket.notifications?.[0]?.unreadCount,
unreadCount: this.ticket.notification?.unreadCount,
};
}
}
Expand Down
2 changes: 1 addition & 1 deletion next/api/src/router/ticket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ router.get(
finalQuery.preload('files');
}
if (params.includeUnreadCount) {
finalQuery.preload('notifications', {
finalQuery.preload('notification', {
onQuery: (query) => {
return query.where('user', '==', currentUser.toPointer());
},
Expand Down

0 comments on commit fe55aad

Please sign in to comment.