Add missing relation endpoints for content_mentor migration
- GET /api/objects?picture_id=X filter via object_pictures join - GET /api/objects/:id/words — full word details - GET /api/objects/:id/pairs — pairs with nested question + statements - GET /api/words?titel_de=X case-insensitive filter - GET /api/pictures/:id/words — full word details - DELETE /api/pictures/:id/words/:wordId — convenience unlink Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -28,10 +28,13 @@ async function getWithRelations(id) {
|
||||
// GET /api/objects
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const { status, limit = 50, offset = 0 } = req.query;
|
||||
const { status, picture_id, limit = 50, offset = 0 } = req.query;
|
||||
const params = [Math.min(parseInt(limit), 500), parseInt(offset)];
|
||||
const where = status ? `WHERE o.status = $3` : '';
|
||||
if (status) params.push(status);
|
||||
const conditions = [];
|
||||
if (status) { conditions.push(`o.status = $${params.length + 1}`); params.push(status); }
|
||||
if (picture_id) { conditions.push(`op2.picture_id = $${params.length + 1}`); params.push(picture_id); }
|
||||
const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
|
||||
const join2 = picture_id ? `LEFT JOIN object_pictures op2 ON op2.object_id = o.id` : '';
|
||||
const result = await query(
|
||||
`SELECT o.*,
|
||||
COALESCE(json_agg(DISTINCT ow.word_id) FILTER (WHERE ow.word_id IS NOT NULL), '[]') AS word_ids,
|
||||
@@ -41,6 +44,7 @@ router.get('/', async (req, res, next) => {
|
||||
LEFT JOIN object_words ow ON ow.object_id = o.id
|
||||
LEFT JOIN object_pictures op ON op.object_id = o.id
|
||||
LEFT JOIN object_pairs opr ON opr.object_id = o.id
|
||||
${join2}
|
||||
${where}
|
||||
GROUP BY o.id
|
||||
ORDER BY o.created_at DESC
|
||||
@@ -110,6 +114,69 @@ router.delete('/:id', async (req, res, next) => {
|
||||
} catch (err) { next(err); }
|
||||
});
|
||||
|
||||
// --- Relation detail reads ---
|
||||
|
||||
// GET /api/objects/:id/words — full word objects
|
||||
router.get('/:id/words', async (req, res, next) => {
|
||||
try {
|
||||
const result = await query(
|
||||
`SELECT w.* FROM words w
|
||||
JOIN object_words ow ON ow.word_id = w.id
|
||||
WHERE ow.object_id = $1
|
||||
ORDER BY w.created_at DESC`,
|
||||
[req.params.id]
|
||||
);
|
||||
res.json(result.rows);
|
||||
} catch (err) { next(err); }
|
||||
});
|
||||
|
||||
// GET /api/objects/:id/pairs — full pair objects with nested question + statements
|
||||
router.get('/:id/pairs', async (req, res, next) => {
|
||||
try {
|
||||
const pairsResult = await query(
|
||||
`SELECT p.* FROM pairs p
|
||||
JOIN object_pairs op ON op.pair_id = p.id
|
||||
WHERE op.object_id = $1
|
||||
ORDER BY p.created_at DESC`,
|
||||
[req.params.id]
|
||||
);
|
||||
const pairs = pairsResult.rows;
|
||||
if (!pairs.length) return res.json([]);
|
||||
|
||||
// Fetch linked questions and statements in bulk
|
||||
const qIds = [...new Set(pairs.map(p => p.question_id).filter(Boolean))];
|
||||
const posIds = [...new Set(pairs.map(p => p.positive_statement_id).filter(Boolean))];
|
||||
const negIds = [...new Set(pairs.map(p => p.negative_statement_id).filter(Boolean))];
|
||||
const stmtIds = [...new Set([...posIds, ...negIds])];
|
||||
|
||||
const [qRows, stmtRows] = await Promise.all([
|
||||
qIds.length ? query(`SELECT * FROM questions WHERE id = ANY($1)`, [qIds]) : { rows: [] },
|
||||
stmtIds.length ? query(
|
||||
`SELECT s.*,
|
||||
COALESCE(json_agg(DISTINCT spw.word_id) FILTER (WHERE spw.word_id IS NOT NULL), '[]') AS positive_word_ids,
|
||||
COALESCE(json_agg(DISTINCT snw.word_id) FILTER (WHERE snw.word_id IS NOT NULL), '[]') AS negative_word_ids
|
||||
FROM statements s
|
||||
LEFT JOIN statement_positive_words spw ON spw.statement_id = s.id
|
||||
LEFT JOIN statement_negative_words snw ON snw.statement_id = s.id
|
||||
WHERE s.id = ANY($1)
|
||||
GROUP BY s.id`, [stmtIds]
|
||||
) : { rows: [] },
|
||||
]);
|
||||
|
||||
const qMap = Object.fromEntries(qRows.rows.map(r => [r.id, r]));
|
||||
const stmtMap = Object.fromEntries(stmtRows.rows.map(r => [r.id, r]));
|
||||
|
||||
const enriched = pairs.map(p => ({
|
||||
...p,
|
||||
question: p.question_id ? qMap[p.question_id] || null : null,
|
||||
positive_statement: p.positive_statement_id ? stmtMap[p.positive_statement_id] || null : null,
|
||||
negative_statement: p.negative_statement_id ? stmtMap[p.negative_statement_id] || null : null,
|
||||
}));
|
||||
|
||||
res.json(enriched);
|
||||
} catch (err) { next(err); }
|
||||
});
|
||||
|
||||
// --- Relations ---
|
||||
|
||||
// POST /api/objects/:id/words/:wordId
|
||||
|
||||
@@ -33,6 +33,31 @@ router.get('/:id', async (req, res, next) => {
|
||||
} catch (err) { next(err); }
|
||||
});
|
||||
|
||||
// GET /api/pictures/:id/words — full word objects linked to this picture
|
||||
router.get('/:id/words', async (req, res, next) => {
|
||||
try {
|
||||
const result = await query(
|
||||
`SELECT w.* FROM words w
|
||||
JOIN word_pictures wp ON wp.word_id = w.id
|
||||
WHERE wp.picture_id = $1
|
||||
ORDER BY w.created_at DESC`,
|
||||
[req.params.id]
|
||||
);
|
||||
res.json(result.rows);
|
||||
} catch (err) { next(err); }
|
||||
});
|
||||
|
||||
// DELETE /api/pictures/:id/words/:wordId
|
||||
router.delete('/:id/words/:wordId', async (req, res, next) => {
|
||||
try {
|
||||
await query(
|
||||
`DELETE FROM word_pictures WHERE picture_id = $1 AND word_id = $2`,
|
||||
[req.params.id, req.params.wordId]
|
||||
);
|
||||
res.status(204).end();
|
||||
} catch (err) { next(err); }
|
||||
});
|
||||
|
||||
// POST /api/pictures — neuen Eintrag anlegen
|
||||
router.post('/', async (req, res, next) => {
|
||||
try {
|
||||
|
||||
@@ -12,10 +12,12 @@ const STATUS_TIMESTAMP = {
|
||||
// GET /api/words
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const { status, limit = 50, offset = 0 } = req.query;
|
||||
const { status, titel_de, limit = 50, offset = 0 } = req.query;
|
||||
const params = [Math.min(parseInt(limit), 500), parseInt(offset)];
|
||||
const where = status ? `WHERE w.status = $3` : '';
|
||||
if (status) params.push(status);
|
||||
const conditions = [];
|
||||
if (status) { conditions.push(`w.status = $${params.length + 1}`); params.push(status); }
|
||||
if (titel_de) { conditions.push(`lower(w.titel_de) = lower($${params.length + 1})`); params.push(titel_de); }
|
||||
const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
|
||||
const result = await query(
|
||||
`SELECT w.*,
|
||||
COALESCE(json_agg(DISTINCT p.id) FILTER (WHERE p.id IS NOT NULL), '[]') AS picture_ids,
|
||||
|
||||
Reference in New Issue
Block a user