fix(feed): load pictures via object_pairs instead of sentence UUID heuristic
Pictures are now fetched through the canonical pair→object relationship (object_pairs table) rather than by guessing objects from sentence placeholder UUIDs. Removes the word→object indirect lookup hack added previously. One query covers all pairs in the batch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -88,7 +88,6 @@ router.get('/', requireJwt, async (req, res, next) => {
|
|||||||
|
|
||||||
// 6. Resolve UUIDs → words (direct) or via objects→words
|
// 6. Resolve UUIDs → words (direct) or via objects→words
|
||||||
const placeholderMap = {}; // uuid → { de, en, sv }
|
const placeholderMap = {}; // uuid → { de, en, sv }
|
||||||
const wordObjectMap = {}; // wordId → objectId (for picture lookup)
|
|
||||||
if (uuidArr.length) {
|
if (uuidArr.length) {
|
||||||
// Direct word lookup
|
// Direct word lookup
|
||||||
const wordRes = await query(
|
const wordRes = await query(
|
||||||
@@ -125,16 +124,6 @@ router.get('/', requireJwt, async (req, res, next) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// For word-type placeholders: find linked objects so we can load their pictures
|
|
||||||
const wordUuids = uuidArr.filter(u => placeholderMap[u]?.type === 'word');
|
|
||||||
if (wordUuids.length) {
|
|
||||||
const wObjRes = await query(
|
|
||||||
`SELECT DISTINCT ON (ow.word_id) ow.word_id, ow.object_id
|
|
||||||
FROM object_words ow WHERE ow.word_id = ANY($1)`,
|
|
||||||
[wordUuids]
|
|
||||||
);
|
|
||||||
wObjRes.rows.forEach(r => { wordObjectMap[r.word_id] = r.object_id; });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Fetch statement→word links (positive + negative)
|
// 7. Fetch statement→word links (positive + negative)
|
||||||
@@ -159,52 +148,25 @@ router.get('/', requireJwt, async (req, res, next) => {
|
|||||||
negRes.rows.forEach(r => { statWordMap[r.statement_id]?.negative.push({ id: r.id, de: r.de, en: r.en, sv: r.sv }); });
|
negRes.rows.forEach(r => { statWordMap[r.statement_id]?.negative.push({ id: r.id, de: r.de, en: r.en, sv: r.sv }); });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Find pictures: via objects referenced in sentences, or via object_pairs
|
// 8. Find pictures via object_pairs (direct pair → object link)
|
||||||
const objectUuids = uuidArr.filter(u => !Object.values(questionsMap).concat(Object.values(statementsMap))
|
const pairIds = pairs.map(p => p.id);
|
||||||
.flatMap(x => [x.sentence_de || '', x.positive_sentence_de || ''])
|
const pairPictureMap = {}; // pairId → { url, blurhash }
|
||||||
.some(s => s.includes(u) && placeholderMap[u])
|
if (pairIds.length) {
|
||||||
? false : placeholderMap[u] !== undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
// Simpler: collect all object-UUIDs (those resolved via object_words)
|
|
||||||
const resolvedObjectIds = new Set();
|
|
||||||
if (uuidArr.length) {
|
|
||||||
const objCheckRes = await query(
|
|
||||||
`SELECT DISTINCT object_id AS id FROM object_words WHERE object_id = ANY($1)`,
|
|
||||||
[uuidArr]
|
|
||||||
);
|
|
||||||
objCheckRes.rows.forEach(r => resolvedObjectIds.add(r.id));
|
|
||||||
}
|
|
||||||
// Also include objects linked to word-type placeholders
|
|
||||||
Object.values(wordObjectMap).forEach(oid => resolvedObjectIds.add(oid));
|
|
||||||
|
|
||||||
const pictureMap = {}; // objectId → { url, blurhash }
|
|
||||||
if (resolvedObjectIds.size) {
|
|
||||||
const picRes = await query(
|
const picRes = await query(
|
||||||
`SELECT DISTINCT ON (op.object_id)
|
`SELECT DISTINCT ON (op2.pair_id)
|
||||||
op.object_id, p.picture_link AS url, p.blurhash
|
op2.pair_id, p.picture_link AS url, p.blurhash
|
||||||
FROM object_pictures op
|
FROM object_pairs op2
|
||||||
|
JOIN object_pictures op ON op.object_id = op2.object_id
|
||||||
JOIN pictures p ON p.id = op.picture_id
|
JOIN pictures p ON p.id = op.picture_id
|
||||||
WHERE op.object_id = ANY($1)
|
WHERE op2.pair_id = ANY($1)
|
||||||
ORDER BY op.object_id, p.created_at`,
|
ORDER BY op2.pair_id, p.created_at`,
|
||||||
[[...resolvedObjectIds]]
|
[pairIds]
|
||||||
);
|
);
|
||||||
picRes.rows.forEach(r => {
|
picRes.rows.forEach(r => {
|
||||||
pictureMap[r.object_id] = { url: r.url, blurhash: r.blurhash };
|
pairPictureMap[r.pair_id] = { url: r.url, blurhash: r.blurhash };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick first available picture across all placeholders in the pair
|
|
||||||
// Checks both direct object UUIDs and word-type UUIDs linked to objects
|
|
||||||
function pickPicture(pairUuids) {
|
|
||||||
for (const u of pairUuids) {
|
|
||||||
if (pictureMap[u]) return pictureMap[u];
|
|
||||||
const oid = wordObjectMap[u];
|
|
||||||
if (oid && pictureMap[oid]) return pictureMap[oid];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildStatement(id) {
|
function buildStatement(id) {
|
||||||
if (!id || !statementsMap[id]) return null;
|
if (!id || !statementsMap[id]) return null;
|
||||||
const s = statementsMap[id];
|
const s = statementsMap[id];
|
||||||
@@ -247,7 +209,7 @@ router.get('/', requireJwt, async (req, res, next) => {
|
|||||||
question: buildQuestion(p.question_id),
|
question: buildQuestion(p.question_id),
|
||||||
positive_statement: buildStatement(p.positive_statement_id),
|
positive_statement: buildStatement(p.positive_statement_id),
|
||||||
negative_statement: buildStatement(p.negative_statement_id),
|
negative_statement: buildStatement(p.negative_statement_id),
|
||||||
picture: pickPicture(pairUuids),
|
picture: pairPictureMap[p.id] || null,
|
||||||
placeholders: Object.fromEntries(pairUuids.map(u => [u, placeholderMap[u] || null])),
|
placeholders: Object.fromEntries(pairUuids.map(u => [u, placeholderMap[u] || null])),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user