Skip to main content

Querying

The Query class provides a powerful, fluent API for building and executing database queries. Inspired by Apple's Cocoa Predicates and LINQ, it offers a clean, readable syntax for expressing complex query logic.

Query Construction

Basic Factory Methods

1
// Query a specific type
2
Query<Article> query = Query.from(Article.class);
3
4
// Query all types
5
Query<Object> allQuery = Query.fromAll();
6
7
// Query by ObjectType
8
ObjectType articleType = ObjectType.getInstance(Article.class);
9
Query<Object> typeQuery = Query.fromType(articleType);
10
11
// Query by type group (multiple related types)
12
Query<Object> contentQuery = Query.fromGroup("content");

Using a Specific Database

1
Database searchDb = Database.Static.getInstance("search");
2
3
Query<Article> query = Query.from(Article.class)
4
.using(searchDb);

Predicates and Filtering

where - Initial Predicate

Set the initial query predicate:

1
// Simple equality
2
Query.from(Article.class)
3
.where("title = ?", "Getting Started");
4
5
// Comparison
6
Query.from(Article.class)
7
.where("publishDate > ?", cutoffDate);
8
9
// Multiple parameters
10
Query.from(Article.class)
11
.where("title = ? and author = ?", "Getting Started", author);

and - Add AND Conditions

Chain additional conditions with AND:

1
Query.from(Article.class)
2
.where("publishDate > ?", cutoffDate)
3
.and("author/name = ?", "John Doe")
4
.and("tags = ?", "java");

or - Add OR Conditions

Chain conditions with OR:

1
Query.from(Article.class)
2
.where("author/name = ?", "John Doe")
3
.or("author/name = ?", "Jane Smith");
4
5
// Complex combinations
6
// (publishDate > cutoff) OR (featured = true AND author = john)
7
Query.from(Article.class)
8
.where("publishDate > ?", cutoffDate)
9
.or("featured = ? and author = ?", true, john);

not - Negate Conditions

1
// Articles NOT by John Doe
2
Query.from(Article.class)
3
.where("author/name != ?", "John Doe");
4
5
// Or using not()
6
Query.from(Article.class)
7
.not("author/name = ?", "John Doe");

having - Filter Without Affecting Ranking

Use having to apply predicates that filter results without affecting ranking in full-text search databases (like Solr). Unlike and or or, which can influence the relevance score, having applies the predicate purely as a filter:

1
// Search with full-text ranking, but filter by category without affecting relevance scores
2
// Unlike and() or or(), having() applies the predicate purely as a filter
3
Query.from(Article.class)
4
.where("_any matches ?", "database performance") // Affects ranking
5
.having("category = ?", "technology"); // Filters only, no ranking impact

Predicate Operators

Equality Operators

1
// Equals
2
Query.from(Article.class)
3
.where("status = ?", "published");
4
5
// Not equals
6
Query.from(Article.class)
7
.where("status != ?", "draft");
8
9
// Null/missing check
10
Query.from(Article.class)
11
.where("deletedDate = missing");
12
13
Query.from(Article.class)
14
.where("deletedDate != missing");

Comparison Operators

1
// Greater than
2
Query.from(Article.class)
3
.where("publishDate > ?", date);
4
5
// Greater than or equal
6
Query.from(Article.class)
7
.where("publishDate >= ?", date);
8
9
// Less than
10
Query.from(Article.class)
11
.where("publishDate < ?", date);
12
13
// Less than or equal
14
Query.from(Article.class)
15
.where("publishDate <= ?", date);

String Operators

1
// Starts with
2
Query.from(Article.class)
3
.where("title ^= ?", "Getting");
4
5
// Contains
6
Query.from(Article.class)
7
.where("content contains ?", "database");

Collection Operators

1
// Contains any of the values
2
Query.from(Article.class)
3
.where("tags = ?", Arrays.asList("java", "database"));
4
5
// Contains all values
6
Query.from(Article.class)
7
.where("tags = ?", tag1)
8
.and("tags = ?", tag2); // Both tags must be present

Range Queries

1
// Between dates
2
Query.from(Article.class)
3
.where("publishDate > ? and publishDate < ?", startDate, endDate);
1
// Search across all indexed fields
2
Query.from(Article.class)
3
.where("_any matches ?", "search terms");
4
5
// Search specific field
6
Query.from(Article.class)
7
.where("content matches ?", "search terms");

Special Field Keys

1
// _id - Object ID
2
Query.from(Article.class)
3
.where("_id = ?", articleId);
4
5
// Multiple IDs
6
Query.from(Article.class)
7
.where("_id = ?", Arrays.asList(id1, id2, id3));
8
9
// _type - Object Type
10
Query.from(Article.class)
11
.where("_type = ?", Article.class);
12
13
// _any - Search All Fields
14
Query.from(Article.class)
15
.where("_any matches ?", "search query");
16
17
// _label - Display Label
18
Query.from(Article.class)
19
.where("_label matches ?", "Article");

