// (C) Copyright 2020 Nuxeo (http://nuxeo.com/) and others. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Contributors: // Florent Guillaume // v1.0 // This script updates ancestors ids after a move. // It is NOT a full move. // ========== // var DBNAME = "unittests"; // var COLLNAME = "test"; var DBNAME = "nuxeo" var COLLNAME = "default" var DOC_ID = "f77e10772dbbfa2a"; var DEST_FOLDER_ID = "e02a327272c681c2"; // ========== db = db.getSiblingDB(DBNAME); coll = db.getCollection(COLLNAME); print(); print("Using " + DBNAME + "." + COLLNAME); function check(expected, actual) { var e = JSON.stringify(expected); var a = JSON.stringify(actual); if (e != a) { print("FAIL expected=" + e + ", actual=" + a); quit(99); } } function name(id) { return coll.findOne({'ecm:id': id})['ecm:name']; } function path(id) { var doc = coll.findOne({'ecm:id': id}); if (doc == null) return null; var aids = doc['ecm:ancestorIds']; if (aids == null) return '/'; aids.push(id); return aids.map(i => name(i)).join('/'); } function nuxeoToMongo(id) { if (id.length == 36) { // UUID return id; } else if (id.length == 16) { // hex long var top = parseInt(id.substring(0, 8), 16); var bottom = parseInt(id.substring(8, 16), 16); return NumberLong(0, top, bottom); } else { print("Bad length for id: " + id); quit(99); } } function mongoToNuxeo(id) { if (typeof id == 'string') { return id; } else if (id instanceof NumberLong) { var hex = id.bottom.toString(16); while (hex.length < 8) { hex = "0" + hex; } hex = id.top.toString(16) + hex while (hex.length < 16) { hex = "0" + hex; } return hex; } else { print("Bad id: " + id); quit(99); } } // check("12345678abcdabcd", mongoToNuxeo(nuxeoToMongo("12345678abcdabcd"))); // check("ff345678abcdabcd", mongoToNuxeo(nuxeoToMongo("ff345678abcdabcd"))); // check("003456780bcdabcd", mongoToNuxeo(nuxeoToMongo("003456780bcdabcd"))); function debugListAllPaths() { coll.find({}).forEach(doc => { var id = doc['ecm:id']; print(mongoToNuxeo(id) + " " + path(id)); }); } function same(a, b) { if (a instanceof NumberLong && b instanceof NumberLong) { return a.top == b.top && a.bottom == b.bottom; } else { return a === b; } } function computeUpdate(src_aids, src_id, dst_aids) { // print("src_aids=" + src_aids); // print("src_id=" + src_id); // print("dst_aids=" + dst_aids); var i = 0; while (true) { var a = src_aids[i]; var b = dst_aids[i]; // print("i=" + i + " a=" + a + " b=" + b); if (a === undefined && b === undefined) { // print("identical"); return null; } if (b === undefined) { var del = src_aids.slice(i, 999); // print("del " + del); return {'del': del}; } if (!same(a, b)) { if (same(b, src_id)) { print("FAIL cannot move under itself"); return; } var del = src_aids.slice(i, 999); var ins = dst_aids.slice(i, 999); // print("del " + del + " ins " + ins); return {'del': del, 'ins': ins, 'pos': i}; } // a == b, loop again i++; } } // check(null, computeUpdate(['a','b','c','d','e'], 'f', ['a','b','c','d','e'])); // check({'del': ['e'], 'ins': ['i','j','k'], 'pos': 4}, computeUpdate(['a','b','c','d','e'], 'f', ['a','b','c','d','i','j','k'])); // check({'del': ['d','e']}, computeUpdate(['a','b','c','d','e'], 'f', ['a','b','c'])); // check({'del': [], 'ins': ['ff'], 'pos': 5}, computeUpdate(['a','b','c','d','e'], 'f', ['a','b','c','d','e','ff'])); // check(undefined, computeUpdate(['a','b','c','d','e'], 'f', ['a','b','c','d','e','f','g'])); // ========== var doc_id = nuxeoToMongo(DOC_ID); var dest_folder_id = nuxeoToMongo(DEST_FOLDER_ID); print("Moving " + path(doc_id) + " under " + path(dest_folder_id)); var doc_aids = coll.findOne({'ecm:id': doc_id})['ecm:ancestorIds']; var dest_aids = coll.findOne({'ecm:id': dest_folder_id})['ecm:ancestorIds']; dest_aids.push(dest_folder_id); var diff = computeUpdate(doc_aids, doc_id, dest_aids); if (diff == null) { print("Nothing to change"); quit(0); } var query = {$or: [{'ecm:ancestorIds': doc_id}, {'ecm:id': doc_id}]}; print("query"); printjson(query); print("Counting docs to update..."); var n = coll.count(query); print("Docs to update: " + n); var del = diff['del']; var ins = diff['ins']; var pos = diff['pos']; var update = {}; if (del.length != 0) { update['$pullAll'] = {'ecm:ancestorIds': del}; } if (ins != undefined) { update['$push'] = {'ecm:ancestorIds': {$each: ins, $position: pos}}; } print("update"); printjson(update); print(Date() + " Starting ancestors replacement..."); coll.update(query, update, {multi: true}); print(Date() + " Done."); // debugListAllPaths();