mirror of
https://github.com/wassname/talk.git
synced 2026-08-14 12:50:17 +08:00
[CORL-744] Index Optimizations (#2694)
* fix: added comment moderation index * fix: improved user indexing
This commit is contained in:
committed by
Kim Gardner
parent
8ee74b7d24
commit
3394628f27
@@ -49,4 +49,5 @@ export const User: GQLUserTypeResolver<user.User> = {
|
||||
maybeLoadOnlyIgnoredUserID(ctx, info, ignoredUsers),
|
||||
ignoreable: ({ role }) => !roleIsStaff(role),
|
||||
recentCommentHistory: ({ id }): RecentCommentHistoryInput => ({ userID: id }),
|
||||
profiles: ({ profiles }) => (profiles ? profiles : []),
|
||||
};
|
||||
|
||||
@@ -19,6 +19,10 @@ export function hasStaffRole(user: Pick<User, "role">) {
|
||||
}
|
||||
|
||||
export function getSSOProfile(user: Pick<User, "profiles">) {
|
||||
if (!user.profiles) {
|
||||
return;
|
||||
}
|
||||
|
||||
return user.profiles.find(profile => profile.type === "sso") as
|
||||
| SSOProfile
|
||||
| undefined;
|
||||
@@ -45,6 +49,10 @@ export function getLocalProfile(
|
||||
user: Pick<User, "profiles">,
|
||||
withEmail?: string
|
||||
): LocalProfile | undefined {
|
||||
if (!user.profiles) {
|
||||
return;
|
||||
}
|
||||
|
||||
const profile = user.profiles.find(({ type }) => type === "local") as
|
||||
| LocalProfile
|
||||
| undefined;
|
||||
|
||||
@@ -394,9 +394,10 @@ export interface User extends TenantResource {
|
||||
emailVerified?: boolean;
|
||||
|
||||
/**
|
||||
* profiles is the array of profiles assigned to the user.
|
||||
* profiles is the array of profiles assigned to the user. When a user deletes
|
||||
* their account, this is unset.
|
||||
*/
|
||||
profiles: Profile[];
|
||||
profiles?: Profile[];
|
||||
|
||||
/**
|
||||
* tokens lists the access tokens associated with the account.
|
||||
@@ -506,7 +507,6 @@ async function findOrCreateUserInput(
|
||||
digestFrequency: GQLDIGEST_FREQUENCY.NONE,
|
||||
},
|
||||
moderatorNotes: [],
|
||||
profiles: [],
|
||||
digests: [],
|
||||
createdAt: now,
|
||||
};
|
||||
@@ -521,17 +521,20 @@ async function findOrCreateUserInput(
|
||||
});
|
||||
}
|
||||
|
||||
// Store the user's profiles in a new array.
|
||||
const profiles: Profile[] = [];
|
||||
|
||||
// Mutate the profiles to ensure we mask handle any secrets.
|
||||
switch (profile.type) {
|
||||
case "local": {
|
||||
// Hash the user's password with bcrypt.
|
||||
const password = await hashPassword(profile.password);
|
||||
defaults.profiles.push({ ...profile, password });
|
||||
profiles.push({ ...profile, password });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Push the profile onto the User.
|
||||
defaults.profiles.push(profile);
|
||||
profiles.push(profile);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -539,6 +542,7 @@ async function findOrCreateUserInput(
|
||||
return {
|
||||
...defaults,
|
||||
...input,
|
||||
profiles,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@ type IndexCreationFunction<T> = (
|
||||
indexOptions?: IndexOptions
|
||||
) => Promise<string>;
|
||||
|
||||
export function createIndexFactory<T>(
|
||||
collection: Collection<T>
|
||||
): IndexCreationFunction<T> {
|
||||
export async function createIndex<T>(
|
||||
collection: Collection<T>,
|
||||
indexSpec: IndexSpecification<T>,
|
||||
indexOptions: IndexOptions = {}
|
||||
) {
|
||||
const log = logger.child(
|
||||
{
|
||||
collectionName: collection.collectionName,
|
||||
@@ -27,34 +29,38 @@ export function createIndexFactory<T>(
|
||||
true
|
||||
);
|
||||
|
||||
try {
|
||||
// Try to create the index.
|
||||
const start = now();
|
||||
log.debug({ indexSpec, indexOptions }, "creating index");
|
||||
const indexName = await collection.createIndex(indexSpec, indexOptions);
|
||||
log.debug(
|
||||
{ indexName, indexSpec, indexOptions, took: Math.round(now() - start) },
|
||||
"index was created"
|
||||
);
|
||||
|
||||
// Match the interface from the `createIndex` function by returning the
|
||||
// index name.
|
||||
return indexName;
|
||||
} catch (err) {
|
||||
log.error({ err, indexSpec, indexOptions }, "could not create index");
|
||||
|
||||
// Rethrow the error here.
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export function createIndexFactory<T>(
|
||||
collection: Collection<T>
|
||||
): IndexCreationFunction<T> {
|
||||
return async (
|
||||
indexSpec: IndexSpecification<T>,
|
||||
indexOptions: IndexOptions = {}
|
||||
) => {
|
||||
try {
|
||||
// Try to create the index.
|
||||
const start = now();
|
||||
log.debug({ indexSpec, indexOptions }, "creating index");
|
||||
const indexName = await collection.createIndex(indexSpec, indexOptions);
|
||||
log.debug(
|
||||
{ indexName, indexSpec, indexOptions, took: Math.round(now() - start) },
|
||||
"index was created"
|
||||
);
|
||||
|
||||
// Match the interface from the `createIndex` function by returning the
|
||||
// index name.
|
||||
return indexName;
|
||||
} catch (err) {
|
||||
log.error({ err, indexSpec, indexOptions }, "could not create index");
|
||||
|
||||
// Rethrow the error here.
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
) => createIndex(collection, indexSpec, indexOptions);
|
||||
}
|
||||
|
||||
export function createConnectionOrderVariants<T>(
|
||||
createIndex: IndexCreationFunction<T>,
|
||||
createIndexFn: IndexCreationFunction<T>,
|
||||
variants: Array<IndexSpecification<T>>,
|
||||
indexOptions: IndexOptions = { background: true }
|
||||
) {
|
||||
@@ -69,7 +75,7 @@ export function createConnectionOrderVariants<T>(
|
||||
* @param variantSpec the spec that makes this variant different
|
||||
*/
|
||||
const createIndexVariant = (variantSpec: IndexSpecification<T>) =>
|
||||
createIndex(
|
||||
createIndexFn(
|
||||
merge({}, indexSpec, variantSpec),
|
||||
merge({}, indexOptions, variantIndexOptions)
|
||||
);
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { Db } from "mongodb";
|
||||
|
||||
import Migration from "coral-server/services/migrate/migration";
|
||||
import collections from "coral-server/services/mongodb/collections";
|
||||
|
||||
import { createIndexFactory } from "../indexing";
|
||||
|
||||
export default class extends Migration {
|
||||
public async indexes(mongo: Db) {
|
||||
const createIndex = createIndexFactory(
|
||||
collections.commentModerationActions(mongo)
|
||||
);
|
||||
|
||||
await createIndex(
|
||||
{ tenantID: 1, commentID: 1, createdAt: -1 },
|
||||
{ background: true }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Db } from "mongodb";
|
||||
|
||||
import Migration from "coral-server/services/migrate/migration";
|
||||
import collections from "coral-server/services/mongodb/collections";
|
||||
|
||||
import { createIndex } from "../indexing";
|
||||
|
||||
export default class extends Migration {
|
||||
public async indexes(mongo: Db) {
|
||||
// Drop the old index.
|
||||
await collections
|
||||
.users(mongo)
|
||||
.dropIndex("tenantID_1_profiles.type_1_profiles.id_1");
|
||||
|
||||
// Clean up the old users that have deleted their accounts.
|
||||
await collections
|
||||
.users(mongo)
|
||||
.updateMany({ profiles: [] }, { $unset: { profiles: "" } });
|
||||
|
||||
// Add the new index.
|
||||
await createIndex(
|
||||
collections.users(mongo),
|
||||
{
|
||||
tenantID: 1,
|
||||
"profiles.id": 1,
|
||||
"profiles.type": 1,
|
||||
},
|
||||
{
|
||||
unique: true,
|
||||
partialFilterExpression: { profiles: { $exists: true } },
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,8 @@ async function deleteUserActionCounts(
|
||||
async function deleteUserComments(
|
||||
mongo: Db,
|
||||
authorID: string,
|
||||
tenantID: string
|
||||
tenantID: string,
|
||||
now: Date
|
||||
) {
|
||||
await collections.comments(mongo).updateMany(
|
||||
{ tenantID, authorID },
|
||||
@@ -112,7 +113,7 @@ async function deleteUserComments(
|
||||
authorID: null,
|
||||
revisions: [],
|
||||
tags: [],
|
||||
deleted: true,
|
||||
deletedAt: now,
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -143,17 +144,17 @@ export async function deleteUser(
|
||||
await deleteUserActionCounts(mongo, userID, tenantID);
|
||||
|
||||
// Delete the user's comments.
|
||||
await deleteUserComments(mongo, userID, tenantID);
|
||||
await deleteUserComments(mongo, userID, tenantID, now);
|
||||
|
||||
// Mark the user as deleted.
|
||||
const result = await collections.users(mongo).findOneAndUpdate(
|
||||
{ tenantID, id: userID },
|
||||
{
|
||||
$set: {
|
||||
profiles: [],
|
||||
deletedAt: now,
|
||||
},
|
||||
$unset: {
|
||||
profiles: "",
|
||||
email: "",
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user