Path-Based Field Access

Query nested fields using path notation:

1
// Author's name
2
Query.from(Article.class)
3
.where("author/name = ?", "John Doe");
4
5
// Nested objects
6
Query.from(Article.class)
7
.where("author/company/name = ?", "Acme Corp");
8
9
// Collection items
10
Query.from(Article.class)
11
.where("comments/author/name = ?", "Jane Smith");
12
13
// Map values
14
Query.from(Article.class)
15
.where("metadata/category = ?", "technology");

Sorting

sortAscending

1
Query.from(Article.class)
2
.sortAscending("title");
3
4
// Multiple fields
5
Query.from(Article.class)
6
.sortAscending("author/name")
7
.sortAscending("publishDate");

sortDescending

1
Query.from(Article.class)
2
.sortDescending("publishDate");

Sort by Relevance

Sort by relevance to a predicate (requires search-capable database):

1
Query.from(Article.class)
2
.where("_any matches ?", "java database")
3
.sortRelevant(100, "_any matches ?", "java database");

Sort by Newest/Oldest

1
// Newest first
2
Query.from(Article.class)
3
.sortNewest(100, "publishDate");
4
5
// Oldest first
6
Query.from(Article.class)
7
.sortOldest(100, "publishDate");

Multiple Sort Criteria

1
Query.from(Article.class)
2
.sortDescending("featured") // Featured articles first
3
.sortDescending("publishDate") // Then by date
4
.sortAscending("title"); // Then by title

Query Execution

selectAll

Execute query and return all results:

1
List<Article> articles = Query.from(Article.class)
2
.where("publishDate > ?", cutoffDate)
3
.selectAll();

select (Pagination)

Execute with offset and limit:

1
// First page (items 0-9)
2
PaginatedResult<Article> page1 = Query.from(Article.class)
3
.sortDescending("publishDate")
4
.select(0, 10);
5
6
// Second page (items 10-19)
7
PaginatedResult<Article> page2 = Query.from(Article.class)
8
.sortDescending("publishDate")
9
.select(10, 10);
10
11
// Calculate offset from page number
12
int page = 3;
13
int pageSize = 10;
14
int offset = (page - 1) * pageSize;
15
Query<Article> query = Query.from(Article.class);
16
PaginatedResult<Article> pageN = query.select(offset, pageSize);

first

Get the first result (or null):

1
Article article = Query.from(Article.class)
2
.where("slug = ?", "getting-started")
3
.first();
4
5
if (article == null) {
6
// Not found
7
}

findFirst (Optional)

Get the first result as an Optional:

1
Optional<Article> optional = Query.from(Article.class)
2
.where("slug = ?", "getting-started")
3
.findFirst();
4
5
optional.ifPresent(article -> {
6
System.out.println("Found: " + article.getTitle());
7
});
8
9
// Or with default
10
Article article = Query.from(Article.class)
11
.where("slug = ?", "getting-started")
12
.findFirst()
13
.orElse(defaultArticle);

count

Count matching objects:

1
long total = Query.from(Article.class).count();
2
3
long published = Query.from(Article.class)
4
.where("publishDate != missing")
5
.count();

iterable

Get a streaming iterable for large result sets:

1
Query<Article> query = Query.from(Article.class)
2
.where("publishDate > ?", cutoffDate);
3
4
for (Article article : query.iterable(100)) { // Fetch size 100
5
// Process each article
6
// Memory efficient for large datasets
7
}

deleteAll

Delete all matching objects:

1
// Delete old draft articles
2
Query.from(Article.class)
3
.where("status = ? and createdDate < ?", "draft", cutoffDate)
4
.deleteAll();

Grouping and Aggregation

groupBy

Group results by one or more fields:

1
// Group by author
2
List<Grouping<Article>> byAuthor = Query.from(Article.class)
3
.groupBy("author"); // Returns List<Grouping<Article>>
4
5
for (Grouping<Article> group : byAuthor) {
6
Author author = (Author) group.getKeys().get(0);
7
long count = group.getCount();
8
System.out.println(author.getName() + ": " + count + " articles");
9
}
1
// Group by multiple fields
2
List<Grouping<Article>> groups = Query.from(Article.class)
3
.groupBy("author", "publishYear");
4
5
for (Grouping<Article> group : groups) {
6
Author author = (Author) group.getKeys().get(0);
7
Integer year = (Integer) group.getKeys().get(1);
8
long count = group.getCount();
9
System.out.println(author.getName() + " in " + year + ": " + count);
10
}

