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<Article> typeQuery = Query.fromType(articleType);
10
11
// Query by type group (multiple related types)
12
Query<Content> 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");

Complex combinations:

1
// (publishDate > cutoff) OR (featured = true AND author = john)
2
Query.from(Article.class)
3
.where("publishDate > ?", cutoffDate)
4
.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 Grouped Results

Use having for filtering grouped queries:

1
// Find authors with more than 5 articles
2
Query.from(Article.class)
3
.groupBy("author")
4
.having("_count > ?", 5);

Predicate Operators

Equality Operators

1
// Equals
2
.where("status = ?", "published")
3
4
// Not equals
5
.where("status != ?", "draft")
6
7
// Null/missing check
8
.where("deletedDate = missing")
9
.where("deletedDate != missing")

Comparison Operators

1
// Greater than
2
.where("publishDate > ?", date)
3
4
// Greater than or equal
5
.where("publishDate >= ?", date)
6
7
// Less than
8
.where("publishDate < ?", date)
9
10
// Less than or equal
11
.where("publishDate <= ?", date)

String Operators

1
// Starts with
2
.where("title ^= ?", "Getting")
3
4
// Contains
5
.where("content contains ?", "database")

Collection Operators

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

Range Queries

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

Special Field Keys

_id - Object ID

1
// Single ID
2
.where("_id = ?", articleId)
3
4
// Multiple IDs
5
.where("_id = ?", Arrays.asList(id1, id2, id3))

_type - Object Type

1
// Specific type
2
.where("_type = ?", Article.class)
3
4
// Type ID
5
.where("_type = ?", ObjectType.getInstance(Article.class).getId())

_any - Search All Fields

1
// Full-text search across all indexed fields
2
.where("_any matches ?", "search query")

_label - Display Label

1
// Search by label (calls getLabel())
2
.where("_label matches ?", "Article")

Path-Based Field Access

Query nested fields using path notation:

1
// Author's name
2
.where("author/name = ?", "John Doe")
3
4
// Nested objects
5
.where("author/company/name = ?", "Acme Corp")
6
7
// Collection items
8
.where("comments/author/name = ?", "Jane Smith")
9
10
// Map values
11
.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
List<Article> page1 = Query.from(Article.class)
3
.sortDescending("publishDate")
4
.select(0, 10);
5
6
// Second page (items 10-19)
7
List<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
List<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
long deletedCount = Query.from(Article.class)
3
.where("status = ? and createdDate < ?", "draft", cutoffDate)
4
.deleteAll();
5
6
System.out.println("Deleted " + deletedCount + " drafts");

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

Filtering Grouped Results

1
// Authors with more than 5 articles
2
List<Grouping<Article>> prolificAuthors = Query.from(Article.class)
3
.groupBy("author")
4
.having("_count > ?", 5)
5
.selectAll();

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.getFirst();
10
// Use firstArticle...
11
}
12
13
// Get all items in group
14
for (Grouping<Article> group : groups) {
15
List<Article> articles = group.getItems();
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
Query<Article> articles = Query.from(Article.class)
3
.where("author = ?",
4
Query.from(Author.class)
5
.where("state = ?", "CA")
6
)
7
.selectAll();
1
// Find articles NOT by specific authors
2
Query<Article> articles = Query.from(Article.class)
3
.where("author != ?",
4
Query.from(Author.class)
5
.where("blacklisted = ?", true)
6
)
7
.selectAll();
1
// Complex subquery
2
Query<Article> articles = Query.from(Article.class)
3
.where("author = ?",
4
Query.from(Author.class)
5
.where("company = ?",
6
Query.from(Company.class)
7
.where("name = ?", "Acme Corp")
8
)
9
)
10
.selectAll();

Practical Examples

Search with Filters

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

Dynamic Query Building

1
public List<Article> findArticles(Map<String, Object> filters) {
2
Query<Article> query = Query.from(Article.class);
3
boolean first = true;
4
5
for (Map.Entry<String, Object> entry : filters.entrySet()) {
6
String field = entry.getKey();
7
Object value = entry.getValue();
8
9
query.and(field + " = ?", value);
10
}
11
12
return query.selectAll();
13
}
1
public List<Article> findRelatedArticles(Article article, int limit) {
2
// Find articles with overlapping tags, same author, or similar title
3
return Query.from(Article.class)
4
.where("_id != ?", article.getId()) // Exclude current article
5
.and("tags = ?", article.getTags()) // Matching tags
6
.or("author = ?", article.getAuthor()) // Same author
7
.sortDescending("publishDate")
8
.select(0, limit);
9
}
1
public class SearchResult {
2
private List<Article> articles;
3
private Map<String, Long> authorCounts;
4
private Map<String, Long> tagCounts;
5
6
public static SearchResult search(String searchQuery) {
7
Query<Article> baseQuery = Query.from(Article.class)
8
.where("_any matches ?", searchQuery);
9
10
// Get articles
11
List<Article> articles = baseQuery
12
.sortRelevant(100, "_any matches ?", searchQuery)
13
.select(0, 20);
14
15
// Get author facets
16
Map<String, Long> authorCounts = new HashMap<>();
17
List<Grouping<Article>> authorGroups = baseQuery
18
.groupBy("author")
19
.selectAll();
20
for (Grouping<Article> group : authorGroups) {
21
Author author = (Author) group.getKeys().get(0);
22
authorCounts.put(author.getName(), group.getCount());
23
}
24
25
// Get tag facets
26
Map<String, Long> tagCounts = new HashMap<>();
27
List<Grouping<Article>> tagGroups = baseQuery
28
.groupBy("tags")
29
.selectAll();
30
for (Grouping<Article> group : tagGroups) {
31
String tag = (String) group.getKeys().get(0);
32
tagCounts.put(tag, group.getCount());
33
}
34
35
SearchResult result = new SearchResult();
36
result.articles = articles;
37
result.authorCounts = authorCounts;
38
result.tagCounts = tagCounts;
39
return result;
40
}
41
}

Advanced Filtering

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

Count by Category

1
public Map<String, Long> countByCategory() {
2
Map<String, Long> counts = new LinkedHashMap<>();
3
4
List<Grouping<Article>> groups = Query.from(Article.class)
5
.groupBy("category")
6
.sortDescending("_count")
7
.selectAll();
8
9
for (Grouping<Article> group : groups) {
10
String category = (String) group.getKeys().get(0);
11
counts.put(category, group.getCount());
12
}
13
14
return counts;
15
}

Find Duplicates

1
public List<List<Article>> findDuplicateTitles() {
2
List<List<Article>> duplicates = new ArrayList<>();
3
4
// Group by title
5
List<Grouping<Article>> groups = Query.from(Article.class)
6
.groupBy("title")
7
.having("_count > ?", 1) // Only groups with more than 1
8
.selectAll();
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
}
18
19
return duplicates;
20
}

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 count = query.selectAll().size();

Use Specific Predicates

1
// Good - uses index efficiently
2
.where("publishDate > ?", cutoffDate)
3
4
// Less efficient - may scan more rows
5
.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> filtered = 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 article = Query.from(Article.class)
9
.where("_id = ?", id)
10
.first();
11
if (article != null) {
12
String title = article.getTitle();
13
}
14
15
// Or use Optional
16
Query.from(Article.class)
17
.where("_id = ?", id)
18
.findFirst()
19
.map(Article::getTitle)
20
.orElse("Untitled");