References now merge children, fixed infinite reference loops

This commit is contained in:
Stefan Zermatten
2021-09-05 18:51:29 +02:00
parent 7f95680559
commit fc0cc6e689
3 changed files with 25 additions and 31 deletions

View File

@@ -116,7 +116,7 @@ function insertPropertyFromNode(nodeId, ancestors, order){
nodes = [node, ...nodes]; nodes = [node, ...nodes];
// set libraryNodeIds // set libraryNodeIds
storeLibraryNodeReferences(nodes, nodeId); storeLibraryNodeReferences(nodes);
// re-map all the ancestors // re-map all the ancestors
setLineageOfDocs({ setLineageOfDocs({
@@ -149,6 +149,7 @@ function insertPropertyFromNode(nodeId, ancestors, order){
function storeLibraryNodeReferences(nodes){ function storeLibraryNodeReferences(nodes){
nodes.forEach(node => { nodes.forEach(node => {
if (node.libraryNodeId) return;
node.libraryNodeId = node._id; node.libraryNodeId = node._id;
}); });
} }
@@ -162,20 +163,11 @@ function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0){
// Filter out the reference nodes we replace // Filter out the reference nodes we replace
let resultingNodes = nodes.filter(node => { let resultingNodes = nodes.filter(node => {
// We have already visited this ref and replaced it
if (visitedRefs.has(node._id)) return false;
// Already replaced an ancestor node
for (let i; i < node.ancestors.length; i++){
if (visitedRefs.has(node.ancestors[i].id)) return false;
}
// This isn't a reference node, continue as normal // This isn't a reference node, continue as normal
if (node.type !== 'reference') return true; if (node.type !== 'reference') return true;
// We have gone too deep, keep the reference node as an error // We have gone too deep, keep the reference node as an error
if (depth > 10){ if (depth >= 10){
if (Meteor.isClient) console.warn('Reference depth limit exceeded'); if (Meteor.isClient) console.warn('Reference depth limit exceeded');
node.cache = {error: 'Reference depth limit exceeded'}; node.cache = {error: 'Reference depth limit exceeded'};
return true; return true;
@@ -211,26 +203,31 @@ function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0){
oldParent: referencedNode.parent, oldParent: referencedNode.parent,
}); });
// Remove all the looped references and descendents from the new nodes // Filter all the looped references
// We can't rely on the reify recursion to do this, since the IDs are addedNodes = addedNodes.filter(addedNode => {
// getting renewed before it is called // Add all non-reference nodes
addedNodes = addedNodes.filter(node => { if (addedNode.type !== 'reference'){
// Exclude removed referenced return true;
if (visitedRefs.has(node._id)) return false; }
// If this exact reference has already been resolved before, filter it out
// Exclude descendants of removed references if (visitedRefs.has(addedNode._id)){
for (let i; i < node.ancestors.length; i++){ return false;
if (visitedRefs.has(node.ancestors[i].id)) return false; } else {
// Otherwise mark it as visited, and keep it
visitedRefs.add(addedNode._id);
return true;
} }
return true;
}); });
// TODO: Force the referencedNode to take the old id of the reference // Before renewing Ids make sure the library node reference is stored
// such that the reference's children can be kept storeLibraryNodeReferences(addedNodes);
// Give the new referenced sub-tree new ids // Give the new referenced sub-tree new ids
// The referenced node must get the id of the ref node so that the
// descendants of the ref node keep their ancestry intact
renewDocIds({ renewDocIds({
docArray: addedNodes, docArray: addedNodes,
idMap: { [referencedNode._id]: node._id },
}); });
// Reify the subtree as well with recursion // Reify the subtree as well with recursion

View File

@@ -101,7 +101,6 @@ export function getAncestry({parentRef, inheritedFields = {}}){
} }
export function setLineageOfDocs({docArray, oldParent, newAncestry}){ export function setLineageOfDocs({docArray, oldParent, newAncestry}){
//const oldParent = oldAncestry[oldAncestry.length - 1];
const newParent = newAncestry[newAncestry.length - 1]; const newParent = newAncestry[newAncestry.length - 1];
docArray.forEach(doc => { docArray.forEach(doc => {
if(doc.parent.id === oldParent.id){ if(doc.parent.id === oldParent.id){
@@ -109,6 +108,7 @@ export function setLineageOfDocs({docArray, oldParent, newAncestry}){
} }
let oldAncestors = doc.ancestors; let oldAncestors = doc.ancestors;
let oldParentIndex = oldAncestors.findIndex(a => a.id === oldParent.id); let oldParentIndex = oldAncestors.findIndex(a => a.id === oldParent.id);
if (oldParentIndex === -1) return;
doc.ancestors = [...newAncestry, ...oldAncestors.slice(oldParentIndex + 1)]; doc.ancestors = [...newAncestry, ...oldAncestors.slice(oldParentIndex + 1)];
}); });
} }
@@ -117,17 +117,15 @@ export function setLineageOfDocs({docArray, oldParent, newAncestry}){
* Give documents new random ids and transform their references. * Give documents new random ids and transform their references.
* Transform collections of re-IDed docs according to the collection map * Transform collections of re-IDed docs according to the collection map
*/ */
export function renewDocIds({docArray, collectionMap}){ export function renewDocIds({docArray, collectionMap, idMap = {}}){
// map of {oldId: newId} // idMap is a map of {oldId: newId}
let idMap = {};
// Get a random generator that's consistent on client and server // Get a random generator that's consistent on client and server
let randomSrc = DDP.randomStream('renewDocIds'); let randomSrc = DDP.randomStream('renewDocIds');
// Give new ids and map the changes as {oldId: newId} // Give new ids and map the changes as {oldId: newId}
docArray.forEach(doc => { docArray.forEach(doc => {
let oldId = doc._id; let oldId = doc._id;
let newId = randomSrc.id(); let newId = idMap[oldId] || randomSrc.id();
doc._id = newId; doc._id = newId;
idMap[oldId] = newId; idMap[oldId] = newId;
}); });

View File

@@ -4,7 +4,6 @@ import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import { assertViewPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertViewPermission } from '/imports/api/sharing/sharingPermissions.js';
Meteor.publish('selectedLibraryNodes', function(selectedNodeIds){ Meteor.publish('selectedLibraryNodes', function(selectedNodeIds){
console.log('attempting selectedLibraryNodes')
check(selectedNodeIds, Array); check(selectedNodeIds, Array);
// Limit to 20 selected nodes // Limit to 20 selected nodes
if (selectedNodeIds.length > 20){ if (selectedNodeIds.length > 20){