DGQL
Next Generation Application Specific Graph Query Language.
Dynamic GraphQL
Dans Graph Query Language
Dynamic Graph Query Language
Packages
-
Language - The Javascript Implementation For The DGQL Language
-
Client - Executable Client for DGQL Queries
-
Builder - DGQL Query Builder
-
Playground - Graph app Developer playground to issue DGQL queries
Documentation
- Documentation
- TCK tests
- DGQL Recipes
- Blog -
⚠ In progress
Examples
See Examples
Prerequisites
GraphQL can be separated into two sections; language & execution. To truly grasp this implementation one should first remove themselves from the conventional execution paradigms, say using Apollo Server, and look towards the pre-made & rich tooling surrounding the language. DGQL completely breaks the rules
What
This implementation, at its core, is a transpiler from GraphQL to Cypher and simply concerns itself with the AST produced from a given selection set. Traversal of the AST enables the translator to generate Cypher from picking up on Client Directives.
Given the below DGQL Query;
{
MATCH {
user @node(label: User) @where(name: "Dan") {
PROJECT {
id
name
posts @edge(type: AUTHORED, direction: OUT) @node(label: Post) {
title
}
}
}
}
RETURN {
user
}
}
Or equivalent DGQL Builder
const { Builder, node, property, edge } = require("@dgql/builder");
const builder = new Builder();
const [dgql, variables] = builder
.match({
user: node({ label: "User" })
.where({ name: property({ equal: "Dan" }) })
.project({
id: property(),
name: property(),
posts: edge({
type: "AUTHORED",
direction: "OUT",
node: node({ label: "Post" }),
}).project({
title: property(),
}),
}),
})
.return(["user"])
.build();
The following Cypher is produced;
CALL {
MATCH (user:User)
WHERE user.name = "Dan"
RETURN user {
.id,
.name,
posts: [ (user)-[:AUTHORED]->(posts:Post) | { title: posts.title } ]
} as user
}
RETURN user
Using the DGQL Client you can execute this Cypher and receive an object like:
{
"user": [
{
"id": "user-id-01",
"name": "Dan",
"posts": [{ "title": "Checkout DGQL!" }]
}
]
}
Why
Why don't you just use Cypher? - If you are looking for a highly specific answer... Cypher may be the correct tool. If you aren't too familiar with the Cypher, and all you need is a JSON structure, similar in shape to your formulated query, then DGQL is for you. Using a DGQL query will make returning values from the database more predictable & easier to manage.
Why does it use GraphQL? - The GraphQL parser is a widely adopted and maintained project, meaning we can lean on its tools and infrastructure. Not only does GraphQL provide a solid foundation but also comes with developers & library authors. Finally, GraphQL directives are extremely useful and enable DGQL to facilitate powerful abstractions behind them.
Why no Schema? - This implementation is designed to be lightweight and run anywhere. The lack of schema facilitates this but also means no validation or type checking is performed, usually the expensive part of GraphQL execution.
Overview
Retrieve large subgraphs
{
MATCH {
blogs @node(label: Blog) {
PROJECT {
name
posts @edge(type: HAS_POST, direction: OUT) @node(label: Post) {
title
comments @edge(type: HAS_COMMENT, direction: OUT) @node(label: Comment) {
content
authors @edge(type: AUTHORED, direction: IN) @node(label: User) {
name
}
}
}
}
}
}
RETURN {
blogs
}
}
@cypher
Execute custom Sometimes you may have a highly specific question, Cypher could better help you ask. Use the @cypher
directive, in a projection, to break the flow, and execute custom cypher.
{
MATCH {
movies @node(label: Movie) {
PROJECT {
title
similar
@cypher(
arguments: { first: 3 }
statement: """
MATCH (this)-[:ACTED_IN|:IN_GENRE]-()-[:ACTED_IN|:IN_GENRE]-(rec:Movie)
WITH rec, COUNT(*) AS score
RETURN rec ORDER BY score DESC LIMIT $first
"""
) {
title
actors @edge(type: ACTED_IN, direction: IN) @node {
name
}
}
}
}
}
RETURN {
movies
}
}
Compose Queries
Use Fragments to compose and reuse bits of your query:
{
MATCH {
blogs @node(label: Blog) {
PROJECT {
name
...Posts
}
}
}
RETURN {
blogs
}
}
fragment Posts on DGQL {
posts @edge(type: HAS_POST, direction: OUT) @node(label: Post) {
title
...Comments
...Authors
}
}
fragment Comments on DGQL {
comments @edge(type: HAS_COMMENT, direction: OUT) @node(label: Comment) {
content
...Authors
}
}
fragment Authors on DGQL {
authors @edge(type: AUTHORED, direction: IN) @node(label: User) {
name
}
}
Full CRUD Operations
Play on your desktop
Licence
MIT