Everything about filters
This page is a comprehensive reference for all filter factories available in the Cardinal SDK. It covers the filter type hierarchy, combinators, date format conventions, and every filter method for each entity type.
For a gentler introduction to querying, see Querying data.
Data owner variants​
Most filter factories for EBAC (Entity Based Access Control) entities (Patient, Contact, Service, HealthElement, Document, etc.) come in three variants:
| Suffix | When to use |
|---|---|
ForSelf | Default choice. Uses the current logged-in data owner. |
ForDataOwner | When querying as an admin or on behalf of another data owner. Requires the data owner's id. |
ForDataOwnerInGroup | Cross-group queries. Takes an EntityReferenceInGroup to target a data owner in a different group. |
For more details, see Data owners and access control.
Sorting​
Some filters return SortableFilterOptions (or BaseSortableFilterOptions) that can be used to sort results by specific criteria. The sorting rules are documented in the tables below.
Starting from Cardinal SDK 2.6.0 many filter factory methods are no longer sortable: their return type changed from
SortableFilterOptions to FilterOptions (or from BaseSortableFilterOptions to BaseFilterOptions).
This mostly affects the byIdentifiers, byPatients, byPatientsSecretIds, code/tag and demographic filters of
Patient, Contact, Service, HealthElement, Document and Message.
Sorting remains available mainly on the byIds filters (input order) and on the date-range filters (for example
byPatientsOpeningDate, byPatientsDate, byOpeningDate).
If you were passing one of the affected filters to a ...BySorted method, you will have to switch to the non-sorted
variant of the method (for example filterPatientsBy instead of filterPatientsBySorted). The tables below reflect
the current sortability of each method.
Combining filters​
Filter options can be combined using three operators:
intersection (AND)​
Returns entities that match all provided filters. Requires at least two filters.
- Kotlin
- Typescript
- Python
// Function call
val filter = intersection(filterA, filterB, filterC)
// Infix shorthand
val filter2 = filterA and filterB and filterC
import {intersection} from "@icure/cardinal-sdk"
const filter = intersection(filterA, filterB, filterC)
from cardinal_sdk.filters import intersection
filter = intersection(filter_a, filter_b, filter_c)
union (OR)​
Returns entities that match at least one of the provided filters. Requires at least two filters.
- Kotlin
- Typescript
- Python
val filter = union(filterA, filterB)
// Infix shorthand
val filter2 = filterA or filterB
import {union} from "@icure/cardinal-sdk"
const filter = union(filterA, filterB)
from cardinal_sdk.filters import union
filter = union(filter_a, filter_b)
difference (set minus)​
Returns entities that match the first filter (of) but not the second (subtracting).
- Kotlin
- Typescript
- Python
val filter = difference(ofFilter, subtractingFilter)
import {difference} from "@icure/cardinal-sdk"
const filter = difference(ofFilter, subtractingFilter)
from cardinal_sdk.filters import difference
filter = difference(of_filter, subtracting_filter)
Performance considerations​
-
intersection can sometimes be misleading in terms of performance. If the first filter is very broad and the second filter is very selective, the backend may still need to execute the first filter and possibly collect a huge number of entity ids, before being able to apply the second filter. However, in some cases, the backend could optimized by the backend if the first filter is highly selective (i.e. it narrows down to a small set of entities). In that case, the backend could apply the second filter only to that small set, improving performance. This optimisation is not yet available in the Cardinl backend (but we are working on it). It is something to keep in mind when using
intersectionwith very broad filters. -
difference can also be costly if the
subtractingfilter is very broad. Even if the final number of entities returned by the difference is small, the backend may still need to execute thesubtractingfilter and collect a large number of entity ids to determine which ones to exclude.
Sortability rules​
| Combinator | Sortable? | Rule |
|---|---|---|
intersection | If first argument is sortable | Sorts by first argument's criteria |
union | Never | — |
difference | If of is sortable | Sorts by of's criteria |
In Kotlin, and is shorthand for intersection and or for union. You can also use the minus - sign for difference. You can chain them:
val filter = (filterA and filterB) or (filterC and (filterD - filterE))
val sameFilter = union(intersection(filterA, filterB), intersection(filterC, difference(filterD, filterE)))
Date format conventions​
Different entity properties use different date formats:
| Format | Description | Used by |
|---|---|---|
YYYYMMDD | 8-digit integer date | Patient.dateOfBirth |
YYYYMMDDHHmmSS | 14-digit fuzzy date | Contact.openingDate, Service.valueDate, Service.openingDate, HealthElement.openingDate, Form.created, CalendarItem.startTime |
| Unix timestamp (ms) | Milliseconds since epoch | Document.created, Message.sentDate |
For example, 20250315 means March 15, 2025. The fuzzy date 20250315143000 means March 15, 2025 at 14:30:00.
Code and tag filtering patterns​
Many entities have codes (clinical coding: ICD-10, SNOMED, LOINC, etc.) and tags (categorical labels).
Type-only vs type+code​
When you provide only a codeType/tagType and omit the code value, the filter matches all entities with any code
of that type. When you also provide a codeCode/tagCode, the filter narrows to that specific code.
Code+date compound filters​
For Contacts and Services, the code/tag filters support an optional date range. However, you must provide the code value to use a date range — type-only filtering with a date range is not supported and will throw an error.
byServiceTag / byServiceCode on ContactFilters​
ContactFilters has special byServiceTag and byServiceCode methods that match contacts based on codes or tags
present on their services rather than on the contact itself. These are useful when you want to find contacts that
contain services with specific clinical codes.
Patient-linked entity filters​
byPatients vs byPatientsSecretIds​
Most EBAC entity filters provide two patterns for filtering by linked patients:
byPatients— acceptsPatientobjects. The SDK automatically extracts secret ids. Cannot be used withCardinalBaseApis. This is the convenient option for most use cases.byPatientsSecretIds/byPatientSecretIds— accepts raw secret id strings. Use this when you already have the secret ids or need to work withCardinalBaseApis.
Date-range variants​
Many patient-linked filters also accept from/to date bounds and a descending flag for sorting:
| Entity | Method | Date property |
|---|---|---|
| Contact | byPatientsOpeningDate | Contact.openingDate |
| Service | byPatientsDate | Service.valueDate (fallback: openingDate) |
| HealthElement | byPatientsOpeningDate | HealthElement.openingDate |
| Document | byPatientsCreated | Document.created |
| Message | byPatientsSentDate | Message.sentDate |
| CalendarItem | byPatientsStartTime | CalendarItem.startTime |
| Form | byPatientsOpeningDate | Form.created |
| Classification | byPatientsCreated | Classification.created |
| AccessLog | byPatientsDate | AccessLog.date |
Entity filter reference​
The tables below list the ForSelf variant of each method. For most methods, equivalent ForDataOwner and
ForDataOwnerInGroup variants exist (prepend the data owner id or EntityReferenceInGroup as first argument).
Methods with no data-owner scoping are marked with — in the Scoping column.
PatientFilters​
| Method | Key parameters | Return type | Sortable? | Sort order |
|---|---|---|---|---|
allPatientsForSelf | — | FilterOptions | No | — |
byIds | ids | SortableFilterOptions | Yes | Input order |
byIdentifiersForSelf | identifiers | FilterOptions | No | — |
bySsinsForSelf | ssins | FilterOptions | No | — |
byDateOfBirthBetweenForSelf | fromDate, toDate | FilterOptions | No | — |
byNameForSelf | searchString | FilterOptions | No | — |
byGenderEducationProfessionForSelf | gender, education?, profession? | FilterOptions | No | — |
byActiveForSelf | active | FilterOptions | No | — |
byTelecomForSelf | searchString | FilterOptions | No | — |
byAddressPostalCodeHouseNumberForSelf | searchString, postalCode, houseNumber? | FilterOptions | No | — |
byAddressForSelf | searchString | FilterOptions | No | — |
byTagForSelf | tagType, tagCode? | FilterOptions | No | — |
byIds has no data-owner scoping — it returns a SortableFilterOptions (not Base) and works the same for all users.
It is also the only sortable patient filter since SDK 2.6.0.
The ForDataOwner variants for the other methods also exist (e.g. byFuzzyNameForDataOwner accepts a dataOwnerId + searchString).
ContactFilters​
| Method | Key parameters | Return type | Sortable? | Sort order |
|---|---|---|---|---|
allContactsForSelf | — | FilterOptions | No | — |
byFormIdsForSelf | formIds | FilterOptions | No | — |
byPatientsOpeningDateForSelf | patients, from?, to?, descending? | SortableFilterOptions | Yes | openingDate |
byPatientSecretIdsOpeningDateForSelf | secretIds, from?, to?, descending? | SortableFilterOptions | Yes | openingDate |
byIdentifiersForSelf | identifiers | FilterOptions | No | — |
byCodeAndOpeningDateForSelf | codeType, codeCode?, startOfContactOpeningDate?, endOfContactOpeningDate? | SortableFilterOptions | Yes | Code, openingDate |
byTagAndOpeningDateForSelf | tagType, tagCode?, startOfContactOpeningDate?, endOfContactOpeningDate? | SortableFilterOptions | Yes | Tag, openingDate |
byOpeningDateForSelf | startDate?, endDate?, descending? | SortableFilterOptions | Yes | openingDate |
byServiceTagForSelf | tagType, tagCode? | FilterOptions | No | — |
byServiceCodeForSelf | codeType, codeCode? | FilterOptions | No | — |
byPatientsForSelf | patients | FilterOptions | No | — |
byPatientsSecretIdsForSelf | secretIds | FilterOptions | No | — |
byServiceIds | serviceIds | BaseSortableFilterOptions | Yes | Input order |
ServiceFilters​
| Method | Key parameters | Return type | Sortable? | Sort order |
|---|---|---|---|---|
allServicesForSelf | — | FilterOptions | No | — |
byIdentifiersForSelf | identifiers | FilterOptions | No | — |
byCodeAndValueDateForSelf | codeType, codeCode?, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byTagAndValueDateForSelf | tagType, tagCode?, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byCodesAndValueDateForSelf | codeCodes, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byCodePrefixAndValueDateForSelf | codeType, codeCodePrefix, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byTagCodesAndValueDateForSelf | tagCodes, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byTagPrefixAndValueDateForSelf | tagType, tagCodePrefix, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byPatientsCodesAndValueDateForSelf | patients, codeCodes, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byPatientSecretIdsCodesAndValueDateForSelf | secretIds, codeCodes, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byPatientsCodePrefixAndValueDateForSelf | patients, codeType, codeCodePrefix, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byPatientSecretIdsCodePrefixAndValueDateForSelf | secretIds, codeType, codeCodePrefix, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byPatientsTagCodesAndValueDateForSelf | patients, tagCodes, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byPatientSecretIdsTagCodesAndValueDateForSelf | secretIds, tagCodes, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byPatientsTagPrefixAndValueDateForSelf | patients, tagType, tagCodePrefix, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byPatientSecretIdsTagPrefixAndValueDateForSelf | secretIds, tagType, tagCodePrefix, startOfServiceValueDate?, endOfServiceValueDate? | FilterOptions | No | — |
byPatientsForSelf | patients | FilterOptions | No | — |
byPatientsSecretIdsForSelf | secretIds | FilterOptions | No | — |
byHealthElementIdFromSubContactForSelf | healthElementIds | FilterOptions | No | — |
byPatientsDateForSelf | patients, from?, to?, descending? | SortableFilterOptions | Yes | valueDate |
byPatientSecretIdsDateForSelf | secretIds, from?, to?, descending? | SortableFilterOptions | Yes | valueDate |
byIds | ids | BaseSortableFilterOptions | Yes | Input order |
byAssociationId | associationId | BaseFilterOptions | No | — |
byQualifiedLink | linkValues, linkQualification? | BaseFilterOptions | No | — |
The multi-code (...CodesAndValueDate, ...TagCodesAndValueDate) and prefix (...CodePrefixAndValueDate,
...TagPrefixAndValueDate) filters were introduced in SDK 2.3.2. Since SDK 2.4.2 the multi-code variants take the codes
as a map from the code (or tag) type to the set of codes of that type (Map<String, Set<String>>): a service matches if
it has any of the provided codes (or tags). The prefix variants match the services having a code (or tag) of the
given type whose code starts with the given prefix. The optional startOfServiceValueDate and endOfServiceValueDate
are fuzzy date times (YYYYMMDDHHmmSS) restricting Service.valueDate. Like byCodeAndValueDateForSelf and
byTagAndValueDateForSelf, none of these options are sortable.
Each of them also comes in ForDataOwner and ForDataOwnerInGroup variants, which return BaseFilterOptions and can
therefore be used with CardinalBaseApis, except for the byPatients... ones (they need to extract the secret ids of the
patients). These filters are not available in the Dart SDK.
For example, to get all the systolic and diastolic blood pressure measurements of a patient recorded in 2025:
- Kotlin
- Typescript
- Python
import com.icure.cardinal.sdk.CardinalSdk
import com.icure.cardinal.sdk.filters.ServiceFilters
import com.icure.cardinal.sdk.model.Patient
import com.icure.cardinal.sdk.model.embed.DecryptedService
import com.icure.cardinal.sdk.utils.pagination.PaginatedListIterator
suspend fun getBloodPressureMeasurements(
sdk: CardinalSdk,
patient: Patient
): PaginatedListIterator<DecryptedService> =
sdk.contact.filterServicesBy(
ServiceFilters.byPatientsCodesAndValueDateForSelf(
patients = listOf(patient),
codeCodes = mapOf("LOINC" to setOf("8480-6", "8462-4")),
startOfServiceValueDate = 20250101000000,
endOfServiceValueDate = 20251231235959
)
)
import {CardinalSdk, DecryptedService, PaginatedListIterator, Patient, ServiceFilters} from "@icure/cardinal-sdk"
function getBloodPressureMeasurements(
sdk: CardinalSdk,
patient: Patient
): Promise<PaginatedListIterator<DecryptedService>> {
return sdk.contact.filterServicesBy(
ServiceFilters.byPatientsCodesAndValueDateForSelf(
[patient],
{ "LOINC": ["8480-6", "8462-4"] },
{ startOfServiceValueDate: 20250101000000, endOfServiceValueDate: 20251231235959 }
)
)
}
from cardinal_sdk import CardinalSdk
from cardinal_sdk.model import Patient, DecryptedService
from cardinal_sdk.filters import ServiceFilters
from cardinal_sdk.pagination.PaginatedListIterator import PaginatedListIterator
def get_blood_pressure_measurements(
sdk: CardinalSdk,
patient: Patient
) -> PaginatedListIterator[DecryptedService]:
return sdk.contact.filter_services_by_blocking(
ServiceFilters.by_patients_codes_and_value_date_for_self(
[patient],
{"LOINC": {"8480-6", "8462-4"}},
start_of_service_value_date=20250101000000,
end_of_service_value_date=20251231235959
)
)
HealthElementFilters​
| Method | Key parameters | Return type | Sortable? | Sort order |
|---|---|---|---|---|
allHealthElementsForSelf | — | FilterOptions | No | — |
byIdentifiersForSelf | identifiers | FilterOptions | No | — |
byCodeForSelf | codeType, codeCode? | FilterOptions | No | — |
byTagForSelf | tagType, tagCode? | FilterOptions | No | — |
byPatientsForSelf | patients | FilterOptions | No | — |
byPatientsSecretIdsForSelf | secretIds | FilterOptions | No | — |
byIds | ids | BaseSortableFilterOptions | Yes | Input order |
byPatientsOpeningDateForSelf | patients, from?, to?, descending? | SortableFilterOptions | Yes | openingDate |
byPatientSecretIdsOpeningDateForSelf | secretIds, from?, to?, descending? | SortableFilterOptions | Yes | openingDate |
DocumentFilters​
| Method | Key parameters | Return type | Sortable? | Sort order |
|---|---|---|---|---|
byPatientsCreatedForSelf | patients, from?, to?, descending? | SortableFilterOptions | Yes | created |
byMessagesCreatedForSelf | messages, from?, to?, descending? | SortableFilterOptions | Yes | created |
byOwningEntitySecretIdsCreatedForSelf | secretIds, from?, to?, descending? | SortableFilterOptions | Yes | created |
byPatientsAndTypeForSelf | documentType, patients | FilterOptions | No | — |
byMessagesAndTypeForSelf | documentType, messages | FilterOptions | No | — |
byOwningEntitySecretIdsAndTypeForSelf | documentType, secretIds | FilterOptions | No | — |
byCodeForSelf | codeType, codeCode? | SortableFilterOptions | Yes | Code |
byTagForSelf | tagType, tagCode? | FilterOptions | No | — |
HealthcarePartyFilters​
Healthcare party filters are not data-owner scoped — they all return Base* types.
| Method | Key parameters | Return type | Sortable? | Sort order |
|---|---|---|---|---|
all | — | BaseFilterOptions | No | — |
byIdentifiers | identifiers | BaseFilterOptions | No | — |
byCode | codeType, codeCode? | BaseSortableFilterOptions | Yes | Code |
byTag | tagType, tagCode? | BaseSortableFilterOptions | Yes | Tag |
byIds | ids | SortableFilterOptions | Yes | Input order |
byName | searchString, descending? | BaseSortableFilterOptions | Yes | Name/speciality |
byNationalIdentifier | searchString, descending? | BaseSortableFilterOptions | Yes | SSIN |
byParentId | parentId | BaseFilterOptions | No | — |
Other entity filters​
UserFilters​
| Method | Key parameters | Return type | Sortable? |
|---|---|---|---|
all | — | BaseFilterOptions | No |
byIds | ids | BaseSortableFilterOptions | Yes |
byPatientId | patientId | BaseFilterOptions | No |
byHealthcarePartyId | healthcarePartyId | BaseFilterOptions | No |
byNameEmailOrPhone | searchString | BaseFilterOptions | No |
CodeFilters​
| Method | Key parameters | Return type | Sortable? |
|---|---|---|---|
all | — | BaseFilterOptions | No |
byIds | ids | BaseSortableFilterOptions | Yes |
byQualifiedLink | linkType, linkedId? | BaseFilterOptions | No |
byRegionTypeCodeVersion | region, type?, code?, version? | BaseFilterOptions | No |
byLanguageTypeLabelRegion | language, type, label?, region? | BaseFilterOptions | No |
byLanguageTypesLabelRegionVersion | language, types, label, region?, version? | BaseFilterOptions | No |
MessageFilters​
| Method | Key parameters | Return type | Sortable? |
|---|---|---|---|
allMessagesForSelf | — | FilterOptions | No |
byTransportGuidForSelf | transportGuid | FilterOptions | No |
fromAddressForSelf | address | FilterOptions | No |
toAddressForSelf | address | FilterOptions | No |
byPatientsSentDateForSelf | patients, from?, to?, descending? | SortableFilterOptions | Yes |
byPatientSecretIdsSentDateForSelf | secretIds, from?, to?, descending? | SortableFilterOptions | Yes |
byTransportGuidSentDateForSelf | transportGuid, from, to, descending? | FilterOptions | No |
latestByTransportGuidForSelf | transportGuid | FilterOptions | No |
lifecycleBetweenForSelf | startTimestamp?, endTimestamp?, descending | FilterOptions | No |
byCodeForSelf | codeType, codeCode? | FilterOptions | No |
byTagForSelf | tagType, tagCode? | FilterOptions | No |
byInvoiceIds | invoiceIds | BaseFilterOptions | No |
byParentIds | parentIds | BaseFilterOptions | No |
CalendarItemFilters​
| Method | Key parameters | Return type | Sortable? |
|---|---|---|---|
byPatientsStartTimeForSelf | patients, from?, to?, descending? | SortableFilterOptions | Yes |
byPatientSecretIdsStartTimeForSelf | secretIds, from?, to?, descending? | SortableFilterOptions | Yes |
byPeriodAndAgenda | agendaId, from, to, descending? | BaseSortableFilterOptions | Yes |
byPeriodForSelf | from, to | FilterOptions | No |
byRecurrenceId | recurrenceId | FilterOptions | No |
lifecycleBetweenForSelf | startTimestamp?, endTimestamp?, descending | FilterOptions | No |
GroupFilters​
| Method | Key parameters | Return type | Sortable? |
|---|---|---|---|
all | — | BaseFilterOptions | No |
bySuperGroup | superGroupId | BaseFilterOptions | No |
withContent | superGroupId, searchString | BaseSortableFilterOptions | Yes |
FormFilters​
| Method | Key parameters | Return type | Sortable? |
|---|---|---|---|
byParentIdForSelf | parentId | FilterOptions | No |
byPatientsOpeningDateForSelf | patients, from?, to?, descending? | SortableFilterOptions | Yes |
byPatientSecretIdsOpeningDateForSelf | secretIds, from?, to?, descending? | SortableFilterOptions | Yes |
byUniqueId | uniqueId, descending? | BaseSortableFilterOptions | Yes |
AccessLogFilters​
| Method | Key parameters | Return type | Sortable? |
|---|---|---|---|
byPatientsDateForSelf | patients, from?, to?, descending? | SortableFilterOptions | Yes |
byPatientSecretIdsDateForSelf | secretIds, from?, to?, descending? | SortableFilterOptions | Yes |
byDate | from?, to?, descending? | BaseSortableFilterOptions | Yes |
byUserTypeDate | userId, accessType?, from?, descending? | BaseSortableFilterOptions | Yes |
DeviceFilters​
| Method | Key parameters | Return type | Sortable? |
|---|---|---|---|
all | — | BaseFilterOptions | No |
byResponsible | responsibleId | BaseFilterOptions | No |
byIds | ids | BaseSortableFilterOptions | Yes |
AgendaFilters​
| Method | Key parameters | Return type | Sortable? |
|---|---|---|---|
all | — | BaseFilterOptions | No |
byUser | userId | BaseFilterOptions | No |
readableByUser | userId | BaseFilterOptions | No |
readableByUserRights | userId | BaseFilterOptions | No |
byStringProperty | propertyId, propertyValue | BaseFilterOptions | No |
byBooleanProperty | propertyId, propertyValue | BaseFilterOptions | No |
byLongProperty | propertyId, propertyValue | BaseFilterOptions | No |
byDoubleProperty | propertyId, propertyValue | BaseFilterOptions | No |
withProperty | propertyId | BaseFilterOptions | No |
TopicFilters​
| Method | Key parameters | Return type | Sortable? |
|---|---|---|---|
allTopicsForSelf | — | FilterOptions | No |
byParticipant | participantId | FilterOptions | No |
InsuranceFilters​
Insurance filters were introduced in SDK 2.9.0, together with the matchInsurancesBy, filterInsurancesBy,
matchInsurancesBySorted and filterInsurancesBySorted methods on the insurance api.
Insurances are not data-owner scoped: all the filters return Base* types.
| Method | Key parameters | Return type | Sortable? | Sort order |
|---|---|---|---|---|
all | — | BaseFilterOptions | No | — |
byIdentifiers | identifiers | BaseFilterOptions | No | — |
byCode | codeType, codeCode? | BaseSortableFilterOptions | Yes | Code |
byTag | tagType, tagCode? | BaseSortableFilterOptions | Yes | Tag |
In SDK 2.9.0 the boolean fields privateInsurance, hospitalisationInsurance and ambulatoryInsurance were removed
from the Insurance entity. The kind of coverage provided by an insurance is now modelled through the tags and
codes fields, which you can query using the filters above.
RelatedPersonFilters​
Filters for the RelatedPerson entity, introduced in SDK 2.12.0.
| Method | Key parameters | Return type | Sortable? | Sort order |
|---|---|---|---|---|
allRelatedPersonsForSelf | — | FilterOptions | No | — |
byIdentifiersForSelf | identifiers | FilterOptions | No | — |
byNameForSelf | name | FilterOptions | No | — |
byIds | ids | BaseSortableFilterOptions | Yes | Input order |
byNameForSelf matches on the concatenation of the related person's last name and first names.
MaintenanceTaskFilters​
Maintenance task filters have ForSelf and ForDataOwner variants, but no ForDataOwnerInGroup variants.
| Method | Key parameters | Return type | Sortable? | Sort order |
|---|---|---|---|---|
byIds | ids | BaseSortableFilterOptions | Yes | Input order |
byIdentifiersForSelf | identifiers | FilterOptions | No | — |
byTypeForSelf | type | FilterOptions | No | — |
afterDateForSelf | date | SortableFilterOptions | Yes | created |
afterDateForSelf matches the maintenance tasks with a created after date, which is a unix timestamp in
milliseconds (not a fuzzy date).
Discovering filters at runtime​
If you are building a dynamic query builder (for example a UI that lets the user pick a filter for an entity), you can
retrieve the list of available filter options at runtime with sdk.filter.getFilterOptionsDefinitions(). It returns a
map from the entity name ("Patient", "Service", "MaintenanceTask", ...) to a list of
FilterOptionGroupWithViews, each describing a group of factory methods that produce the same kind of filter and differ
only in how the parameters are passed (ForSelf/ForDataOwner/InGroup, patients vs secret ids):
name: a short semantic name for the group (e.g.byPatientsDate).factoryMethods: the names of the factory methods of the correspondingXFiltersobject that belong to this group.targetFilter: the name of the filter that is actually sent to the backend (or the possible filters separated by" or ", for the few groups that depend on their input).views: the database views that the backend uses to resolve this filter.
The SDK builds this result from its static catalog of filter options, keeping only the filters that the backend you are
connected to supports. In Kotlin the static catalog itself is also available, without any request to the backend, as
FilterOptionsCatalog.byEntity (same content without the views).
The filter api is available in Kotlin since SDK 2.7.0 and in TypeScript since SDK 2.13.6, on both the CardinalSdk
and the CardinalBaseSdk.
- Kotlin
- Typescript
- Python
- Dart
import com.icure.cardinal.sdk.CardinalSdk
suspend fun printServiceFilterOptions(sdk: CardinalSdk) {
val definitions = sdk.filter.getFilterOptionsDefinitions()
definitions["Service"]?.forEach { group ->
println("${group.name}: ${group.factoryMethods.joinToString()} -> ${group.targetFilter}")
}
}
import {CardinalSdk} from "@icure/cardinal-sdk"
async function printServiceFilterOptions(sdk: CardinalSdk) {
const definitions = await sdk.filter.getFilterOptionsDefinitions()
for (const group of definitions["Service"] ?? []) {
console.log(`${group.name}: ${group.factoryMethods.join(", ")} -> ${group.targetFilter}`)
}
}
The Python SDK contains a FilterApi class (since SDK 2.11.0), but it is not exposed yet as a filter property of
CardinalSdk. Until it is, you can instantiate it yourself from your sdk instance:
from cardinal_sdk import CardinalSdk
from cardinal_sdk.api import FilterApi
def print_service_filter_options(sdk: CardinalSdk):
definitions = FilterApi(sdk).get_filter_options_definitions_blocking()
for group in definitions.get("Service", []):
print(f"{group.name}: {', '.join(group.factory_methods)} -> {group.target_filter}")
The filter api is not available in the Dart SDK yet.
Complex filter examples​
1. Diabetic patient screening cohort​
Find active female patients over 50 with an ICD-10 E11 (type 2 diabetes) diagnosis. This requires a cross-entity query: first find patients matching demographic criteria, then check their health elements for the diagnosis code.
- Kotlin
- Typescript
- Python
import com.icure.cardinal.sdk.CardinalSdk
import com.icure.cardinal.sdk.filters.HealthElementFilters
import com.icure.cardinal.sdk.filters.PatientFilters
import com.icure.cardinal.sdk.filters.intersection
import com.icure.cardinal.sdk.model.DecryptedPatient
import com.icure.cardinal.sdk.model.embed.Gender
suspend fun findDiabeticScreeningCohort(sdk: CardinalSdk): List<DecryptedPatient> {
// Step 1: Find active females born before 1976 (over ~50)
val patientFilter = intersection(
PatientFilters.byGenderEducationProfessionForSelf(Gender.Female),
PatientFilters.byDateOfBirthBetweenForSelf(
fromDate = 19000101,
toDate = 19760101
),
PatientFilters.byActiveForSelf(true)
)
val candidates = sdk.patient.getPatients(
sdk.patient.matchPatientsBy(patientFilter)
)
// Step 2: For each candidate, check for ICD-10 E11 health elements
val diabetesFilter = intersection(
HealthElementFilters.byPatientsForSelf(candidates),
HealthElementFilters.byCodeForSelf("ICD-10", codeCode = "E11")
)
val diabeticPatientIds = mutableSetOf<String>()
val heIterator = sdk.healthElement.filterHealthElementsBy(diabetesFilter)
while (heIterator.hasNext()) {
heIterator.next(100).forEach { he ->
he.id.let { diabeticPatientIds.add(it) }
}
}
return candidates.filter { it.id in diabeticPatientIds }
}
import {
CardinalSdk,
DecryptedPatient,
Gender,
HealthElementFilters,
intersection,
PatientFilters,
} from "@icure/cardinal-sdk"
async function findDiabeticScreeningCohort(sdk: CardinalSdk): Promise<Array<DecryptedPatient>> {
// Step 1: Find active females born before 1976 (over ~50)
const patientFilter = intersection(
PatientFilters.byGenderEducationProfessionForSelf(Gender.Female),
PatientFilters.byDateOfBirthBetweenForSelf(19000101, 19760101),
PatientFilters.byActiveForSelf(true)
)
const candidates = await sdk.patient.getPatients(
await sdk.patient.matchPatientsBy(patientFilter)
)
// Step 2: For each candidate, check for ICD-10 E11 health elements
const diabetesFilter = intersection(
HealthElementFilters.byPatientsForSelf(candidates),
HealthElementFilters.byCodeForSelf("ICD-10", { codeCode: "E11" })
)
const diabeticPatientIds = new Set<string>()
const heIterator = await sdk.healthElement.filterHealthElementsBy(diabetesFilter)
while (await heIterator.hasNext()) {
const batch = await heIterator.next(100)
batch.forEach(he => diabeticPatientIds.add(he.id))
}
return candidates.filter(p => diabeticPatientIds.has(p.id))
}
from cardinal_sdk import CardinalSdk
from cardinal_sdk.model import DecryptedPatient, Gender
from cardinal_sdk.filters import PatientFilters, HealthElementFilters, intersection
from typing import List
def find_diabetic_screening_cohort(sdk: CardinalSdk) -> List[DecryptedPatient]:
# Step 1: Find active females born before 1976 (over ~50)
patient_filter = intersection(
PatientFilters.by_gender_education_profession_for_self(Gender.Female),
PatientFilters.by_date_of_birth_between_for_self(19000101, 19760101),
PatientFilters.by_active_for_self(True)
)
candidates = sdk.patient.get_patients_blocking(
sdk.patient.match_patients_by_blocking(patient_filter)
)
# Step 2: For each candidate, check for ICD-10 E11 health elements
diabetes_filter = intersection(
HealthElementFilters.by_patients_for_self(candidates),
HealthElementFilters.by_code_for_self("ICD-10", code_code="E11")
)
diabetic_patient_ids = set()
he_iterator = sdk.health_element.filter_health_elements_by_blocking(diabetes_filter)
while he_iterator.has_next_blocking():
for he in he_iterator.next_blocking(100):
diabetic_patient_ids.add(he.id)
return [p for p in candidates if p.id in diabetic_patient_ids]
2. Post-operative follow-up contacts​
Find all contacts for a patient in the past 30 days, excluding contacts tagged as "administrative".
- Kotlin
- Typescript
- Python
import com.icure.cardinal.sdk.CardinalSdk
import com.icure.cardinal.sdk.filters.ContactFilters
import com.icure.cardinal.sdk.filters.difference
import com.icure.cardinal.sdk.model.DecryptedContact
import com.icure.cardinal.sdk.model.Patient
import com.icure.cardinal.sdk.utils.pagination.PaginatedListIterator
suspend fun getPostOpFollowUpContacts(
sdk: CardinalSdk,
patient: Patient,
thirtyDaysAgoFuzzy: Long
): PaginatedListIterator<DecryptedContact> {
val filter = difference(
of = ContactFilters.byPatientsOpeningDateForSelf(
listOf(patient),
from = thirtyDaysAgoFuzzy
),
subtracting = ContactFilters.byTagAndOpeningDateForSelf(
"CD-LIFECYCLE",
tagCode = "administrative"
)
)
return sdk.contact.filterContactsBySorted(filter)
}
import {
CardinalSdk,
ContactFilters,
DecryptedContact,
difference,
PaginatedListIterator,
Patient,
} from "@icure/cardinal-sdk"
function getPostOpFollowUpContacts(
sdk: CardinalSdk,
patient: Patient,
thirtyDaysAgoFuzzy: number
): Promise<PaginatedListIterator<DecryptedContact>> {
const filter = difference(
ContactFilters.byPatientsOpeningDateForSelf(
[patient],
{ from: thirtyDaysAgoFuzzy }
),
ContactFilters.byTagAndOpeningDateForSelf(
"CD-LIFECYCLE",
{ tagCode: "administrative" }
)
)
return sdk.contact.filterContactsBySorted(filter)
}
from cardinal_sdk import CardinalSdk
from cardinal_sdk.model import Patient, DecryptedContact
from cardinal_sdk.filters import ContactFilters, difference
from cardinal_sdk.pagination.PaginatedListIterator import PaginatedListIterator
def get_post_op_follow_up_contacts(
sdk: CardinalSdk,
patient: Patient,
thirty_days_ago_fuzzy: int
) -> PaginatedListIterator[DecryptedContact]:
filter = difference(
ContactFilters.by_patients_opening_date_for_self(
[patient],
_from=thirty_days_ago_fuzzy
),
ContactFilters.by_tag_and_opening_date_for_self(
"CD-LIFECYCLE",
tag_code="administrative"
)
)
return sdk.contact.filter_contacts_by_sorted_blocking(filter)
3. Lab results over a date range​
Retrieve all LOINC-coded blood glucose (code 2345-7) services for a patient in the last year, sorted by value date (most recent first).
- Kotlin
- Typescript
- Python
import com.icure.cardinal.sdk.CardinalSdk
import com.icure.cardinal.sdk.filters.ServiceFilters
import com.icure.cardinal.sdk.filters.intersection
import com.icure.cardinal.sdk.model.Patient
import com.icure.cardinal.sdk.model.embed.DecryptedService
import com.icure.cardinal.sdk.utils.pagination.PaginatedListIterator
suspend fun getBloodGlucoseResults(
sdk: CardinalSdk,
patient: Patient,
oneYearAgoFuzzy: Long
): PaginatedListIterator<DecryptedService> {
val filter = intersection(
ServiceFilters.byPatientsDateForSelf(
listOf(patient),
from = oneYearAgoFuzzy,
descending = true
),
ServiceFilters.byCodeAndValueDateForSelf(
"LOINC",
codeCode = "2345-7"
)
)
return sdk.contact.filterServicesBySorted(filter)
}
import {
CardinalSdk,
DecryptedService,
intersection,
PaginatedListIterator,
Patient,
ServiceFilters,
} from "@icure/cardinal-sdk"
function getBloodGlucoseResults(
sdk: CardinalSdk,
patient: Patient,
oneYearAgoFuzzy: number
): Promise<PaginatedListIterator<DecryptedService>> {
const filter = intersection(
ServiceFilters.byPatientsDateForSelf(
[patient],
{ from: oneYearAgoFuzzy, descending: true }
),
ServiceFilters.byCodeAndValueDateForSelf(
"LOINC",
{ codeCode: "2345-7" }
)
)
return sdk.contact.filterServicesBySorted(filter)
}
from cardinal_sdk import CardinalSdk
from cardinal_sdk.model import Patient, DecryptedService
from cardinal_sdk.filters import ServiceFilters, intersection
from cardinal_sdk.pagination.PaginatedListIterator import PaginatedListIterator
def get_blood_glucose_results(
sdk: CardinalSdk,
patient: Patient,
one_year_ago_fuzzy: int
) -> PaginatedListIterator[DecryptedService]:
filter = intersection(
ServiceFilters.by_patients_date_for_self(
[patient],
_from=one_year_ago_fuzzy,
descending=True
),
ServiceFilters.by_code_and_value_date_for_self(
"LOINC",
code_code="2345-7"
)
)
return sdk.contact.filter_services_by_sorted_blocking(filter)
4. Finding cardiologists excluding an organization​
Find all healthcare parties with the SNOMED code for cardiologist (394579002), but exclude those belonging to a specific parent organization.
- Kotlin
- Typescript
- Python
import com.icure.cardinal.sdk.CardinalSdk
import com.icure.cardinal.sdk.filters.HealthcarePartyFilters
import com.icure.cardinal.sdk.filters.difference
import com.icure.cardinal.sdk.model.HealthcareParty
import com.icure.cardinal.sdk.utils.pagination.PaginatedListIterator
suspend fun findCardiologistsExcludingOrg(
sdk: CardinalSdk,
excludedOrgId: String
): PaginatedListIterator<HealthcareParty> {
val filter = difference(
of = HealthcarePartyFilters.byCode("SNOMED", codeCode = "394579002"),
subtracting = HealthcarePartyFilters.byParentId(excludedOrgId)
)
return sdk.healthcareParty.filterHealthPartiesBySorted(filter)
}
import {
CardinalSdk,
difference,
HealthcareParty,
HealthcarePartyFilters,
PaginatedListIterator,
} from "@icure/cardinal-sdk"
function findCardiologistsExcludingOrg(
sdk: CardinalSdk,
excludedOrgId: string
): Promise<PaginatedListIterator<HealthcareParty>> {
const filter = difference(
HealthcarePartyFilters.byCode("SNOMED", { codeCode: "394579002" }),
HealthcarePartyFilters.byParentId(excludedOrgId)
)
return sdk.healthcareParty.filterHealthPartiesBySorted(filter)
}
from cardinal_sdk import CardinalSdk
from cardinal_sdk.model import HealthcareParty
from cardinal_sdk.filters import HealthcarePartyFilters, difference
from cardinal_sdk.pagination.PaginatedListIterator import PaginatedListIterator
def find_cardiologists_excluding_org(
sdk: CardinalSdk,
excluded_org_id: str
) -> PaginatedListIterator[HealthcareParty]:
filter = difference(
HealthcarePartyFilters.by_code("SNOMED", code_code="394579002"),
HealthcarePartyFilters.by_parent_id(excluded_org_id)
)
return sdk.healthcare_party.filter_health_parties_by_sorted_blocking(filter)
5. Medications for elderly hypertensive patients​
A multi-step cross-entity query: find elderly patients (over 65) with an ICD-10 I10 (essential hypertension) diagnosis, then retrieve their medication services (tagged with ATC code system).
- Kotlin
- Typescript
- Python
import com.icure.cardinal.sdk.CardinalSdk
import com.icure.cardinal.sdk.filters.HealthElementFilters
import com.icure.cardinal.sdk.filters.PatientFilters
import com.icure.cardinal.sdk.filters.ServiceFilters
import com.icure.cardinal.sdk.filters.intersection
import com.icure.cardinal.sdk.model.embed.DecryptedService
import com.icure.cardinal.sdk.utils.pagination.PaginatedListIterator
suspend fun getMedicationsForElderlyHypertensives(
sdk: CardinalSdk
): PaginatedListIterator<DecryptedService> {
// Step 1: Find patients over 65
val elderlyPatients = sdk.patient.getPatients(
sdk.patient.matchPatientsBy(
PatientFilters.byDateOfBirthBetweenForSelf(
fromDate = 19000101,
toDate = 19610101
)
)
)
// Step 2: Among those, find patients with I10 hypertension
val hypertensionFilter = intersection(
HealthElementFilters.byPatientsForSelf(elderlyPatients),
HealthElementFilters.byCodeForSelf("ICD-10", codeCode = "I10")
)
val hypertensivePatientIds = mutableSetOf<String>()
val heIterator = sdk.healthElement.filterHealthElementsBy(hypertensionFilter)
while (heIterator.hasNext()) {
heIterator.next(100).mapNotNullTo(hypertensivePatientIds) { it.id }
}
val hypertensivePatients = elderlyPatients.filter { it.id in hypertensivePatientIds }
// Step 3: Get medication services for those patients
val medicationFilter = intersection(
ServiceFilters.byPatientsForSelf(hypertensivePatients),
ServiceFilters.byTagAndValueDateForSelf("CD-ITEM", tagCode = "medication")
)
return sdk.contact.filterServicesBy(medicationFilter)
}
import {
CardinalSdk,
DecryptedService,
HealthElementFilters,
intersection,
PaginatedListIterator,
PatientFilters,
ServiceFilters,
} from "@icure/cardinal-sdk"
async function getMedicationsForElderlyHypertensives(
sdk: CardinalSdk
): Promise<PaginatedListIterator<DecryptedService>> {
// Step 1: Find patients over 65
const elderlyPatients = await sdk.patient.getPatients(
await sdk.patient.matchPatientsBy(
PatientFilters.byDateOfBirthBetweenForSelf(19000101, 19610101)
)
)
// Step 2: Among those, find patients with I10 hypertension
const hypertensionFilter = intersection(
HealthElementFilters.byPatientsForSelf(elderlyPatients),
HealthElementFilters.byCodeForSelf("ICD-10", { codeCode: "I10" })
)
const hypertensivePatientIds = new Set<string>()
const heIterator = await sdk.healthElement.filterHealthElementsBy(hypertensionFilter)
while (await heIterator.hasNext()) {
const batch = await heIterator.next(100)
batch.forEach(he => hypertensivePatientIds.add(he.id))
}
const hypertensivePatients = elderlyPatients.filter(
p => hypertensivePatientIds.has(p.id)
)
// Step 3: Get medication services for those patients
const medicationFilter = intersection(
ServiceFilters.byPatientsForSelf(hypertensivePatients),
ServiceFilters.byTagAndValueDateForSelf("CD-ITEM", { tagCode: "medication" })
)
return sdk.contact.filterServicesBy(medicationFilter)
}
from cardinal_sdk import CardinalSdk
from cardinal_sdk.model import DecryptedService
from cardinal_sdk.filters import (
PatientFilters,
HealthElementFilters,
ServiceFilters,
intersection,
)
from cardinal_sdk.pagination.PaginatedListIterator import PaginatedListIterator
def get_medications_for_elderly_hypertensives(
sdk: CardinalSdk,
) -> PaginatedListIterator[DecryptedService]:
# Step 1: Find patients over 65
elderly_patients = sdk.patient.get_patients_blocking(
sdk.patient.match_patients_by_blocking(
PatientFilters.by_date_of_birth_between_for_self(19000101, 19610101)
)
)
# Step 2: Among those, find patients with I10 hypertension
hypertension_filter = intersection(
HealthElementFilters.by_patients_for_self(elderly_patients),
HealthElementFilters.by_code_for_self("ICD-10", code_code="I10")
)
hypertensive_patient_ids = set()
he_iterator = sdk.health_element.filter_health_elements_by_blocking(
hypertension_filter
)
while he_iterator.has_next_blocking():
for he in he_iterator.next_blocking(100):
hypertensive_patient_ids.add(he.id)
hypertensive_patients = [
p for p in elderly_patients if p.id in hypertensive_patient_ids
]
# Step 3: Get medication services for those patients
medication_filter = intersection(
ServiceFilters.by_patients_for_self(hypertensive_patients),
ServiceFilters.by_tag_and_value_date_for_self("CD-ITEM", tag_code="medication")
)
return sdk.contact.filter_services_by_blocking(medication_filter)