From 4767a89032e5935ad9303b91ede687ebc6cdba67 Mon Sep 17 00:00:00 2001 From: Monkey Do Date: Fri, 9 Feb 2024 13:32:46 +0100 Subject: [PATCH] chore(search): Send entity models for indexing With the change from the previous commit (accepting an ORM model rather than JSON for search indexing), we need to rewrite accordingly the parts of the code that use the search indexing. Taking this opportunity to rewrite some code from promises to async/await syntax. --- src/common/helpers/search.ts | 3 - src/server/helpers/collectionRouteUtils.js | 20 +--- src/server/routes/editor.tsx | 33 +++--- src/server/routes/entity/entity.tsx | 33 +++--- .../routes/entity/process-unified-form.ts | 4 +- src/server/routes/register.js | 100 ++++++++---------- 6 files changed, 77 insertions(+), 116 deletions(-) diff --git a/src/common/helpers/search.ts b/src/common/helpers/search.ts index 813e11c60f..57a416ced7 100644 --- a/src/common/helpers/search.ts +++ b/src/common/helpers/search.ts @@ -373,7 +373,6 @@ export async function generateIndex(orm) { type: 'ngram' } } - } }, 'index.mapping.ignore_malformed': true } @@ -473,8 +472,6 @@ export async function generateIndex(orm) { const areas = areaCollection.toJSON({omitPivot: true}); - /** To index names, we use aliasSet.aliases.name and bbid, which Areas don't have. - * We massage the area to return a similar format as BB entities /** To index names, we use aliases.name and bbid, which Areas don't have. * We massage the area to return a similar format as BB entities */ diff --git a/src/server/helpers/collectionRouteUtils.js b/src/server/helpers/collectionRouteUtils.js index bd018365b8..286c50c5a2 100644 --- a/src/server/helpers/collectionRouteUtils.js +++ b/src/server/helpers/collectionRouteUtils.js @@ -16,11 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import * as handler from '../helpers/handler'; import * as search from '../../common/helpers/search'; import {camelCase, differenceWith, isEqual, toLower, upperFirst} from 'lodash'; import {BadRequestError} from '../../common/helpers/error'; - +import log from 'log'; /** * A handler for create or edit actions on collections. @@ -89,22 +88,9 @@ export async function collectionCreateOrEditHandler(req, res, next) { // if it's new or the name is changed, // we need to update this collection in ElasticSearch index if (isNew || res.locals.collection.name !== newCollection.get('name')) { - const collectionPromiseForES = new Promise((resolve) => { - const collectionForES = { - aliasSet: { - aliases: [ - {name: newCollection.get('name')} - ] - }, - bbid: newCollection.get('id'), - id: newCollection.get('id'), - type: 'Collection' - }; - resolve(collectionForES); - }); - return handler.sendPromiseResult(res, collectionPromiseForES, search.indexEntity); + await search.indexEntity(newCollection); } - return res.send(newCollection.toJSON()); + return res.status(200).send(newCollection.toJSON()); } catch (err) { return next(err); diff --git a/src/server/routes/editor.tsx b/src/server/routes/editor.tsx index dd6aa81457..de20c06cd8 100644 --- a/src/server/routes/editor.tsx +++ b/src/server/routes/editor.tsx @@ -20,7 +20,6 @@ import * as auth from '../helpers/auth'; import * as commonUtils from '../../common/helpers/utils'; import * as error from '../../common/helpers/error'; -import * as handler from '../helpers/handler'; import * as propHelpers from '../../client/helpers/props'; import * as search from '../../common/helpers/search'; import {AdminActionType, PrivilegeType} from '../../common/helpers/privileges-utils'; @@ -42,6 +41,7 @@ import _ from 'lodash'; import express from 'express'; import {getOrderedCollectionsForEditorPage} from '../helpers/collections'; import {getOrderedRevisionForEditorPage} from '../helpers/revisions'; +import log from 'log'; import target from '../templates/target'; @@ -122,8 +122,8 @@ function isCurrentUser(reqUserID, sessionUser) { return reqUserID === sessionUser.id; } -router.post('/edit/handler', auth.isAuthenticatedForHandler, (req, res) => { - async function runAsync() { +router.post('/edit/handler', auth.isAuthenticatedForHandler, async (req, res) => { + try { const {Editor} = req.app.locals.orm; if (!isCurrentUser(req.body.id, req.user)) { @@ -140,10 +140,10 @@ router.post('/edit/handler', auth.isAuthenticatedForHandler, (req, res) => { throw new error.NotFoundError('Editor not found', req); }); if (editor.get('areaId') === req.body.areaId && - editor.get('bio') === req.body.bio && - editor.get('name') === req.body.name && - editor.get('titleUnlockId') === req.body.title && - editor.get('genderId') === req.body.genderId + editor.get('bio') === req.body.bio && + editor.get('name') === req.body.name && + editor.get('titleUnlockId') === req.body.title && + editor.get('genderId') === req.body.genderId ) { throw new error.FormSubmissionError('No change to the profile'); } @@ -164,19 +164,14 @@ router.post('/edit/handler', auth.isAuthenticatedForHandler, (req, res) => { }); res.locals.user.name = req.body.name; - const editorJSON = modifiedEditor.toJSON(); - return { - aliasSet: { - aliases: [ - {name: editorJSON.name} - ] - }, - bbid: editorJSON.id, - type: 'Editor' - }; - } - handler.sendPromiseResult(res, runAsync(), search.indexEntity); + await search.indexEntity(modifiedEditor); + + return res.status(200).send(modifiedEditor.toJSON()); + } + catch (err) { + return error.sendErrorAsJSON(res, err); + } }); router.post('/privs/edit/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ADMIN), async (req, res, next) => { diff --git a/src/server/routes/entity/entity.tsx b/src/server/routes/entity/entity.tsx index 6b46762cf3..3cbc39e80a 100644 --- a/src/server/routes/entity/entity.tsx +++ b/src/server/routes/entity/entity.tsx @@ -1165,33 +1165,28 @@ export async function processSingleEntity(formBody, JSONEntity, reqSession, await indexAutoCreatedEditionGroup(orm, savedMainEntity, transacting); } - const entityJSON = savedMainEntity.toJSON({omitPivot: true}); - if (entityJSON && entityJSON.relationshipSet) { - entityJSON.relationshipSet.relationships = await Promise.all(entityJSON.relationshipSet.relationships.map(async (rel) => { + const entityRelationships = savedMainEntity.related('relationshipSet')?.related('relationships'); + if (savedMainEntity.get('type') === 'Work' && entityRelationships?.length) { + const authorsOfWork = await Promise.all(entityRelationships.toJSON().filter( + // "Author wrote Work" relationship + (relation) => relation.typeId === 8 + ).map(async (relationshipJSON) => { try { - rel.source = await commonUtils.getEntity(orm, rel.source.bbid, rel.source.type, {require: false, transacting}); - rel.target = await commonUtils.getEntity(orm, rel.target.bbid, rel.target.type, {require: false, transacting}); + const {source} = relationshipJSON; + const sourceEntity = await commonUtils.getEntity( + orm, source.bbid, source.type, {require: false, transacting} + ); + return sourceEntity.name; } catch (err) { log.error(err); } - return rel; + return null; })); // Attach a work's authors for search indexing - if (savedMainEntity.get('type') === 'Work') { - const authorsOfWork = entityJSON.relationshipSet.relationships - .filter( - // "Author wrote Work" relationship - (relation) => relation.typeId === 8 - ) - .map((relation) => { - const {source} = relation; - return source.name; - }); - entityJSON.authors = authorsOfWork; - } + savedMainEntity.set('authors', authorsOfWork.filter(Boolean)); } - return entityJSON; + return savedMainEntity; } catch (err) { log.error(err); diff --git a/src/server/routes/entity/process-unified-form.ts b/src/server/routes/entity/process-unified-form.ts index 7b3726904c..9a1fdf5812 100644 --- a/src/server/routes/entity/process-unified-form.ts +++ b/src/server/routes/entity/process-unified-form.ts @@ -2,6 +2,7 @@ import * as achievement from '../../helpers/achievement'; import type {Request as $Request, Response as $Response} from 'express'; import {FormSubmissionError, sendErrorAsJSON} from '../../../common/helpers/error'; +import {_bulkIndexEntities, getDocumentToIndex} from '../../../common/helpers/search'; import {authorToFormState, transformNewForm as authorTransform} from './author'; import {editionGroupToFormState, transformNewForm as editionGroupTransform} from './edition-group'; import {editionToFormState, transformNewForm as editionTransform} from './edition'; @@ -13,7 +14,6 @@ import type { EntityTypeString } from 'bookbrainz-data/lib/types/entity'; import _ from 'lodash'; -import {_bulkIndexEntities} from '../../../common/helpers/search'; import log from 'log'; import {processSingleEntity} from './entity'; @@ -206,7 +206,7 @@ export async function handleCreateMultipleEntities( } // log indexing error if any try { - _bulkIndexEntities(processedAchievements); + _bulkIndexEntities(processedEntities.map(en => getDocumentToIndex(en, en.get('type')))); } catch (err) { log.error(err); diff --git a/src/server/routes/register.js b/src/server/routes/register.js index 4a88d7ffa3..109a758037 100644 --- a/src/server/routes/register.js +++ b/src/server/routes/register.js @@ -93,65 +93,53 @@ router.get('/details', middleware.loadGenders, (req, res) => { })); }); -router.post('/handler', (req, res) => { - const {Editor, EditorType} = req.app.locals.orm; +router.post('/handler', async (req, res) => { + try { + const {Editor, EditorType} = req.app.locals.orm; + + // Check whether the user is logged in - if so, redirect to profile page + if (req.user) { + return res.redirect(`/editor/${req.user.id}`); + } + + if (!req.session.mbProfile) { + return res.redirect('/auth'); + } + + // Fetch the default EditorType from the database + const editorType = await EditorType.forge({label: 'Editor'}) + .fetch({require: true}); + + // Create a new Editor and add to the database + + const editor = await new Editor({ + cachedMetabrainzName: req.session.mbProfile.sub, + genderId: req.body.gender, + metabrainzUserId: req.session.mbProfile.metabrainz_user_id, + name: req.body.displayName, + typeId: editorType.id + }) + .save(); - // Check whether the user is logged in - if so, redirect to profile page - if (req.user) { - return res.redirect(`/editor/${req.user.id}`); - } + req.session.mbProfile = null; - if (!req.session.mbProfile) { - return res.redirect('/auth'); + await search.indexEntity(editor); + return res.status(200).send(editor.toJSON()); + } + catch (err) { + if (_.isMatch(err, {constraint: 'editor_name_key'})) { + return error.sendErrorAsJSON(res, new error.FormSubmissionError( + 'That username already exists - please try using another,' + + ' or contact us to have your existing BookBrainz account' + + ' linked to a MusicBrainz account.' + )); + } + log.error(err); + + return error.sendErrorAsJSON(res, new error.FormSubmissionError( + 'Something went wrong when registering, please try again!' + )); } - - // Fetch the default EditorType from the database - const registerPromise = EditorType.forge({label: 'Editor'}) - .fetch({require: true}) - .then( - // Create a new Editor and add to the database - (editorType) => - new Editor({ - cachedMetabrainzName: req.session.mbProfile.sub, - genderId: req.body.gender, - metabrainzUserId: req.session.mbProfile.metabrainz_user_id, - name: req.body.displayName, - typeId: editorType.id - }) - .save() - ) - .then((editor) => { - req.session.mbProfile = null; - const editorJSON = editor.toJSON(); - - // in ES index, we're storing the editor as entity - const editorForES = {}; - editorForES.bbid = editorJSON.id; - editorForES.aliasSet = { - aliases: [ - {name: editorJSON.name} - ] - }; - editorForES.type = 'Editor'; - return editorForES; - }) - .catch((err) => { - log.debug(err); - - if (_.isMatch(err, {constraint: 'editor_name_key'})) { - throw new error.FormSubmissionError( - 'That username already exists - please try using another,' + - ' or contact us to have your existing BookBrainz account' + - ' linked to a MusicBrainz account.' - ); - } - - throw new error.FormSubmissionError( - 'Something went wrong when registering, please try again!' - ); - }); - - return handler.sendPromiseResult(res, registerPromise, search.indexEntity); }); export default router;