Added more group merging and toString
diff --git a/public/js/spec/vcSpec.js b/public/js/spec/vcSpec.js
index aa57b4f..dbb60b0 100644
--- a/public/js/spec/vcSpec.js
+++ b/public/js/spec/vcSpec.js
@@ -9,7 +9,10 @@
for (var prop in overwrites) {
newObj[prop] = overwrites[prop];
};
- return objClass.create().fromJson(newObj);
+ if (objClass === KorAP.VirtualCollection)
+ return objClass.render(newObj);
+ else
+ return objClass.create().fromJson(newObj);
}
}
};
@@ -172,7 +175,7 @@
expect(doc).toBeUndefined();
});
- it('should be serializale', function () {
+ it('should be serializale to JSON', function () {
// Empty doc
var doc = KorAP.Doc.create();
@@ -229,6 +232,38 @@
"key" : 'pubDate'
}));
});
+
+ it('should be serializale to String', function () {
+
+ // Empty doc
+ var doc = KorAP.Doc.create();
+ expect(doc.toString()).toEqual("");
+
+ // Serialize string
+ doc = stringFactory.create();
+ expect(doc.toString()).toEqual('author = "Max Birkendale"');
+
+ // Serialize string with quotes
+ doc = stringFactory.create({ "value" : 'Max "Der Coole" Birkendate'});
+ expect(doc.toString()).toEqual('author = "Max \\"Der Coole\\" Birkendate"');
+
+ // Serialize regex
+ doc = regexFactory.create();
+ expect(doc.toString()).toEqual('title = /[^b]ee.+?/');
+
+ doc = regexFactory.create({
+ match: "match:ne"
+ });
+ expect(doc.toString()).toEqual('title != /[^b]ee.+?/');
+
+ doc = dateFactory.create();
+ expect(doc.toString()).toEqual('pubDate in 2014-11-05');
+
+ doc = dateFactory.create({
+ value : "2014"
+ });
+ expect(doc.toString()).toEqual('pubDate in 2014');
+ });
});
@@ -302,7 +337,7 @@
expect(op2.value()).toEqual("2014-12-05");
expect(op2.matchop()).toEqual("eq");
- // Create empty group
+ // Append empty group
var newGroup = docGroup.append(KorAP.DocGroup.create());
newGroup.operation('or');
newGroup.append(docFactory.create());
@@ -349,7 +384,7 @@
expect(op5.matchop()).toEqual("ne");
});
- it('should be serializable', function () {
+ it('should be serializable to JSON', function () {
var docGroup = docGroupFactory.create();
expect(docGroup.toJson()).toEqual(jasmine.objectContaining({
@@ -373,6 +408,55 @@
]
}));
});
+
+ it('should be serializable to String', function () {
+ var docGroup = docGroupFactory.create();
+ expect(docGroup.toString()).toEqual('author = "Max Birkendale" & pubDate in 2014-12-05');
+
+ docGroup = docGroupFactory.create({
+ "@type" : "korap:docGroup",
+ "operation" : "operation:or",
+ "operands" : [
+ {
+ "@type": 'korap:doc',
+ "key" : 'author',
+ "match": 'match:eq',
+ "value": 'Max Birkendale',
+ "type": 'type:string'
+ },
+ {
+ "@type" : "korap:docGroup",
+ "operation" : "operation:and",
+ "operands" : [
+ {
+ "@type": 'korap:doc',
+ "key": 'pubDate',
+ "match": 'match:geq',
+ "value": '2014-05-12',
+ "type": 'type:date'
+ },
+ {
+ "@type": 'korap:doc',
+ "key": 'pubDate',
+ "match": 'match:leq',
+ "value": '2014-12-05',
+ "type": 'type:date'
+ },
+ {
+ "@type": 'korap:doc',
+ "key": 'foo',
+ "match": 'match:ne',
+ "value": '[a]?bar',
+ "type": 'type:regex'
+ }
+ ]
+ }
+ ]
+ });
+ expect(docGroup.toString()).toEqual(
+ 'author = "Max Birkendale" | (pubDate since 2014-05-12 & pubDate until 2014-12-05 & foo != /[a]?bar/)'
+ );
+ });
});
describe('KorAP.UnspecifiedDoc', function () {
@@ -399,11 +483,12 @@
"type": 'type:date'
});
+ // Add unspecified object
+ docGroup.append();
+
expect(docGroup.element().getAttribute('class')).toEqual('docGroup');
expect(docGroup.element().children[0].getAttribute('class')).toEqual('doc');
- // Add unspecified object
- docGroup.append();
var unspec = docGroup.element().children[1];
expect(unspec.getAttribute('class')).toEqual('unspecified');
@@ -558,6 +643,90 @@
});
describe('KorAP.VirtualCollection', function () {
+
+ var simpleGroupFactory = buildFactory(KorAP.DocGroup, {
+ "@type" : "korap:docGroup",
+ "operation" : "operation:and",
+ "operands" : [
+ {
+ "@type": 'korap:doc',
+ "key" : 'author',
+ "match": 'match:eq',
+ "value": 'Max Birkendale',
+ "type": 'type:string'
+ },
+ {
+ "@type": 'korap:doc',
+ "key": 'pubDate',
+ "match": 'match:eq',
+ "value": '2014-12-05',
+ "type": 'type:date'
+ }
+ ]
+ });
+
+ var nestedGroupFactory = buildFactory(KorAP.VirtualCollection, {
+ "@type" : "korap:docGroup",
+ "operation" : "operation:or",
+ "operands" : [
+ {
+ "@type": 'korap:doc',
+ "key" : 'author',
+ "match": 'match:eq',
+ "value": 'Max Birkendale',
+ "type": 'type:string'
+ },
+ {
+ "@type" : "korap:docGroup",
+ "operation" : "operation:and",
+ "operands" : [
+ {
+ "@type": 'korap:doc',
+ "key": 'pubDate',
+ "match": 'match:geq',
+ "value": '2014-05-12',
+ "type": 'type:date'
+ },
+ {
+ "@type": 'korap:doc',
+ "key": 'pubDate',
+ "match": 'match:leq',
+ "value": '2014-12-05',
+ "type": 'type:date'
+ }
+ ]
+ }
+ ]
+ });
+
+ var flatGroupFactory = buildFactory(KorAP.VirtualCollection, {
+ "@type" : "korap:docGroup",
+ "operation" : "operation:and",
+ "operands" : [
+ {
+ "@type": 'korap:doc',
+ "key": 'pubDate',
+ "match": 'match:geq',
+ "value": '2014-05-12',
+ "type": 'type:date'
+ },
+ {
+ "@type": 'korap:doc',
+ "key": 'pubDate',
+ "match": 'match:leq',
+ "value": '2014-12-05',
+ "type": 'type:date'
+ },
+ {
+ "@type": 'korap:doc',
+ "key": 'foo',
+ "match": 'match:eq',
+ "value": 'bar',
+ "type": 'type:string'
+ }
+ ]
+ });
+
it('should be initializable', function () {
var vc = KorAP.VirtualCollection.render();
expect(vc.element().getAttribute('class')).toEqual('vc');
@@ -590,26 +759,7 @@
});
it('should be based on a docGroup', function () {
- var vc = KorAP.VirtualCollection.render({
- "@type" : "korap:docGroup",
- "operation" : "operation:and",
- "operands" : [
- {
- "@type": 'korap:doc',
- "key" : 'author',
- "match": 'match:eq',
- "value": 'Max Birkendale',
- "type": 'type:string'
- },
- {
- "@type": 'korap:doc',
- "key": 'pubDate',
- "match": 'match:eq',
- "value": '2014-12-05',
- "type": 'type:date'
- }
- ]
- });
+ var vc = KorAP.VirtualCollection.render(simpleGroupFactory.create().toJson());
expect(vc.element().getAttribute('class')).toEqual('vc');
expect(vc.root().element().getAttribute('class')).toEqual('docGroup');
@@ -630,39 +780,8 @@
it('should be based on a nested docGroup', function () {
- var vc = KorAP.VirtualCollection.render({
- "@type" : "korap:docGroup",
- "operation" : "operation:or",
- "operands" : [
- {
- "@type": 'korap:doc',
- "key" : 'author',
- "match": 'match:eq',
- "value": 'Max Birkendale',
- "type": 'type:string'
- },
- {
- "@type" : "korap:docGroup",
- "operation" : "operation:and",
- "operands" : [
- {
- "@type": 'korap:doc',
- "key": 'pubDate',
- "match": 'match:geq',
- "value": '2014-05-12',
- "type": 'type:date'
- },
- {
- "@type": 'korap:doc',
- "key": 'pubDate',
- "match": 'match:leq',
- "value": '2014-12-05',
- "type": 'type:date'
- }
- ]
- }
- ]
- });
+ var vc = nestedGroupFactory.create();
+
expect(vc.element().getAttribute('class')).toEqual('vc');
expect(vc.element().firstChild.getAttribute('class')).toEqual('docGroup');
expect(vc.element().firstChild.children[0].getAttribute('class')).toEqual('doc');
@@ -674,53 +793,141 @@
expect(vc.element().firstChild.children[2].getAttribute('class')).toEqual('operators');
});
- it('should be modifiable by deletion', function () {
- var vc = KorAP.VirtualCollection.render({
- "@type" : "korap:docGroup",
- "operation" : "operation:and",
- "operands" : [
- {
- "@type": 'korap:doc',
- "key": 'pubDate',
- "match": 'match:geq',
- "value": '2014-05-12',
- "type": 'type:date'
- },
- {
- "@type": 'korap:doc',
- "key": 'pubDate',
- "match": 'match:leq',
- "value": '2014-12-05',
- "type": 'type:date'
- },
- {
- "@type": 'korap:doc',
- "key": 'foo',
- "match": 'match:eq',
- "value": 'bar',
- "type": 'type:string'
- }
- ]
- });
-
+ it('should be modifiable by deletion in flat docGroups', function () {
+ var vc = flatGroupFactory.create();
var docGroup = vc.root();
+
+ expect(docGroup.element().getAttribute('class')).toEqual('docGroup');
+
var doc = docGroup.getOperand(1);
expect(doc.key()).toEqual("pubDate");
+ expect(doc.value()).toEqual("2014-12-05");
// Remove operand 1
- expect(docGroup.delOperand(doc)).not.toBeUndefined();
+ expect(docGroup.delOperand(doc).update()).not.toBeUndefined();
expect(doc._element).toEqual(undefined);
doc = docGroup.getOperand(1);
expect(doc.key()).toEqual("foo");
// Remove operand 1
- expect(docGroup.delOperand(doc)).not.toBeUndefined();
+ expect(docGroup.delOperand(doc).update()).not.toBeUndefined();
expect(doc._element).toEqual(undefined);
- // Only one operand - but there shouldn't be a group anymore!
+ // Only one operand left ...
expect(docGroup.getOperand(1)).toBeUndefined();
+ // ... but there shouldn't be a group anymore at all!
+ expect(docGroup.getOperand(0)).toBeUndefined();
+
+ var obj = vc.root();
+ expect(obj.ldType()).toEqual("doc");
+ expect(obj.key()).toEqual("pubDate");
+ expect(obj.value()).toEqual("2014-05-12");
+
+ expect(obj.element().getAttribute('class')).toEqual('doc');
});
+
+
+ it('should be modifiable by deletion in nested docGroups (root case)', function () {
+ var vc = nestedGroupFactory.create();
+
+ var docGroup = vc.root();
+ expect(docGroup.ldType()).toEqual("docGroup");
+ expect(docGroup.operation()).toEqual("or");
+
+ var doc = docGroup.getOperand(0);
+ expect(doc.key()).toEqual("author");
+ expect(doc.value()).toEqual("Max Birkendale");
+
+ docGroup = docGroup.getOperand(1);
+ expect(docGroup.operation()).toEqual("and");
+
+ doc = docGroup.getOperand(0);
+ expect(doc.key()).toEqual("pubDate");
+ expect(doc.matchop()).toEqual("geq");
+ expect(doc.value()).toEqual("2014-05-12");
+ expect(doc.type()).toEqual("date");
+
+ doc = docGroup.getOperand(1);
+ expect(doc.key()).toEqual("pubDate");
+ expect(doc.matchop()).toEqual("leq");
+ expect(doc.value()).toEqual("2014-12-05");
+ expect(doc.type()).toEqual("date");
+
+ // Remove first operand so everything becomes root
+ expect(
+ vc.root().delOperand(
+ vc.root().getOperand(0)
+ ).update().ldType()
+ ).toEqual("docGroup");
+ expect(vc.root().ldType()).toEqual("docGroup");
+ expect(vc.root().operation()).toEqual("and");
+ });
+
+ it('should be modifiable by deletion in nested docGroups (resolve group case)', function () {
+ var vc = nestedGroupFactory.create();
+
+ // Get nested group
+ var firstGroup = vc.root().getOperand(1);
+ firstGroup.append(simpleGroupFactory.create({ "operation" : "operation:or" }));
+
+ // Structur is now:
+ // or(doc, and(doc, doc, or(doc, doc)))
+
+ // Get nested or in and
+ var orGroup = vc.root().getOperand(1).getOperand(2);
+ expect(orGroup.ldType()).toEqual("docGroup");
+ expect(orGroup.operation()).toEqual("or");
+
+ // Remove
+ // Structur is now:
+ // or(doc, and(doc, doc, doc)))
+ expect(orGroup.delOperand(orGroup.getOperand(0)).update().operation()).toEqual("and");
+ expect(vc.root().getOperand(1).operands().length).toEqual(3);
+ });
+
+ it('should be modifiable by deletion in nested docGroups (identical group case)', function () {
+ var vc = nestedGroupFactory.create();
+
+ // Get nested group
+ var firstGroup = vc.root().getOperand(1);
+ firstGroup.append(simpleGroupFactory.create({ "operation" : "operation:or" }));
+
+ // Structur is now:
+ // or(doc, and(doc, doc, or(doc, doc)))
+ expect(vc.toString()).toEqual(
+ 'author = "Max Birkendale" | (pubDate since 2014-05-12 & pubDate until 2014-12-05 & (author = "Max Birkendale" | pubDate in 2014-12-05))'
+ );
+
+ var andGroup = vc.root().getOperand(1);
+
+ // Get leading docs in and
+ var doc1 = andGroup.getOperand(0);
+ expect(doc1.ldType()).toEqual("doc");
+ expect(doc1.value()).toEqual("2014-05-12");
+ var doc2 = andGroup.getOperand(1);
+ expect(doc2.ldType()).toEqual("doc");
+ expect(doc2.value()).toEqual("2014-12-05");
+
+ // Remove 2
+ expect(andGroup.delOperand(doc2).update().operation()).toEqual("and");
+ // Structur is now:
+ // or(doc, and(doc, or(doc, doc)))
+
+ expect(vc.toString()).toEqual(
+ 'author = "Max Birkendale" | (pubDate since 2014-05-12 & (author = "Max Birkendale" | pubDate in 2014-12-05))'
+ );
+
+
+ // Remove 1
+ expect(andGroup.delOperand(doc1).update().operation()).toEqual("or");
+ // Structur is now:
+ // or(doc, doc, doc)
+
+ expect(vc.toString()).toEqual(
+ 'author = "Max Birkendale" | author = "Max Birkendale" | pubDate in 2014-12-05'
+ );
+ });
});
describe('KorAP.Operators', function () {
diff --git a/public/js/src/vc.js b/public/js/src/vc.js
index 31ddc85..a713b09 100644
--- a/public/js/src/vc.js
+++ b/public/js/src/vc.js
@@ -32,11 +32,13 @@
KorAP._validDateMatchRE = new RegExp("^[lg]?eq$");
KorAP._validDateRE = new RegExp("^(?:\\d{4})(?:-\\d\\d(?:-\\d\\d)?)?$");
KorAP._validGroupOpRE = new RegExp("^(?:and|or)$");
+ KorAP._quote = new RegExp("([\"\\\\])", 'g');
- var loc = (KorAP.Locale = KorAP.Locale || {} );
- loc.AND = loc.AND || 'and';
- loc.OR = loc.OR || 'or';
- loc.DEL = loc.DEL || '×';
+ var loc = (KorAP.Locale = KorAP.Locale || {} );
+ loc.AND = loc.AND || 'and';
+ loc.OR = loc.OR || 'or';
+ loc.DEL = loc.DEL || '×';
+ loc.EMPTY = loc.EMPTY || '⋯'
function _bool (bool) {
return (bool === undefined || bool === false) ? false : true;
@@ -50,7 +52,9 @@
};
KorAP.VirtualCollection = {
- _root : undefined,
+ ldType : function () {
+ return null;
+ },
create : function () {
return Object.create(KorAP.VirtualCollection);
},
@@ -60,10 +64,10 @@
if (json !== undefined) {
// Root object
if (json['@type'] == 'korap:doc') {
- obj._root = KorAP.Doc.create(undefined, json);
+ obj._root = KorAP.Doc.create(obj, json);
}
else if (json['@type'] == 'korap:docGroup') {
- obj._root = KorAP.DocGroup.create(undefined, json);
+ obj._root = KorAP.DocGroup.create(obj, json);
}
else {
KorAP.log(813, "Collection type is not supported");
@@ -73,7 +77,7 @@
else {
// Add unspecified object
- obj._root = KorAP.UnspecifiedDoc.create();
+ obj._root = KorAP.UnspecifiedDoc.create(obj);
};
// Add root element to root node
@@ -83,7 +87,9 @@
return obj;
},
- root : function () {
+ root : function (obj) {
+ if (arguments.length === 1)
+ this._root = obj;
return this._root;
},
element : function () {
@@ -93,6 +99,12 @@
this._element = document.createElement('div');
this._element.setAttribute('class', 'vc');
return this._element;
+ },
+ toJson : function () {
+ return this._root.toJson();
+ },
+ toString : function () {
+ return this._root.toString();
}
};
@@ -106,7 +118,7 @@
KorAP._delete = function (e) {
var obj = this.parentNode.refTo;
- obj.parent().delOperand(obj);
+ obj.parent().delOperand(obj).update();
// Todo: CLEAR ALL THE THINGS!
};
@@ -220,7 +232,7 @@
_removeChildren(this._element);
var ellipsis = document.createElement('span');
- ellipsis.appendChild(document.createTextNode('⋯'));
+ ellipsis.appendChild(document.createTextNode(loc.EMPTY));
this._element.appendChild(ellipsis);
// Set operators
@@ -228,7 +240,7 @@
false,
false,
// No delete object, if this is the root
- this._parent !== undefined ? true : false
+ (this._parent !== undefined && this.parent().ldType() !== null) ? true : false
);
this._element.appendChild(
@@ -315,11 +327,11 @@
// Wrap a new operation around the doc element
wrap : function (op) {
+/*
var group = KorAP.DocGroup.create(undefined);
group.append(this);
group.append(null);
this.parent(group);
-/*
var div = document.createElement('div');
div.setAttribute('data-operation', op);
var parent = this.element.parent;
@@ -462,6 +474,49 @@
"value" : this.value() || '',
"type" : "type:" + this.type()
};
+ },
+ toString : function () {
+ if (!this.matchop() || !this.key())
+ return "";
+
+ // Build doc string based on key
+ var string = this.key() + ' ';
+
+ // Add match operator
+ switch (this.matchop()) {
+ case "ne":
+ string += '!=';
+ break;
+ case "contains":
+ string += '~';
+ break;
+ case "geq":
+ string += 'since';
+ break;
+ case "leq":
+ string += 'until';
+ break;
+ default:
+ string += (this.type() == 'date') ? 'in' : '=';
+ break;
+ };
+
+ string += ' ';
+
+ // Add value
+ switch (this.type()) {
+ case "date":
+ return string + this.value();
+ break;
+ case "regex":
+ return string + '/' + this.value() + '/';
+ break;
+ case "string":
+ return string + '"' + this.value().replace(KorAP._quote, '\\$1') + '"';
+ break;
+ };
+
+ return "...";
}
};
@@ -487,7 +542,6 @@
// Be aware of cyclic structures!
operand = KorAP.UnspecifiedDoc.create(this);
this._operands.push(operand);
- this.update();
return operand;
};
@@ -502,7 +556,6 @@
// Be aware of cyclic structures!
operand.parent(this);
this._operands.push(operand);
- this.update();
return operand;
};
@@ -515,7 +568,6 @@
if (doc === undefined)
return;
this._operands.push(doc);
- this.update();
return doc;
case "korap:docGroup":
@@ -524,7 +576,6 @@
if (docGroup === undefined)
return;
this._operands.push(docGroup);
- this.update();
return docGroup;
default:
@@ -533,12 +584,25 @@
};
},
update : function () {
- if (this._operands.length == 1) {
- if (this.parent() !== undefined) {
- return this.parent().replaceOperand(
+
+ // There is only one operand in group
+ if (this._operands.length === 1) {
+ var parent = this.parent();
+
+ // Parent is a group
+ if (parent.ldType() !== null) {
+ return parent.replaceOperand(
this,
this.getOperand(0)
- );
+ ).update();
+ }
+
+ // Parent is vc root
+ else {
+console.log("parent is vc");
+ parent.root(this.getOperand(0));
+ this.destroy();
+ return parent.root();
};
};
@@ -577,6 +641,7 @@
this._element = document.createElement('div');
this._element.setAttribute('class', 'docGroup');
+ // Update the object - including optimization
this.update();
return this._element;
@@ -601,12 +666,30 @@
},
// Replace operand
- replaceOperand : function (group, obj) {
+ replaceOperand : function (oldOp, newOp) {
for (var i in this._operands) {
- if (this._operands[i] === group) {
- this._operands[i] = obj;
- group.destroy();
- return this.update();
+ if (this._operands[i] === oldOp) {
+
+ // Just insert a doc
+ if (newOp.ldType() === "doc") {
+ console.log("Insert doc in group");
+ this._operands[i] = newOp;
+ }
+ // Insert a group of a different operation
+ // (i.e. "and" in "or"/"or" in "and")
+ else if (newOp.operation() != oldOp.operation()) {
+ console.log("Insert group in group - no flatten");
+ this._operands[i] = newOp;
+ }
+
+ // Flatten the group
+ else {
+ console.log("Insert group in group - flatten");
+ for (var op in newOp.operands().reverse())
+ this._operands.splice(i, 1, newOp.getOperand(op))
+ };
+ oldOp.destroy();
+ return this;
}
};
return false;
@@ -623,7 +706,7 @@
// Todo: Update has to check
// that this may mean the group is empty etc.
- return this.update();
+ return this;
};
};
@@ -675,6 +758,13 @@
"operation" : "operation:" + this.operation(),
"operands" : opArray
};
+ },
+ toString : function () {
+ return this._operands.
+ map(function (op) {
+ return op.ldType() === 'docGroup' ? '(' + op.toString() + ')' : op.toString()
+ }).
+ join(this.operation() === 'or' ? ' | ' : ' & ')
}
};
@@ -723,6 +813,7 @@
if (this._operands !== undefined) {
for (var i in this._operands)
this.getOperand(i).destroy();
+ this._operands = [];
};
},