From 04022d3cc6e13bb21c25047d8686de514b12e8bb Mon Sep 17 00:00:00 2001 From: Toni Lastre Date: Thu, 21 May 2026 00:25:12 +0200 Subject: [PATCH 1/5] new: Add set of Memgraph and Memgraph Lab skills --- .../SKILL.md | 473 ++++++++++++++++++ memgraph-lab-write-gss/SKILL.md | 332 ++++++++++++ memgraph-lab-write-gss/references/EXAMPLES.md | 409 +++++++++++++++ .../references/REFERENCE.md | 173 +++++++ memgraph-model-graph-data/SKILL.md | 318 ++++++++++++ memgraph-python-query-modules/SKILL.md | 2 + memgraph-run-mage-algorithms/SKILL.md | 402 +++++++++++++++ .../references/REFERENCE.md | 147 ++++++ memgraph-write-cypher/SKILL.md | 398 +++++++++++++++ memgraph-write-cypher/references/REFERENCE.md | 157 ++++++ 10 files changed, 2811 insertions(+) create mode 100644 memgraph-indexes-and-database-administration/SKILL.md create mode 100644 memgraph-lab-write-gss/SKILL.md create mode 100644 memgraph-lab-write-gss/references/EXAMPLES.md create mode 100644 memgraph-lab-write-gss/references/REFERENCE.md create mode 100644 memgraph-model-graph-data/SKILL.md create mode 100644 memgraph-run-mage-algorithms/SKILL.md create mode 100644 memgraph-run-mage-algorithms/references/REFERENCE.md create mode 100644 memgraph-write-cypher/SKILL.md create mode 100644 memgraph-write-cypher/references/REFERENCE.md diff --git a/memgraph-indexes-and-database-administration/SKILL.md b/memgraph-indexes-and-database-administration/SKILL.md new file mode 100644 index 0000000..e9e2259 --- /dev/null +++ b/memgraph-indexes-and-database-administration/SKILL.md @@ -0,0 +1,473 @@ +--- +name: memgraph-indexes-and-database-administration +description: >- + Create and manage Memgraph indexes, constraints, triggers, enums, transactions, + storage modes, and data durability settings. Use when the user asks about + indexing strategy, schema enforcement, data type constraints, triggers, + transaction isolation, storage modes, snapshots, WAL, or performance tuning via + schema-level DDL. +compatibility: Any language with a Bolt-compatible driver. Memgraph instance required. +integrations: + - memgraph-lab>=3.11 +metadata: + version: "0.0.1" + author: memgraph +--- + +# Managing Indexes & Schema in Memgraph + +## Indexes + +Indexes are NOT created automatically unless explicitly enabled via flags. +Adding an index speeds reads but slows writes and uses extra memory. + +### Index types + +| Type | Create | Drop | +|------|--------|------| +| Label | `CREATE INDEX ON :Label;` | `DROP INDEX ON :Label;` | +| Label-property | `CREATE INDEX ON :Label(prop);` | `DROP INDEX ON :Label(prop);` | +| Composite | `CREATE INDEX ON :Label(p1, p2);` | `DROP INDEX ON :Label(p1, p2);` | +| Descending | `CREATE INDEX ON :L(p) WITH CONFIG {"order": "DESC"};` | `DROP INDEX ON :L(p) WITH CONFIG {"order": "DESC"};` | +| Edge-type | `CREATE EDGE INDEX ON :TYPE;` | `DROP EDGE INDEX ON :TYPE;` | +| Edge-type property | `CREATE EDGE INDEX ON :TYPE(prop);` | `DROP EDGE INDEX ON :TYPE(prop);` | +| Global edge property | `CREATE GLOBAL EDGE INDEX ON :(prop);` | `DROP GLOBAL EDGE INDEX ON :(prop);` | +| Point | `CREATE POINT INDEX ON :L(prop);` | `DROP POINT INDEX ON :L(prop);` | +| Text | `CREATE TEXT INDEX name ON :L;` | `DROP TEXT INDEX name;` | +| Vector | `CREATE VECTOR INDEX name ON :L(prop) WITH CONFIG {...};` | `DROP VECTOR INDEX name;` | +| Drop all | — | `DROP ALL INDEXES;` | + +Show: `SHOW INDEX INFO;` or `SHOW INDEXES;` + +### Label-property index + +```cypher +CREATE INDEX ON :Person(age); +``` + +Creating a label-property index does NOT create a label index — create both if +needed. Best performance on high-cardinality properties (unique IDs, names). +Avoid on booleans or low-cardinality fields. + +### Composite index + +```cypher +CREATE INDEX ON :Person(name, occupation); +``` + +Follows the leftmost prefix rule: + +| Query filters | Uses index? | +|---------------|-------------| +| `p0` | Yes | +| `p0, p1` | Yes | +| `p0, p1, p2` | Yes | +| `p0, p2` (skip p1) | Yes + extra filter | +| `p1` only | No | +| `p2` only | No | + +Put highest-cardinality property first. + +### Descending index + +```cypher +CREATE INDEX ON :Person(age) WITH CONFIG {"order": "DESC"}; +``` + +Optimizes `ORDER BY prop DESC` and top-N queries. ASC and DESC can coexist on +the same label+property. Only supported in `IN_MEMORY_TRANSACTIONAL`. + +Dropping without config removes both ASC and DESC variants. + +### Nested (map) property index + +```cypher +CREATE INDEX ON :Project(delivery.status.due_date); +``` + +Must use `WHERE` clause to query — inline map matching won't use the index: + +```cypher +-- Wrong (compares entire map): +MATCH (p:Project {delivery: {status: {due_date: date("2025-06-04")}}}) RETURN p; + +-- Correct: +MATCH (p:Project) WHERE p.delivery.status.due_date = date("2025-06-04") RETURN p; +``` + +### Edge indexes + +Require `--storage-properties-on-edges=true`. + +```cypher +CREATE EDGE INDEX ON :KNOWS; +CREATE EDGE INDEX ON :KNOWS(since); +CREATE GLOBAL EDGE INDEX ON :(weight); +``` + +### Point index + +```cypher +CREATE POINT INDEX ON :Location(coords); +MATCH (n:Location) WHERE point.distance(point({x:1, y:1}), n.coords) < 1000 RETURN n; +MATCH (n:Location) WHERE point.withinbbox(n.coords, point({x:0, y:0}), point({x:10, y:10})) RETURN n; +``` + +### Index hinting + +```cypher +USING INDEX :Person(name) +MATCH (n:Person {name: "Alice", gender: "F"}) RETURN n; +``` + +### ANALYZE GRAPH + +Run once after indexes are created and data is loaded: + +```cypher +ANALYZE GRAPH; +ANALYZE GRAPH ON LABELS :Person, :City; +ANALYZE GRAPH DELETE STATISTICS; +``` + +Calculates property value distribution for optimal index and MERGE selection. +Statistics persist via snapshots/WAL. + +### Automatic index creation + +Only in `IN_MEMORY_TRANSACTIONAL`. Flags: + +- `--storage-automatic-label-index-creation-enabled` +- `--storage-automatic-edge-type-index-creation-enabled` + +### Concurrent index creation + +Label, label-property, composite, and edge indexes are created concurrently. +Brief `READ ONLY` during registration phase (waits for pending writes), then +background indexing proceeds while reads and writes resume. + +--- + +## Constraints + +### Existence constraint + +```cypher +CREATE CONSTRAINT ON (n:Employee) ASSERT EXISTS (n.first_name); +DROP CONSTRAINT ON (n:Employee) ASSERT EXISTS (n.first_name); +SHOW CONSTRAINT INFO; +``` + +One label + one property at a time. + +### Uniqueness constraint + +```cypher +CREATE CONSTRAINT ON (n:Employee) ASSERT n.email IS UNIQUE; +CREATE CONSTRAINT ON (n:Employee) ASSERT n.name, n.address IS UNIQUE; +DROP CONSTRAINT ON (n:Employee) ASSERT n.email IS UNIQUE; +``` + +Multi-property: same name OR same address is allowed; same name AND address is +forbidden. Uniqueness constraints do NOT create indexes — add them separately. + +### Data type constraint + +```cypher +CREATE CONSTRAINT ON (n:Person) ASSERT n.name IS TYPED STRING; +CREATE CONSTRAINT ON (n:Person) ASSERT n.age IS TYPED INTEGER; +DROP CONSTRAINT ON (n:Person) ASSERT n.name IS TYPED STRING; +``` + +Supported types: `NULL`, `STRING`, `BOOLEAN`, `INTEGER`, `FLOAT`, `LIST`, `MAP`, +`DURATION`, `DATE`, `LOCALTIME`, `LOCALDATETIME`, `ZONEDDATETIME`, `ENUM`, `POINT`. + +Only one type constraint per label-property pair. + +### Drop all + +```cypher +DROP ALL CONSTRAINTS; +``` + +### Constraint behavior + +Constraints are checked optimistically on commit. In multi-query explicit +transactions, intermediate violations are allowed as long as the final state +satisfies all constraints. + +--- + +## Enums + +```cypher +CREATE ENUM Status VALUES { Good, Okay, Bad }; +SHOW ENUMS; +ALTER ENUM Status ADD VALUE Excellent; +ALTER ENUM Status UPDATE VALUE Bad TO Poor; +``` + +Use in queries: + +```cypher +CREATE (:Machine {status: Status::Good}); +MATCH (n:Machine) WHERE n.status = Status::Bad RETURN n; +RETURN ToEnum("Status", "Good"); +RETURN ToEnum("Status::Okay"); +``` + +Less memory and faster comparison than string properties. + +--- + +## Triggers + +### Syntax + +``` +CREATE TRIGGER name + [SECURITY DEFINER|INVOKER] + [ON [() | -->] CREATE|UPDATE|DELETE] + [BEFORE|AFTER COMMIT] + EXECUTE +``` + +Default security: `DEFINER`. Triggers are persisted to disk. + +### Predefined variables by event + +| Event | Available variables | +|-------|--------------------| +| `ON () CREATE` | `createdVertices` | +| `ON --> CREATE` | `createdEdges` | +| `ON CREATE` | `createdVertices`, `createdEdges`, `createdObjects` | +| `ON () UPDATE` | `setVertexProperties`, `removedVertexProperties`, `setVertexLabels`, `removedVertexLabels`, `updatedVertices` | +| `ON --> UPDATE` | `setEdgeProperties`, `removedEdgeProperties`, `updatedEdges` | +| `ON UPDATE` | All update vars + `updatedObjects` | +| `ON () DELETE` | `deletedVertices` | +| `ON --> DELETE` | `deletedEdges` | +| `ON DELETE` | `deletedVertices`, `deletedEdges`, `deletedObjects` | +| No event | All variables available | + +### Examples + +Auto-set creation timestamp: + +```cypher +CREATE TRIGGER setCreatedAt +ON () CREATE AFTER COMMIT EXECUTE +UNWIND createdVertices AS v SET v.created_at = timestamp(); +``` + +Auto-set updated_at: + +```cypher +CREATE TRIGGER setUpdatedAt +ON UPDATE AFTER COMMIT EXECUTE +UNWIND updatedObjects AS obj +WITH CASE + WHEN obj.vertex IS NOT null THEN obj.vertex + WHEN obj.edge IS NOT null THEN obj.edge +END AS entity +SET entity.updated_at = timestamp(); +``` + +Dynamic PageRank via trigger: + +```cypher +CREATE TRIGGER pagerankTrigger +BEFORE COMMIT EXECUTE +CALL pagerank_online.update(createdVertices, createdEdges, deletedVertices, deletedEdges) +YIELD node, rank SET node.rank = rank; +``` + +### Manage + +```cypher +DROP TRIGGER name; +SHOW TRIGGERS; +``` + +--- + +## Transactions + +### Explicit transactions + +```cypher +BEGIN; +MATCH (n:Account {id: 1}) SET n.balance = n.balance - 100; +MATCH (n:Account {id: 2}) SET n.balance = n.balance + 100; +COMMIT; +``` + +`ROLLBACK;` to discard. If any query fails, the transaction cannot be committed. + +### Show and terminate + +```cypher +SHOW TRANSACTIONS; +SHOW RUNNING TRANSACTIONS; +TERMINATE TRANSACTIONS "tid1", "tid2"; +``` + +Requires `TRANSACTION_MANAGEMENT` privilege to see/terminate other users' transactions. + +### Isolation levels + +```cypher +SET GLOBAL TRANSACTION ISOLATION LEVEL SNAPSHOT ISOLATION; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +SET NEXT TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SHOW STORAGE INFO; +``` + +| Level | Default | Protects against | +|-------|---------|------------------| +| `SNAPSHOT ISOLATION` | Yes (IN_MEMORY_TRANSACTIONAL) | Dirty read, non-repeatable read, phantom | +| `READ COMMITTED` | | Dirty read | +| `READ UNCOMMITTED` | | Nothing (read-only access) | + +`IN_MEMORY_ANALYTICAL` has no isolation levels. +`ON_DISK_TRANSACTIONAL` supports only `SNAPSHOT ISOLATION`. + +--- + +## Data types + +### Property types + +| Type | Notes | +|------|-------| +| `Null` | Same as property absent | +| `String` | | +| `Boolean` | | +| `Integer` | | +| `Float` | Resolution via `--storage-floating-point-resolution-bits` | +| `List` | Homogeneous for storage as node property | +| `Map` | Must replace entirely | +| `Duration` | `duration("P2DT3H")` | +| `Date` | `date("2024-01-15")` | +| `LocalTime` | `localTime("14:30:00")` | +| `LocalDateTime` | `localDateTime("2024-01-15T14:30:00")` | +| `ZonedDateTime` | `datetime("2024-01-15T14:30:00Z")` | +| `Enum` | Must be defined first | +| `Point` | 2D/3D, Cartesian or WGS-84 | + +Lists and Maps cannot be mutated element-by-element — replace the whole value. + +### Temporal arithmetic + +Duration +/- Duration = Duration. Date +/- Duration = Date. Date - Date = Duration. +Same for LocalTime, LocalDateTime, ZonedDateTime. + +### Point types + +| Type | SRID | Constructor | +|------|------|-------------| +| WGS-84 2D | 4326 | `point({longitude: -73.93, latitude: 40.73})` | +| WGS-84 3D | 4979 | `point({longitude: -73.93, latitude: 40.73, height: 10})` | +| Cartesian 2D | 7203 | `point({x: 0, y: 1})` | +| Cartesian 3D | 9157 | `point({x: 0, y: 1, z: 2})` | + +--- + +## Storage modes + +```cypher +STORAGE MODE IN_MEMORY_TRANSACTIONAL; +STORAGE MODE IN_MEMORY_ANALYTICAL; +STORAGE MODE ON_DISK_TRANSACTIONAL; +SHOW STORAGE INFO; +``` + +| Mode | ACID | WAL | Periodic snapshots | Use case | +|------|------|-----|-------------------|----------| +| `IN_MEMORY_TRANSACTIONAL` | Full | Yes | Yes | Default — concurrent reads/writes | +| `IN_MEMORY_ANALYTICAL` | No | No | Manual only | Bulk import, analytics (up to 6x faster) | +| `ON_DISK_TRANSACTIONAL` | Snapshot isolation | RocksDB | — | Experimental, larger-than-memory | + +Cannot switch in-memory to on-disk with data present. Cannot switch with active transactions. + +--- + +## Data durability + +### Snapshots + +```cypher +CREATE SNAPSHOT; +SHOW SNAPSHOTS; +``` + +Periodic interval (runtime): + +```cypher +SET DATABASE SETTING "storage.snapshot.interval" TO "1200"; +SET DATABASE SETTING "storage.snapshot.interval" TO "* * 12 * * *"; +``` + +Flags: `--storage-snapshot-interval`, `--storage-snapshot-on-exit`, +`--storage-parallel-snapshot-creation`. + +### WAL + +Enabled by default (`--storage-wal-enabled`). Cannot use WAL without snapshots. +Older WAL files are deleted after each snapshot. + +### Data directory management + +```cypher +LOCK DATA DIRECTORY; +UNLOCK DATA DIRECTORY; +DATA DIRECTORY LOCK STATUS; +``` + +### Memory management + +```cypher +MATCH (n) RETURN n QUERY MEMORY LIMIT 50 MB; +CALL proc() PROCEDURE MEMORY LIMIT 100 MB YIELD *; +FREE MEMORY; +SHOW STORAGE INFO; +``` + +Instance limit: `--memory-limit` (MiB). Property compression: +`--storage-property-store-compression-enabled`. + +### Memory estimation + +``` +RAM ≈ nodes × 204B + edges × 154B + properties + indexes +``` + +For 50+ indexes add ~20% overhead. + +--- + +## Storage access + +Index/constraint/enum DDL requires exclusive (unique) access — briefly blocks +other queries during execution. Normal Cypher queries use shared access. + +Timeout for write access during index creation: + +```cypher +SET DATABASE SETTING 'storage.access_timeout_sec' TO '30'; +``` + +--- + +## Key rules summary + +1. Indexes are not auto-created unless flags are enabled (IN_MEMORY_TRANSACTIONAL only) +2. Uniqueness constraints do NOT create indexes — add them separately +3. Label-property index does NOT imply a label index +4. Composite index follows the leftmost prefix rule +5. Nested map indexes require WHERE clause, not inline map matching +6. Edge indexes require `--storage-properties-on-edges=true` +7. Descending indexes only work in IN_MEMORY_TRANSACTIONAL +8. Run `ANALYZE GRAPH` once after data load for optimal index selection +9. `DROP INDEX` without config drops both ASC and DESC variants +10. Constraints are checked on commit (optimistic) +11. Snapshots and WAL are not compatible across Memgraph versions diff --git a/memgraph-lab-write-gss/SKILL.md b/memgraph-lab-write-gss/SKILL.md new file mode 100644 index 0000000..2f19a09 --- /dev/null +++ b/memgraph-lab-write-gss/SKILL.md @@ -0,0 +1,332 @@ +--- +name: memgraph-lab-write-gss +description: >- + Write and edit Graph Style Script (GSS) code for Memgraph Lab graph + visualization. Use when the user asks to style graphs, change node or edge + colors, add labels, set images, configure graph layouts, or write any GSS / + Graph Style Script code for Memgraph Lab. +compatibility: Memgraph Lab required. +integrations: + - memgraph-lab>=3.11 +metadata: + version: "0.0.1" + author: memgraph +--- + +# Writing Graph Style Script (GSS) + +GSS is Memgraph's language for customizing graph visualization in Memgraph Lab. +A GSS file is a sequence of **expressions** and **directives**. + +## Language basics + +### Value types + +`Boolean`, `Color`, `Number`, `String`, `Array`, `Map`, `Function`, `Null` + +### Expressions + +Three forms: + +1. **Literals** -- strings `"Hello"`, numbers `42` / `3.14`, colors `#ff0000`, + booleans `True` / `False`, `Null` +2. **Name expressions** -- identifiers bound via `Define`; all CSS/X11 color + names are built-in (e.g. `dodgerblue`, `gold`, `red`) +3. **Function calls** -- `FunctionName(arg1, arg2, ...)` + +Name rules: starts with a letter, may contain digits, `-`, `_`. + +Strings support `\` escaping for `"` and newlines. + +### Truthy / Falsy + +Six falsy values: `False`, `0`, `""`, `Null`, `[]` (empty array), `{}` (empty map). +Everything else is truthy. + +### Short-circuit evaluation + +`If`, `And`, `Or` skip branches that are not needed. +`Function()` does not evaluate argument names or body until call time. + +## Directives + +Directives are CSS-like blocks that set visual properties. + +``` +@ { + : + ... +} +``` + +- Later directives **override** earlier ones (cascade, like CSS). +- Properties are `name: expression` pairs separated by newlines. +- The predicate is an optional boolean expression that filters which + elements the directive applies to. + +### Available directives + +| Directive | Styles | Binds variable | +|-----------|--------|----------------| +| `@NodeStyle` | Graph nodes | `node` | +| `@EdgeStyle` | Graph relationships | `edge` | +| `@ViewStyle` | Canvas / layout | `graph` | + +`@ViewStyle.Map` is **deprecated** since Lab 3.4 -- use `@ViewStyle` with +`map-tile-layer` instead. + +### Scope variables + +| Variable | Available in | Type | +|----------|-------------|------| +| `node` | `@NodeStyle` only | Map (properties, labels, id) | +| `edge` | `@EdgeStyle` only | Map (properties, type, start, end, id) | +| `graph` | Everywhere (global + all directives) | Graph | + +Using `node` outside `@NodeStyle` or `edge` outside `@EdgeStyle` causes a +**compile error**. + +## File structure and execution order + +1. **Global context**: all expressions outside directives run first. Use for + `Define` calls and cached computations. `graph` is available here. +2. **`@NodeStyle`**: evaluated per node. +3. **`@EdgeStyle`**: evaluated per relationship. + +Global names are visible inside directives. `Define` inside a directive creates +a **local** variable (useful for per-element caching). + +``` +Define(EDGE_COUNT, EdgeCount(graph)) + +@NodeStyle { + size: Sqrt(NodeCount(graph)) +} + +@EdgeStyle { + width: If(Greater(EDGE_COUNT, 1000), 1, 2) +} +``` + +## `@NodeStyle` properties + +| Property | Type | Notes | +|----------|------|-------| +| `border-color` | Color | | +| `border-color-hover` | Color | | +| `border-color-selected` | Color | | +| `border-width` | Number | px | +| `border-width-selected` | Number | px | +| `color` | Color | Background fill | +| `color-hover` | Color | | +| `color-selected` | Color | | +| `font-background-color` | Color | Label background | +| `font-color` | Color | Label text | +| `font-family` | String | e.g. `"sans-serif"` | +| `font-size` | Number | px | +| `image-url` | String | PNG/JPEG/GIF(static)/WEBP/base64; overrides `color` | +| `image-url-selected` | String | | +| `label` | String | Text below node | +| `shadow-color` | Color | | +| `shadow-size` | Number | Blur radius (0 = solid) | +| `shadow-offset-x` | Number | | +| `shadow-offset-y` | Number | | +| `shape` | String | `"dot"` (default), `"square"`, `"diamond"`, `"triangle"`, `"triangleDown"`, `"star"` | +| `size` | Number | Node radius in px | +| `z-index` | Number | Stack order | + +## `@EdgeStyle` properties + +| Property | Type | Notes | +|----------|------|-------| +| `arrow-size` | Number | 0 = no arrow | +| `color` | Color | Line color | +| `color-hover` | Color | | +| `color-selected` | Color | | +| `font-background-color` | Color | | +| `font-color` | Color | | +| `font-family` | String | | +| `font-size` | Number | | +| `label` | String | | +| `shadow-color` | Color | | +| `shadow-size` | Number | | +| `shadow-offset-x` | Number | | +| `shadow-offset-y` | Number | | +| `width` | Number | Line width in px | +| `width-hover` | Number | | +| `width-selected` | Number | | +| `z-index` | Number | | + +## `@ViewStyle` properties + +| Property | Type | Default | +|----------|------|---------| +| `background-color` | Color | | +| `view` | String | `"default"` -- also `"map"`, `"force"`, `"tree"` | +| `force-collision-radius` | Number | 15 | +| `force-repel-force` | Number | -100 | +| `force-link-distance` | Number | 30 | +| `force-physics-enabled` | Boolean | | +| `tree-orientation` | String | `"vertical"` or `"horizontal"` | +| `tree-node-gap` | Number | | +| `tree-level-gap` | Number | | +| `map-tile-layer` | String | `"detailed"`, `"light"`, `"dark"`, `"basic"`, `"satellite"` | + +Map view requires nodes with `latitude` and `longitude` properties. + +## Custom functions + +Use `Define` + `Function` to create reusable logic: + +``` +Define(square, Function(x, Mul(x, x))) +square(5) // -> 25 + +Define(pow, Function(x, n, + If(Equals(n, 1), x, Mul(x, pow(x, Sub(n, 1)))) +)) +pow(2, 10) // -> 1024 +``` + +Lambdas for higher-order functions: + +``` +Filter(AsArray(1, 2, 3, 4), Function(item, Greater(item, 2))) +// -> [3, 4] +``` + +Use `Execute` to run side-effect expressions and return the last value: + +``` +Execute( + Set(map, "key1", "value1"), + Set(map, "key2", "value2"), + MapKeys(map) +) +``` + +## Colors + +Three ways to specify a color: + +1. **Named** -- any X11/CSS color name: `red`, `dodgerblue`, `gold`, etc. +2. **Hex literal** -- `#FF0000`, `#1E90FF` +3. **Functions** -- `RGB(r, g, b)`, `RGBA(r, g, b, a)`, `HSL(h, s, l)`, + `HSLA(h, s, l, a)`, `Darker(color)`, `Lighter(color)`, `Mix(c1, c2)` + +## Common patterns + +### Label from node property + +``` +@NodeStyle { + label: Property(node, "name") +} +``` + +### Conditional styling with predicates + +``` +@NodeStyle HasLabel(node, "Person") { + color: dodgerblue + size: 40 +} + +@NodeStyle And(HasLabel(node, "Person"), Greater(Property(node, "age"), 30)) { + color: coral +} +``` + +### Image on nodes + +``` +@NodeStyle HasLabel(node, "Country") { + image-url: Format("https://flagcdn.com/w320/{}.png", + LowerCase(Property(node, "code"))) +} +``` + +### Override cascade (general then specific) + +``` +@NodeStyle { + color: lightgray + size: 20 +} + +@NodeStyle Equals(Property(node, "name"), "Special") { + color: gold + size: 50 +} +``` + +### Normalization with caching + +``` +Define(MIN_SIZE, 5) +Define(MAX_SIZE, 20) +Define(PROP, "age") +Define(SIZE_RANGE, Sub(MAX_SIZE, MIN_SIZE)) + +Define(GetProps, Function(nodes, prop, + Map(nodes, Function(n, Property(n, prop))) +)) +Define(KeepNumbers, Function(vals, + Filter(vals, Function(v, IsNumber(v))) +)) + +Define(MAX_VAL, If(Greater(NodeCount(graph), 0), + Max(KeepNumbers(GetProps(Nodes(graph), PROP))), 0)) +Define(MIN_VAL, If(Greater(NodeCount(graph), 0), + Min(KeepNumbers(GetProps(Nodes(graph), PROP))), 0)) + +Define(Normalize, Function(n, + Add(MIN_SIZE, Mul(SIZE_RANGE, + Div(Sub(Property(n, PROP), MIN_VAL), Sub(MAX_VAL, MIN_VAL)) + )) +)) + +@NodeStyle And(HasLabel(node, "Person"), IsNumber(Property(node, PROP))) { + Define(NORM, Normalize(node)) + size: NORM + label: Format("Age: {}", AsText(Property(node, PROP))) +} +``` + +### Tree view auto-detection + +``` +@ViewStyle IsTreeStructure(graph) { + view: "tree" +} +``` + +### Edge styling by relationship type + +``` +@EdgeStyle Equals(Type(edge), "FRIENDS_WITH") { + color: dodgerblue + width: 2 + label: Type(edge) +} +``` + +## Validation + +If the `validate-gss` tool is available, always run it after writing or +modifying GSS to catch syntax errors before applying the style in Memgraph Lab. + +## Preview + +If the `preview-gss` tool is available, run it after validation to see how +styles resolve against actual graph data. Pass custom nodes and relationships +to the tool and inspect the computed style properties (color, size, label, etc.) +for each node and edge. This catches logic errors that syntax validation +cannot — such as a `Property` call on a missing key, overlapping predicates +where the wrong rule wins, or a `Format` expression producing unexpected text. + +## Additional resources + +- For the complete built-in function reference (90+ functions), see + [REFERENCE.md](references/REFERENCE.md) +- For more real-world GSS examples and patterns, see [EXAMPLES.md](references/EXAMPLES.md) diff --git a/memgraph-lab-write-gss/references/EXAMPLES.md b/memgraph-lab-write-gss/references/EXAMPLES.md new file mode 100644 index 0000000..2b44468 --- /dev/null +++ b/memgraph-lab-write-gss/references/EXAMPLES.md @@ -0,0 +1,409 @@ +# GSS Examples + +Real-world Graph Style Script patterns and recipes. + +## Basic node labels + +Show node name as label, with label-per-label-type formatting: + +``` +@NodeStyle HasLabel(node, "Country") { + label: Property(node, "name") +} + +@NodeStyle HasLabel(node, "City") { + label: Format("{}, {}", + Property(node, "name"), + Property(node, "country")) +} +``` + +## Show all labels as a joined string + +``` +@NodeStyle Greater(Size(Labels(node)), 0) { + label: Format(":{}", Join(Labels(node), " :")) +} +``` + +## Basic edge labels + +Show the relationship type as the edge label: + +``` +@EdgeStyle { + label: Type(edge) +} +``` + +## Conditional node styling with predicates + +Style nodes differently based on labels: + +``` +@NodeStyle HasLabel(node, "Country") { + color: #ffd700 + color-hover: #ffa500 + color-selected: #dd2222 + size: 35 +} + +@NodeStyle HasLabel(node, "City") { + color: dodgerblue + size: 25 +} +``` + +Compound conditions: + +``` +@NodeStyle And(HasLabel(node, "City"), + Less(Property(node, "drinks_USD"), 5)) { + size: 50 + shadow-color: red + shadow-size: 10 + color: limegreen +} +``` + +## Specific node override (cascade) + +Place more specific rules after general ones. Later directives win: + +``` +@NodeStyle HasLabel(node, "Country") { + image-url: Format("https://cdn.countryflags.com/thumbs/{}/flag-800.png", + LowerCase(Property(node, "name"))) +} + +@NodeStyle Equals(Property(node, "name"), "England") { + image-url: "https://upload.wikimedia.org/wikipedia/en/thumb/b/be/Flag_of_England.svg/2560px-Flag_of_England.svg.png" +} + +@NodeStyle Equals(Property(node, "name"), "Scotland") { + image-url: "https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Flag_of_Scotland.svg/1200px-Flag_of_Scotland.svg.png" +} +``` + +## Node with property-based label and conditional color + +``` +@NodeStyle HasProperty(node, "name") { + label: AsText(Property(node, "name")) +} + +@NodeStyle And(HasProperty(node, "name"), + Equals(Property(node, "name"), "Tony Stark")) { + color: gold + shadow-color: red + label: "You know who I am" +} +``` + +## Node images from properties + +Use a node property as the image URL: + +``` +@NodeStyle HasProperty(node, "profile_image") { + image-url: Property(node, "profile_image") +} +``` + +Or use base64-encoded images inline: + +``` +@NodeStyle { + image-url: "data:image/png;base64,iVBOR..." +} +``` + +## Edge styling by relationship type + +``` +@EdgeStyle Equals(Type(edge), "FRIENDS_WITH") { + color: dodgerblue + width: 2 + label: "friends" +} + +@EdgeStyle Equals(Type(edge), "WORKS_AT") { + color: coral + width: 3 + label: Type(edge) + arrow-size: 10 +} +``` + +## Thin edges with no arrows + +``` +@EdgeStyle { + width: 1 + arrow-size: 0 + color: #6AA84F + label: Type(edge) +} +``` + +## Shadow and hover effects + +``` +@NodeStyle { + size: 35 + border-width: 5 + border-color: white + shadow-color: #333333 + shadow-size: 20 + color: #dd2222 + color-hover: Darker(#dd2222) +} +``` + +## Node shapes + +``` +@NodeStyle HasLabel(node, "Alert") { + shape: "triangle" + color: red +} + +@NodeStyle HasLabel(node, "Server") { + shape: "square" + color: steelblue +} + +@NodeStyle HasLabel(node, "Star") { + shape: "star" + color: gold +} +``` + +## Custom function: simple utility + +``` +Define(square, Function(x, Mul(x, x))) + +@NodeStyle { + size: square(Property(node, "rank")) +} +``` + +## Custom function: recursive power + +``` +Define(pow, Function(x, n, + If(Equals(n, 1), x, Mul(x, pow(x, Sub(n, 1)))) +)) + +@NodeStyle { + size: pow(Property(node, "level"), 2) +} +``` + +## Higher-order functions: Map, Filter, Reduce + +Extract and filter property values across nodes: + +``` +Define(GetProps, Function(nodes, prop, + Map(nodes, Function(n, Property(n, prop))) +)) + +Define(KeepNumbers, Function(vals, + Filter(vals, Function(v, IsNumber(v))) +)) + +Define(allAges, KeepNumbers(GetProps(Nodes(graph), "age"))) +``` + +Reduce to compute a sum: + +``` +Define(totalAge, Reduce( + allAges, + Function(prev, current, Add(prev, current)), + 0 +)) +``` + +## Value normalization with global caching + +Cache expensive computations in the global scope so they run once, +not per-node: + +``` +Define(MIN_SIZE, 5) +Define(MAX_SIZE, 20) +Define(PROP, "age") +Define(SIZE_RANGE, Sub(MAX_SIZE, MIN_SIZE)) + +Define(GetProps, Function(nodes, prop, + Map(nodes, Function(n, Property(n, prop))) +)) +Define(KeepNumbers, Function(vals, + Filter(vals, Function(v, IsNumber(v))) +)) + +Define(MAX_VAL, If(Greater(NodeCount(graph), 0), + Max(KeepNumbers(GetProps(Nodes(graph), PROP))), 0)) +Define(MIN_VAL, If(Greater(NodeCount(graph), 0), + Min(KeepNumbers(GetProps(Nodes(graph), PROP))), 0)) + +Define(Normalize, Function(n, + Add(MIN_SIZE, Mul(SIZE_RANGE, + Div(Sub(Property(n, PROP), MIN_VAL), Sub(MAX_VAL, MIN_VAL)) + )) +)) + +@NodeStyle And(HasLabel(node, "Person"), IsNumber(Property(node, PROP))) { + Define(NORM, Normalize(node)) + color: white + size: NORM + label: Format("Age: {}", AsText(Property(node, PROP))) +} +``` + +The `Define(NORM, ...)` inside `@NodeStyle` creates a local variable to +avoid calling `Normalize` twice per node. + +## Dynamic color from property value + +Use `If` chains or computed colors: + +``` +@NodeStyle { + color: If(Greater(Property(node, "risk"), 80), red, + If(Greater(Property(node, "risk"), 50), orange, + green)) +} +``` + +Or compute color dynamically: + +``` +@NodeStyle { + color: HSL( + Mul(Div(Property(node, "score"), 100), 120), + 80, + 50 + ) +} +``` + +## Size by edge count (degree) + +``` +@NodeStyle { + size: Add(10, Mul(Size(Edges(node)), 3)) +} +``` + +## Edge label with start/end node info + +``` +@EdgeStyle { + label: Format("{} -> {}", + Property(StartNode(edge), "name"), + Property(EndNode(edge), "name")) +} +``` + +## Tree view with auto-detection + +``` +@ViewStyle IsTreeStructure(graph) { + view: "tree" + tree-orientation: "horizontal" + tree-node-gap: 50 + tree-level-gap: 100 +} +``` + +## Map view + +Requires nodes with `latitude` and `longitude` properties: + +``` +@ViewStyle { + view: "map" + map-tile-layer: "detailed" +} +``` + +## Force layout tuning + +``` +@ViewStyle { + view: "force" + force-collision-radius: 20 + force-repel-force: -200 + force-link-distance: 50 + force-physics-enabled: True +} + +@ViewStyle Greater(NodeCount(graph), 100) { + force-physics-enabled: False +} +``` + +## Dark background canvas + +``` +@ViewStyle { + background-color: #1a1a2e +} + +@NodeStyle { + color: #e94560 + font-color: white + border-color: #533483 + border-width: 2 +} + +@EdgeStyle { + color: #533483 + font-color: #e94560 +} +``` + +## Complete example: Europe backpacking dataset + +``` +@NodeStyle { + size: 35 + border-width: 5 + border-color: #ffffff + shadow-color: #333333 + shadow-size: 20 +} + +@NodeStyle Greater(Size(Labels(node)), 0) { + label: Format(":{}", Join(Labels(node), " :")) +} + +@NodeStyle HasLabel(node, "Country") { + color: #ffd700 + color-hover: #ffa500 + color-selected: #dd2222 +} + +@NodeStyle HasProperty(node, "name") { + label: AsText(Property(node, "name")) +} + +@EdgeStyle { + width: 1 + label: Type(edge) + arrow-size: 0 + color: #6AA84F +} + +@NodeStyle Equals(Property(node, "name"), "Russia") { + image-url: "https://upload.wikimedia.org/wikipedia/en/thumb/f/f3/Flag_of_Russia.svg/320px-Flag_of_Russia.svg.png" +} + +@NodeStyle Equals(Property(node, "name"), "Spain") { + image-url: "https://upload.wikimedia.org/wikipedia/en/thumb/9/9a/Flag_of_Spain.svg/320px-Flag_of_Spain.svg.png" +} +``` diff --git a/memgraph-lab-write-gss/references/REFERENCE.md b/memgraph-lab-write-gss/references/REFERENCE.md new file mode 100644 index 0000000..fcd458b --- /dev/null +++ b/memgraph-lab-write-gss/references/REFERENCE.md @@ -0,0 +1,173 @@ +# GSS Built-in Function Reference + +Complete catalog of all built-in functions in Graph Style Script. + +## Color functions + +| Function | Signature | Returns | Description | +|----------|-----------|---------|-------------| +| `Darker` | `(color: Color)` | Color | Darker version of the color | +| `Lighter` | `(color: Color)` | Color | Lighter version of the color | +| `Mix` | `(color1: Color, color2: Color)` | Color | Linear interpolation of two colors | +| `Red` | `(color: Color)` | Number (0-255) | Red component | +| `Green` | `(color: Color)` | Number (0-255) | Green component | +| `Blue` | `(color: Color)` | Number (0-255) | Blue component | +| `Alpha` | `(color: Color)` | Number (0-1) | Alpha (transparency) component | +| `RGB` | `(red, green, blue: Number)` | Color | Create color from RGB | +| `RGBA` | `(red, green, blue, alpha: Number)` | Color | Create color from RGBA | +| `Hue` | `(color: Color)` | Number (0-359) | HSL hue component | +| `Saturation` | `(color: Color)` | Number (0-100) | HSL saturation component | +| `Lightness` | `(color: Color)` | Number (0-100) | HSL lightness component | +| `HSL` | `(hue, saturation, lightness: Number)` | Color | Create color from HSL | +| `HSLA` | `(hue, saturation, lightness, alpha: Number)` | Color | Create color from HSLA | + +## Conditional functions + +| Function | Signature | Returns | Description | +|----------|-----------|---------|-------------| +| `And` | `(value1, value2, ...valueN: Any)` | Boolean | True if all values truthy; short-circuits | +| `Or` | `(value1, value2, ...valueN: Any)` | Boolean | True if any value truthy; short-circuits | +| `Not` | `(value: Any)` | Boolean | Negation (truthy becomes false) | +| `Equals` | `(value1, value2: Any)` | Boolean | Equality check (by value for primitives, by identity for nodes/edges) | +| `Greater` | `(value1, value2: Number)` | Boolean | value1 > value2 | +| `Less` | `(value1, value2: Number)` | Boolean | value1 < value2 | +| `If` | `(condition, then, else: Any)` | Any | Conditional; returns `then` if truthy, `else` otherwise | + +## Graph functions + +| Function | Signature | Returns | Description | +|----------|-----------|---------|-------------| +| `HasLabel` | `(node: Node, label: String)` | Boolean | True if node has the label | +| `HasProperty` | `(nodeOrEdge: Node\|Rel, name: String)` | Boolean | True if entity has property | +| `Property` | `(nodeOrEdge: Node\|Rel, name: String)` | Any | Get property value | +| `Labels` | `(node: Node)` | List[String] | All labels of a node | +| `Type` | `(edge: Rel)` | String | Relationship type | +| `Id` | `(nodeOrEdge: Node\|Rel)` | Number | Internal ID | +| `Identity` | `(nodeOrEdge: Node\|Rel)` | Number | Same as `Id` | +| `InEdges` | `(node: Node)` | List[Rel] | Inbound relationships | +| `OutEdges` | `(node: Node)` | List[Rel] | Outbound relationships | +| `Edges` | `(graphOrNode: Graph\|Node)` | List[Rel] | All relationships (of graph or node) | +| `InNodes` | `(node: Node)` | List[Node] | Unique inbound neighbor nodes | +| `OutNodes` | `(node: Node)` | List[Node] | Unique outbound neighbor nodes | +| `Nodes` | `(graphOrEdge: Graph\|Rel)` | List[Node] | All nodes (of graph) or start+end nodes (of edge) | +| `AdjacentNodes` | `(node: Node)` | List[Node] | All directly connected nodes | +| `StartNode` | `(edge: Rel)` | Node | Source node of relationship | +| `EndNode` | `(edge: Rel)` | Node | Target node of relationship | +| `NodeCount` | `(graph: Graph)` | Number | Total node count | +| `EdgeCount` | `(graph: Graph)` | Number | Total relationship count | +| `IsTreeStructure` | `(graph: Graph, minDepth?: Number)` | Boolean | True if graph is a directed tree (default min depth: 2) | + +## Map functions + +| Function | Signature | Returns | Description | +|----------|-----------|---------|-------------| +| `MapKeys` | `(map: Map)` | List[String] | Array of all map keys | +| `MapValues` | `(map: Map)` | List[Any] | Array of all map values | + +See also: `AsMap`, `IsMap`, `Get`, `Set`, `Del` in Utility and Type sections. + +## Math functions + +| Function | Signature | Returns | Description | +|----------|-----------|---------|-------------| +| `Add` | `(value1, value2, ...valueN: Number)` | Number | Sum of values | +| `Sub` | `(value1, value2: Number)` | Number | value1 - value2 | +| `Mul` | `(value1, value2, ...valueN: Number)` | Number | Product of values | +| `Div` | `(value1, value2: Number)` | Number | value1 / value2 | +| `Sqrt` | `(value: Number)` | Number | Square root | +| `Exp` | `(value: Number)` | Number | e^value | +| `Log` | `(value: Number)` | Number | Natural logarithm | +| `Log10` | `(value: Number)` | Number | Base-10 logarithm | +| `Floor` | `(value: Number)` | Number | Round down | +| `Ceil` | `(value: Number)` | Number | Round up | +| `Round` | `(value: Number)` | Number | Round to nearest integer | +| `Random` | `()` | Number | Random float in [0, 1) | +| `RandomInt` | `(bound: Number)` | Number | Random integer in [0, bound) | +| `Sum` | `(array: List[Number])` | Number | Sum of array (0 for empty) | +| `Avg` | `(array: List[Number])` | Number | Average of array | +| `Min` | `(array: List[Number])` | Number | Minimum of array | +| `Max` | `(array: List[Number])` | Number | Maximum of array | + +## Text functions + +| Function | Signature | Returns | Description | +|----------|-----------|---------|-------------| +| `Concat` | `(v1, v2, ...vN: String\|List)` | String\|List | Concatenate strings or arrays | +| `Slice` | `(value: String\|List, start: Number, end?: Number)` | String\|List | Substring/subarray; supports negative indexes | +| `Split` | `(text, delimiter: String)` | List[String] | Split string by delimiter | +| `Format` | `(fmt: String, v1, ...vN: Any)` | String | Substitute `{}` placeholders with values (text inside braces ignored) | +| `Matches` | `(text, regex: String)` | Boolean | Regex test (JS `RegExp.test`) | +| `Replace` | `(text, regex, replacement: String)` | String | Replace first regex match | +| `LowerCase` | `(text: String)` | String | To lower case | +| `UpperCase` | `(text: String)` | String | To upper case | +| `Trim` | `(text: String)` | String | Strip leading/trailing whitespace | + +## Array functions + +| Function | Signature | Returns | Description | +|----------|-----------|---------|-------------| +| `Join` | `(array: List, delimiter: String)` | String | Join elements into string | +| `Contains` | `(array: List, value: Any)` | Boolean | True if array contains value | +| `RandomOf` | `(array: List)` | Any\|Null | Random element | +| `Find` | `(array: List, fn: Function)` | Any\|Null | First element where fn returns truthy | +| `Filter` | `(array: List, fn: Function)` | List | Elements where fn returns truthy | +| `Map` | `(array: List, fn: Function)` | List | Transform each element | +| `Reduce` | `(array: List, fn: Function, init: Any)` | Any | Fold array; fn receives (prev, current) | +| `All` | `(array: List, fn: Function)` | Boolean | True if fn truthy for all elements | +| `Any` | `(array: List, fn: Function)` | Boolean | True if fn truthy for any element | +| `Uniq` | `(array: List)` | List | Remove duplicates (preserves order) | +| `Reverse` | `(array: List)` | List | Reverse order | +| `Sort` | `(array: List[String\|Number\|Boolean])` | List | Sort primitives | +| `Next` | `(iterator: Iterator)` | Any\|Null | Next item from iterator | + +## Type functions + +| Function | Signature | Returns | Description | +|----------|-----------|---------|-------------| +| `AsArray` | `(v1, v2, ...vN: Any)` | List | Create array; also converts Iterator back to array | +| `AsMap` | `(k1, v1, ...kN, vN: Any)` | Map | Create map from key-value pairs (keys must be strings) | +| `AsIterator` | `(array: List)` | Iterator | Create iterator from array | +| `AsNumber` | `(value: String\|Number\|Boolean)` | Number | Parse to number (`True`->1, `False`->0) | +| `AsText` | `(value: Any)` | String | Convert to string | +| `TypeOf` | `(value: Any)` | String | Type name: `"number"`, `"boolean"`, `"string"`, `"Null"`, `"Color"`, `"Node"`, `"Edge"`, `"Graph"`, `"List"`, `"Iterator"`, `"Map"`, `"Function"` | +| `IsArray` | `(value: Any)` | Boolean | Type check | +| `IsMap` | `(value: Any)` | Boolean | Type check | +| `IsIterator` | `(value: Any)` | Boolean | Type check | +| `IsNumber` | `(value: Any)` | Boolean | Type check | +| `IsBoolean` | `(value: Any)` | Boolean | Type check | +| `IsString` | `(value: Any)` | Boolean | Type check | +| `IsNull` | `(value: Any)` | Boolean | Type check | + +## Utility functions + +| Function | Signature | Returns | Description | +|----------|-----------|---------|-------------| +| `Define` | `(name: Variable, value: Any)` | -- | Bind value to name; global outside directives, local inside | +| `Function` | `(arg1, ...argN, body: Any)` | Function | Create a function; args are not evaluated at definition time | +| `Execute` | `(expr1, ...exprN: Any)` | Any | Run all expressions, return last value | +| `Get` | `(obj: List\|Map\|String\|Node\|Rel, key, default?: Any)` | Any | Get element by index/key; returns default or Null on miss | +| `Set` | `(obj: List\|Map, key, value: Any)` | Any\|Null | Set element; returns value on success, Null on out of bounds | +| `Del` | `(map: Map, key: String)` | Any\|Null | Remove key from map; returns removed value | +| `Size` | `(value: List\|Map\|String\|Node\|Rel\|Graph)` | Number | Length / count | +| `Coalesce` | `(v1, ...vN: Any)` | Any\|Null | First non-null value | + +## Named colors + +All X11/CSS SVG color names are available as identifiers. Common examples: + +`red`, `blue`, `green`, `yellow`, `orange`, `purple`, `pink`, `cyan`, +`magenta`, `white`, `black`, `gray`, `grey`, `gold`, `silver`, `coral`, +`crimson`, `tomato`, `salmon`, `chocolate`, `maroon`, `navy`, `teal`, +`olive`, `lime`, `aqua`, `fuchsia`, `indigo`, `violet`, `turquoise`, +`dodgerblue`, `forestgreen`, `firebrick`, `steelblue`, `slategray`, +`darkblue`, `darkgreen`, `darkred`, `darkcyan`, `darkorange`, +`darkviolet`, `deeppink`, `deepskyblue`, `lightblue`, `lightgreen`, +`lightcoral`, `lightgray`, `lightyellow`, `mediumseagreen`, +`mediumslateblue`, `midnightblue`, `royalblue`, `saddlebrown`, +`springgreen`, `yellowgreen`, `aliceblue`, `antiquewhite`, `azure`, +`beige`, `bisque`, `cornflowerblue`, `gainsboro`, `honeydew`, `ivory`, +`khaki`, `lavender`, `linen`, `mintcream`, `mistyrose`, `moccasin`, +`oldlace`, `papayawhip`, `peachpuff`, `plum`, `powderblue`, `seashell`, +`snow`, `tan`, `thistle`, `wheat`, `whitesmoke` + +For the full list, see the [W3C CSS Color 3 specification](https://www.w3.org/TR/css-color-3/#svg-color). diff --git a/memgraph-model-graph-data/SKILL.md b/memgraph-model-graph-data/SKILL.md new file mode 100644 index 0000000..8479513 --- /dev/null +++ b/memgraph-model-graph-data/SKILL.md @@ -0,0 +1,318 @@ +--- +name: memgraph-model-graph-data +description: >- + Design graph data models for Memgraph using Labeled Property Graph (LPG) + principles. Use when the user asks how to model data as a graph, design a + schema, decide between properties and relationships, structure nodes and edges, + import CSV/Parquet into a graph, build a knowledge graph, or optimize a + Memgraph data model for performance and memory. +compatibility: Any language with a Bolt-compatible driver. Memgraph instance required. +integrations: + - memgraph-lab>=3.11 +metadata: + version: "0.0.1" + author: memgraph +--- + +# Graph Data Modeling for Memgraph + +Memgraph uses the Labeled Property Graph (LPG) model: nodes and relationships +both carry properties (key-value pairs), and nodes can have multiple labels. + +## Core concepts + +### Four components + +1. **Nodes** — entities with zero or more labels +2. **Relationships** — directed edges with exactly one type (immutable after creation) +3. **Properties** — key-value pairs on nodes and/or relationships +4. **Labels** — classify nodes; enable indexed lookups + +```cypher +(:Person:Student {name: "Alice", age: 20})-[:STUDIES {grade: "A"}]->(:Subject {name: "Math"}) +``` + +### Key properties of LPG + +- Flexible, dynamic schema — add labels/properties anytime without migrations +- Relationships are first-class (not inferred via JOINs) +- Optimized for multi-hop traversals +- In-memory storage means property types directly affect RAM + +--- + +## Design methodology + +### Step 1: Define requirements + +Write down what the database is for — use case drives the model. Ask: +- What entities exist? +- How do entities relate to each other? +- What questions will the graph answer? +- What properties are needed, and where should they live? + +### Step 2: Identify nodes and relationships + +**Nouns become nodes.** Group similar entities under labels. +**Verbs become relationships.** Name them as actions: `KNOWS`, `WORKS_ON`, `BELONGS_TO`. + +### Step 3: Place properties strategically + +| Data describes... | Store as... | +|-------------------|-------------| +| The entity itself | Node property | +| The connection | Relationship property | +| A shared category/concept | Separate node + relationship | + +### Step 4: Validate with real queries + +Write the queries you'll actually run. Use `PROFILE` to check the query plan. +If a query is slow or awkward, revisit the model. + +--- + +## Property vs relationship — decision framework + +### Use a property when + +- The value is unique/specific to that node +- It's rarely used to find related entities +- It doesn't represent a shared concept + +```cypher +(:Product {name: "Milk", price: 2.99, expirationDate: "2024-01-01"}) +``` + +### Use a relationship when + +- The data connects entities and can be shared across nodes +- You frequently query "which other nodes share this value?" + +Anti-pattern — categories as array property: + +```cypher +(:Product {name: "Milk", categories: ["Dairy", "Organic"]}) +``` + +This forces scanning every node's array to find shared categories. + +Pattern — categories as nodes: + +```cypher +(:Product {name: "Milk"})-[:BELONGS_TO]->(:Category {name: "Dairy"}) +(:Product {name: "Milk"})-[:BELONGS_TO]->(:Category {name: "Organic"}) +``` + +Query for related products: + +```cypher +MATCH (:Product {name: "Milk"})-[:BELONGS_TO]->(c)<-[:BELONGS_TO]-(other:Product) +RETURN other.name; +``` + +### Supernode warning + +When a shared-value node accumulates too many relationships (e.g., a `Country` +node connected to millions of `Person` nodes), it becomes a **supernode** — +traversals through it are expensive. Strategies: +- Keep high-cardinality shared data as properties when traversal isn't needed +- Partition supernodes (e.g., `Country` + `Region` hierarchy) + +--- + +## Anti-patterns and patterns + +| Anti-pattern | Better pattern | +|--------------|----------------| +| Model every detail as nodes/relationships | Focus on key entities and direct relationships | +| Duplicate entity data on multiple nodes | Single canonical node + relationships | +| Categories/tags as array properties | Category/tag nodes with typed relationships | +| String dates and numbers | Temporal types (`date()`, `datetime()`) and `toInteger()` | +| No indexes on frequently queried properties | Targeted label and label-property indexes | +| Index every property | Index only hot query fields | +| SQL-style JOIN thinking | Traversal-first Cypher | +| Knowledge encoded only in query strings | Encode semantics in graph structure | +| `CREATE` for bulk relationship import | `MERGE` + indexes + `LIMIT` | +| Unconstrained cartesian `MATCH (a), (b)` | Filtered MATCH with indexes and limits | +| Design without profiling | `PROFILE` real queries during modeling | + +--- + +## Knowledge graph modeling + +A knowledge graph encodes domain knowledge in the graph structure so the graph +itself is "readable as sentences." + +### Evolution example + +Weak model — skills stored as property arrays: + +```cypher +MATCH (p:Person)-[:WORKS_ON]->(t:Task) +WHERE all(skill IN t.Skills WHERE skill IN p.Skills) +RETURN *; +``` + +Knowledge is in the query, not the graph. + +Strong model — skills as nodes with semantic relationships: + +```cypher +(:Person)-[:HAS]->(:Skill) +(:Task)-[:NEEDS]->(:Skill) +(:Person)-[:WORKS_ON]->(:Task) +``` + +Find sufficient skills: + +```cypher +MATCH (p:Person)-[:HAS]->(s:Skill)<-[:NEEDS]-(t:Task) +WHERE exists((p)-[:WORKS_ON]->(t)) +RETURN p.name, collect(s.name); +``` + +Find skill gaps: + +```cypher +MATCH (p:Project)-[:REQUIRES]->(s:Skill) +OPTIONAL MATCH (person:Person)-[:HAS]->(s) +WITH p, s, collect(person) AS people +WHERE size(people) = 0 +RETURN p.name AS project, collect(s.name) AS missingSkills; +``` + +### GraphRAG integration + +1. Embed a question → vector search → find a **pivot node** +2. Expand from pivot (e.g., 2-hop neighborhood): + ```cypher + MATCH path=(pivot:Project {name: "Data Pipeline"})-[*..2]-(n) + RETURN path; + ``` +3. Pass subgraph context to LLM + +--- + +## Memgraph-specific modeling considerations + +### Memory estimation + +``` +RAM ≈ nodes × 204B + edges × 154B + properties + indexes +``` + +- For 50+ indexes: add ~20% overhead +- Query multiplier: 1.5x (basic) to 2x (algorithms) +- Use the [storage calculator](https://memgraph.com/storage-calculator) + +### Data type choices for memory + +| Prefer | Over | Why | +|--------|------|-----| +| `Integer` | String numbers | Smaller, faster comparison | +| `Boolean` | String "true"/"false" | Lightest type | +| `date()`, `datetime()` | String dates | ~15B vs ~22B; enables temporal queries | +| `Enum` | Repeated strings | Less memory, faster comparison | + +### Indexing during modeling + +- Create indexes BEFORE large imports on MATCH/MERGE key properties +- Label-property indexes are NOT auto-created (unless flag enabled) +- Uniqueness constraints do NOT create indexes — add them separately +- Run `ANALYZE GRAPH` after data load + +### Storage modes + +| Mode | Best for | +|------|----------| +| `IN_MEMORY_TRANSACTIONAL` | Default — ACID, concurrent reads/writes | +| `IN_MEMORY_ANALYTICAL` | Bulk import (up to 6x faster), analytics workloads | + +Switch for bulk loading: + +```cypher +STORAGE MODE IN_MEMORY_ANALYTICAL; +-- bulk import here +STORAGE MODE IN_MEMORY_TRANSACTIONAL; +``` + +Create a manual snapshot before switching back: `CREATE SNAPSHOT;` + +--- + +## CSV import workflow + +### 1. Design the model from CSV columns + +Map CSV columns to node properties. Identify which columns represent entities +(nodes) vs connections (relationships). + +### 2. Create indexes before import + +```cypher +CREATE INDEX ON :Person; +CREATE INDEX ON :Person(name); +``` + +### 3. Load and transform + +```cypher +LOAD CSV FROM "/path/data.csv" WITH HEADER AS row +CREATE (:Person { + name: row.Name, + age: toInteger(row.Age), + dob: date(row.Date_of_Birth) +}); +``` + +CSV values are always strings — convert with `toInteger()`, `toFloat()`, +`date()`, `toBoolean()`. + +### 4. Create relationships + +```cypher +MATCH (a:Person {name: "Alice"}), (b:Person {name: "Bob"}) +MERGE (a)-[:FRIENDS_WITH]->(b); +``` + +Use `MERGE` (not `CREATE`) to avoid duplicate relationships. Use `LIMIT` and +`WHERE a <> b` to control combinatorial explosion. + +### 5. Batch large imports + +```cypher +USING PERIODIC COMMIT 1000 +LOAD CSV FROM "/path/data.csv" WITH HEADER AS row +CREATE (:Node {id: row.id, name: row.name}); +``` + +--- + +## LPG vs RDF + +| Feature | LPG (Memgraph) | RDF | +|---------|-----------------|-----| +| Data structure | Labeled nodes/edges + properties | Subject-predicate-object triples | +| Schema | Flexible, optional | Ontology-driven (RDFS/OWL) | +| Query language | Cypher | SPARQL | +| Performance | Real-time traversal + analytics | Semantic web / linked data | +| Ease of use | Intuitive, developer-friendly | Standards-heavy | + +Choose LPG when: real-time analytics, schema flexibility, graph algorithms, +developer ergonomics. + +Choose RDF when: linked data integration, semantic reasoning, ontology required. + +--- + +## Checklist for a good data model + +- [ ] Clear node labels that group similar entities +- [ ] Well-named relationship types (verbs: KNOWS, OWNS, BELONGS_TO) +- [ ] Properties on the right entity (node vs relationship vs separate node) +- [ ] No unnecessary data duplication +- [ ] Temporal and numeric types (not strings) for dates and numbers +- [ ] Indexes on properties used in MATCH/WHERE +- [ ] No supernodes without mitigation strategy +- [ ] Validated with `PROFILE` on real queries +- [ ] Memory estimate fits available RAM diff --git a/memgraph-python-query-modules/SKILL.md b/memgraph-python-query-modules/SKILL.md index 50c4494..307f44c 100644 --- a/memgraph-python-query-modules/SKILL.md +++ b/memgraph-python-query-modules/SKILL.md @@ -2,6 +2,8 @@ name: memgraph-python-query-modules description: Develop custom query modules in Python for Memgraph graph database. Use when creating custom graph algorithms, procedures (@mgp.read_proc, @mgp.write_proc), or functions (@mgp.function) for Memgraph. Covers the mgp Python API, graph traversal, data transformations, and module deployment. compatibility: Requires Memgraph instance (Docker recommended), Python 3.7+ +integrations: + - memgraph-lab>=3.11 metadata: version: "0.0.1" author: memgraph diff --git a/memgraph-run-mage-algorithms/SKILL.md b/memgraph-run-mage-algorithms/SKILL.md new file mode 100644 index 0000000..435af26 --- /dev/null +++ b/memgraph-run-mage-algorithms/SKILL.md @@ -0,0 +1,402 @@ +--- +name: memgraph-run-mage-algorithms +description: >- + Run MAGE (Memgraph Advanced Graph Extensions) graph algorithms from Cypher, + including built-in deep path traversals (BFS, DFS, WSP, ASP, KSP), PageRank, + community detection, centrality, node embeddings, LLM integration, and 70+ + algorithm modules. Use when the user asks to run a graph algorithm, find + shortest paths, detect communities, compute centrality, generate embeddings, + or call any MAGE procedure. +compatibility: Any language with a Bolt-compatible driver. Memgraph instance required. +integrations: + - memgraph-lab>=3.11 +metadata: + version: "0.0.1" + author: memgraph +--- + +# Running MAGE Graph Algorithms + +MAGE is Memgraph's graph algorithm library. Algorithms are exposed as **query +modules** — each module has one or more **procedures** (via `CALL`) or +**functions** (via `RETURN`). + +## Installation + +MAGE is pre-installed in Docker images `memgraph/memgraph-mage` and +`memgraph/memgraph-platform`. + +```shell +docker run -p 7687:7687 --name memgraph memgraph/memgraph-mage:3.2 +``` + +For GPU algorithms, add `--gpus all` and use a `-cuda` image tag. + +## CALL syntax + +```cypher +CALL module.procedure(arg1, arg2, ...) YIELD col1, col2; +CALL module.procedure() YIELD *; +``` + +Rules: +- First parameter can optionally be a `Graph` from `project()` to run on a subgraph +- `YIELD` selects output columns; use `YIELD *` for all +- Can embed in larger queries: `MATCH ... CALL ... YIELD ... RETURN ...` +- Write procedures: `CALL` can only be followed by `YIELD` and/or `RETURN` +- Alias with `AS` if column names conflict with outer variables + +## Functions vs procedures + +```cypher +CALL pagerank.get() YIELD node, rank; -- procedure +RETURN llm.complete("Summarize this."); -- function +RETURN collections.sort([3, 1, 2]); -- function in expression +``` + +## Subgraph projection + +Run algorithms on a subset of the graph using `project()`: + +```cypher +MATCH p=(n:Person)-[r:KNOWS]->(m:Person) +WITH project(p) AS subgraph +CALL pagerank.get(subgraph) YIELD node, rank +RETURN node.name, rank ORDER BY rank DESC; +``` + +Alternative with explicit lists: + +```cypher +MATCH (a)-[e]-(b) +WITH collect(a) AS nodes, collect(e) AS rels +CALL community_detection.get_subgraph(nodes, rels) YIELD node, community_id +RETURN node, community_id; +``` + +Projection cannot be used with built-in deep path traversal (BFS/DFS/WSP/ASP/KSP). + +## Memory limits + +Default: 100 MB per procedure. + +```cypher +CALL module.procedure() PROCEDURE MEMORY LIMIT 500 MB YIELD *; +CALL module.procedure() PROCEDURE MEMORY UNLIMITED YIELD *; +``` + +## Discovering modules + +```cypher +CALL mg.procedures() YIELD name, signature; +CALL mg.load_all(); +CALL node2vec.help() YIELD name, value; +``` + +--- + +## Built-in deep path traversal (no MAGE required) + +These use relationship expansion syntax directly in MATCH. + +### BFS (unweighted shortest path) + +```cypher +MATCH p=(a {name: "A"})-[*BFS]->(b {name: "E"}) RETURN p; +MATCH p=(a)-[r:ROAD *BFS]->(b) RETURN p; +``` + +### DFS (all paths) + +```cypher +MATCH p=(a)-[*]->(b) RETURN p; +``` + +### Weighted shortest path (WSP) + +```cypher +MATCH p=(a)-[*WSHORTEST (r, n | r.weight) total_weight]->(b) +RETURN p, total_weight; +``` + +Combined node + edge weight: + +```cypher +MATCH p=(a)-[*WSHORTEST (r, n | n.cost + coalesce(r.weight, 0)) total]->(b) +RETURN p, total; +``` + +### All shortest paths (ASP) + +```cypher +MATCH p=(a)-[*ALLSHORTEST (r, n | r.weight)]->(b) RETURN p; +``` + +### K shortest paths (KSP) + +Must match source/target first, pass via `WITH`: + +```cypher +MATCH (a:Node {name: "A"}), (b:Node {name: "E"}) +WITH a, b +MATCH p=(a)-[*KSHORTEST|3]->(b) RETURN p; +``` + +### Filter lambdas + +2-arg `(r, n | predicate)`: +```cypher +MATCH p=(a)-[*BFS (r, n | n.active = true AND r.weight < 10)]->(b) RETURN p; +``` + +3-arg `(r, n, p | predicate)` — p is the path so far: +```cypher +MATCH p=(a)-[* (r, n, p | type(last(relationships(p))) != "BLOCKED")]->(b) RETURN p; +``` + +4-arg WSP/ASP filter `(r, n, p, w | predicate)` — w is accumulated weight: +```cypher +MATCH p=(a)-[*WSHORTEST (r, n | r.w) total (r, n, p, w | w < 1000)]->(b) RETURN p, total; +``` + +### Hop limits + +```cypher +MATCH p=(a)-[*BFS ..5]->(b) RETURN p; +USING HOPS LIMIT 3 MATCH p=(a)-[*BFS]->(b) RETURN p; +``` + +--- + +## Algorithm categories + +For the complete module list with signatures, see [reference.md](reference.md). + +### Centrality + +| Module | Description | +|--------|-------------| +| `pagerank` | PageRank influence ranking | +| `betweenness_centrality` | Brandes betweenness | +| `degree_centrality` | In/out/undirected degree | +| `katz_centrality` | Katz influence | + +### Community detection + +| Module | Description | +|--------|-------------| +| `community_detection` | Louvain modularity maximization | +| `leiden_community_detection` | Leiden (improved Louvain) | +| `weakly_connected_components` | WCC | + +### Path & traversal + +| Module | Description | +|--------|-------------| +| Built-in BFS/DFS/WSP/ASP/KSP | Expansion syntax | +| `algo` | General traversal utilities | +| `path` | Path navigation/analysis | +| `cycles` | Cycle detection | +| `bridges` | Bridge edge detection | +| `biconnected_components` | Maximal biconnected subgraphs | + +### Graph ML + +| Module | Description | +|--------|-------------| +| `node2vec` | Node embeddings via biased random walks | +| `gnn_link_prediction` | GNN link prediction | +| `gnn_node_classification` | GNN node classification | +| `tgn` | Temporal Graph Networks | +| `embeddings` | Sentence embeddings (local or remote) | +| `knn` | K-nearest neighbors | + +### Utilities + +| Module | Description | +|--------|-------------| +| `collections` | List operations (sort, union, partition) | +| `map` | Map operations | +| `text` | String manipulation | +| `llm` | LLM completions via LiteLLM | +| `json_util` | JSON load from file/URL | +| `export_util` | Graph export (JSON) | +| `import_util` | Data import (JSON) | +| `periodic` | Periodic query execution | +| `uuid_generator` | UUID generation | +| `migrate` | MySQL/SQL Server/Oracle access | + +### Dynamic/online (Enterprise) + +| Module | Description | +|--------|-------------| +| `pagerank_online` | Streaming PageRank | +| `community_detection_online` | Streaming LabelRankT | +| `betweenness_centrality_online` | Streaming betweenness | +| `katz_centrality_online` | Streaming Katz | +| `node2vec_online` | Incremental node2vec | + +### Integrations + +| Module | Description | +|--------|-------------| +| `nxalg` | 70+ NetworkX algorithm wrappers | +| `igraphalg` | igraph wrappers | +| `cugraph` | NVIDIA GPU algorithms | + +--- + +## Key algorithm examples + +### PageRank + +```cypher +CALL pagerank.get() YIELD node, rank; +CALL pagerank.get({max_iterations: 100, damping_factor: 0.85, stop_epsilon: 1e-5}) +YIELD node, rank SET node.rank = rank; +``` + +Parameters: `max_iterations` (100), `damping_factor` (0.85), `stop_epsilon` (1e-5), +`num_of_threads` (1). + +### Community detection (Louvain) + +```cypher +CALL community_detection.get() YIELD node, community_id; +``` + +Parameters: `weight` ("weight"), `coloring` (false), `min_graph_shrink` (100000), +`community_alg_threshold` (0.000001), `num_of_threads` (half HW threads). + +Weighted graphs require `--storage-properties-on-edges=true`. + +### Leiden community detection + +```cypher +CALL leiden_community_detection.get() YIELD node, community_id; +``` + +Parameters: `weight_property` ("weight"), `gamma` (1.0), `theta` (0.01), +`resolution_parameter` (0.01), `max_iterations` (inf). + +### Betweenness centrality + +```cypher +CALL betweenness_centrality.get() YIELD node, betweenness_centrality; +CALL betweenness_centrality.get(true, true) YIELD node, betweenness_centrality; +``` + +Parameters: `directed` (true), `normalized` (true), `threads` (half HW threads). + +### Degree centrality + +```cypher +CALL degree_centrality.get() YIELD node, degree; +CALL degree_centrality.get("in") YIELD node, degree; +``` + +Parameters: `type` ("undirected" | "in" | "out"). + +### Node2Vec embeddings + +```cypher +CALL node2vec.set_embeddings(false, 2.0, 0.5, 4, 5, 128) YIELD nodes, embeddings; +MATCH (n) RETURN n.id, n.embedding; +``` + +Key parameters: `is_directed`, `p` (return param), `q` (in-out param), +`num_walks`, `walk_length`, `vector_size`. + +Low `p` → structural equivalence (BFS-like). Low `q` → homophily (DFS-like). + +### LLM completions + +```cypher +RETURN llm.complete("Summarize: Memgraph is a graph database."); +RETURN llm.complete("Hello", {model: "ollama/llama2", api_base: "http://localhost:11434"}); +``` + +Config: `model`, `api_base`, `system_prompt`. Uses LiteLLM (requires +`pip install litellm` + provider API keys). + +With graph data: + +```cypher +MATCH (n:Article) +WITH collect(n.title + ": " + n.abstract) AS texts +WITH reduce(s = "", t IN texts | s + t + "\n") AS combined +RETURN llm.complete(combined, {system_prompt: "Summarize in 2 sentences."}); +``` + +--- + +## Online algorithm pattern (Enterprise) + +1. Initialize with `set()`: + ```cypher + CALL pagerank_online.set(100, 0.2) YIELD node, rank; + ``` + +2. Create a trigger with `update()`: + ```cypher + CREATE TRIGGER pagerankTrigger BEFORE COMMIT EXECUTE + CALL pagerank_online.update(createdVertices, createdEdges, deletedVertices, deletedEdges) + YIELD node, rank SET node.rank = rank; + ``` + +3. Read cached results with `get()`: + ```cypher + CALL pagerank_online.get() YIELD node, rank; + ``` + +4. Reset with `reset()`: + ```cypher + CALL pagerank_online.reset(); + ``` + +--- + +## Common patterns + +### Run algorithm → write results to nodes + +```cypher +CALL pagerank.get() YIELD node, rank SET node.rank = rank; +CALL community_detection.get() YIELD node, community_id SET node.community = community_id; +``` + +### Run on label-filtered subgraph + +```cypher +MATCH p=(n:User)-[r:FOLLOWS]->(m:User) +WITH project(p) AS subgraph +CALL pagerank.get(subgraph) YIELD node, rank +RETURN node.name, rank ORDER BY rank DESC; +``` + +### Chain algorithm with further query + +```cypher +CALL community_detection.get() YIELD node, community_id +WITH community_id, collect(node) AS members +WHERE size(members) > 5 +RETURN community_id, size(members) AS size +ORDER BY size DESC; +``` + +### Decision tree + +1. **Shortest path (unweighted)?** → BFS expansion: `-[*BFS]->` +2. **Shortest path (weighted)?** → WSP: `-[*WSHORTEST (r,n|r.w)]->` +3. **All equal shortest?** → ASP: `-[*ALLSHORTEST (r,n|r.w)]->` +4. **Top-K paths?** → KSP: `-[*KSHORTEST|K]->` +5. **Node importance?** → `pagerank.get()` or `betweenness_centrality.get()` +6. **Groups/clusters?** → `community_detection.get()` or `leiden_community_detection.get()` +7. **Node embeddings?** → `node2vec.set_embeddings()` or `embeddings.node_sentence()` +8. **Real-time updates?** → Enterprise `*_online` modules with triggers +9. **NetworkX algorithm?** → `nxalg.*` (70+ procedures; prefer native C++ for perf) +10. **LLM completion?** → `llm.complete()` function + +## Additional resources + +- For the complete module list with procedure signatures, see [REFERENCE.md](references/REFERENCE.md) diff --git a/memgraph-run-mage-algorithms/references/REFERENCE.md b/memgraph-run-mage-algorithms/references/REFERENCE.md new file mode 100644 index 0000000..0c299f1 --- /dev/null +++ b/memgraph-run-mage-algorithms/references/REFERENCE.md @@ -0,0 +1,147 @@ +# MAGE Module Reference + +Complete list of available algorithm modules organized by category. + +## Centrality + +| Module | Lang | Key procedure | YIELD | +|--------|------|---------------|-------| +| `pagerank` | C++ | `pagerank.get(max_iterations?, damping_factor?, stop_epsilon?, num_of_threads?)` | `node`, `rank` | +| `betweenness_centrality` | C++ | `betweenness_centrality.get(directed?, normalized?, threads?)` | `node`, `betweenness_centrality` | +| `degree_centrality` | C++ | `degree_centrality.get(type?)` | `node`, `degree` | +| `katz_centrality` | C++ | `katz_centrality.get(alpha?, beta?, epsilon?, max_iterations?)` | `node`, `rank` | + +## Community detection + +| Module | Lang | Key procedure | YIELD | +|--------|------|---------------|-------| +| `community_detection` | C++ | `community_detection.get(weight?, coloring?, min_graph_shrink?, community_alg_threshold?, coloring_alg_threshold?, num_of_threads?)` | `node`, `community_id` | +| `leiden_community_detection` | C++ | `leiden_community_detection.get(weight_property?, gamma?, theta?, resolution_parameter?, max_iterations?)` | `node`, `community_id` | +| `weakly_connected_components` | C++ | `weakly_connected_components.get()` | `node`, `component_id` | + +## Path & structure + +| Module | Lang | Key procedure | YIELD | +|--------|------|---------------|-------| +| `algo` | C++ | Various traversal utilities | — | +| `path` | C++ | Path navigation/analysis | — | +| `cycles` | C++ | `cycles.get()` | `cycles` | +| `bridges` | C++ | `bridges.get()` | `bridges` | +| `biconnected_components` | C++ | `biconnected_components.get()` | `bcc_id`, `node_from`, `node_to` | +| `bipartite_matching` | C++ | `bipartite_matching.max()` | `maximum_matching` | +| `max_flow` | Python | `max_flow.get_flow(source, sink, cap_property?)` | `max_flow` | +| `tsp` | Python | `tsp.solve(...)` | `sources`, `destinations` | +| `vrp` | Python | Vehicle routing | — | +| `set_cover` | Python | `set_cover.cp_solve(...)` | `total_cost`, `element_id` | +| `distance_calculator` | C++ | `distance_calculator.single(start, end)` | `distance` | + +## Graph ML + +| Module | Lang | Key procedure | YIELD | +|--------|------|---------------|-------| +| `node2vec` | Python | `node2vec.get_embeddings(is_directed, p, q, num_walks, walk_length, vector_size, ...)` | `nodes`, `embeddings` | +| `node2vec` | Python | `node2vec.set_embeddings(...)` — writes `embedding` property | `nodes`, `embeddings` | +| `gnn` | Python | `gnn.pyg_export(node_props?, edge_props?, label_prop?)` | `json_data` | +| `gnn_link_prediction` | Python | GNN link prediction pipeline | — | +| `gnn_node_classification` | Python | GNN node classification pipeline | — | +| `tgn` | Python | Temporal Graph Networks — `set_params()`, `update()`, `train_and_eval()`, `predict_link_score()` | — | +| `embeddings` | Python | `embeddings.node_sentence(input_nodes?, config?)` | `node`, `embedding` | +| `knn` | Python | K-nearest neighbors | — | +| `kmeans_clustering` | Python | `kmeans_clustering.get(n_clusters, ...)` | `node`, `cluster_id` | +| `graph_coloring` | Python | `graph_coloring.color_graph(...)` | `node`, `color` | +| `node_similarity` | C++ | `node_similarity.jaccard(node1, node2)` | `similarity` | + +## Online / dynamic (Enterprise) + +| Module | Lang | Procedures | YIELD | +|--------|------|-----------|-------| +| `pagerank_online` | C++ | `set(walks?, epsilon?)`, `get()`, `update(cv, ce, dv, de)`, `reset()` | `node`, `rank` | +| `community_detection_online` | C++ | `set(directed?, weighted?, ...)`, `get()`, `update(cv, ce, uv, ue, dv, de)`, `reset()` | `node`, `community_id` | +| `betweenness_centrality_online` | C++ | `set()`, `get()`, `update(...)`, `reset()` | `node`, `betweenness_centrality` | +| `katz_centrality_online` | C++ | `set()`, `get()`, `update(...)`, `reset()` | `node`, `rank` | +| `node2vec_online` | Python | `set()`, `get()`, `update(...)`, `reset()` | `node`, `embedding` | + +## Utility modules + +| Module | Lang | Description | +|--------|------|-------------| +| `collections` | C++ | List operations: `sort`, `union`, `union_all`, `remove_all`, `contains`, `flatten`, `frequencies_as_map`, `pairs`, `to_set`, `sum`, `partition` | +| `map` | C++ | Map operations | +| `text` | C++ | String manipulation | +| `math` | C++ | Math operations | +| `convert` / `convert_c` | C++ | Data structure conversion, `to_tree()` | +| `create` | C++ | `create.node()`, `create.nodes()`, `create.relationship()` | +| `merge` | C++ | MERGE-like operations | +| `refactor` | C++ | Node/relationship refactoring | +| `label` | C++ | Label utilities, `label.exists()` | +| `node` / `nodes` | C++ | Node management | +| `neighbors` | C++ | Direct neighbor queries | +| `set_property` | C++ | Dynamic property access/edit | +| `periodic` | C++ | Periodic query execution | +| `uuid_generator` | C++ | UUID generation | +| `csv_utils` | C++ | CSV file create/delete | +| `date` | Python | Date/time operations | +| `temporal` | Python | Extended temporal operations | +| `do` | C++ | Conditional query execution | +| `meta` | C++ | Graph node/rel info + online stats | +| `meta_util` | Python | Meta-level graph descriptions | +| `mgps` | Python | `mgps.version()`, `mgps.validate_predicate()` | +| `util_module` | C++ | Validation, MD5 hash | + +## LLM / AI + +| Module | Lang | Description | +|--------|------|-------------| +| `llm` | Python | `llm.complete(text, config?)` — LiteLLM completions. Config: `model`, `api_base`, `system_prompt` | +| `llm_util` | Python | **Deprecated** — `llm_util.schema()`. Use `SHOW SCHEMA INFO` instead | +| `embeddings` | Python | Sentence embeddings via SentenceTransformer or LiteLLM | + +## Data import/export + +| Module | Lang | Description | +|--------|------|-------------| +| `export_util` | Python | `export_util.json(path)` — graph to JSON | +| `import_util` | Python | `import_util.json(path)` — JSON to graph | +| `json_util` | Python | `json_util.load_from_path(path)`, `json_util.load_from_url(url)` | +| `xml_module` | Python | XML loading/parsing | +| `migrate` | Python | MySQL, SQL Server, Oracle access | + +## Integrations + +| Module | Lang | Description | +|--------|------|-------------| +| `nxalg` | Python | 70+ NetworkX algorithm wrappers | +| `igraphalg` | Python | igraph algorithm wrappers | +| `cugraph` | CUDA | NVIDIA GPU algorithms (centrality, link analysis, clustering) | +| `elasticsearch_synchronization` | Python | Memgraph ↔ Elasticsearch sync | + +## APOC → MAGE migration map + +| APOC | MAGE | +|------|------| +| `apoc.coll.union` | `collections.union()` | +| `apoc.coll.flatten` | `collections.flatten()` | +| `apoc.coll.toSet` | `collections.to_set()` | +| `apoc.coll.sum` | `collections.sum()` | +| `apoc.convert.toTree` | `convert_c.to_tree()` | +| `apoc.convert.fromJsonList` | `json_util.from_json_list()` | +| `apoc.convert.toJson` | `json_util.to_json()` | +| `apoc.create.node` | `create.node()` | +| `apoc.date.convertFormat` | `date.convert_format()` | +| `apoc.label.exists` | `label.exists()` | +| `apoc.meta.nodeTypeProperties` | `schema.node_type_properties()` | +| `apoc.refactor.*` | `refactor.*` | +| `apoc.text.*` | `text.*` | +| `apoc.util.md5` | `util_module.md5()` | +| `apoc.util.validatePredicate` | `mgps.validate_predicate()` | +| `apoc.version` | `mgps.version()` | + +## Procedure alias configuration + +Map Neo4j/APOC names to MAGE names via JSON file + `--query-callable-mappings-path`: + +```json +{"db.components": "mgps.components", "util.validate": "mgps.validate"} +``` + +Inspect: `SHOW QUERY CALLABLE MAPPINGS;` diff --git a/memgraph-write-cypher/SKILL.md b/memgraph-write-cypher/SKILL.md new file mode 100644 index 0000000..6c915fa --- /dev/null +++ b/memgraph-write-cypher/SKILL.md @@ -0,0 +1,398 @@ +--- +name: memgraph-write-cypher +description: >- + Write Cypher queries for Memgraph, including Memgraph-specific extensions like + deep path traversals (BFS, DFS, WSP, ASP, KSP with filter/weight lambdas), + text and vector search, parallel execution, and CALL subqueries. Use when the + user asks to write, fix, or optimize a Cypher query for Memgraph, or asks + about Cypher syntax differences between Memgraph and Neo4j. +compatibility: Any language with a Bolt-compatible driver. Memgraph instance required. +integrations: + - memgraph-lab>=3.11 +metadata: + version: "0.0.1" + author: memgraph +--- + +# Writing Cypher for Memgraph + +Memgraph implements openCypher with several extensions. This skill covers +Memgraph-specific syntax and patterns that differ from standard Neo4j Cypher. + +## Clauses quick reference + +### Read + +```cypher +MATCH (n:Person {name: "Alice"})-[:KNOWS]->(m) RETURN m; +MATCH (n:Person) WHERE n.age > 30 RETURN n ORDER BY n.age DESC LIMIT 10; +OPTIONAL MATCH (n)-[:LIVES_IN]->(c:City) RETURN n, c; +MATCH (n) RETURN DISTINCT n.name; +UNWIND [1, 2, 3] AS x RETURN x; +``` + +### Write + +```cypher +CREATE (n:Person {name: "Alice", age: 30}); +CREATE (a)-[:KNOWS {since: 2020}]->(b); +MERGE (n:Person {name: "Alice"}) ON CREATE SET n.created = timestamp() ON MATCH SET n.seen = timestamp(); +SET n.age = 31; +SET n:Employee; +REMOVE n:Intern; +REMOVE n.temporary; +DELETE r; +DETACH DELETE n; +DROP GRAPH; +``` + +### Control + +```cypher +WITH n ORDER BY n.name RETURN collect(n.name); +MATCH (a) RETURN a UNION MATCH (b) RETURN b; +FOREACH (x IN [1,2,3] | CREATE (:N {id: x})); +``` + +### Analysis + +```cypher +EXPLAIN MATCH (n:Person) RETURN n; +PROFILE MATCH (n:Person {age: 42}) RETURN n; +``` + +## MATCH patterns + +### Label matching + +```cypher +MATCH (n:Person) RETURN n; +MATCH (n:Person:Employee) RETURN n; -- ALL labels (AND) +MATCH (n:Country|Person) RETURN n; -- ANY label (OR) +``` + +Label OR (`|`) cannot be used in `CREATE` or `MERGE`. + +### Variable-length paths + +```cypher +MATCH (a)-[*1..3]->(b) RETURN a, b; -- 1 to 3 hops +MATCH (a)-[*2]->(b) RETURN a, b; -- exactly 2 hops +MATCH p=(a)-[*]->(b) RETURN relationships(p); +``` + +## Deep path traversals (Memgraph-specific) + +Built-in C++ traversal algorithms using relationship expansion syntax. + +### BFS (unweighted shortest) + +```cypher +MATCH p=(a {name: "A"})-[*BFS]->(b {name: "E"}) RETURN p; +MATCH p=(a)-[r:ROAD *BFS]->(b) RETURN p; +``` + +### DFS (all paths) + +```cypher +MATCH p=(a {name: "A"})-[*]->(b {name: "E"}) RETURN p; +``` + +### Weighted shortest path (WSP) + +```cypher +MATCH p=(a)-[*WSHORTEST (r, n | r.weight) total_weight]->(b) RETURN p, total_weight; +MATCH p=(a)-[*WSHORTEST (r, n | n.cost + coalesce(r.weight, 0)) total]->(b) RETURN p, total; +``` + +### All shortest paths (ASP) + +```cypher +MATCH p=(a)-[*ALLSHORTEST (r, n | r.weight)]->(b) RETURN p; +``` + +### K shortest paths (KSP) + +Source/target must be matched first, then passed via `WITH`: + +```cypher +MATCH (a:Node {name: "A"}), (b:Node {name: "E"}) +WITH a, b +MATCH p=(a)-[*KSHORTEST|3]->(b) RETURN p; +``` + +### Filter lambdas + +2-argument `(r, n | predicate)` -- r is the relationship, n is the target node: + +```cypher +MATCH p=(a)-[*BFS (r, n | r.eu_border = false AND n.drinks < 15)]->(b) RETURN p; +``` + +3-argument `(r, n, p | predicate)` -- p is the current path: + +```cypher +MATCH p=(a)-[* (r, n, p | type(last(relationships(p))) != 'CloseTo')]->(b) RETURN p; +``` + +WSP/ASP with filter (4-argument adds w for current weight): + +```cypher +MATCH p=(a)-[*WSHORTEST (r, n | r.weight) total (r, n, p, w | w < 1000)]->(b) RETURN p, total; +``` + +### Hop limits + +```cypher +MATCH p=(a)-[*BFS ..5]->(b) RETURN p; -- max 5 hops +MATCH p=(a)-[*WSHORTEST 4 (r, n | r.w)]->(b) RETURN p; +USING HOPS LIMIT 3 MATCH p=(a)-[*BFS]->(b) RETURN p; +``` + +## CALL subqueries + +```cypher +MATCH (p:Person) +CALL { + WITH p + MATCH (p)-[:KNOWS]->(friend) + RETURN count(friend) AS friendCount +} +RETURN p.name, friendCount; +``` + +Scoped import (v3.5+): + +```cypher +CALL (p) { + MATCH (p)-[:KNOWS]->(f) RETURN count(f) AS cnt +} +``` + +Batching writes: + +```cypher +LOAD CSV FROM "file.csv" WITH HEADER AS row +CALL { + WITH row + CREATE (:Node {id: row.id}) +} IN TRANSACTIONS OF 1000 ROWS; +``` + +## LOAD CSV / LOAD PARQUET + +```cypher +LOAD CSV FROM "/path/file.csv" WITH HEADER AS row +CREATE (:Person {name: row.Name, age: toInteger(row.Age)}); + +LOAD CSV FROM "https://example.com/data.csv" WITH HEADER IGNORE BAD DELIMITER "|" AS row +CREATE (:Node {id: row.id}); + +LOAD PARQUET FROM "/path/file.parquet" AS row CREATE (:Node {val: row.value}); +LOAD PARQUET FROM "s3://bucket/data.parquet" WITH CONFIG { + aws_region: "us-east-1", aws_access_key: $key, aws_secret_key: $secret +} AS row CREATE (:Node {val: row.value}); +``` + +CSV values are always strings -- cast with `toInteger()`, `toFloat()`, `date()`. + +## Periodic commit + +```cypher +USING PERIODIC COMMIT 1000 +LOAD CSV FROM "/data.csv" WITH HEADER AS row +CREATE (:Node {id: row.id}); +``` + +ACID only per batch. Avoid concurrent writes during periodic execution. + +## Parameters + +Use `$paramName` syntax (NOT curly braces): + +```cypher +MATCH (n:Person {name: $name}) RETURN n; +MATCH (n) SET n.value = $newValue; +MATCH (n) RETURN n LIMIT $limit; +``` + +Label parameterization (v3.1+): + +```cypher +MATCH (n:$label) RETURN n; +SET n:$labels; +``` + +Property map in CREATE only (not MATCH/MERGE): + +```cypher +CREATE (n $propertyMap); +``` + +## Text search + +```cypher +CREATE TEXT INDEX myIdx ON :Article; +CREATE TEXT INDEX myIdx ON :Article(title, body); +CREATE TEXT EDGE INDEX edgeIdx ON :CITES; + +CALL text_search.search("myIdx", "data.title:GraphRAG") YIELD node, score RETURN node, score; +CALL text_search.search_all("myIdx", "GraphRAG") YIELD node RETURN node; +CALL text_search.regex_search("myIdx", "graph.*") YIELD node RETURN node; + +DROP TEXT INDEX myIdx; +``` + +Property names in queries use `data.` prefix. Boolean operators: `AND`, `OR`, `NOT`. + +## Vector search + +```cypher +CREATE VECTOR INDEX vecIdx ON :Article(embedding) + WITH CONFIG {"dimension": 256, "capacity": 10000, "metric": "cos"}; + +CALL vector_search.search("vecIdx", 10, [0.1, 0.2, ...]) YIELD node, distance, similarity +RETURN node, similarity ORDER BY similarity DESC; + +SHOW VECTOR INDEX INFO; +DROP VECTOR INDEX vecIdx; +``` + +Metrics: `l2sq` (default), `cos`, `ip`, `pearson`, `haversine`, `hamming`, `jaccard`. + +## Parallel execution (Enterprise) + +```cypher +GRANT PARALLEL_EXECUTION TO user; +USING PARALLEL EXECUTION MATCH (n:Person) RETURN count(n); +USING PARALLEL EXECUTION 8 MATCH (n) WHERE n.age > 30 RETURN count(*); +``` + +Best for aggregation queries on scan operators. Requires `priority_queue` scheduler. + +## Schema procedures + +```cypher +SHOW SCHEMA INFO; +CALL schema.node_type_properties() YIELD nodeType, nodeLabels, propertyName, propertyTypes; +CALL schema.rel_type_properties() YIELD relType, propertyName, propertyTypes; +``` + +Requires `--schema-info-enabled=true`. + +## TTL (Enterprise) + +```cypher +ENABLE TTL EVERY "1h" AT "03:00:00"; +CREATE (:TTL {ttl: timestamp(), name: "temp"}); +STOP TTL; +DISABLE TTL; +``` + +Reserved label `TTL`, reserved property `ttl` (microseconds since epoch). + +## Expressions + +### String predicates + +```cypher +WHERE n.name STARTS WITH "Al" +WHERE n.name ENDS WITH "son" +WHERE n.name CONTAINS "ice" +WHERE n.name =~ "^A.*e$" +``` + +### CASE + +```cypher +RETURN CASE n.status WHEN "active" THEN "A" WHEN "inactive" THEN "I" ELSE "?" END; +RETURN CASE WHEN n.age < 18 THEN "minor" WHEN n.age >= 65 THEN "senior" ELSE "adult" END; +``` + +`exists()` cannot be used inside CASE. + +### Pattern comprehension + +```cypher +RETURN [(n)-->(m:Movie) WHERE m.year > 2000 | m.title] AS recentMovies; +``` + +### List comprehension + +```cypher +RETURN [x IN range(1, 10) WHERE x % 2 = 0 | x * x] AS evenSquares; +``` + +### Existential subqueries + +```cypher +WHERE EXISTS { MATCH (n)-[:KNOWS]->(:Person {city: "Berlin"}) } +WHERE NOT EXISTS { MATCH (n)-[:BLOCKED]->() } +``` + +## Query optimization + +### Key operators in PROFILE output + +| Operator | Meaning | Action | +|----------|---------|--------| +| `ScanAll` | Full scan | Add indexes | +| `ScanAllByLabel` | Label index | OK for label-only queries | +| `ScanAllByLabelProperties` | Label+property index | Best | +| `EdgeUniquenessFilter` | Cyphermorphism | Split MATCH if unwanted | +| `Cartesian` | Cartesian product | Ensure smaller set is left branch | + +### Index hinting + +```cypher +USING INDEX :Person(name) +MATCH (n:Person {name: "Alice"}) RETURN n; +``` + +### Analyze graph (run after data load) + +```cypher +ANALYZE GRAPH; +ANALYZE GRAPH ON LABELS :Person, :City; +``` + +### Reduce roundtrip + +Use `project(path)` to deduplicate nodes in path results: + +```cypher +MATCH path=(a)-[*BFS]->(b) WITH project(path) AS subgraph RETURN subgraph; +``` + +Return only needed data: + +```cypher +MATCH (n:Person) RETURN n.name, n.age; +``` + +### Efficient deletes (large graphs) + +```cypher +USING PERIODIC COMMIT 10000 MATCH ()-[r]->() DELETE r; +USING PERIODIC COMMIT 10000 MATCH (n) DELETE n; +``` + +Never `MATCH (n) DETACH DELETE n` on >1M nodes (Delta memory spike). + +## Key differences from Neo4j + +| Feature | Neo4j | Memgraph | +|---------|-------|----------| +| Shortest path | `shortestPath(...)` | `-[*BFS]->` | +| K shortest | `SHORTEST k` | `-[*KSHORTEST\|k]->` | +| Fixed length | `--{2}--` | `-[*2]-` | +| NOT label | `WHERE NOT n:Label` | `WHERE NOT n:Label` (same since v3) | +| `elementId()` | Returns string | Use `id()` (integer) | +| COUNT/COLLECT subquery | Supported | Use `count()`/`collect()` aggregations | +| Type predicate | `val IS :: INTEGER` | `valueType(val) = "INTEGER"` | +| Index creation | Sometimes auto | Always manual (unless flag enabled) | +| Constraint naming | Stored | Parsed but not stored (warning) | + +## Additional resources + +- For the complete built-in function catalog, see [REFERENCE.md](references/REFERENCE.md) diff --git a/memgraph-write-cypher/references/REFERENCE.md b/memgraph-write-cypher/references/REFERENCE.md new file mode 100644 index 0000000..d363be1 --- /dev/null +++ b/memgraph-write-cypher/references/REFERENCE.md @@ -0,0 +1,157 @@ +# Cypher Built-in Function Reference (Memgraph) + +## Scalar functions + +| Function | Signature | Returns | +|----------|-----------|---------| +| `id` | `(Node\|Rel) -> integer` | Persisted internal ID | +| `type` | `(Rel) -> string` | Relationship type | +| `labels` | `(Node) -> List[string]` | Node labels | +| `properties` | `(Node\|Rel) -> Map` | All properties as map | +| `propertySize` | `(entity, propName) -> integer` | RAM bytes for property | +| `keys` | `(Map\|Node\|Rel) -> List` | Property keys | +| `values` | `(Map\|Node\|Rel) -> List` | Property values | +| `coalesce` | `(expr, ...) -> any` | First non-null | +| `head` | `(List) -> any` | First element | +| `last` | `(List) -> any` | Last element | +| `tail` | `(List) -> List` | All except first | +| `size` | `(List\|string\|Map\|Path) -> integer` | Length/count | +| `length` | `(List\|string\|Map\|Path) -> integer` | Same as size | +| `degree` | `(Node) -> integer` | Total degree | +| `inDegree` | `(Node) -> integer` | Inbound degree | +| `outDegree` | `(Node) -> integer` | Outbound degree | +| `startNode` | `(Rel) -> Node` | Source node | +| `endNode` | `(Rel) -> Node` | Target node | +| `nodes` | `(Path) -> List[Node]` | Nodes in path | +| `relationships` | `(Path) -> List[Rel]` | Rels in path | +| `counter` | `(name, initial, incr?) -> integer` | Per-query unique counter | +| `randomUUID` | `() -> string` | UUID v4 | +| `timestamp` | `() -> integer` | Microseconds since epoch | +| `valueType` | `(any) -> string` | Type name as string | +| `assert` | `(bool, msg?) -> void` | Raises if false | + +## Conversion functions + +| Function | Signature | Returns | +|----------|-----------|---------| +| `toBoolean` | `(bool\|int\|string) -> boolean` | | +| `toFloat` | `(number\|string) -> float` | | +| `toInteger` | `(bool\|number\|string) -> integer` | | +| `toString` | `(any) -> string` | Errors on unstringifiable | +| `toStringOrNull` | `(any) -> string\|null` | Null on failure | +| `toBooleanList` | `(List) -> List[boolean]` | | +| `toFloatList` | `(List) -> List[float]` | | +| `toIntegerList` | `(List) -> List[integer]` | | +| `toSet` | `(List) -> List` | Remove duplicates | + +## String functions + +| Function | Signature | Returns | +|----------|-----------|---------| +| `toLower` | `(string) -> string` | Lower case | +| `toUpper` | `(string) -> string` | Upper case | +| `trim` | `(string) -> string` | Strip whitespace | +| `lTrim` | `(string) -> string` | Strip leading | +| `rTrim` | `(string) -> string` | Strip trailing | +| `replace` | `(original, search, replacement) -> string` | | +| `reverse` | `(string) -> string` | | +| `split` | `(string, delimiter) -> List[string]` | | +| `substring` | `(string, start, length?) -> string` | | +| `left` | `(string, length) -> string` | | +| `right` | `(string, length) -> string` | | +| `contains` | `(string, substring) -> boolean` | Also: `a CONTAINS b` | +| `startsWith` | `(string, prefix) -> boolean` | Also: `a STARTS WITH b` | +| `endsWith` | `(string, suffix) -> boolean` | Also: `a ENDS WITH b` | + +## Math functions + +| Function | Returns | Notes | +|----------|---------|-------| +| `abs(x)` | number | Absolute value | +| `ceil(x)` | integer | Round up | +| `floor(x)` | integer | Round down | +| `round(x)` | integer | Commercial rounding | +| `sign(x)` | integer | -1, 0, or 1 | +| `sqrt(x)` | float | | +| `exp(x)` | float | e^x | +| `log(x)` | float | Natural log | +| `log10(x)` | float | Base-10 log | +| `rand()` | float | [0, 1) | +| `e()` | float | Euler's number | +| `pi()` | float | Pi | +| `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2` | float | Trig | + +## Aggregation functions + +| Function | Returns | Notes | +|----------|---------|-------| +| `count(expr)` | integer | Non-null count; `count(*)` for all rows | +| `sum(expr)` | number | | +| `avg(expr)` | float | | +| `min(expr)` | any | Supports temporal types | +| `max(expr)` | any | Supports temporal types | +| `collect(expr)` | List | Collect into list | +| `collect(key, value)` | Map | Collect into map (keys must be strings) | + +All aggregations support `DISTINCT`. + +## List functions + +| Function | Signature | Returns | +|----------|-----------|---------| +| `range` | `(start, end, step?) -> List[integer]` | | +| `reduce` | `(acc = init, var IN list \| expr) -> any` | | +| `extract` | `(var IN list \| expr) -> List` | | +| `all` | `(var IN list WHERE pred) -> boolean` | | +| `any` | `(var IN list WHERE pred) -> boolean` | | +| `none` | `(var IN list WHERE pred) -> boolean` | | +| `single` | `(var IN list WHERE pred) -> boolean` | | +| `uniformSample` | `(list, size) -> List` | Random sample | + +## Temporal functions + +| Function | Constructor | +|----------|-------------| +| `duration` | `duration("P2DT3H")` or `duration({hour: 3})` | +| `date` | `date("2024-01-15")` or `date({year: 2024, month: 1, day: 15})` | +| `localTime` | `localTime("14:30:00")` or `localTime({hour: 14, minute: 30})` | +| `localDateTime` | `localDateTime("2024-01-15T14:30:00")` | +| `datetime` | `datetime("2024-01-15T14:30:00Z")` or `datetime({year:2024, timezone:"UTC"})` | + +Temporal arithmetic: Date +/- Duration = Date, DateTime - DateTime = Duration, etc. + +## Spatial functions + +| Function | Signature | Returns | +|----------|-----------|---------| +| `point` | `({x, y, [z], [crs\|srid]}) -> Point` | WGS-84 or Cartesian | +| `point.distance` | `(Point, Point) -> float` | Meters for WGS-84 | +| `point.withinbbox` | `(Point, lowerLeft, upperRight) -> boolean` | | + +## Graph projection + +| Function | Signature | +|----------|-----------| +| `project` | `(path) -> {nodes, edges}` | +| `project` | `(List[Node], List[Rel]) -> {nodes, edges}` | + +## Auth functions + +| Function | Returns | +|----------|---------| +| `username()` | Current user (null if unauth) | +| `roles(db?)` | List of roles | + +## Enum + +| Function | Signature | +|----------|-----------| +| `ToEnum` | `("EnumName::Value") -> Enum` | +| `ToEnum` | `("EnumName", "Value") -> Enum` | + +## Discovery + +```cypher +CALL mg.functions() YIELD *; +CALL mg.procedures() YIELD name, signature; +``` From d86349414f524abcf3b3d00c468fd41d8cc467be Mon Sep 17 00:00:00 2001 From: Toni Lastre Date: Thu, 21 May 2026 00:30:20 +0200 Subject: [PATCH 2/5] chore: Move integrations to metadata --- memgraph-indexes-and-database-administration/SKILL.md | 4 ++-- memgraph-lab-write-gss/SKILL.md | 4 ++-- memgraph-model-graph-data/SKILL.md | 4 ++-- memgraph-python-query-modules/SKILL.md | 4 ++-- memgraph-run-mage-algorithms/SKILL.md | 4 ++-- memgraph-write-cypher/SKILL.md | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/memgraph-indexes-and-database-administration/SKILL.md b/memgraph-indexes-and-database-administration/SKILL.md index e9e2259..ca1c233 100644 --- a/memgraph-indexes-and-database-administration/SKILL.md +++ b/memgraph-indexes-and-database-administration/SKILL.md @@ -7,11 +7,11 @@ description: >- transaction isolation, storage modes, snapshots, WAL, or performance tuning via schema-level DDL. compatibility: Any language with a Bolt-compatible driver. Memgraph instance required. -integrations: - - memgraph-lab>=3.11 metadata: version: "0.0.1" author: memgraph + integrations: + - memgraph-lab>=3.11 --- # Managing Indexes & Schema in Memgraph diff --git a/memgraph-lab-write-gss/SKILL.md b/memgraph-lab-write-gss/SKILL.md index 2f19a09..1e80a05 100644 --- a/memgraph-lab-write-gss/SKILL.md +++ b/memgraph-lab-write-gss/SKILL.md @@ -6,11 +6,11 @@ description: >- colors, add labels, set images, configure graph layouts, or write any GSS / Graph Style Script code for Memgraph Lab. compatibility: Memgraph Lab required. -integrations: - - memgraph-lab>=3.11 metadata: version: "0.0.1" author: memgraph + integrations: + - memgraph-lab>=3.11 --- # Writing Graph Style Script (GSS) diff --git a/memgraph-model-graph-data/SKILL.md b/memgraph-model-graph-data/SKILL.md index 8479513..bc2b669 100644 --- a/memgraph-model-graph-data/SKILL.md +++ b/memgraph-model-graph-data/SKILL.md @@ -7,11 +7,11 @@ description: >- import CSV/Parquet into a graph, build a knowledge graph, or optimize a Memgraph data model for performance and memory. compatibility: Any language with a Bolt-compatible driver. Memgraph instance required. -integrations: - - memgraph-lab>=3.11 metadata: version: "0.0.1" author: memgraph + integrations: + - memgraph-lab>=3.11 --- # Graph Data Modeling for Memgraph diff --git a/memgraph-python-query-modules/SKILL.md b/memgraph-python-query-modules/SKILL.md index 307f44c..b0d243d 100644 --- a/memgraph-python-query-modules/SKILL.md +++ b/memgraph-python-query-modules/SKILL.md @@ -2,11 +2,11 @@ name: memgraph-python-query-modules description: Develop custom query modules in Python for Memgraph graph database. Use when creating custom graph algorithms, procedures (@mgp.read_proc, @mgp.write_proc), or functions (@mgp.function) for Memgraph. Covers the mgp Python API, graph traversal, data transformations, and module deployment. compatibility: Requires Memgraph instance (Docker recommended), Python 3.7+ -integrations: - - memgraph-lab>=3.11 metadata: version: "0.0.1" author: memgraph + integrations: + - memgraph-lab>=3.11 --- # Memgraph Python Query Modules diff --git a/memgraph-run-mage-algorithms/SKILL.md b/memgraph-run-mage-algorithms/SKILL.md index 435af26..5a0aabd 100644 --- a/memgraph-run-mage-algorithms/SKILL.md +++ b/memgraph-run-mage-algorithms/SKILL.md @@ -8,11 +8,11 @@ description: >- shortest paths, detect communities, compute centrality, generate embeddings, or call any MAGE procedure. compatibility: Any language with a Bolt-compatible driver. Memgraph instance required. -integrations: - - memgraph-lab>=3.11 metadata: version: "0.0.1" author: memgraph + integrations: + - memgraph-lab>=3.11 --- # Running MAGE Graph Algorithms diff --git a/memgraph-write-cypher/SKILL.md b/memgraph-write-cypher/SKILL.md index 6c915fa..e8b9e6c 100644 --- a/memgraph-write-cypher/SKILL.md +++ b/memgraph-write-cypher/SKILL.md @@ -7,11 +7,11 @@ description: >- user asks to write, fix, or optimize a Cypher query for Memgraph, or asks about Cypher syntax differences between Memgraph and Neo4j. compatibility: Any language with a Bolt-compatible driver. Memgraph instance required. -integrations: - - memgraph-lab>=3.11 metadata: version: "0.0.1" author: memgraph + integrations: + - memgraph-lab>=3.11 --- # Writing Cypher for Memgraph From 83c06b56b2d8622970490aed12884855c33d16ff Mon Sep 17 00:00:00 2001 From: Toni Lastre Date: Fri, 22 May 2026 11:09:17 +0200 Subject: [PATCH 3/5] chore: Fix GSS function call --- memgraph-lab-write-gss/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/memgraph-lab-write-gss/SKILL.md b/memgraph-lab-write-gss/SKILL.md index 1e80a05..9912b42 100644 --- a/memgraph-lab-write-gss/SKILL.md +++ b/memgraph-lab-write-gss/SKILL.md @@ -318,7 +318,7 @@ modifying GSS to catch syntax errors before applying the style in Memgraph Lab. ## Preview -If the `preview-gss` tool is available, run it after validation to see how +If the `evaluate-gss` tool is available, run it after validation to see how styles resolve against actual graph data. Pass custom nodes and relationships to the tool and inspect the computed style properties (color, size, label, etc.) for each node and edge. This catches logic errors that syntax validation From 74ea5b889f3ef49c95fc5f90d6d6c8179758e09a Mon Sep 17 00:00:00 2001 From: Toni Lastre Date: Mon, 25 May 2026 13:28:11 +0200 Subject: [PATCH 4/5] chore: Add plugin for lab integration --- memgraph-brand-ui/SKILL.md | 38 +++++++++---------- .../SKILL.md | 4 +- .../references/REFERENCE.md | 0 .../SKILL.md | 20 +++++----- memgraph-lab-write-gss/SKILL.md | 4 +- memgraph-model-graph-data/SKILL.md | 32 ++++++++-------- memgraph-python-query-modules/SKILL.md | 2 - memgraph-run-mage-algorithms/SKILL.md | 8 ++-- .../references/REFERENCE.md | 24 ++++++------ plugin-lab.json | 19 ++++++++++ 10 files changed, 79 insertions(+), 72 deletions(-) rename {memgraph-write-cypher => memgraph-cypher-syntax}/SKILL.md (99%) rename {memgraph-write-cypher => memgraph-cypher-syntax}/references/REFERENCE.md (100%) create mode 100644 plugin-lab.json diff --git a/memgraph-brand-ui/SKILL.md b/memgraph-brand-ui/SKILL.md index aa54570..348e5a9 100644 --- a/memgraph-brand-ui/SKILL.md +++ b/memgraph-brand-ui/SKILL.md @@ -9,7 +9,7 @@ metadata: # Memgraph Brand Skill -You are designing for **Memgraph** — an in-memory graph database. The product surface is workbench-dense, neutral-first, technical. Engineers, not consumers. +You are designing for **Memgraph** - an in-memory graph database. The product surface is workbench-dense, neutral-first, technical. Engineers, not consumers. ## When to Use @@ -33,26 +33,26 @@ If a choice fights either rule, it's wrong. --- -## Tokens (use these literally — do not invent variants) +## Tokens (use these literally - do not invent variants) ### Color ``` --brand-black: #231F20 /* primary text, strong borders, dark surfaces */ ---brand-orange: #FB6E00 /* accent only — primary buttons, active states, links */ +--brand-orange: #FB6E00 /* accent only - primary buttons, active states, links */ --orange-tint: #FFF4EB /* soft orange badge bg */ --gray-1: #646265 /* secondary text */ --gray-2: #857F87 /* muted text */ --gray-3: #BAB8BB /* placeholder, disabled */ --gray-4: #E6E6E6 /* hairline borders, dividers */ ---gray-5: #F9F9F9 /* canvas/zebra/hover bg — NEVER on cards */ +--gray-5: #F9F9F9 /* canvas/zebra/hover bg - NEVER on cards */ --white: #FFFFFF /* default surface, including all cards */ --success: #19AF66 --error: #DC2223 --warning: #FFC500 /* brand yellow, used semantically */ ---purple: #720096 /* marketing accent only — not in product chrome */ +--purple: #720096 /* marketing accent only - not in product chrome */ ``` **Color usage:** @@ -62,7 +62,7 @@ If a choice fights either rule, it's wrong. - Orange budget: ~10% of pixels. Primary CTA, active nav indicator, focus ring, the wordmark. That's it. - Yellow / red / purple appear **only semantically** (warning / error / marketing-illustration accent). Not as decoration. -### The Brand Gradient — Handle With Care +### The Brand Gradient - Handle With Care A `#FFC500 → #DC2223 → #720096` gradient exists in the brand kit. It is **never a background fill on a UI surface**. Acceptable uses, max one per page: - A thin 2–4px decorative bar at the top of a marketing hero @@ -73,11 +73,11 @@ If you find yourself painting a card, button, header, or section bg with the gra ### Type -- **UI everything: `Inter Tight`.** Headings, body, buttons, labels, nav, tables — all Inter Tight. +- **UI everything: `Inter Tight`.** Headings, body, buttons, labels, nav, tables - all Inter Tight. - **Code only: `Ubuntu Mono`.** Cypher queries, code blocks, inline code, monospace numbers in dense tables. - Do not introduce Roboto, Encode Sans, system-ui, Inter, or anything else. -Weight scale (use these — do not freestyle): +Weight scale (use these - do not freestyle): ``` Body / paragraph: 400 UI labels, nav: 500 @@ -94,21 +94,21 @@ H4: 20 / 28 H5 / strong: 16 / 20 Body: 16 / 24 Small: 13 / 20 (default for dense UI: chips, tags, table cells) -Micro: 11 / 12 (eyebrow labels, table column heads — uppercase, +0.06em tracking) +Micro: 11 / 12 (eyebrow labels, table column heads - uppercase, +0.06em tracking) Code: 13 / 20 Ubuntu Mono ``` Letter-spacing: `-0.01em` on display headings, `+0.04em` on uppercase button labels and eyebrows, default elsewhere. -### Spacing — Strict 4px Grid +### Spacing - Strict 4px Grid `0, 4, 8, 12, 16, 24, 32, 40, 48, 64, 80`. Nothing in between. Component padding is usually 8/12/16. Card padding 16/24. Section gaps 40/64. -### Radii — Strict 2–8px +### Radii - Strict 2–8px ``` 2px tiny tags, swatches, micro-badges -4px default — buttons, inputs, cards, dropdowns, modals +4px default - buttons, inputs, cards, dropdowns, modals 8px large containers, marketing cards, hero tiles 999px ONLY avatars, status dots, loading spinners ``` @@ -118,7 +118,7 @@ Never 12, 16, 24. Never `border-radius: 9999px` on buttons or chips. ``` --shadow-card: 0 2px 10px 0 rgba(0,0,0,0.10); /* card lift */ ---shadow-button: 2px 2px 8px 0 rgba(0,0,0,0.24); /* primary button — note: asymmetric */ +--shadow-button: 2px 2px 8px 0 rgba(0,0,0,0.24); /* primary button - note: asymmetric */ --shadow-elev: 0 0 4px 0 rgba(0,0,0,0.25); /* outlined button */ --shadow-drop: 0 4px 12px 0 rgba(35,31,32,0.16);/* popovers, dropdowns */ --shadow-focus: 0 0 0 3px rgba(251,110,0,0.24); /* keyboard focus */ @@ -146,11 +146,11 @@ No inner shadows. No glow. Stack at most one shadow per element. ``` -Hover: primary darkens to `#E36300`, secondary fills to `#F9F9F9`. Press: subtle scale `0.98` or shadow flatten — never color shrink. +Hover: primary darkens to `#E36300`, secondary fills to `#F9F9F9`. Press: subtle scale `0.98` or shadow flatten - never color shrink. ### Cards -White background. **Either** a 1px `#E6E6E6` border **or** the card shadow — rarely both. 4px radius. 16/24 inner padding. +White background. **Either** a 1px `#E6E6E6` border **or** the card shadow - rarely both. 4px radius. 16/24 inner padding. ```html
-Memgraph — … +Memgraph - …