- cons :
Object
The constrain object for KB. has many convenience methods. Call via KB.cons.
- addNode ⇒
Promise
Adds node(s) to neo4j with a required JSON prop satisfying the KB constraints, and an optional Label string or array.
- getNode ⇒
Promise
Get node(s) from neo4j with JSON prop, and optional Label.
- addEdge ⇒
Promise
Adds edge(s) to neo4j with propLabel of nodes A -> B with the edge E. The propLabel for A and B is an array of a optional non-empty JSON prop (doesn't have to satisfy KB constraints), and an optional Label string or array. The prop for E must satisfy the KB constraints, and the Label for E is required.
- getEdge ⇒
Promise
Get edge(s) from neo4j with propLabel of nodes A -> B with the edge E. The propLabel for A, B and E is an array of a optional non-empty JSON prop (doesn't have to satisfy KB constraints), and a (optional for A,B; required for E) Label string or array.
- get ⇒
Promise
Get graph and do whatever u want with the search results: filter, RETURN, DELETE, DETACH DELETE. Graph: node(s) and edge(s) from neo4j with propLabel of nodes A -> B with the edge E. The propLabel for A, B and E is an array of a optional non-empty JSON prop (doesn't have to satisfy KB constraints), and a (optional for A,B; required for E) Label string or array. This is a flexible method used for querying node(s), or optionally edge(s) and node(s). It also takes an optional WHERE filter sentence string, and a required RETURN sentence string.The resultant query string is of the form: For nodes: MATCH (a:LabelA {propA}) For nodes and edges: MATCH (a:LabelA {propA})-[e:LabelE ({propE}|*Dist)]-(b:LabelB {propB})
- add ⇒
Promise
Get graph and do whatever u want with the search results: filter, RETURN, DELETE, DETACH DELETE. Graph: node(s) and edge(s) from neo4j with propLabel of nodes A -> B with the edge E. The propLabel for A, B and E is an array of a optional non-empty JSON prop (doesn't have to satisfy KB constraints), and a (optional for A,B; required for E) Label string or array. This is a flexible method used for querying node(s), or optionally edge(s) and node(s). It also takes an optional WHERE filter sentence string, and a required RETURN sentence string.The resultant query string is of the form: For nodes: MATCH (a:LabelA {propA}) For nodes and edges: MATCH (a:LabelA {propA})-[e:LabelE ({propE}|*Dist)]-(b:LabelB {propB})
- query ⇒
Promise
Queries Neo4j with arrays of pairs of query and optional params. Is the right-composition of genStatArr and postQuery. This is is query() method with full power, i.e. no query-string cleaning. Use responsibly. The high level add/get methods call this internall with query-string cleaning to prevent SQL injection. The query cleaning algorithm is (proven( based on the CFG parse tree and logic. Refer to isLegalSentence(). A sentence is a clause starting with the following query operation specifier:
WHERE,SET,REMOVE,RETURN,DELETE,DETACH DELETE,SHORTESTPATH,ALLSHORTESTPATHS
The sentence also consists of variables and operators . The permissible operators by this algorithm areAND,OR,XOR,NOT,=,<>,<,>,<=,>=,IS NULL,IS NOT NULL,+,+=,=~,EXISTS,nodes,labels,keys,",",=,ORDER BY,LIMIT,COUNT,sum,avg,percentileDisc,percentileCont,stdev,stdevp,max,min,collect,DISTINCT,nodes,labels,keys
- literalize(propDistLabel, [name]) ⇒
string
Generalization of literalizeProp. Parses a propDistLabel array of prop-label or dist-label into a 'literal map' string as required by neo4j error (unable to user parameter map in MATCH). On can pass the name of the identifier too. Beware: in the query-param pair, the param prop JSON must use the key 'prop_'. E.g. query param pair:
- log(arg) ⇒
string
A conveience method. JSON-stringify the argument, logs it, and return the string.
- beautify(neoRes, fn, [keepHead]) ⇒
string
Transform and beautify the entire neoRes by internally calling transform(neoRes) -> array of tables -> beautifyMat on each table -> join('\n\n\n'). If neoRes has only one qRes, then returns the single transformed table.
- transBeautify(transNeoRes, fn) ⇒
string
Beautify for post-transformation: beautify the transformed neoRes by internally calling array of tables -> beautifyMat on each table -> join('\n\n\n'). Note that neoRes must be transformed beforehand. This is for when transformation happens separately. If neoRes has only one qRes, then returns the single transformed table.
- beautifyMat(mat) ⇒
string
Beautify a matrix by JSON.stringify -> join('\n\n') -> join('\n\n---\n\n') to separate rows, and wrap with '```'
- transform(neoRes, fn, [keepHead]) ⇒
Array
Format the entire neoRes into an array of qRes tables. Can be called multiply for sequential transformation. If neoRes has only one qRes, then returns the single transformed table.
- parseKV(obj) ⇒
Array
Parse a JSON object into array to ['k: v', 'k: v'], where v is attemptedly stringified.
- cleanUser(userObj) ⇒
JSON
Cleanup the user object by picking out name, real_name, id, email_address.
- parseUser(userObj) ⇒
string
A beautify transformer method to parse user, picking out name, real_name, id, email_address; uses parseKV internally.
- parseObj(obj, keyArr) ⇒
Array
A beautify transformer method to parse object, picking out keys from keyArr; uses parseKV internally.
- leftJoin(propArr, match, [boolOp]) ⇒
string
Helper to generate wOp for matching multiple properties to the same value.
- sorter(iteratees) ⇒
function
Generate a function to sort the rows of a matrix passed to it using _.sortBy and iteratees.
- picker(iteratees) ⇒
function
For use with transform. Generate a picker function using _.pick with a supplied iteratees.
- pickerBy(iteratees) ⇒
function
For use with transform. Generate a picker function using _.pickBy with a supplied iteratees.
- flattenIndex(mat) ⇒
string
Flatten and index a matrix of objects into formatted string. 'name' key will always appear first next to index.
The constrain object for KB. has many convenience methods. Call via KB.cons.
Kind: global variable
- cons :
Object
- .now() ⇒
string
- .legalize(prop, [res], [hash_by]) ⇒
JSON
- .illegalize(prop) ⇒
JSON
- .isLegalSentence(str) ⇒
Boolean
- .now() ⇒
Generates the current timestamp in ISO 8601 format, e.g. 2016-01-22T14:42:27.579Z
Kind: static method of cons
Returns: string
- ISO 8601 timestamp string, e.g. '2016-01-22T14:42:27.579Z'
Legalize a prop obj by inserting the missing mandatory fields with default values. Automatically flattens the JSON with key delimiter '__' since neo4J doesn't take a deep JSON. Also updates the hash as demanded by 'hash_by', and you can supply the 'hash_by' key as an argument. Mutates the object. Used mainly for testing. Use with care.
Kind: static method of cons
Returns: JSON
- mutated prop that is now legal wrt the constraints.
Param | Type | Description |
---|---|---|
prop | JSON |
The properties object for KB node. |
[res] | JSON |
For the robot to extract user id. |
[hash_by] | string |
Specify and set the hash_by key and update it internally. |
Example
var prop = {name: 'A', hash_by: 'name'}
cons.legalize(prop)
// => { name: 'A',
// hash_by: 'name',
// hash: 'A',
// updated_by: 'bot',
// updated_when: 1452802513112 }
// supplying the 'hash_by' key
var prop0 = {name: 'A'}
cons.legalize(prop0, 'name')
// => { name: 'A',
// hash_by: 'name',
// hash: 'A',
// updated_by: 'bot',
// updated_when: 1452802513112 }
var prop1 = {name: 'A', hash_by: 'name', meh: { a:1 }}
cons.legalize(prop1)
// => { name: 'A',
// meh__a: 1,
// hash_by: 'name',
// hash: 'A',
// updated_by: 'bot',
// updated_when: 1452802513112 }
// quick constructor of a legal prop
cons.legalize('A')
// { name: 'A',
// hash_by: 'name',
// hash: 'A',
// updated_by: 'bot',
// updated_when: '2016-02-01T20:34:02.582Z' }
cons.legalize('A', 'updated_when')
// { name: 'A',
// hash_by: 'updated_when',
// hash: '2016-02-01T20:34:02.603Z',
// updated_by: 'bot',
// updated_when: '2016-02-01T20:34:02.603Z' }
The opposite of legalize: Return a new prop with its mandatoryFields removed. Does not mutate object. Used to yield cleaner prop.
Kind: static method of cons
Returns: JSON
- Prop without the mandatory fields.
Param | Type | Description |
---|---|---|
prop | JSON |
The prop to illegalize. |
Example
var prop = {name: 'A'}
cons.legalize(prop)
// => prop = { name: 'A',
hash_by: 'hash',
hash: 'test',
updated_by: 'bot',
updated_when: '2016-02-01T22:02:34.822Z' }
cons.illegalize(prop)
// => { name: 'A' }
Check if the string is a RETURN operation string and is legal, to prevent SQL injection. Algo (str):
- replace "(" and ")" globally
- split by globally, trim spaces, into variables
- check if each variable is legally atomic (shan't be split further): either contains no space "\s+" and no quotes "", '" at all, or is a proper quoted string with quotes on the boundaries but not the inside.
A sentence is a clause starting with the following query operation specifier: WHERE,SET,REMOVE,RETURN,DELETE,DETACH DELETE,SHORTESTPATH,ALLSHORTESTPATHS
The sentence also consists of variables and operators . The permissible operators by this algorithm are AND,OR,XOR,NOT,=,<>,<,>,<=,>=,IS NULL,IS NOT NULL,+,+=,=~,EXISTS,nodes,labels,keys,",",=,ORDER BY,LIMIT,COUNT,sum,avg,percentileDisc,percentileCont,stdev,stdevp,max,min,collect,DISTINCT,nodes,labels,keys
Proof: Some terminologies: A sentence is a string that can properly evaluate to its output. It consists of operators and variables. Operators (single argument on either side here) operate on a single variable on either side (left and right) to yield another variable. We can take the CFG parse tree of the whole string, split by the ops. We do not care about operator precedence in checking the legality of the string. Also note that parentheses "(", ")" exists only to assert operator precedence to give an unambiguous parse tree, thus removing them entirely is simply making the tree flat, i.e. all operations run from left to right. Furthermore, note that from the terminologies we can see that in a sentence, there can only be adjacent occurence of operators, but not of variables. This can also be seen from the parse tree, where the sentence is split by operators, and variables are the leaves. Leaves are always atomic. This justifies our algorithm.
Kind: static method of cons
Param | Type | Description |
---|---|---|
str | string |
A sentence of Ops and Variables. |
Adds node(s) to neo4j with a required JSON prop satisfying the KB constraints, and an optional Label string or array.
Kind: global variable
Returns: Promise
- From the query.
Param | Type | Description |
---|---|---|
single_query, | * |
As (fn, *, *, ...), e.g. (fn, propLabel) |
multi_queries | * |
As (fn, [*], [*], [*]...), e.g. (fn, [propLabel0], [propLabel1], ...) |
multi_queries_one_array | * |
As (fn, [[*], [*], [*]...]), e.g. (fn, [[propLabel0], [propLabel1], ...]) |
Example
var propA = {name: 'A', hash_by: 'name'}
var propB = {name: 'B', hash_by: 'name'}
// legalize the prop objects subject to constraints
KB.cons.legalize(propA)
KB.cons.legalize(propB)
addNode(propA, 'alpha').then(KB.log)
// {"results":[{"columns":["u"],"data":[{"row":[{"created_when":1452801392345,"updated_by":"tester","name":"A","hash_by":"name","created_by":"tester","hash":"A","updated_when":1452802417919}]}]}],"errors":[]}
// The node is added/updated to KB.
// batch node query by array of pairs
addNode([propA, 'alpha'], [propB, 'alpha']).then(KB.log)
// equivalently
addNode([[propA, 'alpha'], [propB, 'alpha']]).then(KB.log)
// {"results":[{"columns":["u"],"data":[{"row":[{"created_when":1452801392345,"updated_by":"tester","name":"A","hash_by":"name","created_by":"tester","hash":"A","updated_when":1452803465461}]}]},{"columns":["u"],"data":[{"row":[{"created_when":1452803465462,"name":"B","updated_by":"tester","hash_by":"name","created_by":"tester","hash":"B","updated_when":1452803465462}]}]}],"errors":[]}
// propA node is updated; propB node is added.
Get node(s) from neo4j with JSON prop, and optional Label.
Kind: global variable
Returns: Promise
- From the query.
Param | Type | Description |
---|---|---|
single_query, | * |
As (fn, *, *, ...), e.g. (fn, propLabel) |
multi_queries | * |
As (fn, [*], [*], [*]...), e.g. (fn, [propLabel0], [propLabel1], ...) |
multi_queries_one_array | * |
As (fn, [[*], [*], [*]...]), e.g. (fn, [[propLabel0], [propLabel1], ...]) |
Example
var prop2 = {name: 'A', hash_by: 'name'}
var prop3 = {name: 'B', hash_by: 'name'}
// no constrain needed when getting node from KB
get nodes from just the prop
getNode(prop2).then(KB.log)
// {"results":[{"columns":["u"],"data":[{"row":[{"created_when":1452807183847,"updated_by":"bot","name":"A","hash_by":"name","created_by":"tester","hash":"A","updated_when":1453244315302}]}]}],"errors":[]}
get nodes from just the label
getNode('alpha').then(KB.log)
// {"results":[{"columns":["u"],"data":[{"row":[{"created_when":1452807183847,"updated_by":"bot","name":"A","hash_by":"name","created_by":"tester","hash":"A","updated_when":1453244315302}]},{"row":[{"created_when":1452807183848,"updated_by":"bot","name":"B","hash_by":"name","created_by":"tester","hash":"B","updated_when":1453244315304}]},{"row":[{"created_when":1453143013572,"updated_by":"bot","name":"C","hash_by":"name","created_by":"bot","hash":"C","updated_when":1453143013572}]}]}],"errors":[]}
// get nodes from a propLabel pair
getNode(prop2, 'alpha').then(KB.log)
// {"results":[{"columns":["u"],"data":[{"row":[{"created_when":1452801392345,"updated_by":"tester","name":"A","hash_by":"name","created_by":"tester","hash":"A","updated_when":1452803465461}]}]}],"errors":[]}
// get nodes from many propLabel pairs
getNode([prop2, 'alpha'], [prop3, 'alpha']).then(KB.log)
// equivalently
getNode([[prop2, 'alpha'], [prop3, 'alpha']]).then(KB.log)
// {"results":[{"columns":["u"],"data":[{"row":[{"created_when":1452801392345,"updated_by":"tester","name":"A","hash_by":"name","created_by":"tester","hash":"A","updated_when":1452803465461}]}]},{"columns":["u"],"data":[{"row":[{"created_when":1452803465462,"updated_by":"tester","name":"B","hash_by":"name","created_by":"tester","hash":"B","updated_when":1452803465462}]}]}],"errors":[]}
Adds edge(s) to neo4j with propLabel of nodes A -> B with the edge E. The propLabel for A and B is an array of a optional non-empty JSON prop (doesn't have to satisfy KB constraints), and an optional Label string or array. The prop for E must satisfy the KB constraints, and the Label for E is required.
Kind: global variable
Returns: Promise
- From the query.
Param | Type | Description |
---|---|---|
single_query, | * |
As (fn, *, *, ...), e.g. (fn, propLabelA, propLabelE, propLabelB) |
multi_queries | * |
As (fn, [*], [*], [*]...), e.g. (fn, [propLabelA0, propLabelE0, propLabelB0], [propLabelA1, propLabelE1, propLabelB1], ...) |
multi_queries_one_array | * |
As (fn, [[*], [*], [*]...]), e.g. (fn, [[propLabelA0, propLabelE0, propLabelB0], [propLabelA1, propLabelE1, propLabelB1], ...]) |
Example
var propE = {name: 'lexicography', hash_by: 'name'}
KB.cons.legalize(propE)
var labelE = 'next'
var labelE2 = 'after'
var labelEArr = ['next', 'after']
var propA = {name: 'A', hash_by: 'name'}
var propB = {name: 'B', hash_by: 'name'}
var labelA = 'alpha'
var labelB = 'alpha'
// add edge E from node A to node B
addEdge([propA, labelA], [propE, labelE], [propB, labelB]).then(KB.log)
// {"results":[{"columns":["e"],"data":[{"row":[{"created_when":1452825908415,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"tester","hash":"lexicography","updated_when":1452884323471}]}]},{"columns":["e"],"data":[{"row":[{"created_when":1452884323471,"name":"lexicography","updated_by":"bot","hash_by":"name","created_by":"bot","hash":"lexicography","updated_when":1452884323471}]}]}],"errors":[]}
// The edge labeled 'next' is added/updated to KB.
Constraints only for propE, required Label for edge E. No constraints or requirements for nodes A and B.
// addEdge([propE, labelE], [labelA], [propB, labelB]).then(KB.log)
// {"results":[{"columns":["e"],"data":[{"row":[{"created_when":1452825908415,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"tester","hash":"lexicography","updated_when":1453259876938}]},{"row":[{"created_when":1453259876938,"name":"lexicography","updated_by":"bot","hash_by":"name","created_by":"bot","hash":"lexicography","updated_when":1453259876938}]},{"row":[{"created_when":1453259876938,"name":"lexicography","updated_by":"bot","hash_by":"name","created_by":"bot","hash":"lexicography","updated_when":1453259876938}]}]}],"errors":[]}
// batch edge query by array of pairs
addEdge(
[ [propA, labelA], [propE, labelE], [propB, labelB] ],
[ [propA, labelA], [propE, labelE2], [propB, labelB] ]
).then(KB.log)
// equivalently
addEdge([
[ [propA, labelA], [propE, labelE], [propB, labelB] ],
[ [propA, labelA], [propE, labelE2], [propB, labelB] ]
]).then(KB.log)
// {"results":[{"columns":["e"],"data":[{"row":[{"created_when":1452825908415,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"tester","hash":"lexicography","updated_when":1452884568091}]}]},{"columns":["e"],"data":[{"row":[{"created_when":1452884323471,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"bot","hash":"lexicography","updated_when":1452884568091}]}]}],"errors":[]}
// edge 'next' is updated, edge 'after' is added
shorthand for edge with multiple labels but same prop
addEdge([propA, labelA], [propE, labelEArr], [propB, labelB]).then(KB.log)
// {"results":[{"columns":["e"],"data":[{"row":[{"created_when":1452825908415,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"tester","hash":"lexicography","updated_when":1452884620930}]}]},{"columns":["e"],"data":[{"row":[{"created_when":1452884323471,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"bot","hash":"lexicography","updated_when":1452884620930}]}]}],"errors":[]}
Get edge(s) from neo4j with propLabel of nodes A -> B with the edge E. The propLabel for A, B and E is an array of a optional non-empty JSON prop (doesn't have to satisfy KB constraints), and a (optional for A,B; required for E) Label string or array.
Kind: global variable
Returns: Promise
- From the query.
Param | Type | Description |
---|---|---|
single_query, | * |
As (fn, *, *, ...), e.g. (fn, propLabelA, propLabelE, propLabelB) |
multi_queries | * |
As (fn, [*], [*], [*]...), e.g. (fn, [propLabelA0, propLabelE0, propLabelB0], [propLabelA1, propLabelE1, propLabelB1], ...) |
multi_queries_one_array | * |
As (fn, [[*], [*], [*]...]), e.g. (fn, [[propLabelA0, propLabelE0, propLabelB0], [propLabelA1, propLabelE1, propLabelB1], ...]) |
Example
var propE = {name: 'lexicography', hash_by: 'name'}
var labelE = 'next'
var labelE2 = 'after'
var labelEArr = ['next', 'after']
var propA = {name: 'A', hash_by: 'name'}
var propB = {name: 'B', hash_by: 'name'}
var labelA = 'alpha'
var labelB = 'alpha'
// The below are equivalent for the added edge above, and show that propLabelA and propLabelB are optional.
getEdge(
[propE, labelE]
).then(KB.log)
getEdge(
[propE, labelE],
[propA, labelA]
).then(KB.log)
// label is required for E; The rest are optional.
getEdge(
[labelE],
[propA]
).then(KB.log)
// {"results":[{"columns":["e"],"data":[{"row":[{"created_when":1453143189686,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"bot","hash":"lexicography","updated_when":1453143189686}]},{"row":[{"created_when":1452825908415,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"tester","hash":"lexicography","updated_when":1453259876938}]}]}],"errors":[]}
getEdge(
[propE, labelE],
[propA, labelA],
[propB, labelB]
).then(KB.log)
// {"results":[{"columns":["e"],"data":[{"row":[{"created_when":1452825908415,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"tester","hash":"lexicography","updated_when":1452885949550}]}]}],"errors":[]}
// the following are equivalent: batch edge query
getEdge(
[[propE, labelE] ],
[[propE, labelE2] ]
).then(KB.log)
getEdge([
[[propE, labelE] ],
[[propE, labelE2] ]
]).then(KB.log)
// {"results":[{"columns":["e"],"data":[{"row":[{"created_when":1452825908415,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"tester","hash":"lexicography","updated_when":1452885949550}]}]},{"columns":["e"],"data":[{"row":[{"created_when":1452884323471,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"bot","hash":"lexicography","updated_when":1452885949550}]}]}],"errors":[]}
// shorthand: pull multiple edges using multiple labels, and same prop.
getEdge(
[propE, labelEArr]
).then(KB.log)
// {"results":[{"columns":["e"],"data":[{"row":[{"created_when":1452825908415,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"tester","hash":"lexicography","updated_when":1452885949550}]}]},{"columns":["e"],"data":[{"row":[{"created_when":1452884323471,"updated_by":"bot","name":"lexicography","hash_by":"name","created_by":"bot","hash":"lexicography","updated_when":1452885949550}]}]}],"errors":[]}
Get graph and do whatever u want with the search results: filter, RETURN, DELETE, DETACH DELETE. Graph: node(s) and edge(s) from neo4j with propLabel of nodes A -> B with the edge E. The propLabel for A, B and E is an array of a optional non-empty JSON prop (doesn't have to satisfy KB constraints), and a (optional for A,B; required for E) Label string or array. This is a flexible method used for querying node(s), or optionally edge(s) and node(s). It also takes an optional WHERE filter sentence string, and a required RETURN sentence string.The resultant query string is of the form: For nodes: MATCH (a:LabelA {propA}) For nodes and edges: MATCH (a:LabelA {propA})-[e:LabelE ({propE}|*Dist)]-(b:LabelB {propB})
Kind: global variable
Returns: Promise
- From the query.
Param | Type | Description |
---|---|---|
single_query, | * |
As (fn, *, *, ...), e.g. (fn, [p, L], [p |
multi_queries | * |
As (fn, [*], [*], [*]...), e.g. (fn, [[p, L], [p |
multi_queries_one_array | * |
As (fn, [[*], [*], [*]...]), e.g. (fn, [[[p, L], [p |
Example
// a rank 1 arguments.
// Get only node(s)
get([{
name: 'A'
}, 'alpha'], 'RETURN a').then(KB.log)
// a rank 1 arguments.
// Delete node(s)
get([{
name: 'C'
}, 'alpha'], 'DETACH DELETE a').then(KB.log)
// a rank 1 arguments.
// Get nodes and edges. Note that labelE is optional now
get([{
name: 'A'
}, 'alpha'], ['*0..1'], 'RETURN b,e').then(KB.log)
// a rank 2 arguments
// Get nodes and edges
get([
[{
name: 'A'
}, 'alpha'],
['*0..1', 'next'], 'RETURN b,e'
]).then(KB.log)
// a rank 2 arguments
// Get nodes and edges. Edges can have multiple labels in query; piped
get([
[{
name: 'A'
}, 'alpha'],
['*0..1', ['next', 'xiage']], 'RETURN b,e'
]).then(KB.log)
Get graph and do whatever u want with the search results: filter, RETURN, DELETE, DETACH DELETE. Graph: node(s) and edge(s) from neo4j with propLabel of nodes A -> B with the edge E. The propLabel for A, B and E is an array of a optional non-empty JSON prop (doesn't have to satisfy KB constraints), and a (optional for A,B; required for E) Label string or array. This is a flexible method used for querying node(s), or optionally edge(s) and node(s). It also takes an optional WHERE filter sentence string, and a required RETURN sentence string.The resultant query string is of the form: For nodes: MATCH (a:LabelA {propA}) For nodes and edges: MATCH (a:LabelA {propA})-[e:LabelE ({propE}|*Dist)]-(b:LabelB {propB})
Kind: global variable
Returns: Promise
- From the query.
Param | Type | Description |
---|---|---|
single_query, | * |
As (fn, *, *, ...), e.g. (fn, [p, L], [p |
multi_queries | * |
As (fn, [*], [*], [*]...), e.g. (fn, [[p, L], [p |
multi_queries_one_array | * |
As (fn, [[*], [*], [*]...]), e.g. (fn, [[[p, L], [p |
Example
// nodes
var propA = {
name: "A",
hash_by: "name"
}
var propB = {
name: "B",
hash_by: "name"
}
KB.cons.legalize(propA)
KB.cons.legalize(propB)
// edge
var propE = {
name: "E"
}
KB.cons.legalize(propE)
// add a node
add([propA, 'test']).then(KB.log)
// [{"columns":["a"],"data":[{"row":[{"created_when":"2016-01-23T16:21:51.674Z","name":"A","updated_by":"bot","hash_by":"name","created_by":"bot","hash":"A","updated_when":"2016-01-23T16:21:51.674Z"}]}]}]
// add nodes
add([[propA, 'test'], ], [[propB, 'test'], ]).then(KB.log)
// equivalently
add([[[propA, 'test'], ], [[propB, 'test'], ]]).then(KB.log)
// [{"columns":["a"],"data":[{"row":[{"created_when":"2016-01-23T16:21:51.674Z","name":"A","updated_by":"bot","hash_by":"name","created_by":"bot","hash":"A","updated_when":"2016-01-23T16:24:14.242Z"}]}]},{"columns":["a"],"data":[{"row":[{"created_when":"2016-01-23T16:23:53.406Z","name":"B","updated_by":"bot","hash_by":"name","created_by":"bot","hash":"B","updated_when":"2016-01-23T16:24:14.244Z"}]}]}]
// must supply target node if adding edge
add([propA, 'test'], [propE, 'test_next']).then(KB.log)
// Error: You must provide propLabel for either A or A,E,B.
//add edge. Note: don't use the legalized props to search for nodes A, B, otherwise it will want to match every field including the timestamp
add([{name: 'A'}, 'test'], [propE, 'test_next'], [{name: 'B'}, 'test']).then(KB.log)
// [{"columns":["e"],"data":[{"row":[{"created_when":"2016-01-23T16:25:12.706Z","name":"E","updated_by":"bot","hash_by":"hash","created_by":"bot","hash":"test","updated_when":"2016-01-23T16:25:12.706Z"}]}]}]
// add edges
// add([[{name: 'A'}, 'test'], [propE, 'test_next'], [{name: 'B'}, 'test']],
// [[{name: 'A'}, 'test'], [propE, 'test_next_2'], [{name: 'B'}, 'test']]).then(KB.log)
// equivalently
add([{
name: 'A'
}, 'test'], [propE, ['test_next', 'test_next_2']], [{
name: 'B'
}, 'test']).then(KB.log)
// [{"columns":["e"],"data":[{"row":[{"created_when":"2016-01-23T16:25:12.706Z","name":"E","updated_by":"bot","hash_by":"hash","created_by":"bot","hash":"test","updated_when":"2016-01-23T16:41:22.955Z"}]}]},{"columns":["e"],"data":[{"row":[{"created_when":"2016-01-23T16:37:55.865Z","name":"E","updated_by":"bot","hash_by":"hash","created_by":"bot","hash":"t
Queries Neo4j with arrays of pairs of query and optional params. Is the right-composition of genStatArr and postQuery.
This is is query() method with full power, i.e. no query-string cleaning. Use responsibly.
The high level add/get methods call this internall with query-string cleaning to prevent SQL injection.
The query cleaning algorithm is (proven( based on the CFG parse tree and logic. Refer to isLegalSentence().
A sentence is a clause starting with the following query operation specifier: WHERE,SET,REMOVE,RETURN,DELETE,DETACH DELETE,SHORTESTPATH,ALLSHORTESTPATHS
The sentence also consists of variables and operators . The permissible operators by this algorithm are AND,OR,XOR,NOT,=,<>,<,>,<=,>=,IS NULL,IS NOT NULL,+,+=,=~,EXISTS,nodes,labels,keys,",",=,ORDER BY,LIMIT,COUNT,sum,avg,percentileDisc,percentileCont,stdev,stdevp,max,min,collect,DISTINCT,nodes,labels,keys
Kind: global variable
Returns: Promise
- A promise object resolved with the query results from the request module.
Param | Type | Description |
---|---|---|
......Pairs | Arrays |
Of queries and optional params. |
query | string |
A query string, if the first argument isn't an array. |
params | JSON |
The parameters JSON to the query string, if the first argument isn't an array. |
Example
Normal query string
query('MATCH (n:Alphabet) DETACH DELETE (n)')
.then(KB.log)
// => {"results":[{"columns":[],"data":[]}],"errors":[]}
// Deleted all Alphabet nodes and relations
// query with params, creates multiple nodes at once
query(
['CREATE (n:Alphabet {prop}) RETURN n',
{
prop: [{ name: 'a', num: 1}, { name: 'b', num: 2 }]
}]
).then(KB.log)
// => {"results":[{"columns":["n"],"data":[{"row":[{"num":1,"name":"a"}]},{"row":[{"num":2,"name":"b"}]}]}],"errors":[]}
// Added nodes 'a', 'b' to the graph
// multiple queries at once
query(
['CREATE (n:Alphabet {prop}) RETURN n', { prop: [{ name: 'c', num: 3}, { name: 'd', num: 4 }] }],
["MATCH (c),(d) WHERE c.name='c' AND d.name='d' CREATE UNIQUE (c)-[r:Next]->(d) RETURN r"]
).then(KB.log)
// equivalently can nest under one array
query(
[
['CREATE (n:Alphabet {prop}) RETURN n', { prop: [{ name: 'c', num: 3}, { name: 'd', num: 4 }] }],
["MATCH (c),(d) WHERE c.name='c' AND d.name='d' CREATE UNIQUE (c)-[r:Next]->(d) RETURN r"]
]
).then(KB.log)
// => {"results":[{"columns":["n"],"data":[{"row":[{"num":3,"name":"c"}]},{"row":[{"num":4,"name":"d"}]}]},{"columns":["r"],"data":[{"row":[{}]}]}],"errors":[]}
Created nodes 'c', 'd', then added an edge (c)->(d)
Generalization of literalizeProp. Parses a propDistLabel array of prop-label or dist-label into a 'literal map' string as required by neo4j error (unable to user parameter map in MATCH). On can pass the name of the identifier too. Beware: in the query-param pair, the param prop JSON must use the key 'prop_'. E.g. query param pair:
Kind: global function
Returns: string
- The result literal map string.
Param | Type | Default | Description |
---|---|---|---|
propDistLabel | Array |
Array containing pair of (JSON)prop-(string)label or (string)dist-(string)label. | |
[name] | string |
"'a'" |
Name of the identifier. |
Example
var prop = {name: 'nodeName'}
var label = 'alpha'
var labelArr = ['alpha', 'beta']
var dist = '*0..2'
// for node
'(' + literalize([prop, label]) + ')'
// => (a:alpha {name: {prop_a}.name})
// for node with multiple labels; custom identifier name 'b'
// Note that the param must use the same key 'prop_b' too
var nodeStr = '(' + literalize([prop, labelArr], 'b') + ')'
// => (b:alpha:beta {name: {prop_b}.name})
// to make the query param pair:
var query = `CREATE ${nodeStr} RETURN b`
var param = { prop_b: prop }
qpPair = [query, param]
// make the query:
KB.query(qpPair)
// for edge, custom identifier name 'e'
// Note that the param must use the same key 'prop_e' too
'[' + literalize([prop, label], 'e') + ']'
// => [e:alpha {name: {prop_e}.name}]
// for edge with dist and label, custom identifier name 'e'
'[' + literalize([dist, label], 'e') + ']'
// => [e:alpha *0..2]
A conveience method. JSON-stringify the argument, logs it, and return the string.
Kind: global function
Returns: string
- the stringified JSON.
Param | Type | Description |
---|---|---|
arg | JSON |
The JSON to be stringified. |
Transform and beautify the entire neoRes by internally calling transform(neoRes) -> array of tables -> beautifyMat on each table -> join('\n\n\n'). If neoRes has only one qRes, then returns the single transformed table.
Kind: global function
Returns: string
- The beautified neo4j result
Param | Type | Default | Description |
---|---|---|---|
neoRes | Array |
Result from Neo4j | |
fn | function | Array |
A transformer function or many of them in an array | |
[keepHead] | Boolean |
false |
To drop the header or not. |
Example
var neoRes = [{"columns":["a"],"data":[{"row":[{"slack__profile__fields ...
beautify(neoRes)
// => '```
"a"
{ "slack__profile__fields__Xf0DAVBL83__alt": "", ...
// if supply a transformer
function parseUser(userObj) {
return _.pick(userObj, ['name', 'real_name', 'id', 'email_address'])
}
beautify(neoRes, parseUser)
// => '```
a
name: alice
id: ID0000001
email_address: alice@email.com
---
name: bob
id: ID0000002
email_address: bob@email.com
---
name: slackbot
real_name: slackbot
id: USLACKBOT
email_address: null
```'
<a name="transBeautify"></a>
## transBeautify(transNeoRes, fn) ⇒ <code>string</code>
Beautify for post-transformation: beautify the transformed neoRes by internally calling array of tables -> beautifyMat on each table -> join('\n\n\n'). Note that neoRes must be transformed beforehand. This is for when transformation happens separately. If neoRes has only one qRes, then returns the single transformed table.
**Kind**: global function
**Returns**: <code>string</code> - The beautified neo4j result
| Param | Type | Description |
| --- | --- | --- |
| transNeoRes | <code>Array</code> | (Transformed) result from Neo4j |
| fn | <code>function</code> | <code>Array</code> | A transformer function or many of them in an array |
**Example**
var neoRes = [{"columns":["a"],"data":[{"row":[{"slack__profile__fields ...
var transNeoRes = transform(neoRes, parseUser)
transBeautify(transNeoRes)
// => '```
a
name: alice
id: ID0000001
email_address: alice@email.com
---
name: bob
id: ID0000002
email_address: bob@email.com
---
name: slackbot
real_name: slackbot
id: USLACKBOT
email_address: null
```'
<a name="beautifyMat"></a>
## beautifyMat(mat) ⇒ <code>string</code>
Beautify a matrix by JSON.stringify -> join('\n\n') -> join('\n\n---\n\n') to separate rows, and wrap with '```'
**Kind**: global function
**Returns**: <code>string</code> - The beautified matrix string
| Param | Type | Description |
| --- | --- | --- |
| mat | <code>Array</code> | Matrix of data to beautify |
**Example**
var mat = [[{a:1, b:{c:2}}, 0], [{a:3, b:{c:4}}, 1]]
beautifyMat(mat)
// =>
'```
{
"a": 1,
"b": {
"c": 2
}
}
0
---
{
"a": 3,
"b": {
"c": 4
}
}
1
```'
<a name="transform"></a>
## transform(neoRes, fn, [keepHead]) ⇒ <code>Array</code>
Format the entire neoRes into an array of qRes tables. Can be called multiply for sequential transformation. If neoRes has only one qRes, then returns the single transformed table.
**Kind**: global function
**Returns**: <code>Array</code> - neoRes as an array of qRes tables with transformed elements.
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| neoRes | <code>Array</code> | | Neo4j raw results, neoRes = [q0res, q1res, ...], or the transformed neoRes. |
| fn | <code>function</code> | <code>Array</code> | | A transformer function or many of them in an array |
| [keepHead] | <code>Boolean</code> | <code>false</code> | To drop the header or not. |
**Example**
```js
var neoRes = [{"columns":["a"],"data":[{"row":[{"slack__profile__fields ...
neoRes = transform(neoRes)
// => [
[{ slack__profile__fields__Xf0DAVBL83__alt: '',
...]
]
// if supply a transformer
function parseUser(userObj) {
return _.pick(userObj, ['name', 'real_name', 'id', 'email_address'])
}
// second call in sequence, keep transforming
// If neoRes has > 1 qRes table
transform(neoRes, parseUser)
// => [
[{ name: 'alice',
real_name: 'Alice Bloom',
id: 'ID0000001',
email_address: 'alice@email.com' },
... ]
]
// If neoRes has 1 qRes table, return a non-nested result
transform(neoRes, parseUser)
// => [{ name: 'alice',
real_name: 'Alice Bloom',
id: 'ID0000001',
email_address: 'alice@email.com' },
... ]
Parse a JSON object into array to ['k: v', 'k: v'], where v is attemptedly stringified.
Kind: global function
Returns: Array
- of strings like ['k: v', 'k: v']
var obj = { a: 1, b: {c:2} } parseKV(obj) // => 'a: 1\nb: {\n "c": 2\n}'
Param | Type | Description |
---|---|---|
obj | JSON |
Object to parse |
Cleanup the user object by picking out name, real_name, id, email_address.
Kind: global function
Returns: JSON
- The cleaned prop object
Param | Type | Description |
---|---|---|
userObj | JSON |
The user node property object |
Example
var user = {
"id": "ID0000001",
"name": "alice",
"email_address": "alice@email.com",
"slack": {
"id": "ID0000001",
"team_id": "TD0000001",
"name": "alice",
"deleted": false,
"presence": "away"
}
}
cleanUser(user)
// => {
// "id": "ID0000001",
// "name": "alice",
// "email_address": "alice@email.com",
// }
A beautify transformer method to parse user, picking out name, real_name, id, email_address; uses parseKV internally.
Kind: global function
Returns: string
- The parsed string of user.
Param | Type | Description |
---|---|---|
userObj | JSON |
The user node property object |
Example
var user = {
"id": "ID0000001",
"name": "alice",
"email_address": "alice@email.com",
"slack": {
"id": "ID0000001",
"team_id": "TD0000001",
"name": "alice",
"deleted": false,
"presence": "away"
}
}
parseUser(user)
// => 'name: alice
// id: ID0000001
// email_address: alice@email.com'
A beautify transformer method to parse object, picking out keys from keyArr; uses parseKV internally.
Kind: global function
Returns: Array
- The parsed string of object.
Param | Type | Description |
---|---|---|
obj | JSON |
The object |
keyArr | Array |
Of key to pick |
Helper to generate wOp for matching multiple properties to the same value.
Kind: global function
Returns: string
- wOp string.
Param | Type | Default | Description |
---|---|---|---|
propArr | Array |
Array of strings of prop, may be prepended with the subjects 'a, e, b' or not (defaulted to a.) | |
match | string |
The match operator string. | |
[boolOp] | string |
"'OR'" |
The boolean to concat these matches together. |
Example
var ws = 'WHERE ' + leftJoin(['name', 'real_name', 'a.id', 'a.email_address'], '=~ "(?i).*alice.*"')
// => WHERE a.name=~ "(?i).*alice.*" OR a.real_name=~ "(?i).*alice.*" OR a.id=~ "(?i).*alice.*" OR a.email_address=~ "(?i).*alice.*"
// note that 'name' and 'real_name' and defaulted to 'a.name' and 'a.real_name'
// changing the default operator to AND
var ws = 'WHERE' + leftJoin(['name', 'real_name', 'a.id', 'a.email_address'], '=~ "(?i).*alice.*"', 'AND')
// => WHERE a.name=~ "(?i).*alice.*" AND a.real_name=~ "(?i).*alice.*" AND a.id=~ "(?i).*alice.*" AND a.email_address=~ "(?i).*alice.*"
Generate a function to sort the rows of a matrix passed to it using _.sortBy and iteratees.
Kind: global function
Returns: function
- To sort rows in, and flatten a taken matrix.
Param | Type | Description |
---|---|---|
iteratees | function | Object | string | Array |
Of _.sortBy |
For use with transform. Generate a picker function using _.pick with a supplied iteratees.
Kind: global function
Returns: function
- That picks iteratees of its argument.
Param | Type | Description |
---|---|---|
iteratees | string | Array |
Of _.pick |
For use with transform. Generate a picker function using _.pickBy with a supplied iteratees.
Kind: global function
Returns: function
- That picks by iteratees of its argument.
Param | Type | Description |
---|---|---|
iteratees | string | Array |
Of _.pickBy |
Flatten and index a matrix of objects into formatted string. 'name' key will always appear first next to index.
Kind: global function
Returns: string
- The formatted string
Param | Type | Description |
---|---|---|
mat | Array |
Matrix of JSON objects or string. |
Example
flattenIndex([
[{
name: 'alice',
id: 'ID0000001',
email_address: 'alice@email.com'
}],
[{
name: 'bob',
id: 'ID0000002',
email_address: 'bob@email.com'
}],
[{
name: 'slackbot',
real_name: 'slackbot',
id: 'USLACKBOT',
email_address: null
}]
])
// => 0. alice
id: ID0000001
email_address: alice@email.com
1. bob
id: ID0000002
email_address: bob@email.com
2. slackbot
real_name: slackbot
id: USLACKBOT
email_address: null
flattenIndex([
[{
name: 'alice'
}],
[{
name: 'bob'
}],
[{
name: 'slackbot'
}]
])
// => 0. alice
1. bob
2. slackbot