in source/packages/services/assetlibrary/src/data/common.full.dao.ts [38:132]
public async listRelated(entityDbId: string, relationship: string, direction:string, template:string, filterRelatedBy:{ [key: string] : ModelAttributeValue}, offset:number, count:number, sort:SortKeys) : Promise<Node> {
logger.debug(`common.full.dao listRelated: in: entityDbId:${entityDbId}, relationship:${relationship}, direction:${direction}, template:${template}, filterRelatedBy:${JSON.stringify(filterRelatedBy)}, offset:${offset}, count:${count}, ${JSON.stringify(sort)}`);
// define the traversers that handle finding associated edges/vertices
const relatedIn = __.inE();
const relatedOut = __.outE();
// filter the edges based on the relationship type (if requiested)
if (relationship==='*') {
relationship=undefined;
}
if (relationship) {
[relatedIn, relatedOut].forEach(t=> t.hasLabel(relationship).as('e'));
}
// navigate to the linked vertex for each relation, filtering the type
[relatedIn, relatedOut].forEach(t=> t.as('e').otherV().hasLabel(template).as('v'));
// apply filtering to the linked vertex (if required)
if (filterRelatedBy!==undefined) {
Object.keys(filterRelatedBy).forEach(k=> {
[relatedIn, relatedOut].forEach(t=> t.has(k, filterRelatedBy[k]));
});
}
// return the info we need to understand about each relation
[relatedIn, relatedOut].forEach(t=> t.valueMap().with_(process.withOptions.tokens).as('vProps'));
relatedIn.constant('in').as('dir');
relatedOut.constant('out').as('dir');
[relatedIn, relatedOut].forEach(t=> t.select('dir','e','vProps'));
// build the union traversal that combines the incoming and outgoing relations (depending on what was requested)
let relatedUnion : process.GraphTraversal;
if (direction==='in') {
relatedUnion = relatedIn;
} else if (direction==='out') {
relatedUnion = relatedOut;
} else {
relatedUnion = __.union(relatedIn, relatedOut);
}
// apply sorting to the related (if requested)
if (sort?.length>0) {
relatedUnion.order();
sort.forEach(s=> {
const order = (s.direction==='ASC') ? process.order.asc : process.order.desc;
// sort using an attribute from the connected vertices, with a failsafe incase the attribute is undefined
relatedUnion.by(__.coalesce(__.select('v').values(s.field),__.constant('')), order);
});
}
// apply pagination to the related (if requested)
if (offset!==undefined && count!==undefined) {
// note: workaround for weird typescript issue. even though offset/count are declared as numbers
// throughout, they are being interpreted as strings within gremlin, therefore need to force to int beforehand
const offsetAsInt = this.typeUtils.parseInt(offset);
const countAsInt = this.typeUtils.parseInt(count);
relatedUnion.range(offsetAsInt, offsetAsInt + countAsInt);
}
// build the main part of the query, unioning the related traversers with the main entity we want to return
let results;
const conn = super.getConnection();
try {
const traverser = conn.traversal.V(entityDbId).as('main')
.union(
relatedUnion,
__.select('main').valueMap().with_(process.withOptions.tokens)
);
// execute and retrieve the results
logger.debug(`common.full.dao listRelated: traverser: ${JSON.stringify(traverser.toString())}`);
results = await traverser.toList();
logger.debug(`common.full.dao listRelated: results: ${JSON.stringify(results)}`);
} finally {
await conn.close();
}
if (results===undefined || results.length===0) {
logger.debug(`common.full.dao listRelated: exit: node: undefined`);
return undefined;
}
// the result should contain a vertex representing the entity requested as 1 row, then all requested relations as other rows
// find the main entity first
const mainEntity = results.filter(r=> isVertexDto(r))[0] as VertexDto;
const node = this.fullAssembler.assembleNode(mainEntity);
const relatedEntities = results.filter(r=> isRelatedEntityDto(r)).map(r=> r as unknown as RelatedEntityDto);
relatedEntities.forEach(r=> this.fullAssembler.assembleAssociation(node,r));
logger.debug(`common.full.dao listRelated: exit: node: ${JSON.stringify(node)}`);
return node;
}