#1239: QueryFilter.kt

projectforge-business/src/main/kotlin/org/projectforge/framework/persistence/api/QueryFilter.kt Query Builder — Persistence / Database Query Engine, projectforge-business/src/main/kotlin/org/projectforge/framework/persistence/api/QueryFilter.kt 382 lines · 258 code · 75 comments · 49 blank
The central database query specification for ProjectForge. Accumulates DBPredicate objects, sort properties, joins, full-text search directives, history search parameters, and pagination controls. Automatically detects which query strategy to use (JPA Criteria API for indexed fields, Hibernate Search full-text for non-indexed, result-list filtering as fallback). Serves as the bridge between MagicFilterProcessor output and BaseDao/DBFilter query execution.

Architecture

Query Strategy Auto-Detection

QueryFilter accumulates predicates without specifying the execution strategy. The BaseDao and DBFilter classes determine at query time which approach to use: - Criteria API (JPA): For indexed/typed predicates on persistent fields - Hibernate Search (full-text): For DBPredicate.FullSearch using the Lucene index - Result-list filtering: As a last resort for predicates that can't be expressed in SQL/HQL

Core Fields

FieldTypeDefaultPurpose
predicatesMutableList<DBPredicate>emptyAccumulated search conditions
joinListMutableList<DBJoin>emptyJPA joins for nested property access
sortPropertiesMutableList<SortProperty>emptySort order specification
fullTextSearchFieldsArray<String>?nullFields to search for full-text queries
fulltextSearchStringString?nullThe full-text query string
deletedBoolean?nullSoft-delete filter (null = show all)
maxRowsInt50,000Maximum number of returned rows
limitResultSizeIntInt.MAX_VALUEWorkaround limit for legacy UITable pages
sortAndLimitMaxRowsWhileSelectBooleantrueWhether limiting is done in the DB query
entityGraphNameString?nullJPA entity graph for fetch optimization
autoWildcardSearchBooleanfalseAuto-append wildcard to search strings
extendedMutableMap<String, Any>emptyExtension variables map

History Search Fields

Delegated to internal DBHistorySearchParams object:

FieldTypePurpose
searchHistoryString?Hibernate Envers history entry types to search
modifiedFromPFDateTime?Lower bound for modification timestamp
modifiedToPFDateTime?Upper bound for modification timestamp
modifiedByUserIdLong?Filter by the user who modified the entry

Joins

createJoin(attr, joinType, fetch, parent) adds a DBJoin to the join list, supporting: - Nested property paths (e.g., "order.positions") - INNER/LEFT/FETCH join types - Optional parent alias for sub-joins

Predicate Factory Methods (Companion)

MethodReturnsPurpose
eq(field, value)DBPredicate.EqualEquality comparison
ne(field, value)DBPredicate.NotEqualNot-equal comparison
like(field, value)DBPredicate.LikeSQL LIKE with optional auto-wildcard
between(field, from, to)DBPredicate.BetweenRange search (inclusive)
ge/gt/le/lt(field, value)DBPredicate.GreaterEqual etc.Comparison operators
interval(field, from, to)DBPredicateFlexible range: between, ge, or le depending on which bounds are given
isNull(field)DBPredicate.IsNullNULL check
isNotNull(field)DBPredicate.IsNotNullNOT NULL check
isIn(field, values)DBPredicate.IsInIN clause for collections
not(matcher)DBPredicate.NotNegation wrapper
and(vararg matchers)DBPredicate.AndConjunction
or(vararg matchers)DBPredicate.OrDisjunction
taskSearch(field, taskId, recursive)DBPredicateTask tree search (with descendant expansion if recursive)

Time Period Convenience

setYearAndMonth(dateField, year, month) builds a between predicate for a specific year/month period. If month <= 0, the entire year range is used.

Legacy Filter Compatibility

The constructor accepts an optional BaseSearchFilter for backward compatibility. It copies searchFields, deleted, searchString, modifiedSince, modifiedByUserId, and maxRows from the legacy filter.

Filter Finalization

createDBFilter(): DBFilter materializes the accumulated state into a DBFilter object, adding the implicit deleted predicate if not already present. This is the point where the QueryFilter is converted to its final executable form.

