diff --git a/src/models/followed-log.ts b/src/models/followed-log.ts new file mode 100644 index 000000000..4d8ecf684 --- /dev/null +++ b/src/models/followed-log.ts @@ -0,0 +1,11 @@ +import { ObjectID } from 'mongodb'; +import db from '../db/mongodb'; + +const FollowedLog = db.get('followedLogs'); +export default FollowedLog; + +export type IFollowedLog = { + _id: ObjectID; + userId: ObjectID; + count: number; +}; diff --git a/src/models/following-log.ts b/src/models/following-log.ts new file mode 100644 index 000000000..f18707db8 --- /dev/null +++ b/src/models/following-log.ts @@ -0,0 +1,11 @@ +import { ObjectID } from 'mongodb'; +import db from '../db/mongodb'; + +const FollowingLog = db.get('followingLogs'); +export default FollowingLog; + +export type IFollowingLog = { + _id: ObjectID; + userId: ObjectID; + count: number; +}; diff --git a/src/models/following.ts b/src/models/following.ts index fe9ce550d..b4090d8c7 100644 --- a/src/models/following.ts +++ b/src/models/following.ts @@ -8,7 +8,6 @@ export default Following; export type IFollowing = { _id: mongo.ObjectID; createdAt: Date; - deletedAt: Date; followeeId: mongo.ObjectID; followerId: mongo.ObjectID; }; diff --git a/src/models/user.ts b/src/models/user.ts index d3c94cab3..f817c33aa 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -234,8 +234,7 @@ export const pack = ( _user.isFollowing = (async () => { const follow = await Following.findOne({ followerId: meId, - followeeId: _user.id, - deletedAt: { $exists: false } + followeeId: _user.id }); return follow !== null; })(); @@ -244,8 +243,7 @@ export const pack = ( _user.isFollowed = (async () => { const follow2 = await Following.findOne({ followerId: _user.id, - followeeId: meId, - deletedAt: { $exists: false } + followeeId: meId }); return follow2 !== null; })(); @@ -275,15 +273,13 @@ export const pack = ( // Get following you know count _user.followingYouKnowCount = Following.count({ followeeId: { $in: myFollowingIds }, - followerId: _user.id, - deletedAt: { $exists: false } + followerId: _user.id }); // Get followers you know count _user.followersYouKnowCount = Following.count({ followeeId: _user.id, - followerId: { $in: myFollowingIds }, - deletedAt: { $exists: false } + followerId: { $in: myFollowingIds } }); } } diff --git a/src/post/distribute.ts b/src/post/distribute.ts index 3925d4128..4def2c27f 100644 --- a/src/post/distribute.ts +++ b/src/post/distribute.ts @@ -48,9 +48,7 @@ export default async (user, mentions, post) => { // Fetch all followers const followers = await Following .find({ - followeeId: user._id, - // 削除されたドキュメントは除く - deletedAt: { $exists: false } + followeeId: user._id }, { followerId: true, _id: false diff --git a/src/processor/http/follow.ts b/src/processor/http/follow.ts index 7ec1ee675..29ac9fa55 100644 --- a/src/processor/http/follow.ts +++ b/src/processor/http/follow.ts @@ -3,6 +3,8 @@ import { sign } from 'http-signature'; import { URL } from 'url'; import User, { isLocalUser, pack as packUser } from '../../models/user'; import Following from '../../models/following'; +import FollowingLog from '../../models/following-log'; +import FollowedLog from '../../models/followed-log'; import event from '../../publishers/stream'; import notify from '../../publishers/notify'; import context from '../../remote/activitypub/renderer/context'; @@ -21,6 +23,11 @@ export default ({ data }, done) => Following.findOne({ _id: data.following }).th } }), + promisedFollower.then(({ followingCount }) => FollowingLog.insert({ + userId: followerId, + count: followingCount + 1 + })), + // Increment followers count User.update({ _id: followeeId }, { $inc: { @@ -28,6 +35,11 @@ export default ({ data }, done) => Following.findOne({ _id: data.following }).th } }), + promisedFollowee.then(({ followersCount }) => FollowedLog.insert({ + userId: followerId, + count: followersCount + 1 + })), + // Notify promisedFollowee.then(followee => followee.host === null ? notify(followeeId, followerId, 'follow') : null), diff --git a/src/server/api/common/get-friends.ts b/src/server/api/common/get-friends.ts index e0942e029..c1cc3957d 100644 --- a/src/server/api/common/get-friends.ts +++ b/src/server/api/common/get-friends.ts @@ -6,9 +6,7 @@ export default async (me: mongodb.ObjectID, includeMe: boolean = true) => { // SELECT followee const myfollowing = await Following .find({ - followerId: me, - // 削除されたドキュメントは除く - deletedAt: { $exists: false } + followerId: me }, { fields: { followeeId: true diff --git a/src/server/api/endpoints/aggregation/users/followers.ts b/src/server/api/endpoints/aggregation/users/followers.ts index dda34ed7b..580d31a3f 100644 --- a/src/server/api/endpoints/aggregation/users/followers.ts +++ b/src/server/api/endpoints/aggregation/users/followers.ts @@ -2,8 +2,9 @@ * Module dependencies */ import $ from 'cafy'; +import { ObjectID } from 'mongodb'; import User from '../../../../../models/user'; -import Following from '../../../../../models/following'; +import FollowedLog from '../../../../../models/followed-log'; /** * Aggregate followers of a user @@ -29,47 +30,36 @@ module.exports = (params) => new Promise(async (res, rej) => { return rej('user not found'); } - const startTime = new Date(new Date().setMonth(new Date().getMonth() - 1)); - - const following = await Following - .find({ - followeeId: user._id, - $or: [ - { deletedAt: { $exists: false } }, - { deletedAt: { $gt: startTime } } - ] - }, { - sort: { createdAt: -1 }, - fields: { - _id: false, - followerId: false, - followeeId: false - } - }); - + const today = new Date(); const graph = []; + today.setMinutes(0); + today.setSeconds(0); + today.setMilliseconds(0); + + let cursorDate = new Date(today.getTime()); + let cursorTime = cursorDate.setDate(new Date(today.getTime()).getDate() + 1); + for (let i = 0; i < 30; i++) { - let day = new Date(new Date().setDate(new Date().getDate() - i)); - day = new Date(day.setMilliseconds(999)); - day = new Date(day.setSeconds(59)); - day = new Date(day.setMinutes(59)); - day = new Date(day.setHours(23)); - // day = day.getTime(); + graph.push(FollowedLog.findOne({ + _id: { $lt: ObjectID.createFromTime(cursorTime / 1000) }, + userId: user._id + }, { + sort: { _id: -1 }, + }).then(log => { + cursorDate = new Date(today.getTime()); + cursorTime = cursorDate.setDate(today.getDate() - i); - const count = following.filter(f => - f.createdAt < day && (f.deletedAt == null || f.deletedAt > day) - ).length; - - graph.push({ - date: { - year: day.getFullYear(), - month: day.getMonth() + 1, // In JavaScript, month is zero-based. - day: day.getDate() - }, - count: count - }); + return { + date: { + year: cursorDate.getFullYear(), + month: cursorDate.getMonth() + 1, // In JavaScript, month is zero-based. + day: cursorDate.getDate() + }, + count: log ? log.count : 0 + }; + })); } - res(graph); + res(await Promise.all(graph)); }); diff --git a/src/server/api/endpoints/aggregation/users/following.ts b/src/server/api/endpoints/aggregation/users/following.ts index cd08d89e4..3ac0e3a53 100644 --- a/src/server/api/endpoints/aggregation/users/following.ts +++ b/src/server/api/endpoints/aggregation/users/following.ts @@ -2,8 +2,9 @@ * Module dependencies */ import $ from 'cafy'; +import { ObjectID } from 'mongodb'; import User from '../../../../../models/user'; -import Following from '../../../../../models/following'; +import FollowingLog from '../../../../../models/following-log'; /** * Aggregate following of a user @@ -29,46 +30,36 @@ module.exports = (params) => new Promise(async (res, rej) => { return rej('user not found'); } - const startTime = new Date(new Date().setMonth(new Date().getMonth() - 1)); - - const following = await Following - .find({ - followerId: user._id, - $or: [ - { deletedAt: { $exists: false } }, - { deletedAt: { $gt: startTime } } - ] - }, { - sort: { createdAt: -1 }, - fields: { - _id: false, - followerId: false, - followeeId: false - } - }); - + const today = new Date(); const graph = []; + today.setMinutes(0); + today.setSeconds(0); + today.setMilliseconds(0); + + let cursorDate = new Date(today.getTime()); + let cursorTime = cursorDate.setDate(new Date(today.getTime()).getDate() + 1); + for (let i = 0; i < 30; i++) { - let day = new Date(new Date().setDate(new Date().getDate() - i)); - day = new Date(day.setMilliseconds(999)); - day = new Date(day.setSeconds(59)); - day = new Date(day.setMinutes(59)); - day = new Date(day.setHours(23)); + graph.push(FollowingLog.findOne({ + _id: { $lt: ObjectID.createFromTime(cursorTime / 1000) }, + userId: user._id + }, { + sort: { _id: -1 }, + }).then(log => { + cursorDate = new Date(today.getTime()); + cursorTime = cursorDate.setDate(today.getDate() - i); - const count = following.filter(f => - f.createdAt < day && (f.deletedAt == null || f.deletedAt > day) - ).length; - - graph.push({ - date: { - year: day.getFullYear(), - month: day.getMonth() + 1, // In JavaScript, month is zero-based. - day: day.getDate() - }, - count: count - }); + return { + date: { + year: cursorDate.getFullYear(), + month: cursorDate.getMonth() + 1, // In JavaScript, month is zero-based. + day: cursorDate.getDate() + }, + count: log ? log.count : 0 + }; + })); } - res(graph); + res(await Promise.all(graph)); }); diff --git a/src/server/api/endpoints/following/create.ts b/src/server/api/endpoints/following/create.ts index 03c13ab7f..e56859521 100644 --- a/src/server/api/endpoints/following/create.ts +++ b/src/server/api/endpoints/following/create.ts @@ -42,8 +42,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Check if already following const exist = await Following.findOne({ followerId: follower._id, - followeeId: followee._id, - deletedAt: { $exists: false } + followeeId: followee._id }); if (exist !== null) { diff --git a/src/server/api/endpoints/following/delete.ts b/src/server/api/endpoints/following/delete.ts index 3facfdcdd..5deddc919 100644 --- a/src/server/api/endpoints/following/delete.ts +++ b/src/server/api/endpoints/following/delete.ts @@ -42,8 +42,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Check not following const exist = await Following.findOne({ followerId: follower._id, - followeeId: followee._id, - deletedAt: { $exists: false } + followeeId: followee._id }); if (exist === null) { @@ -51,12 +50,8 @@ module.exports = (params, user) => new Promise(async (res, rej) => { } // Delete following - await Following.update({ + await Following.findOneAndDelete({ _id: exist._id - }, { - $set: { - deletedAt: new Date() - } }); // Send response diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts index 39b69a6aa..0222313e8 100644 --- a/src/server/api/endpoints/users/followers.ts +++ b/src/server/api/endpoints/users/followers.ts @@ -46,8 +46,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Construct query const query = { - followeeId: user._id, - deletedAt: { $exists: false } + followeeId: user._id } as any; // ログインしていてかつ iknow フラグがあるとき diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts index aa6628dde..2372f57fb 100644 --- a/src/server/api/endpoints/users/following.ts +++ b/src/server/api/endpoints/users/following.ts @@ -46,8 +46,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Construct query const query = { - followerId: user._id, - deletedAt: { $exists: false } + followerId: user._id } as any; // ログインしていてかつ iknow フラグがあるとき diff --git a/test/api.js b/test/api.js index 962730836..98d3f2faf 100644 --- a/test/api.js +++ b/test/api.js @@ -609,20 +609,6 @@ describe('API', () => { res.should.have.status(204); })); - it('過去にフォロー歴があった状態でフォローできる', async(async () => { - const hima = await insertHimawari(); - const me = await insertSakurako(); - await db.get('following').insert({ - followeeId: hima._id, - followerId: me._id, - deletedAt: new Date() - }); - const res = await request('/following/create', { - userId: hima._id.toString() - }, me); - res.should.have.status(204); - })); - it('既にフォローしている場合は怒る', async(async () => { const hima = await insertHimawari(); const me = await insertSakurako();