@douglasgabr/cypher-builder
Advanced tools
Comparing version 2.0.0 to 2.1.0
@@ -0,1 +1,18 @@ | ||
# 2.1.0 / 2021-10-09 | ||
- fix(patterns): don't add brackets to empty relationship patterns (`.relationship()`) | ||
- before: `-[]-` | ||
- after: `--` | ||
- feat(clauses): add label predicates to where: | ||
```typescript | ||
builder.where((w) => { | ||
w.andLabel('user', 'User').andLabel('admin', ['User', 'Admin']); | ||
}); | ||
``` | ||
``` | ||
WHERE user:User AND admin:User:Admin | ||
``` | ||
# 2.0.0 / 2021-10-07 | ||
@@ -2,0 +19,0 @@ |
import { StringBuilder } from '../types/string-builder'; | ||
import { ParametersBag } from '../parameters/ParametersBag'; | ||
import { PatternBuilder } from '../patterns/PatternBuilder'; | ||
import { CypherBuilderNodes } from '..'; | ||
declare type Comparisons = '=' | '=~' | '>' | '>=' | '<' | '<=' | '<>' | 'IN' | 'STARTS WITH' | 'ENDS WITH' | 'CONTAINS'; | ||
declare type NullComparison = 'IS NULL'; | ||
declare type NullComparison = 'IS NULL' | 'IS NOT NULL'; | ||
/** | ||
@@ -57,2 +58,8 @@ * @see [WHERE](https://neo4j.com/docs/cypher-manual/current/clauses/where/) | ||
xorNotLiteral(field: string, comparator: Comparisons, value: string): this; | ||
andLabel<Label extends keyof CypherBuilderNodes & string>(alias: string, labels: Label | Label[]): this; | ||
andNotLabel<Label extends keyof CypherBuilderNodes & string>(alias: string, labels: Label | Label[]): this; | ||
orLabel<Label extends keyof CypherBuilderNodes & string>(alias: string, labels: Label | Label[]): this; | ||
orNotLabel<Label extends keyof CypherBuilderNodes & string>(alias: string, labels: Label | Label[]): this; | ||
xorLabel<Label extends keyof CypherBuilderNodes & string>(alias: string, labels: Label | Label[]): this; | ||
xorNotLabel<Label extends keyof CypherBuilderNodes & string>(alias: string, labels: Label | Label[]): this; | ||
} | ||
@@ -59,0 +66,0 @@ export declare class WhereClauseStringBuilder extends WhereClause implements StringBuilder { |
@@ -7,3 +7,3 @@ "use strict"; | ||
}; | ||
var _WhereClause_instances, _WhereClause_addPredicate, _WhereClause_addWhere, _WhereClause_addPattern, _WhereClause_add, _WhereClause_addLiteral; | ||
var _WhereClause_instances, _WhereClause_addPredicate, _WhereClause_addWhere, _WhereClause_addPattern, _WhereClause_add, _WhereClause_addLiteral, _WhereClause_addLabel; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -13,3 +13,3 @@ exports.WhereClauseStringBuilder = exports.WhereClause = void 0; | ||
const PatternBuilder_1 = require("../patterns/PatternBuilder"); | ||
const nullComparison = 'IS NULL'; | ||
const nullComparisons = ['IS NULL', 'IS NOT NULL']; | ||
class Comparator { | ||
@@ -34,2 +34,11 @@ constructor(comparator, field, value) { | ||
} | ||
class LabelComparator { | ||
constructor(alias, labels) { | ||
this.alias = alias; | ||
this.labels = labels; | ||
} | ||
build() { | ||
return `${this.alias}:${this.labels.join(':')}`; | ||
} | ||
} | ||
class WherePredicate { | ||
@@ -134,2 +143,20 @@ constructor(prefix, predicate, not, shouldAddPrefix, shouldAddParenthesis) { | ||
} | ||
andLabel(alias, labels) { | ||
return __classPrivateFieldGet(this, _WhereClause_instances, "m", _WhereClause_addLabel).call(this, 'AND', false, alias, labels); | ||
} | ||
andNotLabel(alias, labels) { | ||
return __classPrivateFieldGet(this, _WhereClause_instances, "m", _WhereClause_addLabel).call(this, 'AND', true, alias, labels); | ||
} | ||
orLabel(alias, labels) { | ||
return __classPrivateFieldGet(this, _WhereClause_instances, "m", _WhereClause_addLabel).call(this, 'OR', false, alias, labels); | ||
} | ||
orNotLabel(alias, labels) { | ||
return __classPrivateFieldGet(this, _WhereClause_instances, "m", _WhereClause_addLabel).call(this, 'OR', true, alias, labels); | ||
} | ||
xorLabel(alias, labels) { | ||
return __classPrivateFieldGet(this, _WhereClause_instances, "m", _WhereClause_addLabel).call(this, 'XOR', false, alias, labels); | ||
} | ||
xorNotLabel(alias, labels) { | ||
return __classPrivateFieldGet(this, _WhereClause_instances, "m", _WhereClause_addLabel).call(this, 'XOR', true, alias, labels); | ||
} | ||
} | ||
@@ -179,2 +206,5 @@ exports.WhereClause = WhereClause; | ||
return __classPrivateFieldGet(this, _WhereClause_instances, "m", _WhereClause_addPredicate).call(this, prefix, comp, not); | ||
}, _WhereClause_addLabel = function _WhereClause_addLabel(prefix, not, alias, labels) { | ||
__classPrivateFieldGet(this, _WhereClause_instances, "m", _WhereClause_addPredicate).call(this, prefix, new LabelComparator(alias, typeof labels === 'string' ? [labels] : labels), not); | ||
return this; | ||
}; | ||
@@ -194,3 +224,4 @@ class WhereClauseStringBuilder extends WhereClause { | ||
function isNullComparator(comparator) { | ||
return nullComparison === comparator; | ||
return (typeof comparator === 'string' && | ||
nullComparisons.includes(comparator)); | ||
} |
@@ -8,2 +8,3 @@ import { StringBuilder } from '../types/string-builder'; | ||
export declare class Relationship implements StringBuilder { | ||
#private; | ||
private direction; | ||
@@ -10,0 +11,0 @@ private alias; |
"use strict"; | ||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); | ||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); | ||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); | ||
}; | ||
var _Relationship_instances, _Relationship_buildArrowsString, _Relationship_buildLimitsString, _Relationship_buildBracketsString, _Relationship_buildPropertiesString, _Relationship_buildTypesString; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -9,2 +15,3 @@ exports.Relationship = void 0; | ||
constructor(direction, alias, types, properties, limits) { | ||
_Relationship_instances.add(this); | ||
this.direction = direction !== null && direction !== void 0 ? direction : 'either'; | ||
@@ -17,38 +24,54 @@ this.alias = alias !== null && alias !== void 0 ? alias : ''; | ||
build() { | ||
let relationshipString = ''; | ||
if (this.direction === 'in') { | ||
relationshipString += '<'; | ||
const aliasString = this.alias; | ||
const typesString = __classPrivateFieldGet(this, _Relationship_instances, "m", _Relationship_buildTypesString).call(this); | ||
const limitsString = __classPrivateFieldGet(this, _Relationship_instances, "m", _Relationship_buildLimitsString).call(this); | ||
const propertiesString = __classPrivateFieldGet(this, _Relationship_instances, "m", _Relationship_buildPropertiesString).call(this); | ||
const [leftBracket, rightBracket] = __classPrivateFieldGet(this, _Relationship_instances, "m", _Relationship_buildBracketsString).call(this, aliasString, typesString, limitsString, propertiesString); | ||
const [leftArrow, rightArrow] = __classPrivateFieldGet(this, _Relationship_instances, "m", _Relationship_buildArrowsString).call(this); | ||
return `${leftArrow}${leftBracket}${aliasString}${typesString}${limitsString}${propertiesString}${rightBracket}${rightArrow}`; | ||
} | ||
} | ||
exports.Relationship = Relationship; | ||
_Relationship_instances = new WeakSet(), _Relationship_buildArrowsString = function _Relationship_buildArrowsString() { | ||
switch (this.direction) { | ||
case 'in': | ||
return ['<-', '-']; | ||
case 'out': | ||
return ['-', '->']; | ||
case 'either': | ||
default: | ||
return ['-', '-']; | ||
} | ||
}, _Relationship_buildLimitsString = function _Relationship_buildLimitsString() { | ||
let limitsString = ''; | ||
if (this.limits) { | ||
limitsString += '*'; | ||
if (typeof this.limits === 'number') { | ||
limitsString += this.limits.toString(); | ||
} | ||
relationshipString += `-[${this.alias}`; | ||
if (this.types.length > 0) { | ||
relationshipString += `:${this.types.join('|')}`; | ||
} | ||
if (this.limits) { | ||
relationshipString += '*'; | ||
if (typeof this.limits === 'number') { | ||
relationshipString += this.limits.toString(); | ||
else if (Array.isArray(this.limits)) { | ||
const [start, end] = this.limits; | ||
if (typeof start === 'number') { | ||
limitsString += start.toString(); | ||
} | ||
else if (Array.isArray(this.limits)) { | ||
const [start, end] = this.limits; | ||
if (typeof start === 'number') { | ||
relationshipString += start.toString(); | ||
} | ||
relationshipString += '..'; | ||
if (typeof end === 'number') { | ||
relationshipString += end.toString(); | ||
} | ||
limitsString += '..'; | ||
if (typeof end === 'number') { | ||
limitsString += end.toString(); | ||
} | ||
} | ||
if (this.properties) { | ||
relationshipString += ` { ${Object.entries(this.properties) | ||
.map(([label, value]) => `${label}: ${value}`) | ||
.join(', ')} }`; | ||
} | ||
relationshipString += ']-'; | ||
if (this.direction === 'out') { | ||
relationshipString += '>'; | ||
} | ||
return relationshipString; | ||
} | ||
} | ||
exports.Relationship = Relationship; | ||
return limitsString; | ||
}, _Relationship_buildBracketsString = function _Relationship_buildBracketsString(aliasString, typesString, limitsString, propertiesString) { | ||
return aliasString || typesString || limitsString || propertiesString | ||
? ['[', ']'] | ||
: ['', '']; | ||
}, _Relationship_buildPropertiesString = function _Relationship_buildPropertiesString() { | ||
if (this.properties) { | ||
return ` { ${Object.entries(this.properties) | ||
.map(([label, value]) => `${label}: ${value}`) | ||
.join(', ')} }`; | ||
} | ||
return ''; | ||
}, _Relationship_buildTypesString = function _Relationship_buildTypesString() { | ||
return this.types.length > 0 ? `:${this.types.join('|')}` : ''; | ||
}; |
{ | ||
"name": "@douglasgabr/cypher-builder", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "Fluent CQL builder for Neo4j", | ||
@@ -29,2 +29,4 @@ "main": "dist/index.js", | ||
"devDependencies": { | ||
"@commitlint/cli": "^13.2.1", | ||
"@commitlint/config-conventional": "^13.2.0", | ||
"@types/jest": "^27.0.0", | ||
@@ -35,2 +37,3 @@ "@typescript-eslint/eslint-plugin": "^4.28.2", | ||
"eslint-config-prettier": "^8.3.0", | ||
"husky": "^7.0.2", | ||
"jest": "^27.0.6", | ||
@@ -37,0 +40,0 @@ "jest-extended": "^0.11.5", |
@@ -23,12 +23,51 @@ # cypher-builder | ||
### Type inference (required for typescript projects) | ||
First, you must create a `*.d.ts` file in your project, in order to type the possible nodes, relationships and their properties. | ||
```typescript | ||
// neo4j-types.d.ts | ||
import '@douglasgabr/cypher-builder'; | ||
declare module '@douglasgabr/cypher-builder' { | ||
export interface CypherBuilderNodes { | ||
User: { | ||
id: string; | ||
}; | ||
} | ||
export interface CypherBuilderRelationships { | ||
KNOWS: { | ||
level: 'friendship' | 'colleague'; | ||
}; | ||
} | ||
} | ||
``` | ||
That will enable your IDE (tested only in VSCode) to suggest values for your node labels, relationship types and its properties. | ||
**Node Label suggestion:** | ||
 | ||
**Node Properties suggestion:** | ||
 | ||
**Relationship Type suggestion:** | ||
 | ||
### Example | ||
```typescript | ||
import { Builder, RelationshipDirection } from '@douglasgabr/cypher-builder'; | ||
const queryBuilder = new Builder() | ||
.match((match) => | ||
.match((match) => { | ||
match | ||
.node('person', 'Person', { name: 'Alice' }) | ||
.relationship(RelationshipDirection.Either, 'KNOWS') | ||
.relationship('either', 'KNOWS') | ||
.node('friend', 'Person'), | ||
) | ||
}) | ||
.where((where) => where.and('friend.age', '>=', 18)) | ||
@@ -42,4 +81,4 @@ .return('person', 'friend'); | ||
``` | ||
MATCH (alice:Person{ name: $param1 })-[:KNOWS]-(friend:Person) | ||
WHERE friend.age >= $param2 | ||
MATCH (person:Person{ name: $person_name })-[:KNOWS]-(friend:Person) | ||
WHERE friend.age >= $friend_age | ||
RETURN person, friend | ||
@@ -52,5 +91,5 @@ ``` | ||
{ | ||
param1: 'Alice', | ||
param2: 18 | ||
person_name: 'Alice', | ||
friend_age: 18 | ||
} | ||
``` |
70059
1576
93
13