Skip to content

Instantly share code, notes, and snippets.

@RichAyotte
Created November 18, 2017 03:13
Show Gist options
  • Save RichAyotte/cb4cae6147f2f8ca1c31371b640793b5 to your computer and use it in GitHub Desktop.
Save RichAyotte/cb4cae6147f2f8ca1c31371b640793b5 to your computer and use it in GitHub Desktop.
Sequelize Dataloader Advices
/**
* @overview Sequelize find - dataloader advice
* @author Richard Ayotte
* @copyright Copyright © 2017 Richard Ayotte
* @date 2017-11-03
* @license GNU GPL-3.0
* @flow
*/
'use strict'
const DataLoader = require('dataloader')
const hash = require('object-hash')
const {
find
, get
, isPlainObject
, union
} = require('lodash')
const {Op} = require('sequelize')
const log = require('../../lib/log')
const loaders = new Map()
const symbolKeysToStrings = obj => {
const newObj = {}
Object.getOwnPropertySymbols(obj).forEach(k => {
newObj[String(k)] = isPlainObject(obj[k]) ? symbolKeysToStrings(obj[k]) : obj[k]
})
Object.keys(obj).forEach(k => {
newObj[k] = isPlainObject(obj[k]) ? symbolKeysToStrings(obj[k]) : obj[k]
})
return newObj
}
module.exports = function adviseDataloader({args, proceed, proceedCount, target}) {
const [options, transaction] = args
const where = get(options, 'where')
let id
// Don't dataload when there's not where.
if (!where) {
return proceed()
}
const whereKeys = [
...Object.keys(where)
, ...Object.getOwnPropertySymbols(where)
]
// Don't dataload if 'or' is found in the where keys.
if (whereKeys.includes(Op.or) || whereKeys.includes('$or')) {
return proceed()
}
// Only look into the where object 1 level deep. I don't think I'll need to
// go much deeper so leaving the recursive complexity out for now. RA
if (whereKeys.includes('id')) {
id = where.id
delete where.id
}
else if (whereKeys.includes(Op.and)) {
if (!where[Op.and].id) {
return proceed()
}
id = where[Op.and].id
delete where[Op.and].id
}
if (!id) {
return proceed()
}
// Create a new where object with all the Symbols transformed into strings.
const loaderWhere = symbolKeysToStrings(where)
const loaderId = hash(
JSON.stringify({
attributes: options.attributes
, include: options.include
, table: target.tableName
, where
})
, {
encoding: 'binary'
}
)
let loader = loaders.get(loaderId)
if (!loader) {
loader = new DataLoader(function(keys) {
return proceed({
meta: options.meta
, attributes: union(['id'], options.attributes)
, where: {
[Op.and]: {
id: [...new Set(keys)]
, ...where
}
}
})
.then(sqlResult => {
return keys.reduce((result, key) => {
result.push(find(sqlResult, {id: key}))
return result
}, [])
})
}, { cache: false })
loaders.set(loaderId, loader)
}
return loader.load(id)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment