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 type2Query<Article> query = Query.from(Article.class);34// Query all types5Query<Object> allQuery = Query.fromAll();67// Query by ObjectType8ObjectType articleType = ObjectType.getInstance(Article.class);9Query<Article> typeQuery = Query.fromType(articleType);1011// Query by type group (multiple related types)12Query<Content> contentQuery = Query.fromGroup("content");
Using a Specific Database
1Database searchDb = Database.Static.getInstance("search");23Query<Article> query = Query.from(Article.class)4.using(searchDb);
Predicates and Filtering
where - Initial Predicate
Set the initial query predicate:
1// Simple equality2Query.from(Article.class)3.where("title = ?", "Getting Started");45// Comparison6Query.from(Article.class)7.where("publishDate > ?", cutoffDate);89// Multiple parameters10Query.from(Article.class)11.where("title = ? and author = ?", "Getting Started", author);
and - Add AND Conditions
Chain additional conditions with AND:
1Query.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:
1Query.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)2Query.from(Article.class)3.where("publishDate > ?", cutoffDate)4.or("featured = ? and author = ?", true, john);
not - Negate Conditions
1// Articles NOT by John Doe2Query.from(Article.class)3.where("author/name != ?", "John Doe");45// Or using not()6Query.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 articles2Query.from(Article.class)3.groupBy("author")4.having("_count > ?", 5);
Predicate Operators
Equality Operators
1// Equals2.where("status = ?", "published")34// Not equals5.where("status != ?", "draft")67// Null/missing check8.where("deletedDate = missing")9.where("deletedDate != missing")
Comparison Operators
1// Greater than2.where("publishDate > ?", date)34// Greater than or equal5.where("publishDate >= ?", date)67// Less than8.where("publishDate < ?", date)910// Less than or equal11.where("publishDate <= ?", date)
String Operators
1// Starts with2.where("title ^= ?", "Getting")34// Contains5.where("content contains ?", "database")
Collection Operators
1// Contains any of the values2.where("tags = ?", Arrays.asList("java", "database"))34// Contains all values5.where("tags = ?", tag1)6.and("tags = ?", tag2) // Both tags must be present
Range Queries
1// Between dates2.where("publishDate > ? and publishDate < ?", startDate, endDate)
Full-Text Search
1// Search across all indexed fields2.where("_any matches ?", "search terms")34// Search specific field5.where("content matches ?", "search terms")
Special Field Keys
_id - Object ID
1// Single ID2.where("_id = ?", articleId)34// Multiple IDs5.where("_id = ?", Arrays.asList(id1, id2, id3))
_type - Object Type
1// Specific type2.where("_type = ?", Article.class)34// Type ID5.where("_type = ?", ObjectType.getInstance(Article.class).getId())
_any - Search All Fields
1// Full-text search across all indexed fields2.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 name2.where("author/name = ?", "John Doe")34// Nested objects5.where("author/company/name = ?", "Acme Corp")67// Collection items8.where("comments/author/name = ?", "Jane Smith")910// Map values11.where("metadata/category = ?", "technology")
Sorting
sortAscending
1Query.from(Article.class)2.sortAscending("title");34// Multiple fields5Query.from(Article.class)6.sortAscending("author/name")7.sortAscending("publishDate");
sortDescending
1Query.from(Article.class)2.sortDescending("publishDate");
Sort by Relevance
Sort by relevance to a predicate (requires search-capable database):
1Query.from(Article.class)2.where("_any matches ?", "java database")3.sortRelevant(100, "_any matches ?", "java database");
Sort by Newest/Oldest
1// Newest first2Query.from(Article.class)3.sortNewest(100, "publishDate");45// Oldest first6Query.from(Article.class)7.sortOldest(100, "publishDate");
Multiple Sort Criteria
1Query.from(Article.class)2.sortDescending("featured") // Featured articles first3.sortDescending("publishDate") // Then by date4.sortAscending("title"); // Then by title
Query Execution
selectAll
Execute query and return all results:
1List<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)2List<Article> page1 = Query.from(Article.class)3.sortDescending("publishDate")4.select(0, 10);56// Second page (items 10-19)7List<Article> page2 = Query.from(Article.class)8.sortDescending("publishDate")9.select(10, 10);1011// Calculate offset from page number12int page = 3;13int pageSize = 10;14int offset = (page - 1) * pageSize;15List<Article> pageN = query.select(offset, pageSize);
first
Get the first result (or null):
1Article article = Query.from(Article.class)2.where("slug = ?", "getting-started")3.first();45if (article == null) {6// Not found7}
findFirst (Optional)
Get the first result as an Optional:
1Optional<Article> optional = Query.from(Article.class)2.where("slug = ?", "getting-started")3.findFirst();45optional.ifPresent(article -> {6System.out.println("Found: " + article.getTitle());7});89// Or with default10Article article = Query.from(Article.class)11.where("slug = ?", "getting-started")12.findFirst()13.orElse(defaultArticle);
count
Count matching objects:
1long total = Query.from(Article.class).count();23long published = Query.from(Article.class)4.where("publishDate != missing")5.count();
iterable
Get a streaming iterable for large result sets:
1Query<Article> query = Query.from(Article.class)2.where("publishDate > ?", cutoffDate);34for (Article article : query.iterable(100)) { // Fetch size 1005// Process each article6// Memory efficient for large datasets7}
deleteAll
Delete all matching objects:
1// Delete old draft articles2long deletedCount = Query.from(Article.class)3.where("status = ? and createdDate < ?", "draft", cutoffDate)4.deleteAll();56System.out.println("Deleted " + deletedCount + " drafts");
Grouping and Aggregation
groupBy
Group results by one or more fields:
1// Group by author2List<Grouping<Article>> byAuthor = Query.from(Article.class)3.groupBy("author")4.selectAll(); // Returns List<Grouping<Article>>56for (Grouping<Article> group : byAuthor) {7Author author = (Author) group.getKeys().get(0);8long count = group.getCount();9System.out.println(author.getName() + ": " + count + " articles");10}
1// Group by multiple fields2List<Grouping<Article>> groups = Query.from(Article.class)3.groupBy("author", "publishYear")4.selectAll();56for (Grouping<Article> group : groups) {7Author author = (Author) group.getKeys().get(0);8Integer year = (Integer) group.getKeys().get(1);9long count = group.getCount();10System.out.println(author.getName() + " in " + year + ": " + count);11}
Filtering Grouped Results
1// Authors with more than 5 articles2List<Grouping<Article>> prolificAuthors = Query.from(Article.class)3.groupBy("author")4.having("_count > ?", 5)5.selectAll();
Aggregation Functions
1// Get count for each group2for (Grouping<Article> group : groups) {3long count = group.getCount();4// Use count...5}67// Access first item in group8for (Grouping<Article> group : groups) {9Article firstArticle = group.getFirst();10// Use firstArticle...11}1213// Get all items in group14for (Grouping<Article> group : groups) {15List<Article> articles = group.getItems();16// Process all articles in this group...17}
Query Modifiers
timeout
Set query timeout in seconds:
1Query.from(Article.class)2.timeout(30.0) // 30 second timeout3.selectAll();
noCache
Bypass query result cache:
1Query.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 replica2Query.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:
1List<Article> refs = Query.from(Article.class)2.resolveToReferenceOnly()3.selectAll();45// refs contains lightweight reference objects6// Good for getting IDs, types, but not full data
resolveInvisible
Include records marked as invisible:
1Query.from(Article.class)2.resolveInvisible()3.selectAll();
option
Set database-specific options:
1Query.from(Article.class)2.option("hint", "USE INDEX (idx_author)") // SQL hint3.option("readPreference", "secondary") // MongoDB option4.selectAll();
comment
Add an informational comment to the query:
1Query.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 California2Query<Article> articles = Query.from(Article.class)3.where("author = ?",4Query.from(Author.class)5.where("state = ?", "CA")6)7.selectAll();
1// Find articles NOT by specific authors2Query<Article> articles = Query.from(Article.class)3.where("author != ?",4Query.from(Author.class)5.where("blacklisted = ?", true)6)7.selectAll();
1// Complex subquery2Query<Article> articles = Query.from(Article.class)3.where("author = ?",4Query.from(Author.class)5.where("company = ?",6Query.from(Company.class)7.where("name = ?", "Acme Corp")8)9)10.selectAll();
Practical Examples
Search with Filters
1public List<Article> searchArticles(String query, Author author,2Date startDate, Date endDate,3int page, int pageSize) {4Query<Article> q = Query.from(Article.class);56// Full-text search7if (query != null && !query.isEmpty()) {8q.where("_any matches ?", query);9}1011// Filter by author12if (author != null) {13q.and("author = ?", author);14}1516// Date range17if (startDate != null) {18q.and("publishDate >= ?", startDate);19}20if (endDate != null) {21q.and("publishDate <= ?", endDate);22}2324// Pagination25int offset = (page - 1) * pageSize;26return q.sortDescending("publishDate")27.select(offset, pageSize);28}
Dynamic Query Building
1public List<Article> findArticles(Map<String, Object> filters) {2Query<Article> query = Query.from(Article.class);3boolean first = true;45for (Map.Entry<String, Object> entry : filters.entrySet()) {6String field = entry.getKey();7Object value = entry.getValue();89query.and(field + " = ?", value);10}1112return query.selectAll();13}
Related Content
1public List<Article> findRelatedArticles(Article article, int limit) {2// Find articles with overlapping tags, same author, or similar title3return Query.from(Article.class)4.where("_id != ?", article.getId()) // Exclude current article5.and("tags = ?", article.getTags()) // Matching tags6.or("author = ?", article.getAuthor()) // Same author7.sortDescending("publishDate")8.select(0, limit);9}
Faceted Search
1public class SearchResult {2private List<Article> articles;3private Map<String, Long> authorCounts;4private Map<String, Long> tagCounts;56public static SearchResult search(String searchQuery) {7Query<Article> baseQuery = Query.from(Article.class)8.where("_any matches ?", searchQuery);910// Get articles11List<Article> articles = baseQuery12.sortRelevant(100, "_any matches ?", searchQuery)13.select(0, 20);1415// Get author facets16Map<String, Long> authorCounts = new HashMap<>();17List<Grouping<Article>> authorGroups = baseQuery18.groupBy("author")19.selectAll();20for (Grouping<Article> group : authorGroups) {21Author author = (Author) group.getKeys().get(0);22authorCounts.put(author.getName(), group.getCount());23}2425// Get tag facets26Map<String, Long> tagCounts = new HashMap<>();27List<Grouping<Article>> tagGroups = baseQuery28.groupBy("tags")29.selectAll();30for (Grouping<Article> group : tagGroups) {31String tag = (String) group.getKeys().get(0);32tagCounts.put(tag, group.getCount());33}3435SearchResult result = new SearchResult();36result.articles = articles;37result.authorCounts = authorCounts;38result.tagCounts = tagCounts;39return result;40}41}
Advanced Filtering
1public List<Article> findFeaturedOrRecent(Date cutoffDate) {2// Articles that are either featured OR published recently3return Query.from(Article.class)4.where("featured = ?", true)5.or("publishDate > ?", cutoffDate)6.sortDescending("publishDate")7.selectAll();8}910public List<Article> findByMultipleTags(List<String> tags) {11// Articles that have ALL specified tags12Query<Article> query = Query.from(Article.class);13for (String tag : tags) {14query.and("tags = ?", tag);15}16return query.selectAll();17}1819public List<Article> findByAnyTag(List<String> tags) {20// Articles that have ANY of the specified tags21return Query.from(Article.class)22.where("tags = ?", tags)23.selectAll();24}
Count by Category
1public Map<String, Long> countByCategory() {2Map<String, Long> counts = new LinkedHashMap<>();34List<Grouping<Article>> groups = Query.from(Article.class)5.groupBy("category")6.sortDescending("_count")7.selectAll();89for (Grouping<Article> group : groups) {10String category = (String) group.getKeys().get(0);11counts.put(category, group.getCount());12}1314return counts;15}
Find Duplicates
1public List<List<Article>> findDuplicateTitles() {2List<List<Article>> duplicates = new ArrayList<>();34// Group by title5List<Grouping<Article>> groups = Query.from(Article.class)6.groupBy("title")7.having("_count > ?", 1) // Only groups with more than 18.selectAll();910for (Grouping<Article> group : groups) {11// Get all articles with this title12String title = (String) group.getKeys().get(0);13List<Article> dupes = Query.from(Article.class)14.where("title = ?", title)15.selectAll();16duplicates.add(dupes);17}1819return duplicates;20}
Performance Tips
Index Your Query Fields
1// Ensure queried fields are indexed2@Indexed3private String title;45@Indexed6private Date publishDate;78@Indexed9private Author author;
Use Pagination
1// Good - loads 10 items2query.select(0, 10);34// Bad - loads everything5query.selectAll();
Use count() Instead of Loading All
1// Good2long count = query.count();34// Bad5long count = query.selectAll().size();
Use Specific Predicates
1// Good - uses index efficiently2.where("publishDate > ?", cutoffDate)34// Less efficient - may scan more rows5.where("publishDate != missing")
Common Pitfalls
Forgetting to Index
1// This will fail if 'title' is not indexed2Query.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 once2List<Article> all = Query.from(Article.class).selectAll();3// Use pagination or iterable() instead
Inefficient Filtering
1// Bad - loads everything then filters in memory2List<Article> all = Query.from(Article.class).selectAll();3List<Article> filtered = all.stream()4.filter(a -> a.getPublishDate().after(cutoffDate))5.collect(Collectors.toList());67// Good - filters at database level8List<Article> filtered = Query.from(Article.class)9.where("publishDate > ?", cutoffDate)10.selectAll();
Not Handling Null Results
1// Bad - can throw NullPointerException2Article article = Query.from(Article.class)3.where("_id = ?", id)4.first();5String title = article.getTitle(); // NPE if article is null67// Good - check for null8Article article = Query.from(Article.class)9.where("_id = ?", id)10.first();11if (article != null) {12String title = article.getTitle();13}1415// Or use Optional16Query.from(Article.class)17.where("_id = ?", id)18.findFirst()19.map(Article::getTitle)20.orElse("Untitled");