Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
tokebe committed Oct 21, 2024
2 parents 3b300fa + eb63a41 commit c0560dd
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 169 deletions.
21 changes: 11 additions & 10 deletions __test__/integration/graph/graph.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ describe('Test graph class', () => {
expect(Array.from(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].apis)).toEqual(['API1']);
expect(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].sources).toHaveProperty('source1');
expect(Array.from(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].publications)).toEqual(['PMID:1', 'PMID:2']);
expect(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].attributes).toHaveProperty('relation', new Set(['relation1']));
expect(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].attributes).toHaveProperty('relation', ['relation1']);
});

test('Multiple query results are correctly updated for two edges having same input, predicate and output', () => {
Expand All @@ -134,13 +134,13 @@ describe('Test graph class', () => {
expect(Array.from(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].apis)).toEqual(['API1']);
expect(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].sources).toHaveProperty('source1');
expect(Array.from(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].publications)).toEqual(['PMID:1', 'PMID:2']);
expect(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].attributes).toHaveProperty('relation', new Set(['relation1']));
expect(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].attributes).toHaveProperty('relation', ['relation1']);

expect(g.edges).toHaveProperty('6930dcb2e9363817e9f6e736829ce278');
expect(Array.from(g.edges['6930dcb2e9363817e9f6e736829ce278'].apis)).toEqual(['API2']);
expect(g.edges['6930dcb2e9363817e9f6e736829ce278'].sources).toHaveProperty('source2');
expect(Array.from(g.edges['6930dcb2e9363817e9f6e736829ce278'].publications)).toEqual(['PMC:1', 'PMC:2']);
expect(g.edges['6930dcb2e9363817e9f6e736829ce278'].attributes).toHaveProperty('relation', new Set(['relation2']));
expect(g.edges['6930dcb2e9363817e9f6e736829ce278'].attributes).toHaveProperty('relation', ['relation2']);
});

test('Multiple query results for different edges are correctly updated', () => {
Expand All @@ -161,19 +161,19 @@ describe('Test graph class', () => {
expect(Array.from(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].apis)).toEqual(['API1']);
expect(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].sources).toHaveProperty('source1');
expect(Array.from(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].publications)).toEqual(['PMID:1', 'PMID:2']);
expect(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].attributes).toHaveProperty('relation', new Set(['relation1']));
expect(g.edges['3eb29a4cead0e5f3c3bdca4997bf215b'].attributes).toHaveProperty('relation', ['relation1']);

expect(g.edges).toHaveProperty('6930dcb2e9363817e9f6e736829ce278');
expect(Array.from(g.edges['6930dcb2e9363817e9f6e736829ce278'].apis)).toEqual(['API2']);
expect(g.edges['6930dcb2e9363817e9f6e736829ce278'].sources).toHaveProperty('source2');
expect(Array.from(g.edges['6930dcb2e9363817e9f6e736829ce278'].publications)).toEqual(['PMC:1', 'PMC:2']);
expect(g.edges['6930dcb2e9363817e9f6e736829ce278'].attributes).toHaveProperty('relation', new Set(['relation2']));
expect(g.edges['6930dcb2e9363817e9f6e736829ce278'].attributes).toHaveProperty('relation', ['relation2']);

expect(g.edges).toHaveProperty('38e8cf1917452c83bb878c5a916ef86a');
expect(Array.from(g.edges['38e8cf1917452c83bb878c5a916ef86a'].apis)).toEqual(['API3']);
expect(g.edges['38e8cf1917452c83bb878c5a916ef86a'].sources).toHaveProperty('source3');
expect(Array.from(g.edges['38e8cf1917452c83bb878c5a916ef86a'].publications)).toEqual(['PMC:3', 'PMC:4']);
expect(g.edges['38e8cf1917452c83bb878c5a916ef86a'].attributes).toHaveProperty('relation', new Set(['relation3']));
expect(g.edges['38e8cf1917452c83bb878c5a916ef86a'].attributes).toHaveProperty('relation', ['relation3']);
});

