UNPKG

@ssb-graphql/whakapapa

Version:

GraphQL types and resolvers for the ssb-whakapapa plugin

128 lines (110 loc) 3.55 kB
const set = require('lodash.set') /* * This is designed to help track which links you've found, * and which parents you've checked links on * * Use: * 1. initialise with starting person "targetId" * 2. do some lookups, registering links with addChildLink / addPartnerLink * 3. call state.newAdults to find which adults have been newly discovered * 4. call state.resetNewAdults() to zero that out * 5. (repeat 2-4) * 6. get final results using state.output */ module.exports = function LinkStateHelper (targetId) { const links = { child: { // [parentId]: { // [childId]: link // } }, partner: { // [partnerA]: { // [partnerB]: link // } } } const adults = new Set([targetId]) // i.e. of parent generation, not child generation const newAdults = new Set([]) return { addChildLink (link) { if (!adults.has(link.parent)) newAdults.add(link.parent) set(links.child, [link.parent, link.child], link) }, addPartnerLink (link) { if (!adults.has(link.parent)) newAdults.add(link.parent) if (!adults.has(link.child)) newAdults.add(link.child) set(links.partner, [link.parent, link.child].sort(), link) // NOTE partner links are undirected // so instead of using saying parentId = link.parent // we define [partnerA, partnerB] = [link.parent, link.child].sort() }, get newAdults () { return Array.from(newAdults) }, resetNewAdults () { newAdults.forEach(adult => adults.add(adult)) newAdults.clear() }, get output () { // if find, then mock an "unknown" partnerLink return { childLinks: linksOutput(links.child), partnerLinks: [ ...linksOutput(links.partner, 'partners'), ...inferredPartnerLinks(links, new Set([...adults, ...newAdults])) ] } } } } function inferredPartnerLinks (links, adults) { const results = [] // for each adult, check for parternLink with other adults adults.forEach(i => { adults.forEach(j => { if (i === j) return const [partnerA, partnerB] = [i, j].sort() if (partnerA !== i) return // avoid duplication from order invariance // if there is a partnerLink, skip if (links.partner[partnerA] && links.partner[partnerA][partnerB]) return // if there is not a partnerLink, see if they have any shared birth-children if (haveSharedBirthChild(links, partnerA, partnerB)) { // if there is, make an "inferred" partnerLink results.push({ parent: partnerA, child: partnerB, relationshipType: 'inferred' }) } }) }) return results } function haveSharedBirthChild (links, partnerA, partnerB) { const childrenOfA = Object.values(links.child[partnerA] || {}) .filter(isBirthLink) .map(link => link.child) const sharedBirthChild = childrenOfA.find(child => ( links.child[partnerB] && links.child[partnerB][child] && isBirthLink(links.child[partnerB][child]) )) return Boolean(sharedBirthChild) } function isBirthLink (link) { return link.relationshipType === 'birth' } function linksOutput (links, defaultRelationshipType) { return flatten(links) .map(link => { if (!link.relationshipType && defaultRelationshipType) { link.relationshipType = defaultRelationshipType } return link }) } function flatten (links) { return Object.values(links) .reduce((acc, item) => acc.concat(Object.values(item)), []) }