205 lines
5.7 KiB
JavaScript
205 lines
5.7 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const { createClient, resultsDir, writeJson } = require('./benchmark-lib');
|
|
|
|
const outFile = path.join(resultsDir, 'validation_search_results.json');
|
|
const mode = process.argv[2] || process.env.BENCHMARK_VALIDATION_PHASE || 'after';
|
|
|
|
const whereSql = `
|
|
WHERE (
|
|
to_tsvector('spanish', COALESCE(search_text, '')) @@ websearch_to_tsquery('spanish', 'marcapasos')
|
|
OR COALESCE(registro_sanitario, '') ILIKE '%marcapasos%'
|
|
OR COALESCE(expediente, '') ILIKE '%marcapasos%'
|
|
OR COALESCE(producto, '') ILIKE '%marcapasos%'
|
|
OR COALESCE(categoria_canonica, '') ILIKE '%marcapasos%'
|
|
OR COALESCE(categoria_linea, '') ILIKE '%marcapasos%'
|
|
)`;
|
|
|
|
const countSql = `SELECT COUNT(*)::bigint AS total FROM invima_api_catalog ${whereSql};`;
|
|
|
|
const pageSql = `
|
|
WITH matched AS MATERIALIZED (
|
|
SELECT
|
|
source_dataset_key,
|
|
source_uid,
|
|
source_dataset_id,
|
|
source_dataset_name,
|
|
categoria_linea,
|
|
categoria_canonica,
|
|
categoria_slug,
|
|
expediente,
|
|
registro_sanitario,
|
|
producto,
|
|
titular,
|
|
estado_registro,
|
|
fecha_expedicion,
|
|
fecha_vencimiento,
|
|
modalidad,
|
|
grupo,
|
|
marca,
|
|
principio_activo,
|
|
forma_farmaceutica,
|
|
presentacion_comercial,
|
|
atc,
|
|
rol_nombre,
|
|
rol_tipo,
|
|
ciudad_titular,
|
|
pais_titular,
|
|
fabricante,
|
|
importador,
|
|
vigencia
|
|
FROM invima_api_catalog
|
|
${whereSql}
|
|
)
|
|
SELECT
|
|
source_dataset_key,
|
|
source_uid,
|
|
source_dataset_id,
|
|
source_dataset_name,
|
|
categoria_linea,
|
|
categoria_canonica,
|
|
categoria_slug,
|
|
expediente,
|
|
registro_sanitario,
|
|
producto,
|
|
titular,
|
|
estado_registro,
|
|
fecha_expedicion,
|
|
fecha_vencimiento,
|
|
modalidad,
|
|
grupo,
|
|
marca,
|
|
principio_activo,
|
|
forma_farmaceutica,
|
|
presentacion_comercial,
|
|
atc,
|
|
rol_nombre,
|
|
rol_tipo,
|
|
ciudad_titular,
|
|
pais_titular,
|
|
fabricante,
|
|
importador,
|
|
vigencia
|
|
FROM matched
|
|
ORDER BY fecha_vencimiento DESC NULLS LAST, producto ASC NULLS LAST, source_dataset_key ASC, source_uid ASC
|
|
LIMIT 25 OFFSET 0;
|
|
`;
|
|
|
|
const rowId = (row) => `${row.source_dataset_key}|${row.source_uid}`;
|
|
const normalizeDate = (value) => {
|
|
if (!value) return '';
|
|
if (value instanceof Date) return value.toISOString();
|
|
return String(value);
|
|
};
|
|
const orderKey = (row) =>
|
|
`${normalizeDate(row.fecha_vencimiento)}|${row.producto || ''}|${row.source_dataset_key}|${row.source_uid}`;
|
|
|
|
const duplicates = (ids) => {
|
|
const seen = new Set();
|
|
const dupes = new Set();
|
|
for (const id of ids) {
|
|
if (seen.has(id)) dupes.add(id);
|
|
seen.add(id);
|
|
}
|
|
return Array.from(dupes);
|
|
};
|
|
|
|
const diff = (before, after) => {
|
|
const beforeIds = before.rows.map(rowId);
|
|
const afterIds = after.rows.map(rowId);
|
|
const beforeOrderKeys = before.rows.map(orderKey);
|
|
const afterOrderKeys = after.rows.map(orderKey);
|
|
const beforeSet = new Set(beforeIds);
|
|
const afterSet = new Set(afterIds);
|
|
const missing = beforeIds.filter((id) => !afterSet.has(id));
|
|
const additional = afterIds.filter((id) => !beforeSet.has(id));
|
|
const orderChanged =
|
|
beforeIds.length !== afterIds.length || beforeIds.some((id, index) => id !== afterIds[index]);
|
|
const orderKeysChanged =
|
|
beforeOrderKeys.length !== afterOrderKeys.length ||
|
|
beforeOrderKeys.some((key, index) => key !== afterOrderKeys[index]);
|
|
const countBefore = Number(before.count);
|
|
const countAfter = Number(after.count);
|
|
const validResultSet =
|
|
countBefore === countAfter &&
|
|
before.rows.length === after.rows.length &&
|
|
missing.length === 0 &&
|
|
additional.length === 0 &&
|
|
duplicates(afterIds).length === 0;
|
|
const validStrictOrder = validResultSet && !orderChanged;
|
|
const validOrderKeys = validResultSet && !orderKeysChanged;
|
|
|
|
return {
|
|
countBefore,
|
|
countAfter,
|
|
countMatches: countBefore === countAfter,
|
|
rowCountBefore: before.rows.length,
|
|
rowCountAfter: after.rows.length,
|
|
rowCountMatches: before.rows.length === after.rows.length,
|
|
missingIds: missing,
|
|
additionalIds: additional,
|
|
duplicateIdsBefore: duplicates(beforeIds),
|
|
duplicateIdsAfter: duplicates(afterIds),
|
|
orderChanged,
|
|
orderKeysChanged,
|
|
idOrderChangedWithinTies: orderChanged && !orderKeysChanged,
|
|
validResultSet,
|
|
validStrictOrder,
|
|
validOrderKeys,
|
|
valid: validResultSet && validOrderKeys,
|
|
note:
|
|
orderChanged && !orderKeysChanged
|
|
? 'Los mismos registros aparecen bajo las mismas claves ORDER BY; cambia el orden interno de empates porque la consulta no incluye un desempate unico.'
|
|
: ''
|
|
};
|
|
};
|
|
|
|
const capture = async () => {
|
|
const client = await createClient();
|
|
try {
|
|
await client.query('SET statement_timeout = 0');
|
|
const count = await client.query(countSql);
|
|
const page = await client.query(pageSql);
|
|
return {
|
|
capturedAt: new Date().toISOString(),
|
|
count: Number(count.rows[0]?.total || 0),
|
|
rows: page.rows,
|
|
ids: page.rows.map(rowId)
|
|
};
|
|
} finally {
|
|
await client.end();
|
|
}
|
|
};
|
|
|
|
const main = async () => {
|
|
const current = fs.existsSync(outFile)
|
|
? JSON.parse(fs.readFileSync(outFile, 'utf8'))
|
|
: {
|
|
query: 'marcapasos',
|
|
validationSql: {
|
|
count: countSql.trim(),
|
|
page: pageSql.trim()
|
|
}
|
|
};
|
|
|
|
if (mode === 'before') {
|
|
current.before = await capture();
|
|
delete current.after;
|
|
delete current.comparison;
|
|
} else {
|
|
if (!current.before) {
|
|
throw new Error('No existe referencia before. Ejecute: node benchmarks/scripts/validate-search-results.js before');
|
|
}
|
|
current.after = await capture();
|
|
current.comparison = diff(current.before, current.after);
|
|
}
|
|
|
|
writeJson(outFile, current);
|
|
console.log(JSON.stringify(current.comparison || { beforeCaptured: Boolean(current.before) }, null, 2));
|
|
};
|
|
|
|
main().catch((error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|