Field references allow you to compare values between different fields in the same entity or related entities. This powerful feature enables complex queries that would otherwise require multiple database calls or complex joins.
Field references use the $ prefix in JSON queries and the field() method in the Java client to reference other
fields in comparisons.
Compare two fields for equality:
POST /speedy/v1/Product/$query
Accept: application/json
Content-Type: application/json
{
"$where": {
"salePrice": "$regularPrice"
}
}
This finds products where the sale price equals the regular price.
Use any comparison operator with field references:
POST /speedy/v1/Product/$query
Accept: application/json
Content-Type: application/json
{
"$where": {
"salePrice": {
"$lt": "$regularPrice"
}
}
}
This finds products where the sale price is less than the regular price.
import static com.github.silent.samurai.speedy.api.client.SpeedyQuery.*;
// Compare two fields for equality
SpeedyQuery query = SpeedyQuery.from("products")
.where(condition("salePrice", eq("$regularPrice")))
.build();
// Less than comparison
SpeedyQuery query = SpeedyQuery.from("products")
.where(condition("salePrice", lt("$regularPrice")))
.build();
// Greater than or equal comparison
SpeedyQuery query = SpeedyQuery.from("products")
.where(condition("currentStock", gte("$minimumStock")))
.build();
// Not equal comparison
SpeedyQuery query = SpeedyQuery.from("users")
.where(condition("createdBy", ne("$updatedBy")))
.build();
Find products on sale:
SpeedyQuery query = SpeedyQuery.from("products")
.where(condition("salePrice", lt("$regularPrice")))
.build();
Find products with significant discounts:
SpeedyQuery query = SpeedyQuery.from("products")
.where(
and(
condition("salePrice", lt("$regularPrice")),
condition("discountPercentage", gte(20))
)
)
.build();
Find events with valid date ranges:
SpeedyQuery query = SpeedyQuery.from("events")
.where(condition("startDate", lte("$endDate")))
.build();
Find overlapping appointments:
SpeedyQuery query = SpeedyQuery.from("appointments")
.where(
or(
condition("startTime", lt("$endTime")),
condition("endTime", gt("$startTime"))
)
)
.build();
Find products with sufficient stock:
SpeedyQuery query = SpeedyQuery.from("products")
.where(condition("currentStock", gte("$minimumStock")))
.build();
Find products that need reordering:
SpeedyQuery query = SpeedyQuery.from("products")
.where(
and(
condition("currentStock", lt("$minimumStock")),
condition("active", eq(true))
)
)
.build();
Find self-updated records:
SpeedyQuery query = SpeedyQuery.from("users")
.where(condition("createdBy", eq("$updatedBy")))
.build();
Find records updated by different users:
SpeedyQuery query = SpeedyQuery.from("documents")
.where(condition("createdBy", ne("$updatedBy")))
.build();
Find transactions with matching amounts:
SpeedyQuery query = SpeedyQuery.from("transactions")
.where(condition("debitAmount", eq("$creditAmount")))
.build();
Find profitable orders:
SpeedyQuery query = SpeedyQuery.from("orders")
.where(condition("revenue", gt("$cost")))
.build();
SpeedyQuery query = SpeedyQuery.from("products")
.where(
and(
// On sale
condition("salePrice", lt("$regularPrice")),
// Sufficient stock
condition("currentStock", gte("$minimumStock")),
// Active products
condition("active", eq(true)),
// In specific categories
condition("category", in("electronics", "computers"))
)
)
.select("id", "name", "salePrice", "regularPrice", "currentStock", "minimumStock")
.orderByAsc("currentStock")
.build();
SpeedyQuery query = SpeedyQuery.from("events")
.where(
and(
// Valid date range
condition("startDate", lte("$endDate")),
// Future events
condition("startDate", gte(LocalDate.now().toString())),
// Active events
condition("status", eq("active")),
// Within budget
condition("actualCost", lte("$budget"))
)
)
.select("id", "title", "startDate", "endDate", "actualCost", "budget")
.orderByAsc("startDate")
.build();
SpeedyQuery query = SpeedyQuery.from("users")
.where(
and(
// Active users
condition("active", eq(true)),
// Recent activity
condition("lastLoginDate", gte(LocalDate.now().minusDays(30).toString())),
// Self-managed accounts
condition("createdBy", eq("$updatedBy")),
// Sufficient login count
condition("loginCount", gte("$minimumLogins"))
)
)
.select("id", "name", "lastLoginDate", "loginCount", "createdBy", "updatedBy")
.orderByDesc("lastLoginDate")
.build();
$fieldName to reference another field"$fieldName" to reference another fieldNotFoundException@SpeedySensitiveThe @SpeedySensitive annotation prevents fields from being used on the
right-hand side of $ field references, protecting sensitive data from
exposure through query conditions.
Only the referenced field (RHS) is blocked — a sensitive field on the
left-hand side with a literal or a non-sensitive $ref on the right is
allowed.
@Entity
public class User {
// Blocked: ?otherField=$secretField → 400 Bad Request
@SpeedySensitive
private String secretField;
// Allowed: ?secretField=$publicField → 200 OK
private String publicField;
}
This blocks queries like:
[GET] /speedy/v1/User?publicField=$secretField
But allows:
[GET] /speedy/v1/User?secretField=literalValue
[GET] /speedy/v1/User?secretField=$publicField
Applying @SpeedySensitive to the entity class makes all fields
sensitive by default. Individual fields can opt out with
@SpeedySensitive(false):
@SpeedySensitive
@Entity
public class SensitiveClassEntity {
// Inherits sensitivity — blocked in $ references
private String fieldA;
// Overrides — allowed in $ references
@SpeedySensitive(false)
private String fieldB;
}
Sensitivity is enforced through foreign-key traversals. Referencing a sensitive field via a dot-notation path also fails:
# Blocked — secretField on related entity is sensitive
POST /speedy/v1/SensitiveFkEntity/$query
{ "$where": { "name": "$sensitiveTestEntity.secretField" } }
# Allowed — publicField on related entity is not sensitive
POST /speedy/v1/SensitiveFkEntity/$query
{ "$where": { "name": "$sensitiveTestEntity.publicField" } }
The /$metadata endpoint exposes sensitivity at both entity and field
levels so clients can discover which fields accept $ references:
{
"name": "SensitiveClassEntity",
"sensitive": true,
"fields": [
{ "outputProperty": "fieldA", "sensitive": true },
{ "outputProperty": "fieldB", "sensitive": false }
]
}
All comparison operators support field references:
| Operator | JSON Example | Java Example |
|---|---|---|
| Equal | "field1": "$field2" |
eq("$field2") |
| Not Equal | "field1": {"$ne": "$field2"} |
ne("$field2") |
| Less Than | "field1": {"$lt": "$field2"} |
lt("$field2") |
| Greater Than | "field1": {"$gt": "$field2"} |
gt("$field2") |
| Less Than or Equal | "field1": {"$lte": "$field2"} |
lte("$field2") |
| Greater Than or Equal | "field1": {"$gte": "$field2"} |
gte("$field2") |
// Good: Clear field names
condition("salePrice", lt("$regularPrice"))
// Avoid: Unclear field names
condition("price1", lt("$price2"))
// Good: Combine field references with literal values
SpeedyQuery query = SpeedyQuery.from("products")
.where(
and(
condition("salePrice", lt("$regularPrice")),
condition("active", eq(true)),
condition("category", in("electronics", "computers"))
)
)
.build();
// Ensure fields exist before using them in queries
public SpeedyQuery buildPriceComparisonQuery() {
// Validate that both fields exist in the entity
if (!entityMetadata.has("salePrice") || !entityMetadata.has("regularPrice")) {
throw new IllegalArgumentException("Required fields not found in entity");
}
return SpeedyQuery.from("products")
.where(condition("salePrice", lt("$regularPrice")))
.build();
}
// Validate data integrity
SpeedyQuery query = SpeedyQuery.from("orders")
.where(
and(
condition("startDate", lte("$endDate")),
condition("totalAmount", gte("$subtotal"))
)
)
.build();
NotFoundException when referencing non-existent fields// Enable pretty printing to see generated queries
SpeedyQuery query = SpeedyQuery.from("products")
.where(condition("salePrice", lt("$regularPrice")))
.prettyPrint() // Logs the query structure
.build();
// Validate field names
System.out.println("Available fields: " + entityMetadata.getAllFieldNames());
For more information about querying, see the Query Operations and SpeedyQuery documentation.