[CORL-744] Index Optimizations (#2694)

* fix: added comment moderation index

* fix: improved user indexing
This commit is contained in:
Wyatt Johnson
2019-11-07 16:32:38 -05:00
committed by Kim Gardner
parent 8ee74b7d24
commit 3394628f27
7 changed files with 108 additions and 35 deletions
@@ -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 : []),
};
+8
View File
@@ -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;
+9 -5
View File
@@ -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,
};
}
+32 -26
View File
@@ -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)
);
@@ -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 } },
}
);
}
}
+5 -4
View File
@@ -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: "",
},
},