Add Vertex support#373
Merged
Merged
Conversation
Closes #361 Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Contributor
Author
Usage Examples0. Setup: Create database and table# Create database
curl -X POST "http://localhost:8080/graph/v3/databases" -H "Content-Type: application/json" -d '{"database": "mydb", "comment": "my database"}'
# Create vertex table
curl -X POST "http://localhost:8080/graph/v3/databases/mydb/tables" -H "Content-Type: application/json" -d '{
"table": "users",
"schema": {
"type": "VERTEX",
"id": {"type": "string", "comment": "user id"},
"properties": [
{"name": "name", "type": "string", "comment": "display name"},
{"name": "age", "type": "long", "comment": "age", "nullable": true}
]
},
"storage": "datastore://my_namespace/users",
"mode": "SYNC",
"comment": "user vertex table"
}'1. Insert verticescurl -X POST "http://localhost:8080/graph/v3/databases/mydb/tables/users/vertices" -H "Content-Type: application/json" -d '{
"mutations": [
{"type": "INSERT", "vertex": {"version": 1, "id": "user1", "properties": {"name": "Alice", "age": 30}}},
{"type": "INSERT", "vertex": {"version": 1, "id": "user2", "properties": {"name": "Bob"}}}
]
}'{
"results": [
{"id": "user1", "status": "CREATED", "count": 1},
{"id": "user2", "status": "CREATED", "count": 1}
]
}2. Update a vertexcurl -X POST "http://localhost:8080/graph/v3/databases/mydb/tables/users/vertices" -H "Content-Type: application/json" -d '{
"mutations": [
{"type": "UPDATE", "vertex": {"version": 2, "id": "user1", "properties": {"name": "Alice Smith"}}}
]
}'{"results": [{"id": "user1", "status": "UPDATED", "count": 1}]}3. Delete a vertexcurl -X POST "http://localhost:8080/graph/v3/databases/mydb/tables/users/vertices" -H "Content-Type: application/json" -d '{
"mutations": [
{"type": "DELETE", "vertex": {"version": 3, "id": "user2", "properties": {}}}
]
}'{"results": [{"id": "user2", "status": "DELETED", "count": 1}]}4. Insert (sync) — wait for write to completecurl -X POST "http://localhost:8080/graph/v3/databases/mydb/tables/users/vertices/sync" -H "Content-Type: application/json" -d '{
"mutations": [
{"type": "INSERT", "vertex": {"version": 1, "id": "user3", "properties": {"name": "Carol", "age": 25}}}
]
}'5. Get vertices (multi-get)curl "http://localhost:8080/graph/v3/databases/mydb/tables/users/vertices/get?id=user1,user2,user3"{
"vertices": [
{"version": 2, "id": "user1", "properties": {"name": "Alice Smith", "age": 30}, "context": {}},
{"version": 1, "id": "user3", "properties": {"name": "Carol", "age": 25}, "context": {}}
],
"count": 2,
"total": 2,
"context": {}
}
|
Contributor
Author
Internal representation noteVertex is stored as an Edge with # Vertex API
curl "http://localhost:8080/graph/v3/databases/mydb/tables/users/vertices/get?id=user1"
# {"vertices": [{"version": 1, "id": "user1", "properties": {"name": "Alice"}, "context": {}}], ...}
# Edge API — same row, internal encoding exposed (do not use this way)
curl "http://localhost:8080/graph/v3/databases/mydb/tables/users/edges/get?source=user1&target=-"
# {"edges": [{"version": 1, "source": "user1", "target": "-", "properties": {"name": "Alice"}, "context": {}}], ...} |
Address review findings on the vertex feature branch before merging: - VertexQueryController: accept `List<Any>` for id query param so LONG-typed vertex tables can multi-get by numeric id (matches MultiEdgeQueryController). Add a LONG-id integration test to lock in the regression. - LabelEntity: vertex stores State only — indices, groups, caches, and IN/BOTH directions are silently dropped by BulkEdgeEncoder and the v3 schema converter. Reject them at construction so inconsistent metadata never reaches storage. Cover each rejection in LabelEntitySpec. - TableUpdateRequest: a vertex update returned `emptyList()` for indices, groups, and caches, which `LabelUpdateRequest.toNotNullMap()` would serialize and overwrite the existing list with. Return `null` instead so vertex updates are no-ops on those fields. - MutationRequestValidator.checkNonNullableFields: revert visibility from public to internal. The vertex payload lives in the same `core` Gradle module, so internal access suffices and avoids broadening the contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the single @test case for VERTEX LabelEntity → V3TableDescriptor with an @ObjectSourceParameterizedTest. Drives both STRING and LONG id types from data and asserts against the parameter values, matching the @ObjectSource convention used elsewhere in this file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Verified locally — all curl examples in the comments above work as expected. Merging. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add
Vertexas a first-class entity type alongsideEdgeandMultiEdge. Vertex reuses the existingEdgeStateencoding withsource=key, target="-"(VERTEX_MARKER), so existing storage format and encoding pipeline are shared.Key design decisions:
LabelType.VERTEXadded to codec-java so Vertex is identified by type.EdgeMutationStrategy.Vertexsuppresses all EdgeCount writes (producesCount = false)Closes #361
Changes
LabelType.VERTEX,BulkEdgeEncoderencodes State only (no index, no counter)ModelSchema.Vertex,TableDescriptor.Vertex,EdgeMutationStrategy.Vertex,VertexBulkMutationRequest,VertexMutationResponse,DataFrameVertexPayloadV3TableDescriptor.create()restoresLabelType.VERTEX → V3TableDescriptor.Vertexon reload;V2BackedTableBindingroutes Vertex tobuildForVertex()VertexMutationController(POST /vertices,POST /vertices/sync),VertexQueryController(GET /vertices/get), schema/DDL wiring inTableCreateRequest,V3CompatService,V3MetadataConverterHow to Test
./gradlew testNew test coverage:
VertexBulkMutationRequestTest:createEvent— source=id, target=VERTEX_MARKER, nullability validation, reserved id guardEdgeMutationStrategyTest/EdgeMutationBuilderTest:Vertexstrategy —producesCount=false, no count records on CREATED/UPDATED/DELETEDBulkEdgeEncoderTests: active/inactive Vertex → State only, no counter rowsV3MetadataConverterTest:LabelType.VERTEX→TableDescriptor.Vertexround-tripMutationServiceSpec: Vertex insert/update/delete/get engine-level flowVertexIntegrationTest: full E2E — table create, mutation, get, update, deleteAI Assistance