Improved migration code substantially, wrote migrations for more properties
This commit is contained in:
112
app/imports/migrations/server/transformFields.js
Normal file
112
app/imports/migrations/server/transformFields.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import { get, set, unset, forEachRight, cloneDeep } from 'lodash';
|
||||
|
||||
export default function transformFields(src, transformList, reversed = false){
|
||||
// don't bash the old document during the transforms
|
||||
let doc = cloneDeep(src);
|
||||
for(let originalTransform of transformList){
|
||||
let transform;
|
||||
// Swap to and from when reversing
|
||||
if (reversed){
|
||||
transform = {
|
||||
to: originalTransform.from,
|
||||
from: originalTransform.to,
|
||||
up: originalTransform.down,
|
||||
}
|
||||
} else {
|
||||
transform = {...originalTransform};
|
||||
}
|
||||
if (transform.from?.includes('$.')){
|
||||
transformArrayField(src, doc, transform, reversed);
|
||||
} else {
|
||||
transformSingleField(src, doc, transform);
|
||||
}
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
function transformSingleField(src, doc, {from, to, up}){
|
||||
// Get the value in the `from` path and delete it
|
||||
let value = undefined;
|
||||
if (from){
|
||||
value = get(src, from);
|
||||
unset(doc, from);
|
||||
}
|
||||
|
||||
// apply the transform function
|
||||
if (up){
|
||||
value = up(value, src, doc);
|
||||
}
|
||||
|
||||
// Store the value in the `to` path or unset it if undefined
|
||||
if (to){
|
||||
if (value === undefined){
|
||||
unset(doc, to);
|
||||
} else {
|
||||
set(doc, to, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* from: 'from.$.here', to: 'to.$.here'
|
||||
* where from and to are an [array, of, objects] that each need to be modified
|
||||
* documents at 'from.x.here' will map to 'to.x.here'
|
||||
* Attempts to support 'from.$.here.$.nested'
|
||||
* by mapping 'from.x.here.y.nest.z.deep' to 'to.y.nest.z.lessDeep'
|
||||
* from depth must be >= to depth
|
||||
*/
|
||||
function transformArrayField(src, doc, {from, to, up}, reversed){
|
||||
const fromSplit = from.split('.$');
|
||||
const toSplit = to.split('.$');
|
||||
|
||||
if (toSplit.length > fromSplit.length){
|
||||
throw 'Can\'t transform array fields where "to" is deeper than "from"'
|
||||
}
|
||||
|
||||
// Stack based depth first traversal of arrays
|
||||
const stack = [{
|
||||
array: get(src, fromSplit[0]),
|
||||
paths: fromSplit.slice(1),
|
||||
currentPath: fromSplit[0],
|
||||
indices: [],
|
||||
}];
|
||||
while(stack.length){
|
||||
const state = stack.pop();
|
||||
// Iterate forwads or backwads defpending on our migration direction
|
||||
if (reversed){
|
||||
forEachRight(state.array, iterate(stack, state, src, doc, toSplit, up));
|
||||
} else {
|
||||
state.array.forEach(iterate(stack, state, src, doc, toSplit, up));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function iterate(stack, state, src, doc, toSplit, up){return function(key, index){
|
||||
const currentPath = `${state.currentPath}[${index}]${state.paths[0]}`
|
||||
if (state.paths.length == 1){
|
||||
transformSingleField(src, doc, {
|
||||
from: currentPath,
|
||||
to: buildToPath(toSplit, [...state.indices, index]),
|
||||
up
|
||||
});
|
||||
} else {
|
||||
stack.push({
|
||||
array: get(src, currentPath),
|
||||
paths: state.paths.slice(1),
|
||||
currentPath,
|
||||
indices: [...state.indices, index],
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
||||
function buildToPath(toSplit, indices){
|
||||
let toPath = '';
|
||||
let offset = indices.length - toSplit.length + 1;
|
||||
toSplit.forEach((path, i) => {
|
||||
toPath += `${path}`;
|
||||
if (i < toSplit.length - 1){
|
||||
toPath += `[${indices[i + offset]}]`
|
||||
}
|
||||
});
|
||||
return toPath;
|
||||
}
|
||||
Reference in New Issue
Block a user