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<Object> typeQuery = Query.fromType(articleType);1011// Query by type group (multiple related types)12Query<Object> 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");45// Complex combinations6// (publishDate > cutoff) OR (featured = true AND author = john)7Query.from(Article.class)8.where("publishDate > ?", cutoffDate)9.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 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 scores2// Unlike and() or or(), having() applies the predicate purely as a filter3Query.from(Article.class)4.where("_any matches ?", "database performance") // Affects ranking5.having("category = ?", "technology"); // Filters only, no ranking impact
Predicate Operators
Equality Operators
1// Equals2Query.from(Article.class)3.where("status = ?", "published");45// Not equals6Query.from(Article.class)7.where("status != ?", "draft");89// Null/missing check10Query.from(Article.class)11.where("deletedDate = missing");1213Query.from(Article.class)14.where("deletedDate != missing");
Comparison Operators
1// Greater than2Query.from(Article.class)3.where("publishDate > ?", date);45// Greater than or equal6Query.from(Article.class)7.where("publishDate >= ?", date);89// Less than10Query.from(Article.class)11.where("publishDate < ?", date);1213// Less than or equal14Query.from(Article.class)15.where("publishDate <= ?", date);
String Operators
1// Starts with2Query.from(Article.class)3.where("title ^= ?", "Getting");45// Contains6Query.from(Article.class)7.where("content contains ?", "database");
Collection Operators
1// Contains any of the values2Query.from(Article.class)3.where("tags = ?", Arrays.asList("java", "database"));45// Contains all values6Query.from(Article.class)7.where("tags = ?", tag1)8.and("tags = ?", tag2); // Both tags must be present
Range Queries
1// Between dates2Query.from(Article.class)3.where("publishDate > ? and publishDate < ?", startDate, endDate);
Full-Text Search
1// Search across all indexed fields2Query.from(Article.class)3.where("_any matches ?", "search terms");45// Search specific field6Query.from(Article.class)7.where("content matches ?", "search terms");
Special Field Keys
1// _id - Object ID2Query.from(Article.class)3.where("_id = ?", articleId);45// Multiple IDs6Query.from(Article.class)7.where("_id = ?", Arrays.asList(id1, id2, id3));89// _type - Object Type10Query.from(Article.class)11.where("_type = ?", Article.class);1213// _any - Search All Fields14Query.from(Article.class)15.where("_any matches ?", "search query");1617// _label - Display Label18Query.from(Article.class)19.where("_label matches ?", "Article");
Path-Based Field Access
Query nested fields using path notation:
1// Author's name2Query.from(Article.class)3.where("author/name = ?", "John Doe");45// Nested objects6Query.from(Article.class)7.where("author/company/name = ?", "Acme Corp");89// Collection items10Query.from(Article.class)11.where("comments/author/name = ?", "Jane Smith");1213// Map values14Query.from(Article.class)15.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)2PaginatedResult<Article> page1 = Query.from(Article.class)3.sortDescending("publishDate")4.select(0, 10);56// Second page (items 10-19)7PaginatedResult<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;15Query<Article> query = Query.from(Article.class);16PaginatedResult<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 articles2Query.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 author2List<Grouping<Article>> byAuthor = Query.from(Article.class)3.groupBy("author"); // Returns List<Grouping<Article>>45for (Grouping<Article> group : byAuthor) {6Author author = (Author) group.getKeys().get(0);7long count = group.getCount();8System.out.println(author.getName() + ": " + count + " articles");9}
1// Group by multiple fields2List<Grouping<Article>> groups = Query.from(Article.class)3.groupBy("author", "publishYear");45for (Grouping<Article> group : groups) {6Author author = (Author) group.getKeys().get(0);7Integer year = (Integer) group.getKeys().get(1);8long count = group.getCount();9System.out.println(author.getName() + " in " + year + ": " + count);10}
Filtering Grouped Results
1// Authors with more than 5 articles2List<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 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.createItemsQuery().first();10// Use firstArticle...11}1213// Get all items in group14for (Grouping<Article> group : groups) {15List<Article> articles = group.createItemsQuery().selectAll();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 California2List<Article> articles = Query.from(Article.class)3.where(4"author = ?",5Query.from(Author.class)6.where("state = ?", "CA")7)8.selectAll();910// Find articles NOT by specific authors11List<Article> filtered = Query.from(Article.class)12.where(13"author != ?",14Query.from(Author.class)15.where("blacklisted = ?", true)16)17.selectAll();
1// Complex subquery2Query<Article> articles = Query.from(Article.class)3.where(4"author = ?",5Query.from(Author.class)6.where(7"company = ?",8Query.from(Company.class)9.where("name = ?", "Acme Corp")10)11);
Practical Examples
Search with Filters
1Query<Article> q = Query.from(Article.class);23// Full-text search4if (query != null && !query.isEmpty()) {5q.where("_any matches ?", query);6}78// Filter by author9if (author != null) {10q.and("author = ?", author);11}1213// Date range14if (startDate != null) {15q.and("publishDate >= ?", startDate);16}17if (endDate != null) {18q.and("publishDate <= ?", endDate);19}2021// Pagination22int offset = (page - 1) * pageSize;23return q.sortDescending("publishDate")24.select(offset, pageSize);
Dynamic Query Building
1Query<Article> query = Query.from(Article.class);23for (Map.Entry<String, Object> entry : filters.entrySet()) {4String field = entry.getKey();5Object value = entry.getValue();67query.and(field + " = ?", value);8}910List<Article> results = query.selectAll();
Related Content
1// Find articles with overlapping tags, same author, or similar title2PaginatedResult<Article> related = Query.from(Article.class)3.where("_id != ?", article.getId()) // Exclude current article4.and("tags = ?", article.getTags()) // Matching tags5.or("author = ?", article.getAuthor()) // Same author6.sortDescending("publishDate")7.select(0, limit);
Faceted Search
1Query<Article> baseQuery = Query.from(Article.class)2.where("_any matches ?", searchQuery);34// Get articles5PaginatedResult<Article> articles = baseQuery6.sortRelevant(100, "_any matches ?", searchQuery)7.select(0, 20);89// Get author facets10Map<String, Long> authorCounts = new HashMap<>();11List<Grouping<Article>> authorGroups = baseQuery12.groupBy("author");13for (Grouping<Article> group : authorGroups) {14Author author = (Author) group.getKeys().get(0);15authorCounts.put(author.getName(), group.getCount());16}1718// Get tag facets19Map<String, Long> tagCounts = new HashMap<>();20List<Grouping<Article>> tagGroups = baseQuery21.groupBy("tags");22for (Grouping<Article> group : tagGroups) {23String tag = (String) group.getKeys().get(0);24tagCounts.put(tag, group.getCount());25}
Advanced Filtering
1// Articles that are either featured OR published recently2Query.from(Article.class)3.where("featured = ?", true)4.or("publishDate > ?", cutoffDate)5.sortDescending("publishDate")6.selectAll();78// Articles that have ALL specified tags9Query<Article> query = Query.from(Article.class);10for (String tag : tags) {11query.and("tags = ?", tag);12}13query.selectAll();1415// Articles that have ANY of the specified tags16Query.from(Article.class)17.where("tags = ?", tags)18.selectAll();
Count by Category
1Map<String, Long> counts = new LinkedHashMap<>();23List<Grouping<Article>> groups = Query.from(Article.class)4.groupBy("category");56for (Grouping<Article> group : groups) {7String category = (String) group.getKeys().get(0);8counts.put(category, group.getCount());9}
Find Duplicates
1List<List<Article>> duplicates = new ArrayList<>();23// Group by title4List<Grouping<Article>> groups = Query.from(Article.class)5.groupBy("title")6.stream()7.filter(group -> group.getCount() > 1)8.collect(Collectors.toList());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}
Performance Tips
Index Your Query Fields
1// Ensure queried fields are indexed2// @Indexed3// private String title;4//5// @Indexed6// private Date publishDate;7//8// @Indexed9// private 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 badCount = query.selectAll().size();
Use Specific Predicates
1// Good - uses index efficiently2Query.from(Article.class)3.where("publishDate > ?", cutoffDate);45// Less efficient - may scan more rows6Query.from(Article.class)7.where("publishDate != missing");
Common Pitfalls
Forgetting to Index
1// This will fail if 'title' is not indexed2// 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 once2// List<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> goodFiltered = 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();5// String title = article.getTitle(); // NPE if article is null67// Good - check for null8Article safeArticle = Query.from(Article.class)9.where("_id = ?", id)10.first();11if (safeArticle != null) {12String title = safeArticle.getTitle();13}1415// Or use Optional16String optionalTitle = Query.from(Article.class)17.where("_id = ?", id)18.findFirst()19.map(Article::getTitle)20.orElse("Untitled");