2016-11-08 4 views
4

Чтобы объяснить, посмотреть на объект ниже, как она меняется:Как управлять версией объекта?

obj = {'a': 1, 'b': 2} // Version 1 
obj['a'] = 2 // Version 2 
obj['c'] = 3 // Version 3 

Я хочу, чтобы иметь возможность получить любой из этих версий объекта, например, получить obj с версии 2. Я не хочу хранить копии всего объекта каждый раз, когда хочу обновить один ключ.

Как я могу достичь этой функциональности?

Фактический объект, который я пытаюсь сделать, имеет около 500 000 ключей. Вот почему я не хочу хранить все копии при каждом обновлении. Мой предпочтительный язык, в котором должно быть закодировано это теоретическое решение, - python или javascript, но я возьму что угодно.

+0

Где вы хотите сохранить, что и как вы хотите, чтобы получить его? Когда вы говорите «контроль версий», моя первая мысль - git. Неважно, будет ли это json или какой-либо другой формат. И это спасет только различия. Опять же, когда вы говорите 500 000 имен: пары значений, я бы сказал, что база данных звучит как хорошая идея. – zvone

+0

Обратите внимание: объекты, о которых вы говорите, не являются JSON. То, что вы определяете и изменяете в этом примере кода, является объектом JavaScript; не JSON. NB: StackOverfow не предназначен для запроса предложений о библиотеках (см. Http://stackoverflow.com/help/on-topic). – trincot

+0

@zvone, у меня в настоящее время есть система баз данных, которая хранит журналы и данные контроля версий прилично, но я нахожу это очень медленным. Я искал алгоритм в коде, который мог бы управлять объектами json, а затем просто хранить json-объект в целом в базе данных. – josneville

ответ

6

Для этого вы можете использовать прокси ES6. Они улавливают любую операцию чтения/записи на вашем объекте и записывают каждое изменение в журнал изменений, который можно использовать для скользящих изменений назад и вперед.

Ниже приведена базовая реализация, которая может потребовать дополнительных функций, если вы намереваетесь применять другие операции, кроме базовых операций обновления, на вашем объекте. Он позволяет получить текущий номер версии и переместить объект обратно (или вперед) в определенную версию. Всякий раз, когда вы вносите изменения в объект, его сначала переносят в его последнюю версию.

Этот фрагмент показывает некоторые операции, такие как изменение свойства строки, добавление в массив и перенос его при перемещении назад и вперед в другие версии.

Редактировать: Теперь он также имеет возможность получить журнал изменений как объект и применить этот журнал изменений к исходному объекту. Таким образом вы можете сохранить JSON как исходного объекта, так и журнала изменений и воспроизвести изменения для получения конечного объекта.

function VersionControlled(obj, changeLog = []) { 
 
    var targets = [], version = 0, savedLength, 
 
     hash = new Map([[obj, []]]), 
 
     handler = { 
 
      get: function(target, property) { 
 
       var x = target[property]; 
 
       if (Object(x) !== x) return x; 
 
       hash.set(x, hash.get(target).concat(property)); 
 
       return new Proxy(x, handler); 
 
      }, 
 
      set: update, 
 
      deleteProperty: update 
 
     }; 
 

 
    function gotoVersion(newVersion) { 
 
     newVersion = Math.max(0, Math.min(changeLog.length, newVersion)); 
 
     var chg, target, path, property, 
 
      val = newVersion > version ? 'newValue' : 'oldValue'; 
 
     while (version !== newVersion) { 
 
      if (version > newVersion) version--; 
 
      chg = changeLog[version]; 
 
      path = chg.path.slice(); 
 
      property = path.pop(); 
 
      target = targets[version] || 
 
        (targets[version] = path.reduce ((o, p) => o[p], obj)); 
 
      if (chg.hasOwnProperty(val)) { 
 
       target[property] = chg[val]; 
 
      } else { 
 
       delete target[property]; 
 
      } 
 
      if (version < newVersion) version++; 
 
     } 
 
     return true; 
 
    } 
 
    
 
    function gotoLastVersion() { 
 
     return gotoVersion(changeLog.length); 
 
    } 
 
    
 
    function update(target, property, value) { 
 
     gotoLastVersion(); // only last version can be modified 
 
     var change = {path: hash.get(target).concat([property])}; 
 
     if (arguments.length > 2) change.newValue = value; 
 
     // Some care concerning the length property of arrays: 
 
     if (Array.isArray(target) && +property >= target.length) { 
 
      savedLength = target.length; 
 
     } 
 
     if (property in target) { 
 
      if (property === 'length' && savedLength !== undefined) { 
 
       change.oldValue = savedLength; 
 
       savedLength = undefined; 
 
      } else { 
 
       change.oldValue = target[property]; 
 
      } 
 
     } 
 
     changeLog.push(change); 
 
     targets.push(target); 
 
     return gotoLastVersion(); 
 
    } 
 
    
 
    this.data = new Proxy(obj, handler); 
 
    this.getVersion = _ => version; 
 
    this.gotoVersion = gotoVersion; 
 
    this.gotoLastVersion = gotoLastVersion; 
 
    this.getChangeLog = _ => changeLog; 
 
    // apply change log 
 
    gotoLastVersion(); 
 
} 
 

 
// sample data 
 
var obj = { list: [1, { p: 'hello' }, 3] }; 
 

 
// Get versioning object for it 
 
var vc = new VersionControlled(obj); 
 
obj = vc.data; // we don't need the original anymore, this one looks the same 
 

 
// Demo of actions: 
 
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Change text:`); 
 
obj.list[1].p = 'bye'; 
 
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Bookmark & add property:`); 
 