test('Multiple attributes with the same name are merged', () => {
Expand All @@ -187,9 +187,10 @@ describe('Test graph class', () => {
'PMC:6',
'PMC:7',
]);
expect(g.edges['38e8cf1917452c83bb878c5a916ef86a'].attributes).toHaveProperty(
'relation',
new Set(['relation3', 'relation3a', 'relation3b']),
);
expect(g.edges['38e8cf1917452c83bb878c5a916ef86a'].attributes).toHaveProperty('relation', [
'relation3',
'relation3a',
'relation3b',
]);
});
});
11 changes: 5 additions & 6 deletions src/graph/kg_edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default class KGEdge {
[qualifier_type_id: string]: string | string[];
};
attributes: {
[attribute_type_id: string]: Set<string> | TrapiAttribute[];
[attribute_type_id: string]: string[] | TrapiAttribute[];
'edge-attributes'?: TrapiAttribute[];
};
constructor(id: string, info: KGEdgeInfo) {
Expand Down Expand Up @@ -120,18 +120,17 @@ export default class KGEdge {
addAdditionalAttributes(name: string, value: string | string[] | TrapiAttribute[]): void {
// special handling for full edge attributes
if (name === 'edge-attributes') {
this.attributes[name] = value as TrapiAttribute[];
if (this.attributes[name]) this.attributes[name] = [...this.attributes[name], ...value as TrapiAttribute[]];
else this.attributes[name] = value as TrapiAttribute[];
return;
}

if (!(name in this.attributes)) {
this.attributes[name] = new Set();
this.attributes[name] = [];
}
if (!Array.isArray(value)) {
value = [value];
}
(value as string[]).map((item) => {
(this.attributes[name] as Set<string>).add(item);
});
(this.attributes[name] as string[]).push(...(value as string[]));
}
}
44 changes: 39 additions & 5 deletions src/graph/knowledge_graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { toArray, Telemetry } from '@biothings-explorer/utils';

const debug = Debug('bte:biothings-explorer-trapi:KnowledgeGraph');

const NON_ARRAY_ATTRIBUTES = ['biolink:knowledge_level', 'biolink:agent_type', 'biolink:evidence_count'];
const NON_ARRAY_ATTRIBUTES = ['biolink:knowledge_level', 'biolink:agent_type'];
const SUM_ATTRIBUTES = ['biolink:evidence_count'];

interface SpecialAttributeHandlers {
[attribute_type_id: string]: (value: Set<string | number>, kgEdge: KGEdge) => TrapiAttribute['value'];
Expand Down Expand Up @@ -149,12 +150,19 @@ export default class KnowledgeGraph {
Object.entries(kgEdge.attributes).forEach(([key, value]) => {
if (key === 'edge-attributes') return;

let formatted_value: TrapiAttribute['value'] = NON_ARRAY_ATTRIBUTES.includes(key)
? Array.from(value as Set<string>).reduce((acc, val) => acc + val)
: Array.from(value as Set<string>);
let formatted_value: TrapiAttribute['value'];
if (SUM_ATTRIBUTES.includes(key)) {
// for sums we don't want to remove duplicates
formatted_value = (value as string[]).reduce((acc, val) => acc + val);
} else if (NON_ARRAY_ATTRIBUTES.includes(key)) {
// for non array attributes we want to remove duplicates (ie. same string for knowledge_level multiple times)
formatted_value = Array.from(new Set(value as string[])).reduce((acc, val) => acc + val);
} else {
formatted_value = Array.from(new Set(value as string[]));
}

if (key in SPECIAL_ATTRIBUTE_HANDLERS) {
formatted_value = SPECIAL_ATTRIBUTE_HANDLERS[key](value as Set<string | number>, kgEdge);
formatted_value = SPECIAL_ATTRIBUTE_HANDLERS[key](new Set(value as string[]), kgEdge);
}

attributes.push({
Expand All @@ -166,9 +174,35 @@ export default class KnowledgeGraph {
});

//handle TRAPI APIs (Situation A of https://github.com/biothings/BioThings_Explorer_TRAPI/issues/208) and APIs that define 'edge-atributes' in x-bte
const seenPmids = new Set();
kgEdge.attributes['edge-attributes']?.forEach((attribute) => {
// Do not add multiple SemmedDB sentences/other "supporting study results" from the same publication
if (attribute.attribute_type_id === "biolink:has_supporting_study_result" && attribute?.attributes?.find((attr) => attr.attribute_type_id === "biolink:publications")) {
const publication = attribute.attributes.find((attr) => attr.attribute_type_id === "biolink:publications").value;
// publication has been seen or cap reached
if (seenPmids.has(publication) || seenPmids.size >= 50) {
seenPmids.add(publication);
return;
}
seenPmids.add(publication);
}

attributes.push(attribute);
});

// update evidence count after PMIDs have been merged (for SemmedDB)
if (seenPmids.size != 0) {
const evidenceAttr = attributes.find(attr => attr.attribute_type_id === 'biolink:evidence_count');
if (evidenceAttr) {
evidenceAttr.value = seenPmids.size;
} else {
attributes.push({
attribute_type_id: 'biolink:evidence_count',
value: seenPmids.size,
});
}
}

return attributes;
}

Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ export default class TRAPIQueryHandler {
]);
this.bteGraph.edges[boundEdgeID] = boundEdge;
} else {
(this.bteGraph.edges[boundEdgeID].attributes['biolink:support_graphs'] as Set<string>).add(supportGraphID);
this.bteGraph.edges[boundEdgeID].addAdditionalAttributes('biolink:support_graphs', supportGraphID);
}
if (!edgesToRebind[edgeID]) edgesToRebind[edgeID] = {};
if (!edgesToRebind[edgeID][subject]) edgesToRebind[edgeID][subject] = {};
Expand Down
Loading

0 comments on commit c0560dd

Please sign in to comment.