Filtering Grouped Results

1
// Authors with more than 5 articles
2
List<Grouping<Article>> prolificAuthors = Query.from(Article.class)
3
.groupBy("author")
4
.stream()
5
.filter(group -> group.getCount() > 5)
6
.collect(Collectors.toList());

Aggregation Functions

1
// Get count for each group
2
for (Grouping<Article> group : groups) {
3
long count = group.getCount();
4
// Use count...
5
}
6
7
// Access first item in group
8
for (Grouping<Article> group : groups) {
9
Article firstArticle = group.createItemsQuery().first();
10
// Use firstArticle...
11
}
12
13
// Get all items in group
14
for (Grouping<Article> group : groups) {
15
List<Article> articles = group.createItemsQuery().selectAll();
16
// Process all articles in this group...
17
}

Query Modifiers

timeout

Set query timeout in seconds:

1
Query.from(Article.class)
2
.timeout(30.0) // 30 second timeout
3
.selectAll();

noCache

Bypass query result cache:

1
Query.from(Article.class)
2
.noCache()
3
.selectAll();

master

Execute on master/primary database (force read from write source):

1
// Ensure we read from master, not replica
2
Query.from(Article.class)
3
.master()
4
.where("_id = ?", articleId)
5
.first();

Useful when:

  • You just wrote data and need to read it immediately
  • Avoiding replication lag
  • Critical reads that must be up-to-date

resolveToReferenceOnly

Load only reference information, not full objects:

1
List<Article> refs = Query.from(Article.class)
2
.resolveToReferenceOnly()
3
.selectAll();
4
5
// refs contains lightweight reference objects
6
// Good for getting IDs, types, but not full data

resolveInvisible

Include records marked as invisible:

1
Query.from(Article.class)
2
.resolveInvisible()
3
.selectAll();

option

Set database-specific options:

1
Query.from(Article.class)
2
.option("hint", "USE INDEX (idx_author)") // SQL hint
3
.option("readPreference", "secondary") // MongoDB option
4
.selectAll();

comment

Add an informational comment to the query:

1
Query.from(Article.class)
2
.comment("Dashboard recent articles widget")
3
.selectAll();

Useful for:

  • Debugging slow queries
  • Monitoring and logging
  • Tracking query origins in database logs

Subqueries

Use queries as predicate values for powerful filtering:

1
// Find articles by authors in California
2
List<Article> articles = Query.from(Article.class)
3
.where(
4
"author = ?",
5
Query.from(Author.class)
6
.where("state = ?", "CA")
7
)
8
.selectAll();
9
10
// Find articles NOT by specific authors
11
List<Article> filtered = Query.from(Article.class)
12
.where(
13
"author != ?",
14
Query.from(Author.class)
15
.where("blacklisted = ?", true)
16
)
17
.selectAll();
1
// Complex subquery
2
Query<Article> articles = Query.from(Article.class)
3
.where(
4
"author = ?",
5
Query.from(Author.class)
6
.where(
7
"company = ?",
8
Query.from(Company.class)
9
.where("name = ?", "Acme Corp")
10
)
11
);

Practical Examples

Search with Filters

1
Query<Article> q = Query.from(Article.class);
2
3
// Full-text search
4
if (query != null && !query.isEmpty()) {
5
q.where("_any matches ?", query);
6
}
7
8
// Filter by author
9
if (author != null) {
10
q.and("author = ?", author);
11
}
12
13
// Date range
14
if (startDate != null) {
15
q.and("publishDate >= ?", startDate);
16
}
17
if (endDate != null) {
18
q.and("publishDate <= ?", endDate);
19
}
20
21
// Pagination
22
int offset = (page - 1) * pageSize;
23
return q.sortDescending("publishDate")
24
.select(offset, pageSize);

Dynamic Query Building

