Closed MrOrz closed 2 years ago
Migration script (not checked in repo)
// src/scripts/migrations/fillFeedbackAuthors.js
/**
* How to use this script to migrate elasticsearch schema
* 1. First of all, ensure latest API has been deployed; new feedbacks have `replyUserId` and `articleReplyUserId` filled.
* 2. On target machine, update schema and reindex the following indexes using `npm run reload -- <index name>`:
* analytics (optional), articlereplyfeedbacks
* 1. On local machine, put script to under rumors-api project directory's src/scripts
* 2. Start ssh port forwarding `ssh -L 62222:staging.server.path.to:62222 root@staging.server.path.to`
* 3. Add `ELASTICSEARCH_URL=http://localhost:62222` to .env
* 4. Run `npx babel-node src/scripts/migrations/fillFeedbackAuthors.js`
*/
import 'dotenv/config';
import { SingleBar } from 'cli-progress';
import getAllDocs from 'util/getAllDocs';
import client from 'util/client';
import DataLoaders from 'graphql/dataLoaders';
// query to list out article reply feedbacks without replyUserId
const QUERY_TO_PROCESS = {
bool: {
must_not: {
exists: { field: 'replyUserId' },
},
},
};
async function processBatch(feedbackMap) {
let operations = [];
for (const [_id, doc] of feedbackMap) {
operations.push(
{ update: { _index: 'articlereplyfeedbacks', _type: 'doc', _id } },
{ doc }
);
}
await client.bulk({ body: operations });
}
async function main() {
const loader = new DataLoaders();
const bar = new SingleBar({ stopOnComplete: true });
const {
body: { count },
} = await client.count({
index: 'articlereplyfeedbacks',
type: 'doc',
body: {
query: QUERY_TO_PROCESS,
},
});
bar.start(count, 0);
// Feedback ID to doc
let feedbackMap = new Map();
let promises = [];
for await (const { _id: id, _source: feedback } of getAllDocs(
'articlereplyfeedbacks',
QUERY_TO_PROCESS,
{ size: 100 }
)) {
bar.increment();
promises.push(
loader.docLoader
.loadMany([
{
index: 'replies',
id: feedback.replyId,
},
{
index: 'articles',
id: feedback.articleId,
},
])
.then(([{ userId: replyUserId }, article]) => {
const articleReply = article.articleReplies.find(
ar => ar.replyId === feedback.replyId
);
if (!articleReply) return;
const { userId: articleReplyUserId } = articleReply;
feedbackMap.set(id, { replyUserId, articleReplyUserId });
})
);
if (promises.length === 100) {
await Promise.all(promises);
promises = [];
await processBatch(feedbackMap);
feedbackMap = new Map();
}
}
await Promise.all(promises);
await processBatch(feedbackMap);
bar.stop();
}
if (require.main === module) {
main();
}
Test target:
Run result:
Staging run result
articlereplyfeedbacks_v1_1_1
to articlereplyfeedbacks_v1_2_0
in 36 seconds.analytics_v1_0_2
to analytics_v1_2_0
in 576 seconds.Migration report on production
Fixes #280
replyUserId
,articleReplyUserId
forArticleReplyFeedback
CreateOrUpdateArticleReplyFeedback
writesreplyUserId
andarticleReplyUserId
on creationListArticleReplyFeedback
can filter byreplyUserId
,articleReplyUserId
orauthorId
authorId
: lists feedbacks whosereplyUserId
is the specified ID, or whosearticleReplyUserId
is the specified IDreplyUserId
andarticleReplyUserId
for existingArticleReplyFeedback
(not checked in, just for copy & paste use)