mirror of
https://github.com/orbitdb/orbitdb.git
synced 2025-03-30 15:08:28 +00:00
Cleanup lists
This commit is contained in:
parent
0aa0e0afd3
commit
523e67b05e
88
src/list/List.js
Normal file
88
src/list/List.js
Normal file
@ -0,0 +1,88 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const Node = require('./Node');
|
||||
|
||||
class List {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
this.seq = 0;
|
||||
this.ver = 0;
|
||||
this._items = [];
|
||||
this._currentBatch = [];
|
||||
}
|
||||
|
||||
get items() {
|
||||
return this._items.concat(this._currentBatch);
|
||||
}
|
||||
|
||||
add(data) {
|
||||
const heads = this._findHeads(this.items);
|
||||
const node = new Node(this.id, this.seq, this.ver, data, heads);
|
||||
this._currentBatch.push(node);
|
||||
this.ver ++;
|
||||
}
|
||||
|
||||
join(other) {
|
||||
if(other.seq && other.seq > this.seq) {
|
||||
this.seq = other.seq + 1;
|
||||
this.ver = 0;
|
||||
} else {
|
||||
this.seq = this.seq + 1;
|
||||
this.ver = 0;
|
||||
}
|
||||
const current = _.differenceWith(this._currentBatch, this._items, this._equals);
|
||||
const others = _.differenceWith(other.items, this._items, this._equals);
|
||||
const final = _.unionWith(current, others, this._equals);
|
||||
this._items = this._items.concat(final);
|
||||
this._currentBatch = [];
|
||||
}
|
||||
|
||||
_findHeads(list) {
|
||||
const grouped = _.groupBy(list, 'id');
|
||||
const heads = Object.keys(grouped).map((g) => _.last(grouped[g]));
|
||||
const cleaned = heads.filter((e) => !this._isReferencedInChain(list, e));
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
_isReferencedInChain(all, item) {
|
||||
let isReferenced = _.findLast(all, (e) => this._references(e, item)) !== undefined;
|
||||
return isReferenced;
|
||||
}
|
||||
|
||||
_equals(a, b) {
|
||||
return a.id == b.id && a.seq == b.seq && a.ver == b.ver;
|
||||
}
|
||||
|
||||
_references(a, b) {
|
||||
for(let i = 0; i < a.next.length; i ++) {
|
||||
if(b.compactId === a.next[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static fromJson(json) {
|
||||
let list = new List(json.id);
|
||||
list.seq = json.seq;
|
||||
list.ver = json.ver;
|
||||
list._items = _.uniqWith(json.items.map((f) => new Node(f.id, f.seq, f.ver, f.data, f.next)), _.isEqual);
|
||||
return list;
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
id: this.id,
|
||||
seq: this.seq,
|
||||
ver: this.ver,
|
||||
items: this._currentBatch.map((f) => f.compact())
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
const items = this.items.map((f) => JSON.stringify(f.compact())).join("\n");
|
||||
return `id: ${this.id}, seq: ${this.seq}, ver: ${this.ver}, items:\n${items}`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = List;
|
21
src/list/Node.js
Normal file
21
src/list/Node.js
Normal file
@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
class Node {
|
||||
constructor(id, seq, ver, data, next) {
|
||||
this.id = id;
|
||||
this.seq = seq;
|
||||
this.ver = ver;
|
||||
this.data = data || null;
|
||||
this.next = next ? next.map((f) => f.compactId ? f.compactId : f) : [];
|
||||
}
|
||||
|
||||
get compactId() {
|
||||
return "" + this.id + "." + this.seq + "." + this.ver;
|
||||
}
|
||||
|
||||
compact() {
|
||||
return { id: this.id, seq: this.seq, ver: this.ver, data: this.data, next: this.next }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Node;
|
61
test/list-node-tests.js
Normal file
61
test/list-node-tests.js
Normal file
@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const List = require('../src/list/List');
|
||||
const Node = require('../src/list/Node');
|
||||
|
||||
describe('Node', () => {
|
||||
describe('Constructor', () => {
|
||||
it('initializes member variables', (done) => {
|
||||
const node = new Node('A', 0, 0, 'hello', []);
|
||||
assert.equal(node.id, 'A');
|
||||
assert.equal(node.seq, 0);
|
||||
assert.equal(node.ver, 0);
|
||||
assert.equal(node.data, 'hello');
|
||||
assert.equal(node.next instanceof Array, true);
|
||||
done();
|
||||
});
|
||||
|
||||
it('initializes member variables without specified data and next', (done) => {
|
||||
const node = new Node('A', 0, 0);
|
||||
assert.equal(node.id, 'A');
|
||||
assert.equal(node.seq, 0);
|
||||
assert.equal(node.ver, 0);
|
||||
assert.equal(node.data, null);
|
||||
assert.equal(node.next instanceof Array, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('compactId', () => {
|
||||
it('presents the node as a string with id, sequence and version', (done) => {
|
||||
const node1 = new Node('A', 0, 0);
|
||||
const node2 = new Node('B', 123, 456);
|
||||
assert.equal(node1.compactId, 'A.0.0');
|
||||
assert.equal(node2.compactId, 'B.123.456');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('compact', () => {
|
||||
it('presents the node as a compacted object', (done) => {
|
||||
const node1 = new Node('A', 0, 0, 'hello');
|
||||
const node2 = new Node('B', 0, 0, 'hello', [node1]);
|
||||
const compacted1 = node1.compact();
|
||||
const compacted2 = node2.compact();
|
||||
|
||||
assert.notEqual(compacted1, null);
|
||||
assert.equal(compacted1.id, 'A');
|
||||
assert.equal(compacted1.seq, 0);
|
||||
assert.equal(compacted1.ver, 0);
|
||||
assert.equal(compacted1.data, 'hello');
|
||||
assert.equal(compacted1.next instanceof Array, true);
|
||||
assert.equal(compacted1.next.length, 0);
|
||||
|
||||
assert.equal(compacted2.id, 'B');
|
||||
assert.equal(compacted2.next.length, 1);
|
||||
assert.equal(compacted2.next[0], 'A.0.0');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
@ -3,8 +3,7 @@
|
||||
// var assert = require('assert');
|
||||
// var async = require('asyncawait/async');
|
||||
// var await = require('asyncawait/await');
|
||||
// var List = require('../test1').List;
|
||||
// var Node = require('../test1').Node;
|
||||
// var List = require('../src/list/List');
|
||||
// var Timer = require('../examples/Timer');
|
||||
|
||||
// describe('List - Performance Measurement', function() {
|
||||
@ -23,8 +22,6 @@
|
||||
|
||||
// ms = timer.stop(true);
|
||||
// console.log(` > ${t} took ${ms} ms`)
|
||||
|
||||
// // assert.equal(ms < t, true);
|
||||
// }
|
||||
|
||||
// assert.equal(true, true);
|
||||
|
@ -1,66 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
var assert = require('assert');
|
||||
var async = require('asyncawait/async');
|
||||
var await = require('asyncawait/await');
|
||||
var List = require('../test1').List;
|
||||
var Node = require('../test1').Node;
|
||||
|
||||
describe('Node', () => {
|
||||
describe('Constructor', () => {
|
||||
it('initializes member variables', (done) => {
|
||||
const node = new Node('A', 0, 0, 'hello', []);
|
||||
assert.equal(node.id, 'A');
|
||||
assert.equal(node.seq, 0);
|
||||
assert.equal(node.ver, 0);
|
||||
assert.equal(node.data, 'hello');
|
||||
assert.equal(node.next instanceof Array, true);
|
||||
done();
|
||||
});
|
||||
|
||||
it('initializes member variables without data and next', (done) => {
|
||||
const node = new Node('A', 0, 0);
|
||||
assert.equal(node.id, 'A');
|
||||
assert.equal(node.seq, 0);
|
||||
assert.equal(node.ver, 0);
|
||||
assert.equal(node.data, null);
|
||||
assert.equal(node.next instanceof Array, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('compactId', () => {
|
||||
it('presents the node as a string with id, sequence and version', (done) => {
|
||||
const node1 = new Node('A', 0, 0);
|
||||
const node2 = new Node('B', 123, 456);
|
||||
assert.equal(node1.compactId, 'A.0.0');
|
||||
assert.equal(node2.compactId, 'B.123.456');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('compact', () => {
|
||||
it('presents the node as a compacted object', (done) => {
|
||||
const node1 = new Node('A', 0, 0, 'hello');
|
||||
const node2 = new Node('B', 0, 0, 'hello', [node1]);
|
||||
const compacted1 = node1.compact();
|
||||
const compacted2 = node2.compact();
|
||||
|
||||
assert.notEqual(compacted1, null);
|
||||
assert.equal(compacted1.id, 'A');
|
||||
assert.equal(compacted1.seq, 0);
|
||||
assert.equal(compacted1.ver, 0);
|
||||
assert.equal(compacted1.data, 'hello');
|
||||
assert.equal(compacted1.next instanceof Array, true);
|
||||
assert.equal(compacted1.next.length, 0);
|
||||
|
||||
assert.equal(compacted2.id, 'B');
|
||||
assert.equal(compacted2.next.length, 1);
|
||||
assert.equal(compacted2.next[0], 'A.0.0');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
var List = require('../src/list/List');
|
||||
|
||||
describe('List', () => {
|
||||
describe('Constructor', () => {
|
||||
@ -77,6 +19,60 @@ describe('List', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromJson', () => {
|
||||
it('creates a list from parsed json', (done) => {
|
||||
const list = new List('A');
|
||||
list.add("hello1")
|
||||
list.add("hello2")
|
||||
list.add("hello3")
|
||||
const str = JSON.stringify(list.toJson())
|
||||
const res = List.fromJson(JSON.parse(str));
|
||||
assert.equal(res.id, 'A');
|
||||
assert.equal(res.seq, 0);
|
||||
assert.equal(res.ver, 3);
|
||||
assert.equal(res.items.length, 3);
|
||||
assert.equal(res.items[0].compactId, 'A.0.0');
|
||||
assert.equal(res.items[1].compactId, 'A.0.1');
|
||||
assert.equal(res.items[2].compactId, 'A.0.2');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('toJson', () => {
|
||||
it('presents the list as json', (done) => {
|
||||
const list = new List('A');
|
||||
list.add("hello1")
|
||||
list.add("hello2")
|
||||
list.add("hello3")
|
||||
const json = list.toJson();
|
||||
const expected = {
|
||||
id: 'A',
|
||||
seq: 0,
|
||||
ver: 3,
|
||||
items: [
|
||||
{ id: 'A', seq: 0, ver: 0, data: 'hello1', next: [] },
|
||||
{ id: 'A', seq: 0, ver: 1, data: 'hello2', next: ['A.0.0'] },
|
||||
{ id: 'A', seq: 0, ver: 2, data: 'hello3', next: ['A.0.1'] }
|
||||
]
|
||||
};
|
||||
assert.equal(_.isEqual(json, expected), true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('toString', () => {
|
||||
it('presents the list as a string', (done) => {
|
||||
const list = new List('A');
|
||||
list.add("hello1")
|
||||
list.add("hello2")
|
||||
list.add("hello3")
|
||||
const str = list.toString();
|
||||
const expected = `id: A, seq: 0, ver: 3, items:\n{"id":"A","seq":0,"ver":0,"data":"hello1","next":[]}\n{"id":"A","seq":0,"ver":1,"data":"hello2","next":["A.0.0"]}\n{"id":"A","seq":0,"ver":2,"data":"hello3","next":["A.0.1"]}`;
|
||||
assert.equal(str, expected);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('items', () => {
|
||||
it('returns items', (done) => {
|
||||
const list = new List('A');
|
||||
@ -99,10 +95,8 @@ describe('List', () => {
|
||||
list.add("hello1")
|
||||
list.add("hello2")
|
||||
list.add("hello3")
|
||||
// list.add("hello4")
|
||||
// list.add("hello5")
|
||||
|
||||
let compacted = list.toJson();
|
||||
// console.log(compacted)
|
||||
assert.equal(compacted.id, 'A');
|
||||
assert.equal(compacted.seq, 0);
|
||||
assert.equal(compacted.ver, 3);
|
||||
@ -210,14 +204,13 @@ describe('List', () => {
|
||||
list1.add("helloA2")
|
||||
list1.add("helloA3")
|
||||
|
||||
// console.log(list1.toString())
|
||||
assert.equal(list1._currentBatch.length, 3);
|
||||
assert.equal(list1._currentBatch[2].next.length, 1);
|
||||
assert.equal(list1._currentBatch[2].next[0], 'A.0.1');
|
||||
done();
|
||||
});
|
||||
|
||||
it('finds the next heads after joining a list with another', (done) => {
|
||||
it('finds the next heads (two) after a join', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
list1.add("helloA1")
|
||||
@ -233,7 +226,7 @@ describe('List', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('finds the next head after a two-way join', (done) => {
|
||||
it('finds the next head (one) after a join', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
list1.add("helloA1")
|
||||
@ -250,7 +243,7 @@ describe('List', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('find sthe next heads after join two-way join', (done) => {
|
||||
it('finds the next heads after two joins', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
list1.add("helloA1")
|
||||
@ -267,13 +260,49 @@ describe('List', () => {
|
||||
list1.add("helloA5")
|
||||
|
||||
const lastItem = list1.items[list1.items.length - 1];
|
||||
// console.log(list1.toString())
|
||||
|
||||
assert.equal(list1.items.length, 7);
|
||||
assert.equal(lastItem.next.length, 1);
|
||||
assert.equal(lastItem.next[0], 'A.2.0');
|
||||
done();
|
||||
});
|
||||
|
||||
it('finds the next heads after multiple joins', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
const list3 = new List('C');
|
||||
const list4 = new List('D');
|
||||
list1.add("helloA1")
|
||||
list1.add("helloA2")
|
||||
list2.add("helloB1")
|
||||
list2.add("helloB2")
|
||||
list1.join(list2);
|
||||
|
||||
list3.add("helloC1")
|
||||
list4.add("helloD1")
|
||||
list1.join(list3);
|
||||
|
||||
list1.add("helloA3")
|
||||
list2.join(list1);
|
||||
list1.join(list2);
|
||||
list2.join(list4);
|
||||
|
||||
list4.add("helloD2")
|
||||
list4.add("helloD3")
|
||||
list1.add("helloA4")
|
||||
list1.join(list4);
|
||||
|
||||
list1.add("helloA5")
|
||||
|
||||
const lastItem = list1.items[list1.items.length - 1];
|
||||
|
||||
assert.equal(list1.items.length, 11);
|
||||
assert.equal(lastItem.next.length, 2);
|
||||
assert.equal(lastItem.next[0], 'A.4.0');
|
||||
assert.equal(lastItem.next[1], 'D.0.2');
|
||||
done();
|
||||
});
|
||||
|
||||
it('joins list of one item with list of two items', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
@ -282,18 +311,17 @@ describe('List', () => {
|
||||
list2.add("helloB2")
|
||||
list1.join(list2);
|
||||
|
||||
// console.log(list1)
|
||||
// console.log(list2)
|
||||
const lastItem = list1.items[list1.items.length - 1];
|
||||
|
||||
assert.equal(list1.id, 'A');
|
||||
assert.equal(list1.seq, 1);
|
||||
assert.equal(list1.ver, 0);
|
||||
assert.equal(list1._currentBatch.length, 0);
|
||||
assert.equal(list1._items.length, 3);
|
||||
assert.equal(list1._items[list1._items.length - 1].id, 'B');
|
||||
assert.equal(list1._items[list1._items.length - 1].seq, 0);
|
||||
assert.equal(list1._items[list1._items.length - 1].ver, 1);
|
||||
assert.equal(list1._items[list1._items.length - 1].data, 'helloB2');
|
||||
assert.equal(lastItem.id, 'B');
|
||||
assert.equal(lastItem.seq, 0);
|
||||
assert.equal(lastItem.ver, 1);
|
||||
assert.equal(lastItem.data, 'helloB2');
|
||||
done();
|
||||
});
|
||||
|
||||
@ -307,25 +335,29 @@ describe('List', () => {
|
||||
list1.join(list2);
|
||||
list2.join(list1);
|
||||
|
||||
const lastItem1 = list1.items[list1.items.length - 1];
|
||||
|
||||
assert.equal(list1.id, 'A');
|
||||
assert.equal(list1.seq, 1);
|
||||
assert.equal(list1.ver, 0);
|
||||
assert.equal(list1._currentBatch.length, 0);
|
||||
assert.equal(list1._items.length, 4);
|
||||
assert.equal(list1._items[list1._items.length - 1].id, 'B');
|
||||
assert.equal(list1._items[list1._items.length - 1].seq, 0);
|
||||
assert.equal(list1._items[list1._items.length - 1].ver, 1);
|
||||
assert.equal(list1._items[list1._items.length - 1].data, 'helloB2');
|
||||
assert.equal(lastItem1.id, 'B');
|
||||
assert.equal(lastItem1.seq, 0);
|
||||
assert.equal(lastItem1.ver, 1);
|
||||
assert.equal(lastItem1.data, 'helloB2');
|
||||
|
||||
const lastItem2 = list2.items[list2.items.length - 1];
|
||||
|
||||
assert.equal(list2.id, 'B');
|
||||
assert.equal(list2.seq, 2);
|
||||
assert.equal(list2.ver, 0);
|
||||
assert.equal(list2._currentBatch.length, 0);
|
||||
assert.equal(list2._items.length, 4);
|
||||
assert.equal(list2._items[list2._items.length - 1].id, 'A');
|
||||
assert.equal(list2._items[list2._items.length - 1].seq, 0);
|
||||
assert.equal(list2._items[list2._items.length - 1].ver, 1);
|
||||
assert.equal(list2._items[list2._items.length - 1].data, 'helloA2');
|
||||
assert.equal(lastItem2.id, 'A');
|
||||
assert.equal(lastItem2.seq, 0);
|
||||
assert.equal(lastItem2.ver, 1);
|
||||
assert.equal(lastItem2.data, 'helloA2');
|
||||
done();
|
||||
});
|
||||
|
||||
@ -341,23 +373,26 @@ describe('List', () => {
|
||||
list2.add("helloB2")
|
||||
list2.join(list1);
|
||||
|
||||
const secondItem = list2.items[1];
|
||||
const lastItem = list2.items[list2.items.length - 1];
|
||||
|
||||
assert.equal(list2.id, 'B');
|
||||
assert.equal(list2.seq, 2);
|
||||
assert.equal(list2.ver, 0);
|
||||
assert.equal(list2._currentBatch.length, 0);
|
||||
assert.equal(list2._items.length, 4);
|
||||
assert.equal(list2._items[1].id, 'A');
|
||||
assert.equal(list2._items[1].seq, 0);
|
||||
assert.equal(list2._items[1].ver, 0);
|
||||
assert.equal(list2._items[1].data, 'helloA1');
|
||||
assert.equal(list2._items[list2._items.length - 1].id, 'A');
|
||||
assert.equal(list2._items[list2._items.length - 1].seq, 0);
|
||||
assert.equal(list2._items[list2._items.length - 1].ver, 1);
|
||||
assert.equal(list2._items[list2._items.length - 1].data, 'helloA2');
|
||||
assert.equal(secondItem.id, 'A');
|
||||
assert.equal(secondItem.seq, 0);
|
||||
assert.equal(secondItem.ver, 0);
|
||||
assert.equal(secondItem.data, 'helloA1');
|
||||
assert.equal(lastItem.id, 'A');
|
||||
assert.equal(lastItem.seq, 0);
|
||||
assert.equal(lastItem.ver, 1);
|
||||
assert.equal(lastItem.data, 'helloA2');
|
||||
done();
|
||||
});
|
||||
|
||||
it('joins 4 lists', (done) => {
|
||||
it('joins 4 lists to one', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
const list3 = new List('C');
|
||||
@ -365,39 +400,36 @@ describe('List', () => {
|
||||
|
||||
list1.add("helloA1")
|
||||
list2.add("helloB1")
|
||||
// list2.join(list1);
|
||||
|
||||
list1.add("helloA2")
|
||||
list2.add("helloB2")
|
||||
// list2.join(list1);
|
||||
|
||||
list3.add("helloC1")
|
||||
list4.add("helloD1")
|
||||
// list2.join(list1);
|
||||
|
||||
list3.add("helloC2")
|
||||
list4.add("helloD2")
|
||||
list1.join(list2);
|
||||
list1.join(list3);
|
||||
list1.join(list4);
|
||||
|
||||
const secondItem = list1.items[1];
|
||||
const lastItem = list1.items[list1.items.length - 1];
|
||||
|
||||
assert.equal(list1.id, 'A');
|
||||
// assert.equal(list1.seq, 1);
|
||||
// assert.equal(list1.ver, 1);
|
||||
assert.equal(list1.seq, 3);
|
||||
assert.equal(list1.ver, 0);
|
||||
assert.equal(list1._currentBatch.length, 0);
|
||||
assert.equal(list1._items.length, 8);
|
||||
assert.equal(list1._items[1].id, 'A');
|
||||
assert.equal(list1._items[1].seq, 0);
|
||||
assert.equal(list1._items[1].ver, 1);
|
||||
assert.equal(list1._items[1].data, 'helloA2');
|
||||
assert.equal(list1._items[list1._items.length - 1].id, 'D');
|
||||
assert.equal(list1._items[list1._items.length - 1].seq, 0);
|
||||
assert.equal(list1._items[list1._items.length - 1].ver, 1);
|
||||
assert.equal(list1._items[list1._items.length - 1].data, 'helloD2');
|
||||
assert.equal(secondItem.id, 'A');
|
||||
assert.equal(secondItem.seq, 0);
|
||||
assert.equal(secondItem.ver, 1);
|
||||
assert.equal(secondItem.data, 'helloA2');
|
||||
assert.equal(lastItem.id, 'D');
|
||||
assert.equal(lastItem.seq, 0);
|
||||
assert.equal(lastItem.ver, 1);
|
||||
assert.equal(lastItem.data, 'helloD2');
|
||||
done();
|
||||
});
|
||||
|
||||
it('joins 4 lists sequentally', (done) => {
|
||||
it('joins lists from 4 lists', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
const list3 = new List('C');
|
||||
@ -428,31 +460,42 @@ describe('List', () => {
|
||||
list4.add("helloD3")
|
||||
list4.add("helloD4")
|
||||
|
||||
// console.log(list4.toString());
|
||||
const secondItem = list4.items[1];
|
||||
const lastItem1 = list4._items[list4._items.length - 1];
|
||||
const lastItem2 = list4.items[list4.items.length - 1];
|
||||
|
||||
assert.equal(list4.id, 'D');
|
||||
assert.equal(list4.seq, 7);
|
||||
assert.equal(list4.ver, 2);
|
||||
assert.equal(list4._currentBatch.length, 2);
|
||||
assert.equal(list4._items.length, 8);
|
||||
assert.equal(list4._items[1].id, 'D');
|
||||
assert.equal(list4._items[1].seq, 0);
|
||||
assert.equal(list4._items[1].ver, 1);
|
||||
assert.equal(list4._items[1].data, 'helloD2');
|
||||
assert.equal(list4._items[list1._items.length - 1].id, 'A');
|
||||
assert.equal(list4._items[list1._items.length - 1].seq, 1);
|
||||
assert.equal(list4._items[list1._items.length - 1].ver, 0);
|
||||
assert.equal(list4._items[list1._items.length - 1].data, 'helloA2');
|
||||
assert.equal(list4.items[list4.items.length - 1].id, 'D');
|
||||
assert.equal(list4.items[list4.items.length - 1].seq, 7);
|
||||
assert.equal(list4.items[list4.items.length - 1].ver, 1);
|
||||
assert.equal(list4.items[list4.items.length - 1].data, 'helloD4');
|
||||
assert.equal(secondItem.id, 'D');
|
||||
assert.equal(secondItem.seq, 0);
|
||||
assert.equal(secondItem.ver, 1);
|
||||
assert.equal(secondItem.data, 'helloD2');
|
||||
assert.equal(lastItem1.id, 'C');
|
||||
assert.equal(lastItem1.seq, 3);
|
||||
assert.equal(lastItem1.ver, 1);
|
||||
assert.equal(lastItem1.data, 'helloC2');
|
||||
assert.equal(lastItem2.id, 'D');
|
||||
assert.equal(lastItem2.seq, 7);
|
||||
assert.equal(lastItem2.ver, 1);
|
||||
assert.equal(lastItem2.data, 'helloD4');
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('solves a graph', (done) => {
|
||||
done();
|
||||
describe('_findHeads', () => {
|
||||
it('TODO', (done) => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_isReferencedInChain', () => {
|
||||
it('TODO', (done) => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
284
test1.js
284
test1.js
@ -2,268 +2,8 @@
|
||||
|
||||
const _ = require('lodash');
|
||||
const Timer = require('./examples/Timer');
|
||||
|
||||
Array.prototype.allEqual = function(val) {
|
||||
for(var i = 1; i < this.length; i++) {
|
||||
if(this[i] !== val || this[i] !== this[0])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Array.prototype.last){
|
||||
Array.prototype.last = function(){
|
||||
return this[this.length - 1];
|
||||
};
|
||||
}
|
||||
|
||||
class Node {
|
||||
constructor(id, seq, ver, data, next) {
|
||||
this.id = id;
|
||||
this.seq = seq;
|
||||
this.ver = ver;
|
||||
this.data = data || null;
|
||||
this.next = next ? next.map((f) => f.compactId ? f.compactId : f) : [];
|
||||
}
|
||||
|
||||
get compactId() {
|
||||
return "" + this.id + "." + this.seq + "." + this.ver;
|
||||
}
|
||||
|
||||
compact() {
|
||||
return { id: this.id, seq: this.seq, ver: this.ver, data: this.data, next: this.next }
|
||||
// return { id: this.id, seq: this.seq, ver: this.ver, data: this.data, next: this.next.map((f) => f.compactId) }
|
||||
}
|
||||
|
||||
// static parseCompact(t) {
|
||||
// let v = t.split('.');
|
||||
// return { id: v[0], seq: parseInt(v[1]), ver: parseInt(v[2]) };
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
class List {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
this.seq = 0;
|
||||
this.ver = 0;
|
||||
this._items = [];
|
||||
this._currentBatch = [];
|
||||
this._referenced = [];
|
||||
}
|
||||
|
||||
static fromJson(json) {
|
||||
let list = new List(json.id);
|
||||
list.seq = json.seq;
|
||||
list.ver = json.ver;
|
||||
// list._items = json.items.map((f) => new Node(f.id, f.seq, f.ver, f.data, f.next));
|
||||
list._items = _.uniqWith(json.items.map((f) => {
|
||||
// console.log(JSON.stringify(f.next, null, 2));
|
||||
return new Node(f.id, f.seq, f.ver, f.data, f.next);
|
||||
}), _.isEqual);
|
||||
return list;
|
||||
}
|
||||
|
||||
get items() {
|
||||
return this._items.concat(this._currentBatch);
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
id: this.id,
|
||||
seq: this.seq,
|
||||
ver: this.ver,
|
||||
items: this._currentBatch.map((f) => f.compact())
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
const items = this.items.map((f) => JSON.stringify(f.compact())).join("\n");
|
||||
return `
|
||||
id: ${this.id},
|
||||
seq: ${this.seq},
|
||||
ver: ${this.ver},
|
||||
items: \n${items}
|
||||
`
|
||||
}
|
||||
|
||||
add(data) {
|
||||
// var ii = this.items.map((f) => f.compact());
|
||||
// console.log("--->", this.seq, this.ver)
|
||||
const heads = this._findHeads(this.items);//.map((f) => f.compact());
|
||||
// const heads = this._findHeads(this.items)
|
||||
// console.log("jjjj", this._referenced, "\n\n")
|
||||
// console.log("jjjj", this.items, "\n\n")
|
||||
const node = new Node(this.id, this.seq, this.ver, data, heads);
|
||||
// console.log("aaaa", node)
|
||||
this._currentBatch.push(node);
|
||||
// this._referenced.push(node);
|
||||
this.ver ++;
|
||||
}
|
||||
|
||||
join(other) {
|
||||
let ms;
|
||||
|
||||
if(other.seq && other.seq > this.seq) {
|
||||
this.seq = other.seq + 1;
|
||||
this.ver = 0;
|
||||
} else {
|
||||
this.seq = this.seq + 1;
|
||||
this.ver = 0;
|
||||
}
|
||||
// if(other.items.last() && other.items.last().seq > this.seq) {
|
||||
// this.seq = other.seq + 1;
|
||||
// this.ver = 0;
|
||||
// }
|
||||
|
||||
// return items that are only in this._currentBatch
|
||||
const current = _.differenceWith(this._currentBatch, this._items, this._equals);
|
||||
// return items that are only in other.items
|
||||
|
||||
let timer = new Timer(true);
|
||||
const others = _.differenceWith(other.items, this._items, this._equals);
|
||||
ms = timer.stop(true);
|
||||
// return unique items from e and d
|
||||
// const final = _.unionWith(others, this._currentBatch, this._equals);
|
||||
const final = _.unionWith(current, others, this._equals);
|
||||
// append new items to all items
|
||||
this._items = this._items.concat(final);
|
||||
|
||||
this._currentBatch = [];
|
||||
if(ms > 20) {
|
||||
console.log("OVER 20MS!!", other.items.length)
|
||||
console.log(`join took ${timer.stop(true)} ms`);
|
||||
}
|
||||
}
|
||||
|
||||
_findHeads(list) {
|
||||
let timer = new Timer(true);
|
||||
let ms;
|
||||
const grouped = _.groupBy(list, 'id');
|
||||
// const heads = Object.keys(grouped).sort((a, b) => a === this.id ? -1 : (a < b ? 1 : 0)).map((g) => grouped[g].last());
|
||||
const heads = Object.keys(grouped).map((g) => grouped[g].last());
|
||||
// const heads = _.differenceWith(list, this._referenced);
|
||||
// console.log("HEADS", JSON.stringify(heads));
|
||||
// const cleaned = heads.filter((e) => !this._isReferencedInChain(list, e, this._referenced, 0));
|
||||
const cleaned = heads.filter((e) => !this._isReferencedInChain2(list, e));
|
||||
// const cleaned = heads;
|
||||
// console.log("CLEANED", JSON.stringify(cleaned), "\n");
|
||||
// this._referenced = [];
|
||||
ms = timer.stop(true);
|
||||
if(ms > 20) {
|
||||
console.log("OVER 20MS!!")
|
||||
console.log(`_findHeads took ${ms} ms`);
|
||||
}
|
||||
// console.log(`_findHeads took ${ms} ms`);
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
|
||||
_isReferencedInChain2(all, item) {
|
||||
// console.log("item:", item);
|
||||
let isReferenced = all.find((e) => this._references(e, item)) !== undefined;
|
||||
if(!isReferenced) {
|
||||
// console.log("check children", item.next)
|
||||
let childHasRef = item.next.map((f) => {
|
||||
const next = all.find((e) => this._equals(e, f));
|
||||
const res = next ? this._isReferencedInChain2(all, next) : false;
|
||||
return _.find(res, (e) => e === true) !== undefined;
|
||||
});
|
||||
isReferenced = _.find(childHasRef, (e) => e === true) !== undefined;
|
||||
// console.log("children have ref", isReferenced)
|
||||
}
|
||||
|
||||
// console.log("red:", isReferenced);
|
||||
return isReferenced;
|
||||
}
|
||||
|
||||
// _parse(t) {
|
||||
// let v = t.split('.');
|
||||
// return { id: v[0], seq: parseInt(v[1]), ver: parseInt(v[2]) };
|
||||
// }
|
||||
|
||||
_equals(a, b) {
|
||||
return a.id == b.id && a.seq == b.seq && a.ver == b.ver;
|
||||
}
|
||||
|
||||
_references(a, b) {
|
||||
// return _.includes(a.next, b.compactId);
|
||||
// faster than _.includes()
|
||||
for(let i = 0; i < a.next.length; i ++) {
|
||||
if(b.compactId === a.next[i]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
_isReferencedInChain(all, item, next2, refs, depth) {
|
||||
console.log("...item:", item);
|
||||
depth += 1;
|
||||
let timer = new Timer(true);
|
||||
let ms;
|
||||
|
||||
if(!item)
|
||||
return false;
|
||||
|
||||
// let isReferenced = refs.find((e) => e.id == item.id && e.seq == item.seq && e.ver == item.ver) !== undefined;
|
||||
let isReferenced = refs.find((e) => this._equals(e, item)) !== undefined;
|
||||
|
||||
if(isReferenced)
|
||||
console.log("ref", item)
|
||||
|
||||
if(isReferenced)
|
||||
return true;
|
||||
|
||||
// if(item.id == 'C' && item.seq == 3 && item.ver == 1) {
|
||||
// console.log("!!!!", refs,"\n", item)
|
||||
// }
|
||||
refs.splice(0, 0, item);
|
||||
|
||||
var check = (o) => {
|
||||
const next = all.find((e) => this._equals(e, this._parse(o)));
|
||||
if(!next)
|
||||
return false;
|
||||
|
||||
return this._isReferencedInChain(all, item, next, refs, depth);
|
||||
};
|
||||
|
||||
// refs.push(item);
|
||||
|
||||
// console.log(item.next);
|
||||
// let res = item.next.map(this._parse).map(check);
|
||||
let res = item.next.map(check);
|
||||
|
||||
if(depth > 5) {
|
||||
console.log("WTF!!!", depth, item, refs, res);
|
||||
}
|
||||
|
||||
// refs.splice(0, 0, item);
|
||||
|
||||
// if(item.id == 'A' && item.seq == 0 && item.ver == 1) {
|
||||
// console.log("????", isReferenced, refs, "\n\n", item, "\n\n", res)
|
||||
// }
|
||||
|
||||
// if(item.id == 'A' && item.seq == 1 && item.ver == 2) {
|
||||
// console.log("+++", res)
|
||||
// }
|
||||
|
||||
// const hasRef2 = _.find(res, (e) => e === true) !== undefined;
|
||||
// const hasRef2 = res.allEqual(true)
|
||||
|
||||
ms = timer.stop(true);
|
||||
if(ms > 20) {
|
||||
console.log("OVER 20MS!!", refs.length)
|
||||
console.log(`_isReferencedInChain took ${ms} ms`);
|
||||
}
|
||||
let hasRef2 = _.find(res, (e) => e === true) !== undefined;
|
||||
// console.log("...res:", hasRef2, res);
|
||||
// if(hasRef2)
|
||||
// refs.splice(0, 0, item);
|
||||
|
||||
return hasRef2;
|
||||
}
|
||||
*/
|
||||
}
|
||||
const List = require('./src/list/List');
|
||||
// const Node = require('./src/list/Node');
|
||||
|
||||
var run = () => {
|
||||
var redis = require("redis");
|
||||
@ -282,18 +22,14 @@ var run = () => {
|
||||
const l = List.fromJson(JSON.parse(event));
|
||||
// console.log("LIST", l);
|
||||
|
||||
// var l = List.fromJson(JSON.parse(event));
|
||||
if(l.id === 'A') {
|
||||
listB.join(l);
|
||||
listC.join(l);
|
||||
// console.log(listB);
|
||||
} else if(l.id === 'B') {
|
||||
listA.join(l);
|
||||
listC.join(l);
|
||||
// console.log("ListA:", listA);
|
||||
} else if(l.id === 'C') {
|
||||
listA.join(l);
|
||||
// console.log("LIST", event);
|
||||
console.log("Items:", listA.items.length);
|
||||
// console.log(JSON.stringify(listA, null, 1));
|
||||
}
|
||||
@ -303,11 +39,6 @@ var run = () => {
|
||||
this.client1.on("message", handleMessage);
|
||||
this.client2.on("message", handleMessage);
|
||||
|
||||
setInterval(() => {
|
||||
// listC.join(listB);
|
||||
// listC.join(listA);
|
||||
}, 5000);
|
||||
|
||||
let h = 0;
|
||||
setInterval(() => {
|
||||
listC.add("C--"+h);
|
||||
@ -318,12 +49,12 @@ var run = () => {
|
||||
let i = 0;
|
||||
setInterval(() => {
|
||||
let a = 0;
|
||||
// for(let a = 0; a < 100; a ++) {
|
||||
for(let a = 0; a < 10; a ++) {
|
||||
listB.add("B--"+(i+a));
|
||||
// }
|
||||
}
|
||||
this.client2.publish(hash, JSON.stringify(listB.toJson()));
|
||||
i++;
|
||||
}, 10);
|
||||
}, 20);
|
||||
|
||||
let k = 0;
|
||||
setInterval(() => {
|
||||
@ -338,8 +69,3 @@ var run = () => {
|
||||
};
|
||||
|
||||
run();
|
||||
|
||||
module.exports = {
|
||||
Node: Node,
|
||||
List: List
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user