Intro
Another proposal for json/object-query which :
- use simple slash delimitted syntax,
- could handle regular expression for step selection,
- could handle RQL (for filtering) on each step selection,
- could be relative to where the query is launched in a object/json
- could handle steps toward any ancestor
- could handle json-schema in RQL filtering
- that could return full descriptors of selected properties (i.e. descriptors).
- is a superset of URL format
deep.query(obj, query, opt)
Perfom a query on any valid js object.
The return's type depends if :
- the query itself is 'straight' or not,
- if something matchs or not,
- and if you need 'full' or 'simple' output
Straight means here that the query is like a path and points to a single element (e.g. /a/b/c).
Straight query that match : returns the matched value.
var r = deep.query({ a:true, b:"hello", c:{ d:12 } }, "/c/d");
deep.log(r);
Straight query that doesn't match : returns 'undefined'.
var r = deep.query({ a:true, b:"hello", c:{ d:12 } }, "/bar/foo");
deep.log(r);
Full query that match : returns the array of matched values.
var r = deep.query({ myProp:true, myProp2:"hello" }, "/(myPro.*)");
deep.log(r);
Full query that doesn't match : returns an empty array.
var r = deep.query({ myProp:true, myProp2:"hello" }, "/(foo.*)");
deep.log(r);
opt.fullOutput
Optional bool : 'true' means you want full descriptors of selected properties in place of simple values.
var obj = {
a:1,
b:true,
c:"hello",
d:{
e:"world"
}
};
var r = deep.query(obj, "/d/e", { fullOutput:true });
deep.log(r.path+" = "+r.value); // /d/e = world
opt.schema
Optional. You could associate a schema to queried object.
deepjs will try to associate correct sub-schema to selected sub-properties.
You could read it in properties descriptors (see above and below).
var obj = {
a:1,
b:true,
c:"hello",
d:{
e:"world"
}
};
var schema = {
properties:{
d:{
properties:{
e:{ type:"string", required:true }
}
}
}
};
var r = deep.query(obj, "/d/e", { fullOutput:true, schema:schema });
deep.log(r.path, " = ", r.value, " - schema : ", r.schema);
opt.allowStraightQueries
Optional. Boolean, true by default.
If true, if the query is a simple path, it will return the property itself rather than an array of results.
If false, it always return an array (empty if nothing matchs).
var obj = {
a:1,
b:true,
c:"hello",
d:{
e:"world"
}
};
var r = deep.query(obj, "/d/e", { allowStraightQueries:false });
deep.log(r); // ['world']
r = deep.query(obj, "/d/e", { allowStraightQueries:true });
deep.log(r); // 'world'
deep.nodes(obj).query(q)...
Chained query call through deep chain.
(for full documentations on chained queries and result management, see deep chain docs)
Perfom a query on current chain success and inject query result (full descriptors of selected properties) as chain success.
Return : a deep chain.
deep.nodes({
a:1,
b:true,
c:"hello",
d:{
e:"world"
}
})
// From root : Select the property 'd' then give me the property 'e' in it
.query("/d/e")
.log('"/d/e" :\n') // 'world'
// From root : give me all property wich is a string
.query("//?_type=string")
.log('\n"//?_type=string" :\n'); // ['hello', 'world']
Syntaxe
A query consist of succession of steps.
A step is : A 'move' followed by a 'step selector' and optionally followed by a 'RQL filter'.
deep.nodes([{a:2}, {a:4}]).query("/*?a=gt=3").log();
The '/' is the move : we start from root.
The '*' is the step selector : we select all child from current position.
The '?a=gt=3' is the RQL filter : we filter currents elements by selecting all elements where 'a > 3'.
start (first move)
When you start a query : you could use any of those 'move' to place play-head somewhere relatively to current node.
- / : start from root
- ./ : start from me
- ../ : start from my parent
- // : start from root and give me any properties at any sub level
deep.nodes({ a:{ b:{ c:"hello world" }, d:true }}).query("//").log();
move (after the first one)
- / : continue query from current level
- // : recursively seek any property from current level
- ../ : go back to my parent level
relative vs root
When you apply a query on a query-results-set (previously produced), you could navigate relatively to current(s) node(s), or restart queries from the root object.
deep.nodes({
a:1,
b:true,
c:"hello",
d:{
e:"world"
}
})
.query("/d") //select /d
.log("/d =")
.query("./e") // from d : select e
.log("/d/e =")
.query("../../b") // from e : select b (two levels up)
.log("/b =")
.query("/") // from b (in fact : from anywhere) : get root.
.log("/ =")
step selector
Any 'step selector' is either :
- a direct string,
- or an int (array index)
- or a regular expression,
- or a union of them (expressed as a coma separated list of them surrounded with square brackets).
You could express range of array indexes as 0:10:2 which says : take items from 0 to 10 (included) by step of two. (see examples below for optionals placement)
Regular expression are always surrounded by parenthesis, and could be ended with 'g', 'i' or 'gi'.
Examples of valid selectors :
- 1
- foo
- (foo.*)
- [0:20:2]
- [:]
- [1:,hello,(^prop.*)gi]
deep.nodes({ myProp1:[1,2,3,4,5,6], myProp2:["a","b","c","d","e"]})
.query("/myProp1/0")
.log()
deep.nodes({ myProp1:[1,2,3,4,5,6], myProp2:["a","b","c","d","e"]})
.query("/myProp2/[:4:2]")
.log()
deep.nodes({ myProp1:[1,2,3,4,5,6], myProp2:["a","b","c","d","e"]})
.query("/(myProp.*)/[3:]")
.log()
deep.nodes({ myProp1:[1,2,3,4,5,6], myProp2:["a","b","c","d","e"]})
.query("/[myProp1,myProp2]")
.log()
All those below are equivalent and say : give me all properties under root.
deep.nodes({ a:1, b:true, c:"hello"})
.query("/(.*)")
.log();
deep.nodes({ a:1, b:true, c:"hello"})
.query("/*")
.log();
deep.nodes({ a:1, b:true, c:"hello"})
.query("/[]")
.log();
deep.nodes({ a:1, b:true, c:"hello"})
.query("/")
.log();
@.length
In array brackets access : you could use @.length to get the length of the parent (only if it's an array).
deep.nodes({ a:[1,2,3,4,5,6] })
.query("/a/[@.length-2]")
.log();
length
Will give you any 'length' property founded in current level
deep.nodes({ a:[1,2,3,4,5,6], b:"hello" })
.query("/a/length")
.log()
.query("/b/length")
.log();
filter
any RQL filter (see deep-rql for full docs) could be added to any selector.
deep.nodes([1,2,3,4,5,6])
.query("/*?=gt=3")
.log();
deep.nodes([1,2,3,4,5,6])
.query("/*?sort(-)")
.log();