1
Query<Article> query = Query.from(Article.class);
2
3
for (Map.Entry<String, Object> entry : filters.entrySet()) {
4
String field = entry.getKey();
5
Object value = entry.getValue();
6
7
query.and(field + " = ?", value);
8
}
9
10
List<Article> results = query.selectAll();
1
// Find articles with overlapping tags, same author, or similar title
2
PaginatedResult<Article> related = Query.from(Article.class)
3
.where("_id != ?", article.getId()) // Exclude current article
4
.and("tags = ?", article.getTags()) // Matching tags
5
.or("author = ?", article.getAuthor()) // Same author
6
.sortDescending("publishDate")
7
.select(0, limit);
1
Query<Article> baseQuery = Query.from(Article.class)
2
.where("_any matches ?", searchQuery);
3
4
// Get articles
5
PaginatedResult<Article> articles = baseQuery
6
.sortRelevant(100, "_any matches ?", searchQuery)
7
.select(0, 20);
8
9
// Get author facets
10
Map<String, Long> authorCounts = new HashMap<>();
11
List<Grouping<Article>> authorGroups = baseQuery
12
.groupBy("author");
13
for (Grouping<Article> group : authorGroups) {
14
Author author = (Author) group.getKeys().get(0);
15
authorCounts.put(author.getName(), group.getCount());
16
}
17
18
// Get tag facets
19
Map<String, Long> tagCounts = new HashMap<>();
20
List<Grouping<Article>> tagGroups = baseQuery
21
.groupBy("tags");
22
for (Grouping<Article> group : tagGroups) {
23
String tag = (String) group.getKeys().get(0);
24
tagCounts.put(tag, group.getCount());
25
}

Advanced Filtering

1
// Articles that are either featured OR published recently
2
Query.from(Article.class)
3
.where("featured = ?", true)
4
.or("publishDate > ?", cutoffDate)
5
.sortDescending("publishDate")
6
.selectAll();
7
8
// Articles that have ALL specified tags
9
Query<Article> query = Query.from(Article.class);
10
for (String tag : tags) {
11
query.and("tags = ?", tag);
12
}
13
query.selectAll();
14
15
// Articles that have ANY of the specified tags
16
Query.from(Article.class)
17
.where("tags = ?", tags)
18
.selectAll();

Count by Category

1
Map<String, Long> counts = new LinkedHashMap<>();
2
3
List<Grouping<Article>> groups = Query.from(Article.class)
4
.groupBy("category");
5
6
for (Grouping<Article> group : groups) {
7
String category = (String) group.getKeys().get(0);
8
counts.put(category, group.getCount());
9
}

Find Duplicates

1
List<List<Article>> duplicates = new ArrayList<>();
2
3
// Group by title
4
List<Grouping<Article>> groups = Query.from(Article.class)
5
.groupBy("title")
6
.stream()
7
.filter(group -> group.getCount() > 1)
8
.collect(Collectors.toList());
9
10
for (Grouping<Article> group : groups) {
11
// Get all articles with this title
12
String title = (String) group.getKeys().get(0);
13
List<Article> dupes = Query.from(Article.class)
14
.where("title = ?", title)
15
.selectAll();
16
duplicates.add(dupes);
17
}

Performance Tips

Index Your Query Fields

1
// Ensure queried fields are indexed
2
// @Indexed
3
// private String title;
4
//
5
// @Indexed
6
// private Date publishDate;
7
//
8
// @Indexed
9
// private Author author;

Use Pagination

1
// Good - loads 10 items
2
query.select(0, 10);
3
4
// Bad - loads everything
5
query.selectAll();

Use count() Instead of Loading All

1
// Good
2
long count = query.count();
3
4
// Bad
5
long badCount = query.selectAll().size();

Use Specific Predicates

1
// Good - uses index efficiently
2
Query.from(Article.class)
3
.where("publishDate > ?", cutoffDate);
4
5
// Less efficient - may scan more rows
6
Query.from(Article.class)
7
.where("publishDate != missing");

Common Pitfalls

Forgetting to Index

1
// This will fail if 'title' is not indexed
2
// Query.from(Article.class)
3
// .where("title = ?", "Getting Started")
4
// .selectAll();
5
// ERROR: Field 'title' is not indexed

Loading Too Much Data

1
// Avoid loading thousands of objects at once
2
// List<Article> all = Query.from(Article.class).selectAll();
3
// Use pagination or iterable() instead

Inefficient Filtering

1
// Bad - loads everything then filters in memory
2
List<Article> all = Query.from(Article.class).selectAll();
3
List<Article> filtered = all.stream()
4
.filter(a -> a.getPublishDate().after(cutoffDate))
5
.collect(Collectors.toList());
6
7
// Good - filters at database level
8
List<Article> goodFiltered = Query.from(Article.class)
9
.where("publishDate > ?", cutoffDate)
10
.selectAll();

Not Handling Null Results

1
// Bad - can throw NullPointerException
2
Article article = Query.from(Article.class)
3
.where("_id = ?", id)
4
.first();
5
// String title = article.getTitle(); // NPE if article is null
6
7
// Good - check for null
8
Article safeArticle = Query.from(Article.class)
9
.where("_id = ?", id)
10
.first();
11
if (safeArticle != null) {
12
String title = safeArticle.getTitle();
13
}
14
15
// Or use Optional
16
String optionalTitle = Query.from(Article.class)
17
.where("_id = ?", id)
18
.findFirst()
19
.map(Article::getTitle)
20
.orElse("Untitled");