UNPKG

graph-explorer

Version:

Graph Explorer can be used to explore and RDF graphs in SPARQL endpoints or on the web.

373 lines (326 loc) 9.6 kB
import { Dictionary, ClassModel, LinkType, ElementModel, LinkModel, LinkCount, Property, PropertyModel, LocalizedString, } from "../model"; const DATA_PROVIDER_PROPERTY = "http://graph-explorer.org/property/DataProvider"; export interface CompositeResponse<Type> { dataSourceName: string; useInStats?: boolean; response: Type; } export function mergeClassTree( composite: CompositeResponse<ClassModel[]>[] ): ClassModel[] { const lists = composite .filter((r) => r.response) .map(({ useInStats, response }) => ({ useInStats, classes: classTreeToArray(response), })); const dictionary: Dictionary<ClassModel> = {}; const topLevelModels: Dictionary<ClassModel> = {}; const childrenMap: Dictionary<string[]> = {}; for (const { useInStats, classes } of lists) { for (const model of classes) { const childrenIds: string[] = childrenMap[model.id] || []; model.children .map((ch) => ch.id) .forEach((id) => { if (childrenIds.indexOf(id) === -1) { childrenIds.push(id); } }); model.children = []; if (!useInStats) { delete model.count; } if (!dictionary[model.id]) { topLevelModels[model.id] = model; dictionary[model.id] = model; childrenMap[model.id] = childrenIds; } else { topLevelModels[model.id] = mergeClassModel(dictionary[model.id], model); dictionary[model.id] = topLevelModels[model.id]; } } } const models = Object.keys(dictionary).map((key) => dictionary[key]); for (const m of models) { m.children = (childrenMap[m.id] || []).map((id) => { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete topLevelModels[id]; return dictionary[id]; }); } return Object.keys(topLevelModels).map((key) => topLevelModels[key]); } export function mergePropertyInfo( response: CompositeResponse<Dictionary<PropertyModel>>[] ): Dictionary<PropertyModel> { const result: Dictionary<PropertyModel> = {}; const props = response.filter((r) => r.response).map((r) => r.response); for (const model of props) { const keys = Object.keys(model); for (const key of keys) { const prop = model[key]; if (!result[key]) { result[key] = prop; } else { result[key].label = mergeLabels(result[key].label, prop.label); } } } return result; } export function mergeClassInfo( response: CompositeResponse<ClassModel[]>[] ): ClassModel[] { const dictionaries = response .filter((r) => r.response) .map((r) => r.response); const dictionary: Dictionary<ClassModel> = {}; for (const models of dictionaries) { for (const model of models) { if (!dictionary[model.id]) { dictionary[model.id] = model; } else { dictionary[model.id] = mergeClassModel(dictionary[model.id], model); } } } return Object.keys(dictionary).map((key) => dictionary[key]); } export function mergeLinkTypesInfo( response: CompositeResponse<LinkType[]>[] ): LinkType[] { const lists = response.filter((r) => r.response).map((r) => r.response); const mergeLinkType = (a: LinkType, b: LinkType): LinkType => { return { id: a.id, label: mergeLabels(a.label, b.label), count: a.count + b.count, }; }; const dictionary: Dictionary<LinkType> = {}; for (const linkTypes of lists) { for (const linkType of linkTypes) { if (!dictionary[linkType.id]) { dictionary[linkType.id] = linkType; } else { dictionary[linkType.id] = mergeLinkType( dictionary[linkType.id], linkType ); } } } return Object.keys(dictionary).map((key) => dictionary[key]); } export function mergeLinkTypes( response: CompositeResponse<LinkType[]>[] ): LinkType[] { return mergeLinkTypesInfo(response); } export function mergeElementInfo( response: CompositeResponse<Dictionary<ElementModel>>[] ): Dictionary<ElementModel> { const mergeElementModels = ( a: ElementModel, b: ElementModel ): ElementModel => { const types = a.types; for (const t of b.types) { if (types.indexOf(t) === -1) { types.push(t); } } const sources: string[] = []; for (const s of a.sources) { if (sources.indexOf(s) === -1) { sources.push(s); } } for (const s of b.sources) { if (sources.indexOf(s) === -1) { sources.push(s); } } return { id: a.id, label: mergeLabels(a.label, b.label), types: types, image: a.image || b.image, properties: mergeProperties(a.properties, b.properties), sources: sources, }; }; const dictionary: Dictionary<ElementModel> = {}; for (const resp of response) { if (!resp.response) { continue; } const list = Object.keys(resp.response).map((k) => resp.response[k]); for (const em of list) { em.sources = [resp.dataSourceName]; em.properties[DATA_PROVIDER_PROPERTY] = { type: "string", values: [{ value: resp.dataSourceName, language: "" }], }; if (!dictionary[em.id]) { dictionary[em.id] = em; } else { dictionary[em.id] = mergeElementModels(dictionary[em.id], em); } } } return dictionary; } export function mergeProperties( a: Dictionary<Property>, b: Dictionary<Property> ): Dictionary<Property> { const aLists = Object.keys(a); const bLists = Object.keys(b); const result: Dictionary<Property> = {}; function createIdForProperty(baseId: string): string { let counter = 1; while (result[baseId + "_" + counter]) { counter++; } return baseId + "_" + counter; } for (const pKey of aLists) { const prop = a[pKey]; if (!result[pKey]) { result[pKey] = prop; } else { result[createIdForProperty(pKey)] = prop; } } for (const pKey of bLists) { const prop = b[pKey]; if (!result[pKey]) { result[pKey] = prop; } else { result[createIdForProperty(pKey)] = prop; } } return result; } export function mergeLinksInfo( response: CompositeResponse<LinkModel[]>[] ): LinkModel[] { const lists = response.filter((r) => r.response).map((r) => r.response); const resultInfo: LinkModel[] = []; function compareLinksInfo(a: LinkModel, b: LinkModel): boolean { return ( a.sourceId === b.sourceId && a.targetId === b.targetId && a.linkTypeId === b.linkTypeId ); } for (const linkInfo of lists) { for (const linkModel of linkInfo) { if (!resultInfo.some((l) => compareLinksInfo(l, linkModel))) { resultInfo.push(linkModel); } } } return resultInfo; } export function mergeLinkTypesOf( response: CompositeResponse<LinkCount[]>[] ): LinkCount[] { const lists = response.filter((r) => r.response).map((r) => r.response); const dictionary: Dictionary<LinkCount> = {}; const merge = (a: LinkCount, b: LinkCount): LinkCount => { return { id: a.id, inCount: a.inCount + b.inCount, outCount: a.outCount + b.outCount, }; }; for (const linkCount of lists) { for (const lCount of linkCount) { if (!dictionary[lCount.id]) { dictionary[lCount.id] = lCount; } else { dictionary[lCount.id] = merge(lCount, dictionary[lCount.id]); } } } return Object.keys(dictionary).map((key) => dictionary[key]); } export function mergeLinkElements( response: CompositeResponse<Dictionary<ElementModel>>[] ): Dictionary<ElementModel> { return mergeElementInfo(response); } export function mergeFilter( response: CompositeResponse<Dictionary<ElementModel>>[] ): Dictionary<ElementModel> { return mergeElementInfo(response); } export function classTreeToArray(models: ClassModel[]): ClassModel[] { let resultArray: ClassModel[] = models; function getDescendants(model: ClassModel): ClassModel[] { let descendants = model.children || []; for (const descendant of descendants) { const nextGeneration = getDescendants(descendant); descendants = descendants.concat(nextGeneration); } return descendants; } for (const model of models) { const descendants = getDescendants(model); resultArray = resultArray.concat(descendants); } return resultArray; } export function mergeLabels( a: { values: LocalizedString[] }, b: { values: LocalizedString[] } ): { values: LocalizedString[] } { function compareLabels(l1: LocalizedString, l2: LocalizedString): boolean { return l1.language === l2.language && l1.value === l2.value; } const mergedValuesList = a.values; for (const locStr of b.values) { if (!mergedValuesList.some((l) => compareLabels(l, locStr))) { mergedValuesList.push(locStr); } } return { values: mergedValuesList, }; } export function mergeCounts(a?: number, b?: number): number | undefined { if (a === undefined && b === undefined) { return undefined; } return (a || 0) + (b || 0); } export function mergeClassModel(a: ClassModel, b: ClassModel): ClassModel { const childrenDictionary: Dictionary<ClassModel> = {}; for (const child of a.children.concat(b.children)) { if (!childrenDictionary[child.id]) { childrenDictionary[child.id] = child; } } return { id: a.id, label: mergeLabels(a.label, b.label), count: mergeCounts(a.count, b.count), children: Object.keys(childrenDictionary).map( (key) => childrenDictionary[key] ), }; }