aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/actor/actor-follow.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/actor/actor-follow.ts')
-rw-r--r--server/models/actor/actor-follow.ts263
1 files changed, 143 insertions, 120 deletions
diff --git a/server/models/actor/actor-follow.ts b/server/models/actor/actor-follow.ts
index 006282530..0f4d3c0a6 100644
--- a/server/models/actor/actor-follow.ts
+++ b/server/models/actor/actor-follow.ts
@@ -1,5 +1,5 @@
1import { difference, values } from 'lodash' 1import { difference, values } from 'lodash'
2import { IncludeOptions, Op, QueryTypes, Transaction, WhereOptions } from 'sequelize' 2import { Includeable, IncludeOptions, literal, Op, QueryTypes, Transaction, WhereOptions } from 'sequelize'
3import { 3import {
4 AfterCreate, 4 AfterCreate,
5 AfterDestroy, 5 AfterDestroy,
@@ -30,12 +30,12 @@ import {
30 MActorFollowFormattable, 30 MActorFollowFormattable,
31 MActorFollowSubscriptions 31 MActorFollowSubscriptions
32} from '@server/types/models' 32} from '@server/types/models'
33import { AttributesOnly } from '@shared/typescript-utils'
34import { ActivityPubActorType } from '@shared/models' 33import { ActivityPubActorType } from '@shared/models'
34import { AttributesOnly } from '@shared/typescript-utils'
35import { FollowState } from '../../../shared/models/actors' 35import { FollowState } from '../../../shared/models/actors'
36import { ActorFollow } from '../../../shared/models/actors/follow.model' 36import { ActorFollow } from '../../../shared/models/actors/follow.model'
37import { logger } from '../../helpers/logger' 37import { logger } from '../../helpers/logger'
38import { ACTOR_FOLLOW_SCORE, CONSTRAINTS_FIELDS, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants' 38import { ACTOR_FOLLOW_SCORE, CONSTRAINTS_FIELDS, FOLLOW_STATES, SERVER_ACTOR_NAME, SORTABLE_COLUMNS } from '../../initializers/constants'
39import { AccountModel } from '../account/account' 39import { AccountModel } from '../account/account'
40import { ServerModel } from '../server/server' 40import { ServerModel } from '../server/server'
41import { doesExist } from '../shared/query' 41import { doesExist } from '../shared/query'
@@ -375,43 +375,46 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
375 Object.assign(followingWhere, { type: actorType }) 375 Object.assign(followingWhere, { type: actorType })
376 } 376 }
377 377
378 const query = { 378 const getQuery = (forCount: boolean) => {
379 distinct: true, 379 const actorModel = forCount
380 offset: start, 380 ? ActorModel.unscoped()
381 limit: count, 381 : ActorModel
382 order: getFollowsSort(sort), 382
383 where: followWhere, 383 return {
384 include: [ 384 distinct: true,
385 { 385 offset: start,
386 model: ActorModel, 386 limit: count,
387 required: true, 387 order: getFollowsSort(sort),
388 as: 'ActorFollower', 388 where: followWhere,
389 where: { 389 include: [
390 id 390 {
391 } 391 model: actorModel,
392 }, 392 required: true,
393 { 393 as: 'ActorFollower',
394 model: ActorModel, 394 where: {
395 as: 'ActorFollowing', 395 id
396 required: true,
397 where: followingWhere,
398 include: [
399 {
400 model: ServerModel,
401 required: true
402 } 396 }
403 ] 397 },
404 } 398 {
405 ] 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 }
406 } 412 }
407 413
408 return ActorFollowModel.findAndCountAll<MActorFollowActorsDefault>(query) 414 return Promise.all([
409 .then(({ rows, count }) => { 415 ActorFollowModel.count(getQuery(true)),
410 return { 416 ActorFollowModel.findAll<MActorFollowActorsDefault>(getQuery(false))
411 data: rows, 417 ]).then(([ total, data ]) => ({ total, data }))
412 total: count
413 }
414 })
415 } 418 }
416 419
417 static listFollowersForApi (options: { 420 static listFollowersForApi (options: {
@@ -429,11 +432,17 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
429 const followerWhere: WhereOptions = {} 432 const followerWhere: WhereOptions = {}
430 433
431 if (search) { 434 if (search) {
432 Object.assign(followWhere, { 435 const escapedSearch = ActorFollowModel.sequelize.escape('%' + search + '%')
433 [Op.or]: [ 436
434 searchAttribute(search, '$ActorFollower.preferredUsername$'), 437 Object.assign(followerWhere, {
435 searchAttribute(search, '$ActorFollower.Server.host$') 438 id: {
436 ] 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 }
437 }) 446 })
438 } 447 }
439 448
@@ -441,39 +450,43 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
441 Object.assign(followerWhere, { type: actorType }) 450 Object.assign(followerWhere, { type: actorType })
442 } 451 }
443 452
444 const query = { 453 const getQuery = (forCount: boolean) => {
445 distinct: true, 454 const actorModel = forCount
446 offset: start, 455 ? ActorModel.unscoped()
447 limit: count, 456 : ActorModel
448 order: getFollowsSort(sort), 457
449 where: followWhere, 458 return {
450 include: [ 459 distinct: true,
451 { 460
452 model: ActorModel, 461 offset: start,
453 required: true, 462 limit: count,
454 as: 'ActorFollower', 463 order: getFollowsSort(sort),
455 where: followerWhere 464 where: followWhere,
456 }, 465 include: [
457 { 466 {
458 model: ActorModel, 467 model: actorModel,
459 as: 'ActorFollowing', 468 required: true,
460 required: true, 469 as: 'ActorFollower',
461 where: { 470 where: followerWhere
462 id: { 471 },
463 [Op.in]: actorIds 472 {
473 model: actorModel,
474 as: 'ActorFollowing',
475 required: true,
476 where: {
477 id: {
478 [Op.in]: actorIds
479 }
464 } 480 }
465 } 481 }
466 } 482 ]
467 ] 483 }
468 } 484 }
469 485
470 return ActorFollowModel.findAndCountAll<MActorFollowActorsDefault>(query) 486 return Promise.all([
471 .then(({ rows, count }) => { 487 ActorFollowModel.count(getQuery(true)),
472 return { 488 ActorFollowModel.findAll<MActorFollowActorsDefault>(getQuery(false))
473 data: rows, 489 ]).then(([ total, data ]) => ({ total, data }))
474 total: count
475 }
476 })
477 } 490 }
478 491
479 static listSubscriptionsForApi (options: { 492 static listSubscriptionsForApi (options: {
@@ -497,58 +510,68 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
497 }) 510 })
498 } 511 }
499 512
500 const query = { 513 const getQuery = (forCount: boolean) => {
501 attributes: [], 514 let channelInclude: Includeable[] = []
502 distinct: true, 515
503 offset: start, 516 if (forCount !== true) {
504 limit: count, 517 channelInclude = [
505 order: getSort(sort), 518 {
506 where, 519 attributes: {
507 include: [ 520 exclude: unusedActorAttributesForAPI
508 { 521 },
509 attributes: [ 'id' ], 522 model: ActorModel,
510 model: ActorModel.unscoped(), 523 required: true
511 as: 'ActorFollowing', 524 },
512 required: true, 525 {
513 include: [ 526 model: AccountModel.unscoped(),
514 { 527 required: true,
515 model: VideoChannelModel.unscoped(), 528 include: [
516 required: true, 529 {
517 include: [ 530 attributes: {
518 { 531 exclude: unusedActorAttributesForAPI
519 attributes: {
520 exclude: unusedActorAttributesForAPI
521 },
522 model: ActorModel,
523 required: true
524 }, 532 },
525 { 533 model: ActorModel,
526 model: AccountModel.unscoped(), 534 required: true
527 required: true, 535 }
528 include: [ 536 ]
529 { 537 }
530 attributes: { 538 ]
531 exclude: unusedActorAttributesForAPI 539 }
532 }, 540
533 model: ActorModel, 541 return {
534 required: true 542 attributes: forCount === true
535 } 543 ? []
536 ] 544 : SORTABLE_COLUMNS.USER_SUBSCRIPTIONS,
537 } 545 distinct: true,
538 ] 546 offset: start,
539 } 547 limit: count,
540 ] 548 order: getSort(sort),
541 } 549 where,
542 ] 550 include: [
551 {
552 attributes: [ 'id' ],
553 model: ActorModel.unscoped(),
554 as: 'ActorFollowing',
555 required: true,
556 include: [
557 {
558 model: VideoChannelModel.unscoped(),
559 required: true,
560 include: channelInclude
561 }
562 ]
563 }
564 ]
565 }
543 } 566 }
544 567
545 return ActorFollowModel.findAndCountAll<MActorFollowSubscriptions>(query) 568 return Promise.all([
546 .then(({ rows, count }) => { 569 ActorFollowModel.count(getQuery(true)),
547 return { 570 ActorFollowModel.findAll<MActorFollowSubscriptions>(getQuery(false))
548 data: rows.map(r => r.ActorFollowing.VideoChannel), 571 ]).then(([ total, rows ]) => ({
549 total: count 572 total,
550 } 573 data: rows.map(r => r.ActorFollowing.VideoChannel)
551 }) 574 }))
552 } 575 }
553 576
554 static async keepUnfollowedInstance (hosts: string[]) { 577 static async keepUnfollowedInstance (hosts: string[]) {