Git History

868d6abb7 2025 -> 2026
7ff207016 AddressPagesRest: Warning of duplicate entries on adding new addresses.
bb42bd659 QueryFilter.like: fix of param autoWildcardSearch (was ignored)
fc0d7f60e DBFullTextResultIterator: block size 100 -> 10_000 for much better performance., QueryFilter limit is now 50_000 instead of 10_000.
63081666f Source file headers: 2024-> 2025.
2d324f2bb Finance: incoming invoices fixed.
d9c9b6184 WIP: workaround for address pages rest without pagination.
04c38072b AddressDao: select optimized, WIP: pagination.
b71b5073d Migration stuff in progress... (all tests of all packages: OK).
3264694a8 Migration stuff in progress... (all tests of all packages: OK).
314408ee0 Migration stuff in progress... (all tests of all packages: OK).
4000abf4e Migration stuff in progress...
e66d5f5f7 Migration stuff in progress... TaskTree -> Kotlin and runs now in isolated entity manager
4c04cfd65 MAJOR-CHANGE! Migration of integer id's to Long id's (including fk's etc.)
06828f490 Migration stuff in progress...
b6092df09 Copyright 2023 -> 2024
ab45d51fa Copyright 2001-2022 -> 2001-2023.
267283973 PROJECTFORGE-3730: Show validation message for list pages if result list was truncated due to max rows in QueryFilter.
5f7ef41b8 Copyright 2021 -> 2022
351392830 DataTransfer: search for personal boxes in list page.
c0f2b9de0 Tenants functionality removed everywhere (untested).
ceb63e8a1 Source code header: (C) 2001-2021.
ac71ed08d AbstractListForm: maxRows/pageSize fixed.
4ac8f0f48 BaseSearchFilter/QueryFilter/AbstractListForm: pageSize vs. maxRows.
b209e00ba PFDay.from -> from, fromOrNow, fromOrNull, PFDateTime.from -> from, fromOrNow, fromOrNull
ae7140482 React: MagicCheckboxInput implemented, deleted entries in list are now decorated with line-through.
611b20d4e WIP: java.sql.Date -> LocalDate.
7c79f1922 Copyright of source header -> 2020.
3ce3f701e CalendarUtils removed.
967cc1cbc Fixed: EmployeeReport, BuchungssatzDao, AccountingRecord{Edit|List}Form, EmployeeSalary{Edit|List}Form, MonthlyEmployeeReport*, PostausgangsListForm, PosteingangsListForm, MonthConverter. months are now 1-based: 1-January, ..., 12-December.
d5742e60c Hotfix QueryFilter.
00d48b950 BuchungssatzDao, QueryFilter: max rows of result set extended to 100,000.
b7bdea161 Support of filter.searchFields added.
34357d266 QueryFilter: NPE fix.
73895b7df WIP: CustomResultFilter...
1165d1557 WIP Addresses and CustomResultFilter introduced.
c50c528ce QueryFilter.toString()
b0a4e8463 tidy up
d36fe6ad9 Heavy WIP: refactoring
9896ea5c5 WIP: Session -> EntityManager...
a5206d947 WIP: DBJoin
14af0e09e DBAlias -> DBJoin.
66d9e3a30 WiP: DBQuery* and QueryFilter: aliases....
efb8e0b16 WiP: DBQuery* and QueryFilter: aliases....
a9f7fb127 WiP: DBQuery* and QueryFilter...
7998890ea WiP: DBQuery* and QueryFilter...
351bde48b WiP: DBQuery* and QueryFilter...
d32649da3 WIP: DBQuery*, QueryFilter and Predicates refactored....
3d1a2711a WiP: DBQuery* and QueryFilter...
20d64abbb WiP: DBQuery* and QueryFilter...
7959dc2e0 WiP: DBQuery* and QueryFilter...
a59e59eb1 WiP: DBQuery* and QueryFilter...
5e65f6b22 WiP: DBQuery*
a48f5a193 WIP: JPA...
76a8fb69d Hibernate.Restrictions -> PF.QueryFilter