Edge Operations¶
Bonsai provides a comprehensive set of operations for managing Edges (also called Variations) in the tree structure. These operations allow you to create, read, update, and delete Edges, as well as manage their properties and conditions.
Creating Edges¶
Create an Edge Directly¶
// Create an Edge directly
Edge edge = bonsai.createEdge(Edge.builder()
.id("customEdgeId") // Optional, Bonsai can generate an ID
.knotId("targetKnotId")
.filters(List.of(
Filter.builder()
.path("$.user.age")
.operator(Operator.GREATER_THAN_EQUAL)
.value(18)
.build()
))
.properties(Map.of("description", "Age verification edge"))
.build());
Add a Variation to a Knot¶
// Add a Variation to a Knot
Edge edge = bonsai.addVariation("sourceKnotId", Variation.builder()
.knotId("targetKnotId")
.filters(List.of(
Filter.builder()
.path("$.user.age")
.operator(Operator.GREATER_THAN_EQUAL)
.value(18)
.build(),
Filter.builder()
.path("$.user.country")
.operator(Operator.IN)
.value(List.of("US", "CA", "UK"))
.build()
))
.build());
Reading Edges¶
Check if an Edge Exists¶
boolean exists = bonsai.containsEdge("edgeId");
Get an Edge by ID¶
Edge edge = bonsai.getEdge("edgeId");
Get Multiple Edges by IDs¶
Set<String> edgeIds = Set.of("edge1", "edge2", "edge3");
Map<String, Edge> edges = bonsai.getAllEdges(edgeIds);
Get Edges for a Knot¶
List<Edge> edges = bonsai.getEdgesForKnot("knotId");
Updating Edges¶
Update a Variation¶
// Update a Variation on a Knot
Edge updatedEdge = bonsai.updateVariation("knotId", "edgeId", Variation.builder()
.knotId("newTargetKnotId")
.filters(List.of(
Filter.builder()
.path("$.user.age")
.operator(Operator.GREATER_THAN_EQUAL)
.value(21)
.build()
))
.build());
Update an Edge's Properties¶
// Update an Edge's properties
Map<String, Object> newProperties = new HashMap<>(edge.getProperties());
newProperties.put("lastUpdated", System.currentTimeMillis());
newProperties.put("updatedBy", "user123");
newProperties.put("version", edge.getVersion());
Edge oldEdge = bonsai.updateEdgeProperties("edgeId", newProperties);
Deleting Edges¶
Delete a Variation¶
// Delete a Variation (without recursive deletion)
TreeEdge deletedEdge = bonsai.deleteVariation("knotId", "edgeId", false);
// Delete a Variation and all its children (recursive deletion)
TreeEdge deletedEdge = bonsai.deleteVariation("knotId", "edgeId", true);
The deleteVariation
method returns a TreeEdge
object representing the deleted subtree. This can be useful for auditing or potentially restoring the deleted structure.
Unlink a Variation¶
// Unlink a Variation (remove the Edge but keep the target Knot)
bonsai.unlinkVariation("knotId", "edgeId");
Edge Ordering¶
Edges on a Knot are evaluated in the order they are added. You can reorder Edges to change the evaluation priority:
// Reorder Edges on a Knot
List<String> newOrder = List.of("edge3", "edge1", "edge2");
bonsai.reorderEdges("knotId", newOrder);
Filter Operations¶
Filters (or conditions) define when an Edge should be followed during tree traversal. Here are some examples of creating filters:
// Equals filter
Filter equalsFilter = Filter.builder()
.path("$.user.type")
.operator(Operator.EQUALS)
.value("premium")
.build();
// Greater than filter
Filter greaterThanFilter = Filter.builder()
.path("$.user.age")
.operator(Operator.GREATER_THAN)
.value(18)
.build();
// In filter (membership in a list)
Filter inFilter = Filter.builder()
.path("$.user.country")
.operator(Operator.IN)
.value(List.of("US", "CA", "UK"))
.build();
// Contains filter (substring match)
Filter containsFilter = Filter.builder()
.path("$.user.email")
.operator(Operator.CONTAINS)
.value("@example.com")
.build();
// Regex filter (pattern match)
Filter regexFilter = Filter.builder()
.path("$.user.phone")
.operator(Operator.REGEX)
.value("^\\+1-\\d{3}-\\d{3}-\\d{4}$")
.build();
Error Handling¶
Edge operations can throw various exceptions:
BonsaiError.EDGE_ABSENT
: When trying to access a non-existent EdgeBonsaiError.KNOT_ABSENT
: When referencing a non-existent KnotBonsaiError.CYCLE_DETECTED
: When an operation would create a cycle in the treeBonsaiError.MAX_CONDITIONS_EXCEEDED
: When adding too many conditions to an EdgeBonsaiError.VARIATION_MUTUAL_EXCLUSIVITY_CONSTRAINT_ERROR
: When Edge conditions violate mutual exclusivity
Example of handling errors:
try {
Edge edge = bonsai.getEdge("nonExistentEdgeId");
} catch (BonsaiError e) {
if (e.getErrorCode() == BonsaiErrorCode.EDGE_NOT_FOUND) {
// Handle edge not found error
System.err.println("Edge not found: " + e.getMessage());
} else {
// Handle other errors
throw e;
}
}
Best Practices¶
- Order Edges from most specific to most general
- Use a default Edge (with no filters) as the last Edge on a Knot to handle all remaining cases
- Keep filter conditions simple and focused
- Consider the performance impact of complex JsonPath expressions
- Use meaningful properties to document the purpose of each Edge
- Be mindful of the maximum number of conditions per Edge (configured in BonsaiProperties)
- Consider mutual exclusivity settings when designing Edge conditions