feat: join node
This commit is contained in:
parent
0e491bd762
commit
10b7447f7d
80
core/entities/nodes/JoinNode.js
Normal file
80
core/entities/nodes/JoinNode.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import Node from '../Node.js'
|
||||||
|
|
||||||
|
class JoinNode extends Node {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
this._assignProps(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
export = () => {
|
||||||
|
const baseTable = this.tables.find(t => t.label === this.baseTableLabel)
|
||||||
|
const baseTableRows = baseTable.getRows()
|
||||||
|
const tablesToJoin = this.tables.filter(t => {
|
||||||
|
return t.label !== this.baseTableLabel
|
||||||
|
})
|
||||||
|
|
||||||
|
const relatedTables = this.joinParams.map(joinParam => {
|
||||||
|
const foreignTable = tablesToJoin.find(t => {
|
||||||
|
return t.label === joinParam.foreignTable
|
||||||
|
})
|
||||||
|
const foreignTableRows = foreignTable.getRows()
|
||||||
|
|
||||||
|
const mergedRows = baseTableRows.map(baseRow => {
|
||||||
|
const matchingForeignRow = foreignTableRows.find(foreignRow => {
|
||||||
|
return baseRow[joinParam.primaryTableKey] === foreignRow[joinParam.matchingKey]
|
||||||
|
})
|
||||||
|
let rowToMerge = {}
|
||||||
|
for (let key in matchingForeignRow) {
|
||||||
|
rowToMerge[`${joinParam.foreignTable}::${key}`] = matchingForeignRow[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
return {...baseRow, ...rowToMerge}
|
||||||
|
})
|
||||||
|
return mergedRows
|
||||||
|
})
|
||||||
|
|
||||||
|
return relatedTables[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
setJoinBy = joinBy => {
|
||||||
|
const joinByValidation = this._validateJoinBy(joinBy)
|
||||||
|
if (joinByValidation.status === 'ERR') throw joinByValidation
|
||||||
|
else {
|
||||||
|
this.baseTableLabel = joinBy.baseTableLabel
|
||||||
|
this.joinParams = joinBy.joinParams
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_assignProps = props => {
|
||||||
|
if (props.joinBy) this.setJoinBy(props.joinBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
_validateJoinBy = joinBy => {
|
||||||
|
const err = {
|
||||||
|
status: 'ERR',
|
||||||
|
error: {
|
||||||
|
label: 'JoinBy Parameters are not valid',
|
||||||
|
messages: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { baseTableLabel, joinParams } = joinBy
|
||||||
|
|
||||||
|
if (!baseTableLabel) err.error.messages.push('No baseTableLabel provided')
|
||||||
|
|
||||||
|
if (!Array.isArray(joinParams)) {
|
||||||
|
const joinParamsType = typeof joinParams
|
||||||
|
err.error.messages.push(`Keys was of type ${joinParamsType} should be an array`)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let p = 0; p < joinParams.length; p++) {
|
||||||
|
if (typeof joinParams[p] !== 'object') err.error.messages.push(`joinParams[${p}] is not an object`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.error.messages.length > 0) return err
|
||||||
|
else return { status: 'OK' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default JoinNode
|
97
tests/core/nodes/joinNodeTests.js
Normal file
97
tests/core/nodes/joinNodeTests.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import JoinNode from '../../../core/entities/nodes/JoinNode.js'
|
||||||
|
import Table from '../../../core/entities/Table.js'
|
||||||
|
|
||||||
|
const joinTables = () => {
|
||||||
|
const pickupTable = new Table({
|
||||||
|
id: 'abc',
|
||||||
|
label: 'receipts',
|
||||||
|
rows: [
|
||||||
|
{ id: '2345676', contractor: 'AshBritt', type: 'row', lat: 54, long: 31 },
|
||||||
|
{ id: '2345676', contractor: 'Jefferson',type: 'lh', lat: 31, long: -71.34 },
|
||||||
|
{ id: '2345676', contractor: 'AshBritt', type: 'lh', lat: 80, long: -41 },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const contractorTable = new Table({
|
||||||
|
id: 'XYZ',
|
||||||
|
label: 'contractors',
|
||||||
|
rows: [
|
||||||
|
{ id: '1WE3V6', name: 'AshBritt', employeeCount: 43, homeState: 'CA' },
|
||||||
|
{ id: 'FG4S67', name: 'Jefferson', employeeCount: 91, homeState: 'AL' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const expectedOutput = [
|
||||||
|
{ id: '2345676', contractor: 'AshBritt', type: 'row', lat: 54, long: 31, 'contractors::id': '1WE3V6', 'contractors::name': 'AshBritt', 'contractors::employeeCount': 43, 'contractors::homeState': 'CA' },
|
||||||
|
{ id: '2345676', contractor: 'Jefferson',type: 'lh', lat: 31, long: -71.34, 'contractors::id': 'FG4S67', 'contractors::name': 'Jefferson', 'contractors::employeeCount': 91, 'contractors::homeState': 'AL' },
|
||||||
|
{ id: '2345676', contractor: 'AshBritt', type: 'lh', lat: 80, long: -41, 'contractors::id': '1WE3V6', 'contractors::name': 'AshBritt', 'contractors::employeeCount': 43, 'contractors::homeState': 'CA' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const joinNode = new JoinNode({
|
||||||
|
id: 'QWE',
|
||||||
|
label: 'Receipts with Contractors',
|
||||||
|
tables: [pickupTable, contractorTable],
|
||||||
|
joinBy: {
|
||||||
|
baseTableLabel: 'receipts',
|
||||||
|
joinParams: [
|
||||||
|
{ foreignTable: 'contractors', primaryTableKey: 'contractor', matchingKey: 'name' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const joinNodeProps = joinNode.export()
|
||||||
|
|
||||||
|
if (JSON.stringify(joinNodeProps) === JSON.stringify(expectedOutput)) return true
|
||||||
|
else return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const setJoinBy = () => {
|
||||||
|
const pickupTable = new Table({
|
||||||
|
id: 'abc',
|
||||||
|
label: 'receipts',
|
||||||
|
rows: [
|
||||||
|
{ id: '2345676', contractor: 'AshBritt', type: 'row', lat: 54, long: 31 },
|
||||||
|
{ id: '2345676', contractor: 'Jefferson',type: 'lh', lat: 31, long: -71.34 },
|
||||||
|
{ id: '2345676', contractor: 'AshBritt', type: 'lh', lat: 80, long: -41 },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const contractorTable = new Table({
|
||||||
|
id: 'XYZ',
|
||||||
|
label: 'contractors',
|
||||||
|
rows: [
|
||||||
|
{ id: '1WE3V6', name: 'AshBritt', employeeCount: 43, homeState: 'CA' },
|
||||||
|
{ id: 'FG4S67', name: 'Jefferson', employeeCount: 91, homeState: 'AL' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const expectedOutput = [
|
||||||
|
{ id: '2345676', contractor: 'AshBritt', type: 'row', lat: 54, long: 31, 'contractors::id': '1WE3V6', 'contractors::name': 'AshBritt', 'contractors::employeeCount': 43, 'contractors::homeState': 'CA' },
|
||||||
|
{ id: '2345676', contractor: 'Jefferson',type: 'lh', lat: 31, long: -71.34, 'contractors::id': 'FG4S67', 'contractors::name': 'Jefferson', 'contractors::employeeCount': 91, 'contractors::homeState': 'AL' },
|
||||||
|
{ id: '2345676', contractor: 'AshBritt', type: 'lh', lat: 80, long: -41, 'contractors::id': '1WE3V6', 'contractors::name': 'AshBritt', 'contractors::employeeCount': 43, 'contractors::homeState': 'CA' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const joinNode = new JoinNode({
|
||||||
|
id: 'QWE',
|
||||||
|
label: 'Receipts with Contractors',
|
||||||
|
tables: [pickupTable, contractorTable]
|
||||||
|
})
|
||||||
|
|
||||||
|
joinNode.setJoinBy({
|
||||||
|
baseTableLabel: 'receipts',
|
||||||
|
joinParams: [
|
||||||
|
{ foreignTable: 'contractors', primaryTableKey: 'contractor', matchingKey: 'name' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const joinNodeProps = joinNode.export()
|
||||||
|
|
||||||
|
if (JSON.stringify(joinNodeProps) === JSON.stringify(expectedOutput)) return true
|
||||||
|
else return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{ name: 'Entity | Join Table Test', test: joinTables },
|
||||||
|
{ name: 'Entity | Join Table setJoinBy', test: setJoinBy },
|
||||||
|
]
|
@ -3,6 +3,7 @@ import ProgressBar from 'progress'
|
|||||||
import tableTests from '../tests/core/tableTests.js'
|
import tableTests from '../tests/core/tableTests.js'
|
||||||
import nodeTests from '../tests/core/nodeTests.js'
|
import nodeTests from '../tests/core/nodeTests.js'
|
||||||
import filterNodeTests from '../tests/core/nodes/filterNodeTests.js'
|
import filterNodeTests from '../tests/core/nodes/filterNodeTests.js'
|
||||||
|
import joinNodeTests from '../tests/core/nodes/joinNodeTests.js'
|
||||||
|
|
||||||
function runTestsAndReturnFailures (tests) {
|
function runTestsAndReturnFailures (tests) {
|
||||||
const testTotalCount = tests.length
|
const testTotalCount = tests.length
|
||||||
@ -39,7 +40,8 @@ function init (tests) {
|
|||||||
const testsArray = [
|
const testsArray = [
|
||||||
tableTests,
|
tableTests,
|
||||||
nodeTests,
|
nodeTests,
|
||||||
filterNodeTests
|
filterNodeTests,
|
||||||
|
joinNodeTests
|
||||||
]
|
]
|
||||||
|
|
||||||
init (testsArray.flat())
|
init (testsArray.flat())
|
Loading…
x
Reference in New Issue
Block a user