Skip to content

Latest commit

 

History

History
1003 lines (852 loc) · 44.2 KB

API.md

File metadata and controls

1003 lines (852 loc) · 44.2 KB

Members

cons : Object

The constrain object for KB. has many convenience methods. Call via KB.cons.

addNodePromise

Adds node(s) to neo4j with a required JSON prop satisfying the KB constraints, and an optional Label string or array.

getNodePromise

Get node(s) from neo4j with JSON prop, and optional Label.

addEdgePromise

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.

getEdgePromise

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.

getPromise

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})

addPromise

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})

queryPromise

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

Functions

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.

cons : Object

The constrain object for KB. has many convenience methods. Call via KB.cons.

Kind: global variable

cons.now() ⇒ string

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'

cons.legalize(prop, [res], [hash_by]) ⇒ JSON

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' }

cons.illegalize(prop) ⇒ JSON

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' }

cons.isLegalSentence(str) ⇒ Boolean

Check if the string is a RETURN operation string and is legal, to prevent SQL injection. Algo (str):

  1. replace "(" and ")" globally
  2. split by globally, trim spaces, into variables
  3. 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.

addNode ⇒ Promise

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.

getNode ⇒ Promise

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":[]}

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.

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":[]}

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.

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 ⇒ 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})

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)

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})

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

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

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:

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]

log(arg) ⇒ string

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.

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.

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> &#124; <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> &#124; <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' },
 ... ]

parseKV(obj) ⇒ Array

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

cleanUser(userObj) ⇒ JSON

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",
// }

parseUser(userObj) ⇒ string

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'

parseObj(obj, keyArr) ⇒ Array

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

leftJoin(propArr, match, [boolOp]) ⇒ string

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.*"

sorter(iteratees) ⇒ function

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

picker(iteratees) ⇒ function

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

pickerBy(iteratees) ⇒ function

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

flattenIndex(mat) ⇒ string

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