var bookmark = vc.getVersion(); 
 
obj.list[1].q = ['added']; 
 
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Push on list, then shift:`); 
 
obj.list.push(4); // changes both length and index '4' property => 2 version increments 
 
obj.list.shift(); // several changes and a deletion 
 
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Go to bookmark:`); 
 
vc.gotoVersion(bookmark); 
 

 
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Go to last version:`); 
 
vc.gotoLastVersion(); 
 
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Get change log:`); 
 
var changeLog = vc.getChangeLog(); 
 
for (var chg of changeLog) { 
 
    console.log(JSON.stringify(chg)); 
 
} 
 

 
console.log('Restart from scratch, and apply the change log:'); 
 
obj = { list: [1, { p: 'hello' }, 3] }; 
 
vc = new VersionControlled(obj, changeLog); 
 
obj = vc.data; 
 
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}`);
.as-console-wrapper { max-height: 100% !important; top: 0; }

+0

поэтому его базовый репозиторий? –

+0

Это похоже на игру: вы можете играть в ходы, а также забирать их обратно, а затем двигаться вперед до последнего записанного движения. – trincot

+0

@ trincot, я не знаком с синтаксисом ES6. Я все еще пытаюсь понять кодовую базу и вернусь к вам. Спасибо, что ответили так подробно. – josneville

0

Вам не нужно сохранять весь объект.

Просто отличия. Для каждой версии.

Эта функция выполняет глубокое сравнение с использованием lodash и возвращает разницу между старым объектом и новым объектом.

var allkeys = _.union(_.keys(obj1), _.keys(obj2)); 
var difference = _.reduce(allkeys, function (result, key) { 
    if (!_.isEqual(obj1[key] !== obj2[key])) { 
    result[key] = {obj1: obj1[key], obj2: obj2[key]} 
    } 
    return result; 
}, {}); 

Вам нужно будет сохранить самый первый объект, но вы можете сохранить версии таким образом, я думаю.

+0

Спасибо за это решение. Мой план состоит в том, чтобы включить эту функциональность в решение выше, чтобы разрешить управление версиями для пакетных обновлений ключами. – josneville

Смежные вопросы