refactoring van de cmap structuren bijna compleet

This commit is contained in:
2026-09-03 16:21:21 +02:00
parent 1abc84489f
commit bd1ef6bed0
75 changed files with 2547 additions and 1967 deletions
@@ -0,0 +1,94 @@
"use strict";
function copy(value) {
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
}
function normalizeConceptName(value) {
return String(value || "").trim().toLocaleLowerCase();
}
/** Index lightweight concept placements across stored CMaps. */
export class CmapReferenceIndex {
constructor() {
this.byConceptId = new Map();
this.byName = new Map();
this.byPage = new Map();
}
clear() {
this.byConceptId.clear();
this.byName.clear();
this.byPage.clear();
}
load(placements = [], conceptMaps = [], canonicalPageReference = (value) => value,
normalizeName = normalizeConceptName) {
this.clear();
const mapTitles = new Map(conceptMaps
.filter((map) => map && map.slug)
.map((map) => [map.slug, map.title || map.slug]));
const pageByConcept = new Map();
for (const placement of placements) {
if (!placement?.conceptId || !placement.pageSlug) continue;
pageByConcept.set(placement.conceptId,
canonicalPageReference(placement.pageSlug).toLocaleLowerCase());
}
for (const placement of placements) {
if (!placement?.conceptId || !placement.cmapSlug) continue;
const conceptId = String(placement.conceptId);
const slug = String(placement.cmapSlug);
const nameKey = normalizeName(placement.label);
if (nameKey) this.byName.set(nameKey, conceptId);
if (!this.byConceptId.has(conceptId)) this.byConceptId.set(conceptId, new Map());
const maps = this.byConceptId.get(conceptId);
const existing = maps.get(slug);
maps.set(slug, {
slug,
title: mapTitles.get(slug) || placement.cmapTitle || slug,
count: (existing?.count || 0) + (Number(placement.count) || 0)
});
const pageKey = pageByConcept.get(conceptId);
if (!pageKey) continue;
if (!this.byPage.has(pageKey)) this.byPage.set(pageKey, new Map());
const pageConcepts = this.byPage.get(pageKey);
if (!pageConcepts.has(conceptId)) {
pageConcepts.set(conceptId, {
conceptId,
label: placement.label || "Concept",
count: 0,
maps: new Map()
});
}
const concept = pageConcepts.get(conceptId);
const count = Number(placement.count) || 0;
concept.count += count;
const pageMap = concept.maps.get(slug);
concept.maps.set(slug, {
slug,
title: mapTitles.get(slug) || placement.cmapTitle || slug,
count: count + (pageMap?.count || 0)
});
}
return this;
}
mapsFor(conceptId) {
return Array.from(this.byConceptId.get(String(conceptId))?.values() || [])
.map(copy);
}
countFor(conceptId) {
return this.mapsFor(conceptId).reduce((sum, map) => sum + map.count, 0);
}
conceptIdForName(label) {
return this.byName.get(normalizeConceptName(label)) || null;
}
pageConcepts(pageReference) {
return Array.from(this.byPage.get(String(pageReference).toLocaleLowerCase())?.values() || [])
.map((concept) => ({ ...copy(concept), maps: new Map(concept.maps) }));
}
}