Skip to content

Commit

Permalink
Merge fee2a8b into b04e7a3
Browse files Browse the repository at this point in the history
  • Loading branch information
s-cheng committed May 2, 2024
2 parents b04e7a3 + fee2a8b commit 01fcb87
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 14 deletions.
35 changes: 24 additions & 11 deletions packages/lesswrong/lib/sql/UpdateQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,31 @@ class UpdateQuery<T extends DbObject> extends Query<T> {
value: any,
format: (resolvedField: string, updateValue: Atom<T>[]) => Atom<T>[],
): Atom<T>[] {
try {
// If we're updating the value of a JSON blob without totally replacing
// it then we need to wrap the update in a call to `JSONB_SET`.
if (field.includes(".")) {
const updateValue = this.compileUpdateExpression(value, { skipTypeHint: true });
const {column, path} = this.buildJsonUpdatePath(field);
return format(
column,
["JSONB_SET(", column, ",", path, "::TEXT[],", ...updateValue, ", TRUE)"],
);
// If we're updating the value of a JSON blob without totally replacing
// it then we need to wrap the update in a call to `JSONB_SET`.
if (field.includes(".")) {
const updateValue = this.compileUpdateExpression(value);
const {column, path} = this.buildJsonUpdatePath(field);
// Check if we're trying to unset the field (currently you can only unset fields in the first level)
if (value === null) {
const fieldTokens = field.split(".")
if (fieldTokens.length === 2) {
return format(
column,
[column, ` - '${fieldTokens[1]}'`],
);
} else {
throw new Error(`Unsetting a field past the first level of a JSON blob is not yet supported`)
}
}


return format(
column,
["JSONB_SET(", column, ",", path, "::TEXT[], TO_JSONB(", ...updateValue, "), TRUE)"],
);
}

try {
const fieldType = this.getField(field);
const arrayValueInNonArrayJsonbField = fieldType && !fieldType.isArray() && fieldType.toConcrete() instanceof JsonType && Array.isArray(value);
const typeForArg = arrayValueInNonArrayJsonbField ? new JsonType() : undefined;
Expand Down
20 changes: 20 additions & 0 deletions packages/lesswrong/server/repos/PostsRepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,26 @@ class PostsRepo extends AbstractRepo<"Posts"> {
constructor() {
super(Posts);
}

moveCoauthorshipToNewUser(oldUserId: string, newUserId: string): Promise<null> {
return this.none(`
-- PostsRepo.moveCoauthorshipToNewUser
UPDATE "Posts"
SET "coauthorStatuses" = array(
SELECT
CASE
WHEN (jsonb_elem->>'userId') = $1
THEN jsonb_set(jsonb_elem, '{userId}', to_jsonb($2::text), false)
ELSE jsonb_elem
END
FROM unnest("coauthorStatuses") AS t(jsonb_elem)
)
WHERE EXISTS (
SELECT 1 FROM unnest("coauthorStatuses") AS sub(jsonb_sub)
WHERE jsonb_sub->>'userId' = $1
);
`, [oldUserId, newUserId]);
}

async postRouteWillDefinitelyReturn200(id: string): Promise<boolean> {
const res = await this.getRawDb().oneOrNone<{exists: boolean}>(`
Expand Down
17 changes: 15 additions & 2 deletions packages/lesswrong/server/scripts/mergeAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Votes } from '../../lib/collections/votes/index';
import { Conversations } from '../../lib/collections/conversations/collection'
import { asyncForeachSequential } from '../../lib/utils/asyncUtils';
import sumBy from 'lodash/sumBy';
import { ConversationsRepo, LocalgroupsRepo, VotesRepo } from '../repos';
import { ConversationsRepo, LocalgroupsRepo, PostsRepo, VotesRepo } from '../repos';
import Localgroups from '../../lib/collections/localgroups/collection';
import { collectionsThatAffectKarma } from '../callbacks/votingCallbacks';
import { filterNonnull, filterWhereFieldsNotNull } from '../../lib/utils/typeGuardUtils';
Expand Down Expand Up @@ -189,6 +189,10 @@ Vulcan.mergeAccounts = async ({sourceUserId, targetUserId, dryRun}: {

// Transfer posts
await transferCollection({sourceUserId, targetUserId, collectionName: "Posts", dryRun})
// Transfer post co-authorship
if (!dryRun) {
await new PostsRepo().moveCoauthorshipToNewUser(sourceUserId, targetUserId)
}

// Transfer comments
await transferCollection({sourceUserId, targetUserId, collectionName: "Comments", dryRun})
Expand All @@ -207,11 +211,20 @@ Vulcan.mergeAccounts = async ({sourceUserId, targetUserId, dryRun}: {

// Transfer reports (i.e. user reporting a comment/tag/etc)
await transferCollection({sourceUserId, targetUserId, collectionName: "Reports", dryRun})

// Transfer election votes
await transferCollection({sourceUserId, targetUserId, collectionName: "ElectionVotes", dryRun})

// Transfer moderator actions
await transferCollection({sourceUserId, targetUserId, collectionName: "ModeratorActions", dryRun})

// Transfer user rate limits
await transferCollection({sourceUserId, targetUserId, collectionName: "UserRateLimits", dryRun})

try {
const [sourceConversationsCount, targetConversationsCount] = await Promise.all([
Conversations.find({participantIds: sourceUserId}).count(),
Conversations.find({participantIds: sourceUserId}).count()
Conversations.find({participantIds: targetUserId}).count()
])
// eslint-disable-next-line no-console
console.log(`conversations from source user: ${sourceConversationsCount}`)
Expand Down
13 changes: 12 additions & 1 deletion packages/lesswrong/unitTests/sql/UpdateQuery.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,20 @@ describe("UpdateQuery", () => {
{
name: "can set a value inside a JSON blob",
getQuery: () => new UpdateQuery<DbTestObject>(testTable, {a: 3}, {$set: {"c.d.e": "hello world"}}),
expectedSql: `UPDATE "TestCollection" SET "c" = JSONB_SET( "c" , '{d, e}' ::TEXT[], $1 , TRUE) WHERE "a" = $2 RETURNING "_id"`,
expectedSql: `UPDATE "TestCollection" SET "c" = JSONB_SET( "c" , '{d, e}' ::TEXT[], TO_JSONB( $1::TEXT ), TRUE) WHERE "a" = $2 RETURNING "_id"`,
expectedArgs: ["hello world", 3],
},
{
name: "can delete a value at the first level of a JSON blob",
getQuery: () => new UpdateQuery<DbTestObject>(testTable, {a: 3}, {$set: {"c.d": null}}),
expectedSql: `UPDATE "TestCollection" SET "c" = "c" - 'd' WHERE "a" = $1 RETURNING "_id"`,
expectedArgs: [3],
},
{
name: "throws an error when trying to delete a value at a further level of a JSON blob",
getQuery: () => new UpdateQuery<DbTestObject>(testTable, {a: 3}, {$set: {"c.d.e": null}}),
expectedError: 'Unsetting a field past the first level of a JSON blob is not yet supported',
},
{
name: "can add a value to a set (native arrays)",
getQuery: () => new UpdateQuery<DbTestObject>(testTable, {a: 3}, {$addToSet: {b: "hello world"}}),
Expand Down

0 comments on commit 01fcb87

Please sign in to comment.