cypher-engine
Version:
Cypher Engine to generate Cypher queries
501 lines (448 loc) • 18.2 kB
text/typescript
import { SetProperty } from '../src/models/SetProperty';
import { Property } from '../src/models/Property';
import { RelationshipDirection } from '../src/models/RelationshipDirection';
import { PathRange } from '../src/models/PathRange';
import { CypherEngine } from './../src/engine/CypherEngine'
describe('Test CypherEngine', () => {
beforeEach(() => {
});
test('GenerateQuery with empty query', () => {
const engine = new CypherEngine();
expect(engine.cypher).toBe('');
});
test('GenerateQuery with empty query on CallQuery', () => {
const engine = new CypherEngine();
engine.callQuery(new CypherEngine());
expect(engine.cypher).toBe(`CALL {${''}}`);
});
test('Match with string property filter', () => {
const engine = new CypherEngine();
const result = engine.match().node(['User'], 'n0', new Property('email', `'test@test.com'`)).returns().toString();
expect(result).toBe(`MATCH (n0:User{email:'test@test.com'})\nRETURN *`);
});
test('Match with number property filter', () => {
const engine = new CypherEngine();
const result = engine.match().node(['User'], 'n0', new Property('age', 34)).returns().toString();
expect(result).toBe(`MATCH (n0:User{age:34})\nRETURN *`);
});
test('Match with no filter', () => {
const engine = new CypherEngine();
const result = engine.match().node(['User', 'Test1'], 'n0').returns().toString();
expect(result).toBe(`MATCH (n0:User:Test1)\nRETURN *`);
});
test('Match with no filter but with limit', () => {
const engine = new CypherEngine();
const result = engine.match().node(['User', 'Test1'], 'n0').returns().limit(100).toString();
expect(result).toBe(`MATCH (n0:User:Test1)\nRETURN * LIMIT 100`);
});
test('OPTIONAL match with no filter', () => {
const engine = new CypherEngine();
const result = engine.optionalMatch().node(['User', 'Test1'], 'n0').returns().toString();
expect(result).toBe(`OPTIONAL MATCH (n0:User:Test1)\nRETURN *`);
});
test('Two MATCH with two property filters', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['User'], 'n0', new Property('email', `'test@test.com'`), new Property('identity', 123))
.match()
.node(['Company'], 'n1', new Property('name', `'test'`), new Property('identity', `'test'`))
.returns()
.toString();
expect(result).toBe(
`MATCH (n0:User{email:'test@test.com', identity:123})\nMATCH (n1:Company{name:'test', identity:'test'})\nRETURN *`
);
});
test('MATCH RELATES to NODE with at least 3 nodes in between', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['User'], 'n0', new Property('email', `'test@test.com'`))
.relates(RelationshipDirection.right, ['works_for'], undefined, new PathRange(3, undefined), new Property('years', 5))
.node(['Company'], 'n1', new Property('name', `'test'`))
.returns()
.toString();
expect(result).toBe(`MATCH (n0:User{email:'test@test.com'})\n-[:works_for*3..{years:5}]->(n1:Company{name:'test'})\nRETURN *`);
});
test('MATCH RELATES to NODE WITH all', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['User'], 'n0', new Property('email', `'test@test.com'`))
.relates(RelationshipDirection.right, ['works_for'], undefined, undefined, new Property('years', 5))
.node(['Company'], 'n1', new Property('name', `'test'`))
.with('n0', 'n1')
.returns()
.toString();
expect(result).toBe(
`MATCH (n0:User{email:'test@test.com'})\n-[:works_for{years:5}]->(n1:Company{name:'test'})\nWITH n0,n1\nRETURN *`
);
});
test('MATCH RELATES to NODE Variable undirected', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['User'], 'u')
.relates(RelationshipDirection.undirected)
.node([], 'company')
.returns('u')
.toString();
expect(result).toBe(
`MATCH (u:User)\n--(company)\nRETURN u`
);
});
test('MATCH RELATES to NODE WITH DELETE both', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['User'], 'n0', new Property('email', `'test@test.com'`))
.relates(RelationshipDirection.left, ['works_for'], undefined, undefined, new Property('years', 5))
.node(['Company'], 'n1', new Property('name', `'test'`))
.delete('n0', 'n1')
.returns()
.toString();
expect(result).toBe(
`MATCH (n0:User{email:'test@test.com'})\n<-[:works_for{years:5}]-(n1:Company{name:'test'})\nDELETE n0,n1\nRETURN *`
);
});
test('MATCH RELATES to NODE WITH DETACH DELETE both', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['User'], 'n0', new Property('email', `'test@test.com'`))
.relates(RelationshipDirection.undirected, ['works_for'], undefined, undefined, new Property('years', 5))
.node(['Company'], 'n1', new Property('name', `'test'`))
.detach()
.delete('n0', 'n1')
.returns()
.toString();
expect(result).toBe(
`MATCH (n0:User{email:'test@test.com'})\n-[:works_for{years:5}]-(n1:Company{name:'test'})\nDETACH DELETE n0,n1\nRETURN *`
);
});
test('SET with one Property', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Person'], 'n0', new Property('identifier', '$identifier'))
.set(new SetProperty('n0', new Property('name', '$name'))).cypher;
expect(result).toBe(`MATCH (n0:Person{identifier:$identifier})\nSET n0.name = $name`);
});
test('MATCH WHERE', () => {
const engine = new CypherEngine();
const result = engine.match().node(['User'], 'n0').where().variable('n0', 'name').equals().value('$name').returns().toString();
expect(result).toBe(`MATCH (n0:User)\nWHERE n0.name = $name RETURN *`);
});
test('MATCH WHERE AND CONTAINS NOT', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['User'], 'n0')
.where()
.variable('n0', 'name')
.equals()
.value('$name')
.and()
.not()
.variable('n0', 'email')
.contains()
.value(`'test'`)
.returns()
.toString();
expect(result).toBe(`MATCH (n0:User)\nWHERE n0.name = $name AND NOT n0.email CONTAINS 'test' RETURN *`);
});
test('MATCH WHERE OR CONTAINS', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['User'], 'n0')
.where()
.variable('n0', 'name')
.equals()
.value('$name')
.or()
.variable('n0', 'email')
.contains()
.value(`'test'`)
.returns()
.toString();
expect(result).toBe(`MATCH (n0:User)\nWHERE n0.name = $name OR n0.email CONTAINS 'test' RETURN *`);
});
test('MATCH SET with two Property', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Person'], 'n0', new Property('identifier', '$identifier'))
.set(new SetProperty('n0', new Property('name', '$name')), new SetProperty('n0', new Property('birthday', '$birthday'))).cypher;
expect(result).toBe(`MATCH (n0:Person{identifier:$identifier})\nSET n0.name = $name, n0.birthday = $birthday`);
});
test('MERGE with one property return it', () => {
const engine = new CypherEngine();
const result = engine.merge().node(['Person'], 'n0', new Property('identifier', '$identifier')).returns('n0').toString();
expect(result).toBe(`MERGE (n0:Person{identifier:$identifier})\nRETURN n0`);
});
test('CREATE with one property return it', () => {
const engine = new CypherEngine();
const result = engine.create().node(['Person'], 'n0', new Property('identifier', '$identifier')).returns('n0').toString();
expect(result).toBe(`CREATE (n0:Person{identifier:$identifier})\nRETURN n0`);
});
test('MATCH with Label using declaredNode', () => {
const engine = new CypherEngine();
const result = engine.match().node(['Person'], 'n0').where().declaredNode('n0', 'Natural').returns('n0').toString();
expect(result).toBe(`MATCH (n0:Person)\nWHERE (n0:Natural)\nRETURN n0`);
});
test('MATCH with Label using declaredNode fails', () => {
const engine = new CypherEngine();
try {
const result = engine.match().node(['Person'], 'n0').where().declaredNode('n', 'Natural').returns('n0').toString();
expect(result).toBe(`MATCH (n0:Person)\nWHERE (n0:Natural)\nRETURN n0`);
} catch (error) {
expect(error.message).toBe('Could not find node n');
}
});
test('MATCH WHERE exists', () => {
const engine = new CypherEngine();
const result = engine.match().node(['Person'], 'n0', new Property('identifier', '$identifier')).where().exists('n0', 'name').with().returns('n0').toString();
expect(result).toBe(`MATCH (n0:Person{identifier:$identifier})\nWHERE exists(n0.name)\nWITH *\nRETURN n0`);
});
test('MATCH WITH Order', () => {
const engine = new CypherEngine();
const sort: Array<{ prop: string; asc: boolean }> = [
{ prop: 'name', asc: true },
{ prop: 'age', asc: true }
];
const result = engine.match().node(['Person'], 'n0').match().node(['Person'], 'n01').orderBy(['n0', 'n1'], sort).returns().toString();
expect(result).toBe(`MATCH (n0:Person)\nMATCH (n01:Person)\nORDER BY n0.name , n0.age , n1.name , n1.age\nRETURN *`);
});
test('Condtional true', () => {
const engine = new CypherEngine();
const result = engine.match().node(['Person'], 'n0').match().node(['Person'], 'n01').conditional(true, `WHERE n0.name = 'Test'`, '').returns().toString();
expect(result).toBe(`MATCH (n0:Person)\nMATCH (n01:Person)\nWHERE n0.name = 'Test' RETURN *`);
});
test('Condtional false', () => {
const engine = new CypherEngine();
const result = engine.match().node(['Person'], 'n0').match().node(['Person'], 'n01').conditional(false, `WHERE n0.name = 'Test'`, '').returns().toString();
expect(result).toBe(`MATCH (n0:Person)\nMATCH (n01:Person)\n RETURN *`);
});
test('CondtionalFunc true', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Person'], 'n0')
.match()
.node(['Person'], 'n01')
.conditionalFunc(
true,
(e: any) => {
return e.where().variable('n0', 'name').equals().value(`'Test'`);
},
(e: any) => e
)
.returns()
.toString();
expect(result).toBe(`MATCH (n0:Person)\nMATCH (n01:Person)\nWHERE n0.name = 'Test' RETURN *`);
});
test('CondtionalFunc false', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Person'], 'n0')
.match()
.node(['Person'], 'n01')
.conditionalFunc(
false,
(e: any) => {
return e.where().variable('n0', 'name').equals().value(`'Test'`);
},
(e: any) => e
)
.returns()
.toString();
expect(result).toBe(`MATCH (n0:Person)\nMATCH (n01:Person)\nRETURN *`);
});
test('MATCH WITH Order', () => {
const engine = new CypherEngine();
const sort: Array<{ prop: string; asc: boolean }> = [
{ prop: 'name', asc: true },
{ prop: 'age', asc: true }
];
const result = engine.match().node(['Person'], 'n0').match().node(['Person'], 'n01').orderBy(['n0', 'n1'], sort).returns().toString();
expect(result).toBe(`MATCH (n0:Person)\nMATCH (n01:Person)\nORDER BY n0.name , n0.age , n1.name , n1.age\nRETURN *`);
});
test('MATCH WITH Order but no parms', () => {
const engine = new CypherEngine();
const result = engine.match().node(['Person'], 'n0').match().node(['Person'], 'n01').orderBy(['n0', 'n1']).returns().toString();
expect(result).toBe(`MATCH (n0:Person)\nMATCH (n01:Person)\nRETURN *`);
});
test('MATCH WHERE Starts with', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Person'], 'n0', new Property('identifier', '$identifier'))
.where()
.variable('n0', 'name')
.startsWith()
.value(`'1a'`)
.with()
.returns('n0')
.toString();
expect(result).toBe(`MATCH (n0:Person{identifier:$identifier})\nWHERE n0.name STARTS WITH '1a' WITH *\nRETURN n0`);
});
test('MATCH WHERE ends with', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Person'], 'n0', new Property('identifier', '$identifier'))
.where()
.variable('n0', 'name')
.endsWith()
.value(`'1a'`)
.with()
.returns('n0')
.toString();
expect(result).toBe(`MATCH (n0:Person{identifier:$identifier})\nWHERE n0.name ENDS WITH '1a' WITH *\nRETURN n0`);
});
test('MATCH WHERE greater than with', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Person'], 'n0', new Property('identifier', '$identifier'))
.where()
.variable('n0', 'age')
.greaterThan()
.value(5)
.with()
.returns('n0')
.toString();
expect(result).toBe(`MATCH (n0:Person{identifier:$identifier})\nWHERE n0.age > 5 WITH *\nRETURN n0`);
});
test('MATCH WHERE less than with', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Person'], 'n0', new Property('identifier', '$identifier'))
.where()
.variable('n0', 'age')
.lessThan()
.value(5)
.with()
.returns('n0')
.toString();
expect(result).toBe(`MATCH (n0:Person{identifier:$identifier})\nWHERE n0.age < 5 WITH *\nRETURN n0`);
});
test('MATCH WHERE regular expression with', () => {
const engine = new CypherEngine();
const result = engine.match().node(['Person'], 'n0').where().variable('n0', 'name').regularExpression().value(`'Tim.*'`).with().returns().toString();
expect(result).toBe(`MATCH (n0:Person)\nWHERE n0.name =~ 'Tim.*' WITH *\nRETURN *`);
});
test('MATCH WHERE IS NOT NULL', () => {
const engine = new CypherEngine();
const result = engine.match().node(['Person'], 'n0').where().variable('n0').is().not().null().returns().toString();
expect(result).toBe(`MATCH (n0:Person)\nWHERE (n0) IS NOT NULL RETURN *`);
});
test('MATCH WHERE IN array', () => {
const engine = new CypherEngine();
const result = engine.match().node(['Person'], 'n0').where().variable('n0').in().value([1, 2, 3]).returns().toString();
expect(result).toBe(`MATCH (n0:Person)\nWHERE (n0) IN [1,2,3] RETURN *`);
});
test('CREATE with UNWIND', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Person'], 'n0')
.unwind()
.value('{modelItems} as modelItem')
.merge()
.variable('n0')
.relates(RelationshipDirection.left, ['created_by'])
.node(['ModelItem'], 'mi', new Property('name', 'modelItem.name'))
.returns()
.toString();
expect(result).toBe(
`MATCH (n0:Person)\nUNWIND {modelItems} as modelItem MERGE (n0) <-[:created_by]-(mi:ModelItem{name:modelItem.name})\nRETURN *`
);
});
test('Call', () => {
const engine = new CypherEngine();
const result = engine.call('apoc.test').cypher;
expect(result).toBe(`CALL apoc.test`);
});
test('MERGE ON CREATE ON MATCH', () => {
const engine = new CypherEngine();
const result = engine
.merge()
.node(['Person'], 'n0', new Property('name', `'Keanu Reeves'`))
.onCreate()
.set(new SetProperty('n0', new Property('created', 123)))
.onMatch()
.set()
.variable('n0', 'modified')
.equals()
.value(123)
.returns()
.toString();
expect(result).toBe(`MERGE (n0:Person{name:'Keanu Reeves'})\nON CREATE SET n0.created = 123\nON MATCH SET\nn0.modified = 123 RETURN *`);
});
test('MATCH WITH SKIP AND LIMIT', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Creation'], 'n0')
.returns('n0')
.skip(10)
.limit(10)
.toString();
expect(result).toBe(`MATCH (n0:Creation)\nRETURN n0 SKIP 10 LIMIT 10`);
});
test('MATCH WITH boolean CASE', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Creation'], 'one')
.match()
.node(['View'], 'two')
.case()
.value('one.condition')
.when()
.value('true')
.then()
.returns('one')
.when()
.value('false')
.then()
.returns('two')
.toString();
expect(result).toBe(
`MATCH (one:Creation)\nMATCH (two:View)\nCASE one.condition WHEN true THEN RETURN one WHEN false THEN RETURN two`
);
});
test('MATCH WITH string CASE', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Creation'], 'one')
.match()
.node(['View'], 'two')
.case()
.value('one.condition')
.when()
.value('one')
.then()
.returns('one')
.else()
.returns('two')
.toString();
expect(result).toBe(`MATCH (one:Creation)\nMATCH (two:View)\nCASE one.condition WHEN one THEN RETURN one ELSE RETURN two`);
});
test('REMOVE', () => {
const engine = new CypherEngine();
const result = engine
.match()
.node(['Creation'], 'n')
.remove()
.value('n:Creation')
.returns()
.toString();
expect(result).toBe(`MATCH (n:Creation)\nREMOVE n:Creation RETURN *`);
});
});