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 nodeTests from '../tests/core/nodeTests.js'
|
||||
import filterNodeTests from '../tests/core/nodes/filterNodeTests.js'
|
||||
import joinNodeTests from '../tests/core/nodes/joinNodeTests.js'
|
||||
|
||||
function runTestsAndReturnFailures (tests) {
|
||||
const testTotalCount = tests.length
|
||||
@ -39,7 +40,8 @@ function init (tests) {
|
||||
const testsArray = [
|
||||
tableTests,
|
||||
nodeTests,
|
||||
filterNodeTests
|
||||
filterNodeTests,
|
||||
joinNodeTests
|
||||
]
|
||||
|
||||
init (testsArray.flat())
|
Loading…
x
Reference in New Issue
Block a user