reworked deffinitions again...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2018-12-18 16:33:48 +03:00
parent fb9570c175
commit bc9962bc64

View File

@ -16,6 +16,11 @@
* *
* *
* TODO: * TODO:
* - stress test / optimize this for:
* - large number of tags, paths
* - larger number of definitions
* - extremely large numbers of values
* ...values should have the least impact on performance.
* - investigate support for sqlite3 * - investigate support for sqlite3
* - will it be faster? * - will it be faster?
* *
@ -264,27 +269,28 @@ var BaseTagsPrototype = {
// Tag definitions... // Tag definitions...
// //
// Format: // Format:
// Set([<tag>, ..])
//
// XXX Q: should definitions be treated as persistent tags???
definitions: null,
// Format:
// { // {
// <tag>: [<tag>, ..], // <tag>: [<tag>, ..],
// ... // ...
// } // }
// //
__definition_index: null, // NOTE: a definition is equivalent to a very specific path tag but
get definition_index(){ // considering it's a special case it is handled allot faster...
var that = this // .define('birds', 'bird:many')
return (this.definitions == null || this.definitions.size == 0) ? // is equivalent to:
{} // .togglePersistent('bird:many/birds')
: (this.__definition_index = this.__definition_index //
|| [...this.definitions] // XXX Q: should definitions be displayed as persistent tags???
.reduce(function(res, p){ definitions: null,
p = that.splitPath(p)
res[p.pop()] = that.splitSet(p[0]) // definitions as paths...
return res }, {})) }, //
// XXX do we need this???
// XXX should we cache this???
get definition_paths(){
return [...Object.entries(this.definitions)]
.map(function(e){
return [e[1].join(':'), e[0]].join('/') }) },
// Props... // Props...
@ -294,10 +300,7 @@ var BaseTagsPrototype = {
// XXX should this include only definition keys, values or both??? // XXX should this include only definition keys, values or both???
get persistentAll(){ get persistentAll(){
return (this.__persistent || new Set()) return (this.__persistent || new Set())
.unite(this.definitions || []) }, .unite(this.definition_paths || []) },
//.unite(Object.entries(this.definitions || {})
// .flat(1)
// .map(function(d){ return d.join ? d.join(':') : d })) },
// Utils... // Utils...
@ -400,7 +403,7 @@ var BaseTagsPrototype = {
// match two tags... // match two tags...
} else { } else {
var definitions = this.definition_index var definitions = this.definitions
var root = /^\s*[\\\/]/.test(a) var root = /^\s*[\\\/]/.test(a)
var base = /[\\\/]\s*$/.test(a) var base = /[\\\/]\s*$/.test(a)
@ -417,7 +420,6 @@ var BaseTagsPrototype = {
return true return true
} }
// Expand definitions... // Expand definitions...
// //
// NOTE: this does not care about infinite recursion... // NOTE: this does not care about infinite recursion...
@ -1034,9 +1036,7 @@ var BaseTagsPrototype = {
} else { } else {
patchSet(this.persistent || []) patchSet(this.persistent || [])
patchObj(this.__index || {}) patchObj(this.__index || {})
//patchObj(this.definitions || {}, true) patchObj(this.definitions || {}, true)
patchSet(this.definitions || [])
delete this.__definition_index
} }
return this return this
@ -1081,106 +1081,6 @@ var BaseTagsPrototype = {
return res return res
}, },
/*/ XXX there are two implementations here:
// 1) using a map index...
// + should be faster...
// - re-implements essentially the same mechanism as path
// searching but in a different way...
// 2) using the existing path mechanic...
// + simpler
// + less code
// - should be slower for lots of deffinitions...
// Get/set/remove tag definitions...
//
// A definition is a single tag that is defined by ("means") a tag set.
//
// Resolve definition (recursive)...
// .define(tag)
// -> value
// -> undefined
//
// Set definition...
// .define(tag, value)
// -> this
//
// Remove definition...
// .define(tag, null)
// -> this
//
//
// Nested recursive definitions (i.e. left side is part of the right
// side) are supported, but literal definition recursion are not (i.e.
// left side is literally reachable in the definitionchain):
// a -> a:b:c - nested (recursive) definition, this is fine.
// a -> a - type A literal recursion, this will fail.
// a -> b -> a - type B literal recursion will fail too.
//
//
// Example:
// // nested recursive definition...
// ts.define('bird', 'bird:one')
//
// ts.define('birds', 'bird:many')
//
// // filter a list...
// ts.match('bird', ['bird', 'birds', 'animal']) // -> ['bird', 'birds']
//
define: function(tag, value){
var definitions = this.definitions = this.definitions || {}
// XXX this seems a bit ugly...
var resolve = function(tag, seen){
seen = seen || []
// check for loops...
if(seen.indexOf(tag) >= 0){
throw new Error(`.alias(..): Recursive alias chain: "${
seen
.concat([seen[0]])
.join('" -> "') }"`) }
var next = definitions[tag]
|| definitions[this.normalize(tag)]
seen.push(tag)
return next != null ?
resolve(next.join(':'), seen)
: seen.length > 1 ?
tag
: undefined
}.bind(this)
tag = this.normalize(tag)
if(/[:\\\/]/.test(tag)){
throw new Error(`.alias(..): tag must be a single tag, got: "${tag}`) }
// resolve...
if(arguments.length == 1){
return resolve(tag)
// remove...
} else if(value == null){
delete definitions[tag]
// set...
} else {
value = this.normalize(value)
if(/[\\\/]/.test(tag)){
throw new Error(`.alias(..): value must not be a path, got: "${value}`) }
// check for recursion...
var chain = []
var target = resolve(value, chain)
if(target == tag || target == this.normalize(tag)){
throw new Error(`.alias(..): Creating a recursive alias chain: "${
chain
.concat([chain[0]])
.join('" -> "') }"`) }
definitions[tag] = this.splitSet(value)
}
return this
},
/*/
// Get/set/remove tag definitions... // Get/set/remove tag definitions...
// //
// A definition is a single tag that is defined by ("means") a tag set. // A definition is a single tag that is defined by ("means") a tag set.
@ -1226,7 +1126,6 @@ var BaseTagsPrototype = {
// in a different location (.definitions), same search rules // in a different location (.definitions), same search rules
// apply. // apply.
// //
// XXX this can be faster if we index definitions via a map...
define: function(tag, value){ define: function(tag, value){
var that = this var that = this
var definitions = this.definitions var definitions = this.definitions
@ -1249,23 +1148,19 @@ var BaseTagsPrototype = {
// get/resolve... // get/resolve...
if(arguments.length == 1){ if(arguments.length == 1){
// NOTE: we expect there to be only one definition... // NOTE: we expect there to be only one definition...
return this.match(tag +'/', [...this.definitions || []]) return this.match(tag +'/', [...Object.keys(definitions) || []])
.map(function(d){ .map(function(d){
return that.splitPath(d).shift() })[0] return definitions[d].join(':') })[0]
// remove... // remove...
} else if(value == null){ } else if(value == null){
// remove all definitions of tag...
this.match(tag +'/', [...this.definitions || []])
.forEach(function(value){
definitions.delete(value +'/'+ tag) })
// delete empty .definitions // delete empty .definitions
definitions definitions
&& definitions.size == 0 && Object.keys(definitions).length == 0
&& (delete this.definitions) && (delete this.definitions)
delete this.__definition_index // clear the index...
delete (definitions || {})[tag]
// set... // set...
} else { } else {
@ -1274,16 +1169,19 @@ var BaseTagsPrototype = {
throw new Error(`.alias(..): value must not be a path, got: "${value}`) } throw new Error(`.alias(..): value must not be a path, got: "${value}`) }
// clear previous definition... // clear previous definition...
// NOTE: this will also clear the index...
this.define(tag, null) this.define(tag, null)
this.definitions = (this.definitions || new Set()) // XXX add the def...
.add(value +'/'+ tag) //this.definitions = (this.definitions || new Set())
// .add(value +'/'+ tag)
// update the index...
this.definitions = definitions || {}
this.definitions[tag] = this.splitSet(value)
} }
return this return this
}, },
//*/
// Optimize tags... // Optimize tags...
@ -1651,10 +1549,8 @@ var BaseTagsPrototype = {
var res = {} var res = {}
// definitions... // definitions...
//this.definitions && Object.keys(this.definitions).length > 0 this.definitions && Object.keys(this.definitions).length > 0
// && (res.definitions = Object.assign({}, this.definitions)) && (res.definitions = Object.assign({}, this.definitions))
this.definitions && this.definitions.size > 0
&& (res.definitions = [...this.definitions])
// persistent tags... // persistent tags...
this.persistent && this.persistent.size > 0 this.persistent && this.persistent.size > 0
@ -1674,8 +1570,7 @@ var BaseTagsPrototype = {
// definitions... // definitions...
json.definitions json.definitions
//&& (this.definitions = Object.assign({}, json.definitions)) && (this.definitions = Object.assign({}, json.definitions))
&& (this.definitions = new Set(json.definitions))
// persistent tags... // persistent tags...
json.persistent json.persistent