157 lines
4.2 KiB
JavaScript
157 lines
4.2 KiB
JavaScript
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
|
|
import getCollectionByName from '/imports/api/parenting/getCollectionByName.js';
|
|
import getDescendantsInDepthFirstOrder from '/imports/api/parenting/getDescendantsInDepthFirstOrder.js'
|
|
|
|
// Docs keep track of their depth-first order amongst their entire ancestor tree
|
|
export function compareOrder(docA, docB){
|
|
// < 0 if A comes before B
|
|
// = 0 if A and B are the same order
|
|
// > 0 if B comes before A
|
|
|
|
// They must share a root ancestor to be meaningfully sorted
|
|
if (docA.ancestors[0].id !== docB.ancestors[0].id){
|
|
return 0;
|
|
} else {
|
|
return docA.order - docB.order;
|
|
}
|
|
}
|
|
|
|
export function getHighestOrder({collection, ancestorId}){
|
|
const highestOrderedDoc = collection.findOne({
|
|
'ancestors.id': ancestorId,
|
|
}, {
|
|
fields: {order: 1},
|
|
sort: {order: -1},
|
|
});
|
|
return highestOrderedDoc ? highestOrderedDoc.order : -1;
|
|
}
|
|
|
|
export function setDocToLastOrder({collection, doc}){
|
|
doc.order = getHighestOrder({
|
|
collection,
|
|
ancestorId: doc.ancestors[0].id,
|
|
}) + 1;
|
|
}
|
|
|
|
// update the order of a doc, and shift the related docs around to suit the new
|
|
// order
|
|
function cheapUpdateDocOrder({docRef, order}){
|
|
let doc = fetchDocByRef(docRef, {fields: {
|
|
order: 1,
|
|
parent: 1,
|
|
}});
|
|
let collection = getCollectionByName(docRef.collection);
|
|
const currentOrder = doc.order;
|
|
if (currentOrder === order){
|
|
return;
|
|
} else {
|
|
// First move the documents that are in the way
|
|
let inBetweenSelector, increment;
|
|
if (order > currentOrder){
|
|
// Move in-between docs backward
|
|
inBetweenSelector = {
|
|
$gt: currentOrder,
|
|
$lte: order
|
|
};
|
|
increment = -1;
|
|
} else if (order < currentOrder){
|
|
// Move in-between docs forward
|
|
inBetweenSelector = {
|
|
$lt: currentOrder,
|
|
$gte: order
|
|
};
|
|
increment = 1;
|
|
}
|
|
collection.update({
|
|
'ancestors.id': doc.ancestors[0].id,
|
|
order: inBetweenSelector,
|
|
}, {
|
|
$inc: {order: increment},
|
|
}, {
|
|
multi: true,
|
|
selector: {type: 'any'},
|
|
});
|
|
// Then move the document itself
|
|
collection.update(doc._id, {$set: {order}}, {selector: {type: 'any'}});
|
|
}
|
|
}
|
|
|
|
export function cheapRemovedDocAtOrder({collection, doc}){
|
|
// Decrement the order of all docs after the removed doc
|
|
collection.update({
|
|
'ancestors.id': doc.ancestors[0].id,
|
|
order: {$gt: doc.order},
|
|
}, {
|
|
$inc: {order: -1},
|
|
}, {
|
|
multi: true,
|
|
selector: {type: 'any'},
|
|
});
|
|
}
|
|
|
|
export function cheapInsertedDocAtOrder({collection, ancestorId, order}){
|
|
// Increment the order of all docs after the inserted doc
|
|
collection.update({
|
|
'ancestors.id': ancestorId,
|
|
order: {$gte: order},
|
|
}, {
|
|
$inc: {order: 1},
|
|
}, {
|
|
multi: true,
|
|
selector: {type: 'any'},
|
|
});
|
|
}
|
|
|
|
// Update the order a single doc and re-order the entire related doc list
|
|
// with the change
|
|
export function safeUpdateDocOrder({docRef, order}){
|
|
let collection = getCollectionByName(docRef.collection);
|
|
// Put the new doc half a step in front of its new order
|
|
// to ensure it's in front of whichever doc was there before
|
|
collection.update(docRef.id, {
|
|
$set: {order}
|
|
}, {
|
|
selector: {type: 'any'}
|
|
});
|
|
// reorder all related docs so that order is back to being a continous
|
|
// set of whole numbers
|
|
let movedDoc = fetchDocByRef(docRef, {fields: {ancestors: 1}});
|
|
let ancestorId = movedDoc.ancestors[0].id;
|
|
reorderDocs({collection, ancestorId});
|
|
}
|
|
|
|
export function reorderDocs({collection, ancestorId}){
|
|
let orderedDocs = getDescendantsInDepthFirstOrder({collection, ancestorId});
|
|
let bulkWrite = [];
|
|
orderedDocs.forEach((doc, index) => {
|
|
if (doc.order !== index){
|
|
bulkWrite.push({
|
|
updateOne : {
|
|
filter: {_id: doc._id},
|
|
update: {$set: {order: index}},
|
|
},
|
|
});
|
|
}
|
|
});
|
|
if (Meteor.isServer && bulkWrite.length){
|
|
collection.rawCollection().bulkWrite(
|
|
bulkWrite,
|
|
{ordered : false},
|
|
function(e){
|
|
if (e) {
|
|
console.error('Bulk write failed: ');
|
|
console.error(e);
|
|
}
|
|
}
|
|
);
|
|
} else {
|
|
bulkWrite.forEach(op => {
|
|
collection.update(
|
|
op.updateOne.filter,
|
|
op.updateOne.update,
|
|
{selector: {type: 'any'}}
|
|
);
|
|
});
|
|
}
|
|
}
|