如何对对象进行版本控制? [英] How to version control an object?

查看:61
本文介绍了如何对对象进行版本控制?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

要说明,请查看下面的对象,因为它正在更改:

To explain, look at the object below as it is being changed:

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

我希望能够获得该对象的所有这些版本,例如从版本2开始获取obj.我不想每次想更新单个键时都存储整个对象的副本.

I want to be able to get any of these versions of the object, for e.g. get obj as of version 2. I don't want to store copies of the entire object every single time I want to update a single key.

如何实现此功能?

我尝试使用的实际对象大约有500,000个键.这就是为什么我不想在每次更新时都存储它的整个副本.我希望将此理论解决方案编码的首选语言是pythonjavascript,但我会采取任何措施.

The actual object I'm trying to do this with has about 500,000 keys. That's why I don't want to store entire copies of it with every update. My preferred language that this theoretical solution should be coded in are python or javascript, but I'll take anything.

推荐答案

您可以为此使用ES6代理.这些将捕获对对象的任何读/写操作,并将每个更改记录在更改日志中,该日志可用于来回滚动更改.

You could use ES6 proxies for that. These would trap any read/write operation on your object and log each change in a change log that can be used for rolling changes back and forward.

下面是一个基本实现,如果要对对象应用除基本更新操作以外的其他功能,可能还需要一些其他功能.它允许获取当前版本号,并将对象后退(或前进)到特定版本.每当您更改对象时,都会首先将其移动到最新版本.

Below is a basic implementation, which might need some more features if you intend to apply other than basic update operations on your object. It allows to get the current version number and move the object back (or forward) to a specific version. Whenever you make a change to the object, it is first moved to its latest version.

此代码段显示了一些操作,例如更改字符串属性,添加到数组并对其进行移位,同时前后​​移动到其他版本.

This snippet shows some operations, like changing a string property, adding to an array, and shifting it, while moving back and forward to other versions.

现在,它还具有将更改日志作为对象并将其应用于初始对象的功能.这样,您可以保存初始对象和更改日志的JSON,然后重播更改以获取最终对象.

It now also has capability to get the change log as an object, and apply that change log to the initial object. This way you can save the JSON of both the initial object and the change log, and replay the changes to get the final object.

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; }

这篇关于如何对对象进行版本控制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