# Query Model ## Philosophy Graph databases excel at complex relationship queries. That complexity belongs in the developer's Gremlin code, not inside the ORM. Clotho's query model is a **bridge**: - Developer writes a Gremlin traversal - Clotho executes it and maps the results back to registered Java types - Developer injects the repository interface like any Spring bean --- ## `@ClothoRepository` Interfaces The primary way to define queries. Clotho generates a dynamic proxy at startup. ```java @ClothoRepository public interface EmployeeQueries { @Query("g.V().hasLabel('Employee').has('dept',:dept)") List findByDepartment(@Param("dept") String dept); @Query(""" g.V().has('Employee','id',:id) .repeat(out('MANAGES')).times(2).emit() .dedup() """) List findTwoLevelsOfReports(@Param("id") String managerId); } ``` ```java @Autowired EmployeeQueries employeeQueries; List staff = employeeQueries.findByDepartment("Engineering"); ``` --- ## Parameter Binding Use `:paramName` placeholders in the Gremlin string and `@Param("paramName")` on the corresponding method parameter. ```java @Query("g.V().has('Order','status',:status).has('total',gt(:minTotal))") List findByStatusAndMinTotal( @Param("status") String status, @Param("minTotal") BigDecimal minTotal); ``` Clotho substitutes parameters before executing the traversal. Parameter types are passed as-is to Gremlin — use Java types that Gremlin/ArcadeDB understands (String, int, long, double, boolean, LocalDate, etc.). --- ## Return Type Rules The declared return type tells Clotho how to map the traversal result. ### `@VertexType` or `@EdgeType` class Clotho maps each result element to the most specific registered class matching its label. The declared type is the upper bound. ```java // May return Employee, HourlyEmployee, SalariedEmployee — all ClothoVertex subtypes List findAll(); // Explicitly mixed: any registered @VertexType List findAllVertices(); ``` When the return type is a single object (not a `List`), Clotho returns the first result or `null` if none. ```java @Nullable @Query("g.V().has('Employee','id',:id)") Employee findById(@Param("id") String id); ``` ### `@SubgraphType` class When the return type is annotated `@SubgraphType`, Clotho expects the traversal to return a `select()` map. It maps each key to the matching field by name (or by `@Alias`). ```java @Query(""" g.V().has('Employee','id',:id).as('employee') .out('MANAGED_BY').as('manager') .out('IN_DEPT').as('dept') .select('employee','manager','dept') """) EmployeeProfile loadProfile(@Param("id") String id); ``` ```java @SubgraphType public class EmployeeProfile { Employee employee; // bound to key "employee" Manager manager; // bound to key "manager" @Alias("dept") Department department; // bound to key "dept" } ``` If the traversal returns a list of select maps, declare `List`. ### Raw result (no mapping) Set `mapResult = false` to receive the Gremlin result directly without Clotho mapping. Return type can be a scalar, `List`, or any type Gremlin produces. ```java @Query(value = "g.V().hasLabel('Employee').count()", mapResult = false) long countEmployees(); @Query(value = "g.V().has('Order','id',:id).values('status')", mapResult = false) String getOrderStatus(@Param("id") String id); ``` --- ## Named Queries on `@VertexType` Classes An alternative to repository interfaces for simple per-type queries. Place `@Query` directly on the vertex class with a `name` attribute: ```java @VertexType("Order") @Query(name = "openForCustomer", gremlin = "g.V().has('Customer','id',:custId).out('PLACED').has('status','OPEN')") public class Order extends ClothoVertex { ... } ``` Invoke via the session: ```java List open = session.query(Order.class, "openForCustomer", Map.of("custId", customerId)); ``` **Note on magic strings**: the query name `"openForCustomer"` is a raw string. Prefer defining it as a constant on the class: ```java @VertexType("Order") @Query(name = Order.QUERY_OPEN_FOR_CUSTOMER, ...) public class Order extends ClothoVertex { public static final String QUERY_OPEN_FOR_CUSTOMER = "openForCustomer"; } // Call site: session.query(Order.class, Order.QUERY_OPEN_FOR_CUSTOMER, params); ``` Multiple `@Query` annotations on a class are allowed (the annotation is `@Repeatable`). --- ## Gremlin Traversal Source The `GraphTraversalSource` (`g`) is managed by `ClothoSessionFactory`. It is configured once and shared across sessions. Clotho configures the traversal source to use the remote ArcadeDB connection. For advanced cases where a developer needs direct traversal source access: ```java @Autowired ClothoSessionFactory factory; GraphTraversalSource g = factory.traversalSource(); // Write raw Gremlin, then map manually: List results = factory.currentSession() .mapVertices(ClothoVertex.class, g.V().has("name", "Alice").out("KNOWS")); ``` --- ## Mapping Detail: How Clotho Maps a Gremlin Vertex to a Java Object 1. Read the vertex label from the TinkerPop `Vertex` object 2. Look up the label in the `TypeRegistry` → find the most specific registered class 3. Construct an instance of that class (via reflection; Lombok builder support planned) 4. Set the `ARID` from the vertex ID 5. For each `@Property` field on the class: read the property from the vertex, set the field 6. For each `@Include` field: recursively execute the boundary traversal (see `OBJECT_BOUNDARY.md`) 7. Register the instance in the session identity map keyed by ARID 8. Return the assembled instance If the same ARID is encountered again during step 6, the identity map returns the already- assembled instance without re-fetching from the DB.