bitflicker64 commented on code in PR #499: URL: https://github.com/apache/hugegraph-doc/pull/499#discussion_r4249364024
########## content/en/docs/language/hugegraph-cypher.md: ########## @@ -0,0 +1,164 @@ +--- +title: "HugeGraph Cypher" +linkTitle: "Cypher Query Language" +weight: 2 +--- + +### Overview + +HugeGraph supports [Cypher](https://opencypher.org/), the declarative graph query language originally created by Neo4j and standardized as [openCypher](https://github.com/opencypher/openCypher). While Gremlin is an imperative traversal language, Cypher lets you describe the graph pattern you want (`MATCH`, `WHERE`, `RETURN`) and lets the engine decide how to execute it. + +HugeGraph executes Cypher by translating the statement to Gremlin through the [cypher-for-gremlin](https://github.com/opencypher/cypher-for-gremlin) transpiler, then running the resulting traversal on the TinkerPop stack. This means Cypher support in HugeGraph covers the openCypher 9 surface that the transpiler implements — see [Known limitations](#known-limitations) below. + +### When to use Cypher vs Gremlin + +| Use Cypher when | Use Gremlin when | +|---|---| +| You are migrating from Neo4j or already know Cypher | You need the full HugeGraph feature surface | +| You want declarative pattern matching (`MATCH (a)-[:KNOWS]->(b)`) | You need fine-grained traversal control, custom steps, or lambdas | +| Your queries are read-mostly patterns and projections | You need graph algorithms, OLAP, or features Cypher does not expose | + +For anything Cypher cannot express, the equivalent Gremlin query always works — examples below show both. + +### Calling the Cypher API + +Cypher statements are submitted to the HugeGraph Server through the REST endpoint: + +```http +GET /graphspaces/{graphspace}/graphs/{graph}/cypher?cypher={statement} +POST /graphspaces/{graphspace}/graphs/{graph}/cypher +``` + +The endpoint always requires an `Authorization` header (`Basic` or `Bearer`), even when server authentication is disabled — the credentials are forwarded to the Gremlin Server. `Basic` credentials must be encoded with URL-safe Base64 (`-` and `_` instead of `+` and `/`), and the password must not contain `:`; use `Bearer` for accounts that cannot meet either condition. See the [Cypher REST API reference](/docs/clients/restful-api/cypher/) for the full request/response format. + +Example: + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + "http://localhost:8080/graphspaces/DEFAULT/graphs/hugegraph/cypher?cypher=MATCH%20(n:person)%20RETURN%20n.name%20LIMIT%2010" +``` + +From the Java client, use `CypherManager` (see [hugegraph-client](/docs/clients/hugegraph-client/)): + +```java +ResultSet resultSet = hugeClient.cypher().execute("MATCH (n:person) RETURN n.name LIMIT 10"); +``` + +### Basic examples + +The examples below assume a graph with `person` vertices (`name`, `age`, `city` properties) and `knows` edges. +They assume the labels declare `nullableKeys("age", "city")` on `person` and nullable properties on `knows` — +for example the schema in `scripts/example.groovy` modified with those nullable keys. On a schema without them +(the released default marks `age` and `city` non-null on `person`), the create and update examples below fail +the non-null property check: set every non-null property in each statement, or declare the labels with +`nullableKeys` first. If the `person` label uses the `PRIMARY_KEY` id strategy (as it does in `scripts/example.groovy`), the two-vertex +shorthand `CREATE (a)-[:knows]->(b)` is rejected with an execution error reporting that the primary keys must be set +(verified on `master` with [#3289](https://github.com/apache/hugegraph/pull/3289) merged), so the edge example below creates the vertices +first and then matches them. The shorthand works with the `AUTOMATIC` id strategy. + +#### Read — MATCH / WHERE / RETURN + +```cypher +// All persons named "marko" +MATCH (n:person) WHERE n.name = 'marko' RETURN n +``` + +```cypher +// Projection with ordering and limit +MATCH (n:person) +WHERE n.age > 30 Review Comment: Important: this read example fails on the schema the section tells readers to use, and none of the three pages says that a property predicate needs a matching index. Line 51 names `scripts/example.groovy` (with nullable keys added). That script gives `person` two SECONDARY indexes (`personByCity` on `city`, `personByAgeAndCity` on `age`, `city`) and no RANGE index on `age`. Translator 1.0.4 turns this statement into `g.V().hasLabel('person').has('age', gt(30))...`. Measured on a 1.7.0 server (RocksDB, started with `-p true` so `example.groovy` is loaded): `POST /graphspaces/DEFAULT/graphs/hugegraph/cypher` with this statement returns HTTP 200, `status.code` 400 and `Don't accept query based on properties [age] that are not indexed in label 'person', may not match secondary/range condition` `WHERE n.age = 29`, `WHERE n.city = 'Beijing'` and the other read and aggregation examples succeed on the same graph. I did not run a 5039e5b server, but `example.groovy` is unchanged there and `GraphIndexTransaction` still throws `NoIndexException` when a range condition has no RANGE index (`matchSingleOrCompositeIndex`, `matchRangeOrSearchIndexLabels`). `cypher-compatibility.md` lines 101-103 say a run on 5039e5b re-executed "projection with ordering and limit" successfully, without naming the schema. The fixture on line 89 of that page has a RANGE index on `age`, which would explain it. Requested change: - Add a RANGE index on `person.age` to the assumed schema in lines 49-57, or use a predicate the example schema indexes. - State in the guide that a `WHERE` property predicate needs an index that covers it, and add that as a row in the "Habits you need to change" table of `migrate-from-neo4j.md` (line 23 lists `MATCH`/`WHERE` as "Works as-is"). - Name the schema and indexes used for the run in `cypher-compatibility.md` lines 101-103. - Apply the same to the CN pages (`hugegraph-cypher.md` line 68, `migrate-from-neo4j.md` line 21). ########## content/en/docs/language/hugegraph-cypher.md: ########## @@ -0,0 +1,164 @@ +--- +title: "HugeGraph Cypher" +linkTitle: "Cypher Query Language" +weight: 2 +--- + +### Overview + +HugeGraph supports [Cypher](https://opencypher.org/), the declarative graph query language originally created by Neo4j and standardized as [openCypher](https://github.com/opencypher/openCypher). While Gremlin is an imperative traversal language, Cypher lets you describe the graph pattern you want (`MATCH`, `WHERE`, `RETURN`) and lets the engine decide how to execute it. + +HugeGraph executes Cypher by translating the statement to Gremlin through the [cypher-for-gremlin](https://github.com/opencypher/cypher-for-gremlin) transpiler, then running the resulting traversal on the TinkerPop stack. This means Cypher support in HugeGraph covers the openCypher 9 surface that the transpiler implements — see [Known limitations](#known-limitations) below. + +### When to use Cypher vs Gremlin + +| Use Cypher when | Use Gremlin when | +|---|---| +| You are migrating from Neo4j or already know Cypher | You need the full HugeGraph feature surface | +| You want declarative pattern matching (`MATCH (a)-[:KNOWS]->(b)`) | You need fine-grained traversal control, custom steps, or lambdas | +| Your queries are read-mostly patterns and projections | You need graph algorithms, OLAP, or features Cypher does not expose | + +For anything Cypher cannot express, the equivalent Gremlin query always works — examples below show both. + +### Calling the Cypher API + +Cypher statements are submitted to the HugeGraph Server through the REST endpoint: + +```http +GET /graphspaces/{graphspace}/graphs/{graph}/cypher?cypher={statement} +POST /graphspaces/{graphspace}/graphs/{graph}/cypher +``` + +The endpoint always requires an `Authorization` header (`Basic` or `Bearer`), even when server authentication is disabled — the credentials are forwarded to the Gremlin Server. `Basic` credentials must be encoded with URL-safe Base64 (`-` and `_` instead of `+` and `/`), and the password must not contain `:`; use `Bearer` for accounts that cannot meet either condition. See the [Cypher REST API reference](/docs/clients/restful-api/cypher/) for the full request/response format. + +Example: + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + "http://localhost:8080/graphspaces/DEFAULT/graphs/hugegraph/cypher?cypher=MATCH%20(n:person)%20RETURN%20n.name%20LIMIT%2010" +``` + +From the Java client, use `CypherManager` (see [hugegraph-client](/docs/clients/hugegraph-client/)): + +```java +ResultSet resultSet = hugeClient.cypher().execute("MATCH (n:person) RETURN n.name LIMIT 10"); +``` + +### Basic examples + +The examples below assume a graph with `person` vertices (`name`, `age`, `city` properties) and `knows` edges. +They assume the labels declare `nullableKeys("age", "city")` on `person` and nullable properties on `knows` — +for example the schema in `scripts/example.groovy` modified with those nullable keys. On a schema without them +(the released default marks `age` and `city` non-null on `person`), the create and update examples below fail +the non-null property check: set every non-null property in each statement, or declare the labels with +`nullableKeys` first. If the `person` label uses the `PRIMARY_KEY` id strategy (as it does in `scripts/example.groovy`), the two-vertex +shorthand `CREATE (a)-[:knows]->(b)` is rejected with an execution error reporting that the primary keys must be set +(verified on `master` with [#3289](https://github.com/apache/hugegraph/pull/3289) merged), so the edge example below creates the vertices +first and then matches them. The shorthand works with the `AUTOMATIC` id strategy. + +#### Read — MATCH / WHERE / RETURN + +```cypher +// All persons named "marko" +MATCH (n:person) WHERE n.name = 'marko' RETURN n +``` + +```cypher +// Projection with ordering and limit +MATCH (n:person) +WHERE n.age > 30 +RETURN n.name AS name, n.age AS age +ORDER BY age DESC +LIMIT 10 +``` + +```cypher +// Relationship pattern +MATCH (a:person)-[:knows]->(b:person) +WHERE a.name = 'marko' +RETURN b.name AS friend +``` + +#### Create — CREATE + +```cypher +// Create a vertex +CREATE (n:person {name: 'josh', age: 32, city: 'beijing'}) +``` + +```cypher +// Create two vertices, then an edge between them (send each statement as its own request) +CREATE (:person {name: 'peter'}) +CREATE (:person {name: 'lop'}) +MATCH (a:person), (b:person) +WHERE a.name = 'peter' AND b.name = 'lop' +CREATE (a)-[:knows]->(b) +``` + +#### Update — SET / REMOVE + +```cypher +MATCH (n:person) WHERE n.name = 'josh' +SET n.age = 33 +REMOVE n.city +``` + +#### Delete — DELETE / DETACH DELETE + +```cypher +// Delete an edge +MATCH (a:person)-[r:knows]->(b:person) +WHERE a.name = 'peter' AND b.name = 'lop' +DELETE r +``` + +```cypher +// Delete a vertex and all its edges +MATCH (n:person) WHERE n.name = 'josh' +DETACH DELETE n +``` + +#### Aggregation + +```cypher +// Total count +MATCH (n:person) RETURN count(n) AS total +``` + +```cypher +// Grouped count +MATCH (n:person) RETURN n.city AS city, count(*) AS cnt ORDER BY cnt DESC +``` + +### Gremlin equivalents + +Every Cypher statement is translated to Gremlin internally. When a Cypher feature is unsupported, write the traversal directly: + +| Cypher | Gremlin equivalent | +|---|---| +| `MATCH (n:person) WHERE n.name='marko' RETURN n` | `g.V().hasLabel('person').has('name','marko')` | +| `MATCH (a)-[:knows]->(b) RETURN b` | `g.V().out('knows')` | +| `CREATE (n:person {name:'x'})` | `g.addV('person').property('name','x')` | +| `MATCH (n) DETACH DELETE n` | `g.V().drop()` | +| `MATCH (n:person) RETURN count(n)` | `g.V().hasLabel('person').count()` | + +Tip: `EXPLAIN MATCH (n:person) RETURN n` is parsed with the `EXPLAIN` option, and the translated Gremlin is expected in `result.data[0].translation` — translator-level behavior, not yet verified end-to-end on a server (see the [compatibility notes](/docs/language/cypher-compatibility/)). It remains the quickest way to draft the equivalent traversal for the table above. `PROFILE` is not supported. + +### Known limitations + +Cypher support in HugeGraph is bounded by the transpiler layer, which is based on openCypher 9 era tooling. Known gaps: + +- **No parameterized queries in released versions** — the API accepts a raw statement string only. A statement using `$param` still runs: a missing binding is inlined as `null` rather than failing, and the result depends on the expression: a predicate such as `WHERE n.name = $name` matches no rows, `RETURN $value AS value` still returns a row holding `null`, and a write stores a null value. Sanitize/escape values on the client side before interpolating them into the statement. On the tested tree with [#3289](https://github.com/apache/hugegraph/pull/3289) merged, this changes: a missing binding is an execution error for every request form, including `GET ?cypher=` and raw-body `POST` — the server always attaches a parameters map and rejects unresolved `$param` references. JSON-bound parameters (`{"cypher": ..., "parameters": {...}}`) are available on `master` after the merged [ASF PR #3289](https://github.com/apache/hugegraph/pull/3289), carried over from [PR #238](https://github .com/hugegraph/hugegraph/pull/238); see the [development compatibility record](/docs/language/cypher-compatibility/) for the tested request contract. Review Comment: Important: on 1.7.0 a write with a missing `$param` does not store a null value. Depending on the statement it deletes the property, stores the translator's marker string, or fails. Measured on a 1.7.0 server (RocksDB) with a vertex label `p2` (`name` as primary key, `age` int and `city` text both nullable), using raw-body `POST .../cypher` and reading back through `GET .../graph/vertices?label=p2`: - `MATCH (n:p2) WHERE n.name = 'josh' SET n.age = $age` returns `status.code` 200 and removes `age`: the vertex goes from `{name: josh, age: 32, city: beijing}` to `{name: josh, city: beijing}`. The translation ends in `sideEffect(properties('age').drop())` for a null value. - `CREATE (n:p2 {name: 'x', city: $city})` returns 200 and stores `city` as the literal string `" cypher.null"`. - `CREATE (n:p2 {name: 'y', age: $age})` fails with `Invalid property value ' cypher.null' for key 'age', expect a value of type Integer, actual type String`. - On the stock `person` label, `SET n.age = $age` fails with `Can't remove non-null vertex property 'vp[age->29]'`. A reader who relies on "a write stores a null value" can lose a property value, or persist the marker string, without any error. Requested change: replace "a write stores a null value" here, and `写入时则会写入 null 值` on line 150 of the CN page, with the observed behaviour: `SET` with a missing binding removes the property (or fails on a non-null key), and `CREATE` stores the string `" cypher.null"` in a text property or fails the type check for a non-text property such as an int. ########## content/en/docs/language/hugegraph-cypher.md: ########## @@ -0,0 +1,164 @@ +--- +title: "HugeGraph Cypher" +linkTitle: "Cypher Query Language" +weight: 2 +--- + +### Overview + +HugeGraph supports [Cypher](https://opencypher.org/), the declarative graph query language originally created by Neo4j and standardized as [openCypher](https://github.com/opencypher/openCypher). While Gremlin is an imperative traversal language, Cypher lets you describe the graph pattern you want (`MATCH`, `WHERE`, `RETURN`) and lets the engine decide how to execute it. Review Comment: Minor: `AGENTS.md` line 24 asks for new Markdown prose to be wrapped at 160 characters. Outside tables and code fences, this file has 9 longer lines (9, 11, 32, 145, 151, 152, 153, 156, 164; line 151 is 1142 characters). `cypher-compatibility.md` has 4 (39, 127, 128, 129) and `migrate-from-neo4j.md` has 1 (99). The CN guide has 7 and the CN compatibility page has 3. Requested change: rewrap the new prose in those five files at 160 characters. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
