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();