93 lines
2.8 KiB
Markdown
93 lines
2.8 KiB
Markdown
# GraphECS
|
|
this is a prototype demonstrating representing underlying ecs data as a graph with a cypher-like query language
|
|
|
|
## Syntax
|
|
graphecs uses a modified subset of neo4j's [cypher](https://neo4j.com/docs/cypher-manual/current/introduction/) query language. neo4j is a graph database with a focus on relationships between data. it has many similar features to SQL-like languages but the ergonomics of relating data is significantly better.
|
|
|
|
### Querying entities
|
|
|
|
```cypher
|
|
MATCH (e)
|
|
RETURN e
|
|
```
|
|
|
|
this is a basic query to return every entity in current ecs world. `MATCH` clauses begin any expression that queries for data. `RETURN` defines what data will be returned by the query. an example of the output may look like this
|
|
|
|
```js
|
|
[
|
|
{ e: 0 },
|
|
{ e: 1 },
|
|
{ e: 2 },
|
|
]
|
|
```
|
|
|
|
```cypher
|
|
MATCH (e:Entity, :Player)
|
|
RETURN e
|
|
```
|
|
|
|
this query specifies two components to match for a single node. here, it explicitly identifies the `:Entity` component but this is optional -- binding a variable without any component label will be bound to the entity's id. additionally, it requires the node to also have the `Player` component. it doesn't bind that component to any variable, however. matching against multiple components in a single node acts like an AND operator -- in this case, `Entity AND Player`.
|
|
|
|
```cypher
|
|
MATCH (:Player)-[:Knows]->(n:NPC)
|
|
RETURN n
|
|
```
|
|
|
|
this query matches against a relationship: any `Player` which `Knows` an `NPC`. it will return a list of all NPCs which have this relationship with the player. in the prototype, edges are just entities with `from` and `to` properties
|
|
|
|
```js
|
|
const Item = defineComponent({ damage: Types.ui8 })
|
|
query('MATCH (item:Item) RETURN item.damage', engine)
|
|
```
|
|
|
|
in this example, we have an `Item` component with a single property: `damage`. it can be useful to only return a single property from a component. the result of this query may look like the following
|
|
|
|
```js
|
|
[
|
|
{ item: { damage: 10 } },
|
|
{ item: { damage: 15 } },
|
|
{ item: { damage: 20 } },
|
|
]
|
|
```
|
|
|
|
accessing nested data like this can get a bit unwieldy, but its possible to alias variables
|
|
|
|
```cypher
|
|
MATCH (item:Item)
|
|
RETURN item.damage AS damage
|
|
```
|
|
|
|
```js
|
|
[
|
|
{ damage: 10 },
|
|
{ damage: 15 },
|
|
{ damage: 20 },
|
|
]
|
|
```
|
|
|
|
### Filtering queries
|
|
|
|
```cypher
|
|
MATCH (h:Health)
|
|
WHERE h.current < 10
|
|
RETURN h
|
|
```
|
|
|
|
it is also possible to filter results via the `WHERE` clause. the following operators are supported in graphecs: `> >= < <= = AND OR XOR ( )`
|
|
|
|
```cypher
|
|
MATCH (n)-[d:Damaged]->(e, h:Health)
|
|
WHERE d.damage > h.current AND h.current > 0
|
|
RETURN e
|
|
```
|
|
|
|
this would return every entity id that has a relationship that deals more damage than its remaining health and isn't already dead
|
|
|
|
### Selecting worlds
|
|
```cypher
|
|
USE ui
|
|
MATCH (button:Button)
|
|
WHERE button.type = 1
|
|
RETURN button
|
|
```
|
|
it's also possible to select different worlds with the `USE` clause
|