diff options
author | Chocobozzz <me@florianbigard.com> | 2022-05-05 10:29:35 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2022-05-05 10:29:35 +0200 |
commit | bae616273d455d225d131eb17c56db6c20a0b6b3 (patch) | |
tree | ed2174122d092320cea34c4ae522da6f2131f968 /server/models/actor/actor-follow.ts | |
parent | d493e2d4bf0ace177d15e9173d1c02df8c100558 (diff) | |
download | PeerTube-bae616273d455d225d131eb17c56db6c20a0b6b3.tar.gz PeerTube-bae616273d455d225d131eb17c56db6c20a0b6b3.tar.zst PeerTube-bae616273d455d225d131eb17c56db6c20a0b6b3.zip |
Convert followers/following in raw SQL queries
Prevent weird bug in SQL generation
Diffstat (limited to 'server/models/actor/actor-follow.ts')
-rw-r--r-- | server/models/actor/actor-follow.ts | 146 |
1 files changed, 10 insertions, 136 deletions
diff --git a/server/models/actor/actor-follow.ts b/server/models/actor/actor-follow.ts index 0f4d3c0a6..af1d85e9f 100644 --- a/server/models/actor/actor-follow.ts +++ b/server/models/actor/actor-follow.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { difference, values } from 'lodash' | 1 | import { difference, values } from 'lodash' |
2 | import { Includeable, IncludeOptions, literal, Op, QueryTypes, Transaction, WhereOptions } from 'sequelize' | 2 | import { Includeable, IncludeOptions, Op, QueryTypes, Transaction } from 'sequelize' |
3 | import { | 3 | import { |
4 | AfterCreate, | 4 | AfterCreate, |
5 | AfterDestroy, | 5 | AfterDestroy, |
@@ -30,7 +30,6 @@ import { | |||
30 | MActorFollowFormattable, | 30 | MActorFollowFormattable, |
31 | MActorFollowSubscriptions | 31 | MActorFollowSubscriptions |
32 | } from '@server/types/models' | 32 | } from '@server/types/models' |
33 | import { ActivityPubActorType } from '@shared/models' | ||
34 | import { AttributesOnly } from '@shared/typescript-utils' | 33 | import { AttributesOnly } from '@shared/typescript-utils' |
35 | import { FollowState } from '../../../shared/models/actors' | 34 | import { FollowState } from '../../../shared/models/actors' |
36 | import { ActorFollow } from '../../../shared/models/actors/follow.model' | 35 | import { ActorFollow } from '../../../shared/models/actors/follow.model' |
@@ -39,9 +38,11 @@ import { ACTOR_FOLLOW_SCORE, CONSTRAINTS_FIELDS, FOLLOW_STATES, SERVER_ACTOR_NAM | |||
39 | import { AccountModel } from '../account/account' | 38 | import { AccountModel } from '../account/account' |
40 | import { ServerModel } from '../server/server' | 39 | import { ServerModel } from '../server/server' |
41 | import { doesExist } from '../shared/query' | 40 | import { doesExist } from '../shared/query' |
42 | import { createSafeIn, getFollowsSort, getSort, searchAttribute, throwIfNotValid } from '../utils' | 41 | import { createSafeIn, getSort, searchAttribute, throwIfNotValid } from '../utils' |
43 | import { VideoChannelModel } from '../video/video-channel' | 42 | import { VideoChannelModel } from '../video/video-channel' |
44 | import { ActorModel, unusedActorAttributesForAPI } from './actor' | 43 | import { ActorModel, unusedActorAttributesForAPI } from './actor' |
44 | import { InstanceListFollowersQueryBuilder, ListFollowersOptions } from './sql/instance-list-followers-query-builder' | ||
45 | import { InstanceListFollowingQueryBuilder, ListFollowingOptions } from './sql/instance-list-following-query-builder' | ||
45 | 46 | ||
46 | @Table({ | 47 | @Table({ |
47 | tableName: 'actorFollow', | 48 | tableName: 'actorFollow', |
@@ -348,144 +349,17 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo | |||
348 | return ActorFollowModel.findAll(query) | 349 | return ActorFollowModel.findAll(query) |
349 | } | 350 | } |
350 | 351 | ||
351 | static listInstanceFollowingForApi (options: { | 352 | static listInstanceFollowingForApi (options: ListFollowingOptions) { |
352 | id: number | ||
353 | start: number | ||
354 | count: number | ||
355 | sort: string | ||
356 | state?: FollowState | ||
357 | actorType?: ActivityPubActorType | ||
358 | search?: string | ||
359 | }) { | ||
360 | const { id, start, count, sort, search, state, actorType } = options | ||
361 | |||
362 | const followWhere = state ? { state } : {} | ||
363 | const followingWhere: WhereOptions = {} | ||
364 | |||
365 | if (search) { | ||
366 | Object.assign(followWhere, { | ||
367 | [Op.or]: [ | ||
368 | searchAttribute(options.search, '$ActorFollowing.preferredUsername$'), | ||
369 | searchAttribute(options.search, '$ActorFollowing.Server.host$') | ||
370 | ] | ||
371 | }) | ||
372 | } | ||
373 | |||
374 | if (actorType) { | ||
375 | Object.assign(followingWhere, { type: actorType }) | ||
376 | } | ||
377 | |||
378 | const getQuery = (forCount: boolean) => { | ||
379 | const actorModel = forCount | ||
380 | ? ActorModel.unscoped() | ||
381 | : ActorModel | ||
382 | |||
383 | return { | ||
384 | distinct: true, | ||
385 | offset: start, | ||
386 | limit: count, | ||
387 | order: getFollowsSort(sort), | ||
388 | where: followWhere, | ||
389 | include: [ | ||
390 | { | ||
391 | model: actorModel, | ||
392 | required: true, | ||
393 | as: 'ActorFollower', | ||
394 | where: { | ||
395 | id | ||
396 | } | ||
397 | }, | ||
398 | { | ||
399 | model: actorModel, | ||
400 | as: 'ActorFollowing', | ||
401 | required: true, | ||
402 | where: followingWhere, | ||
403 | include: [ | ||
404 | { | ||
405 | model: ServerModel, | ||
406 | required: true | ||
407 | } | ||
408 | ] | ||
409 | } | ||
410 | ] | ||
411 | } | ||
412 | } | ||
413 | |||
414 | return Promise.all([ | 353 | return Promise.all([ |
415 | ActorFollowModel.count(getQuery(true)), | 354 | new InstanceListFollowingQueryBuilder(this.sequelize, options).countFollowing(), |
416 | ActorFollowModel.findAll<MActorFollowActorsDefault>(getQuery(false)) | 355 | new InstanceListFollowingQueryBuilder(this.sequelize, options).listFollowing() |
417 | ]).then(([ total, data ]) => ({ total, data })) | 356 | ]).then(([ total, data ]) => ({ total, data })) |
418 | } | 357 | } |
419 | 358 | ||
420 | static listFollowersForApi (options: { | 359 | static listFollowersForApi (options: ListFollowersOptions) { |
421 | actorIds: number[] | ||
422 | start: number | ||
423 | count: number | ||
424 | sort: string | ||
425 | state?: FollowState | ||
426 | actorType?: ActivityPubActorType | ||
427 | search?: string | ||
428 | }) { | ||
429 | const { actorIds, start, count, sort, search, state, actorType } = options | ||
430 | |||
431 | const followWhere = state ? { state } : {} | ||
432 | const followerWhere: WhereOptions = {} | ||
433 | |||
434 | if (search) { | ||
435 | const escapedSearch = ActorFollowModel.sequelize.escape('%' + search + '%') | ||
436 | |||
437 | Object.assign(followerWhere, { | ||
438 | id: { | ||
439 | [Op.in]: literal( | ||
440 | `(` + | ||
441 | `SELECT "actor".id FROM actor LEFT JOIN server on server.id = actor."serverId" ` + | ||
442 | `WHERE "preferredUsername" ILIKE ${escapedSearch} OR "host" ILIKE ${escapedSearch}` + | ||
443 | `)` | ||
444 | ) | ||
445 | } | ||
446 | }) | ||
447 | } | ||
448 | |||
449 | if (actorType) { | ||
450 | Object.assign(followerWhere, { type: actorType }) | ||
451 | } | ||
452 | |||
453 | const getQuery = (forCount: boolean) => { | ||
454 | const actorModel = forCount | ||
455 | ? ActorModel.unscoped() | ||
456 | : ActorModel | ||
457 | |||
458 | return { | ||
459 | distinct: true, | ||
460 | |||
461 | offset: start, | ||
462 | limit: count, | ||
463 | order: getFollowsSort(sort), | ||
464 | where: followWhere, | ||
465 | include: [ | ||
466 | { | ||
467 | model: actorModel, | ||
468 | required: true, | ||
469 | as: 'ActorFollower', | ||
470 | where: followerWhere | ||
471 | }, | ||
472 | { | ||
473 | model: actorModel, | ||
474 | as: 'ActorFollowing', | ||
475 | required: true, | ||
476 | where: { | ||
477 | id: { | ||
478 | [Op.in]: actorIds | ||
479 | } | ||
480 | } | ||
481 | } | ||
482 | ] | ||
483 | } | ||
484 | } | ||
485 | |||
486 | return Promise.all([ | 360 | return Promise.all([ |
487 | ActorFollowModel.count(getQuery(true)), | 361 | new InstanceListFollowersQueryBuilder(this.sequelize, options).countFollowers(), |
488 | ActorFollowModel.findAll<MActorFollowActorsDefault>(getQuery(false)) | 362 | new InstanceListFollowersQueryBuilder(this.sequelize, options).listFollowers() |
489 | ]).then(([ total, data ]) => ({ total, data })) | 363 | ]).then(([ total, data ]) => ({ total, data })) |
490 | } | 364 | } |
491 | 365 | ||