Skip to main content

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.

Filter type hierarchy​

The SDK provides four filter option types arranged by two axes: scoped vs unscoped and sortable vs unsortable.

TypeScoped?Sortable?Typical factory suffix
BaseFilterOptions<T>NoNoForDataOwner, ForDataOwnerInGroup, or no suffix
BaseSortableFilterOptions<T>NoYesForDataOwner, ForDataOwnerInGroup, or no suffix
FilterOptions<T>YesNoForSelf
SortableFilterOptions<T>YesYesForSelf

Scoped filter options (FilterOptions, SortableFilterOptions) are bound to the current SDK user's session. They are produced by ForSelf factory methods. Unscoped options (Base*) require an explicit data owner id and can be used by administrators or across groups.

You can always pass a more-specific type where a less-specific type is expected (e.g. a SortableFilterOptions wherever a BaseFilterOptions is expected).

Data owner variants​

Most filter factories for EBAC entities (Patient, Contact, Service, HealthElement, Document, etc.) come in three variants:

SuffixWhen to use
ForSelfDefault choice. Uses the current logged-in data owner.
ForDataOwnerWhen querying as an admin or on behalf of another data owner. Requires the data owner's id.
ForDataOwnerInGroupCross-group queries. Takes an EntityReferenceInGroup to target a data owner in a different group.

For more details, see Data owners and access control.

Combining filters​

Filter options can be combined using three operators:

intersection (AND)​

Returns entities that match all provided filters. Requires at least two filters.

// Function call
val filter = intersection(filterA, filterB, filterC)
// Infix shorthand
val filter = filterA and filterB and filterC

union (OR)​

Returns entities that match at least one of the provided filters. Requires at least two filters.

val filter = union(filterA, filterB)
// Infix shorthand
val filter = filterA or filterB

difference (set minus)​

Returns entities that match the first filter (of) but not the second (subtracting).

val filter = difference(ofFilter, subtractingFilter)

Sortability rules​

CombinatorSortable?Rule
intersectionIf first argument is sortableSorts by first argument's criteria
unionNever—
differenceIf of is sortableSorts by of's criteria
Kotlin infix syntax

In Kotlin, and is shorthand for intersection and or for union. You can chain them:

val filter = (filterA and filterB) or (filterC and filterD)

Date format conventions​

Different entity properties use different date formats:

FormatDescriptionUsed by
YYYYMMDD8-digit integer datePatient.dateOfBirth
YYYYMMDDHHmmSS14-digit fuzzy dateContact.openingDate, Service.valueDate, Service.openingDate, HealthElement.openingDate, Form.created, CalendarItem.startTime
Unix timestamp (ms)Milliseconds since epochDocument.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 — accepts Patient objects. The SDK automatically extracts secret ids. Cannot be used with CardinalBaseApis. 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 with CardinalBaseApis.

Date-range variants​

Many patient-linked filters also accept from/to date bounds and a descending flag for sorting:

EntityMethodDate property
ContactbyPatientsOpeningDateContact.openingDate
ServicebyPatientsDateService.valueDate (fallback: openingDate)
HealthElementbyPatientsOpeningDateHealthElement.openingDate
DocumentbyPatientsCreatedDocument.created
MessagebyPatientsSentDateMessage.sentDate
CalendarItembyPatientsStartTimeCalendarItem.startTime
FormbyPatientsOpeningDateForm.created
ClassificationbyPatientsCreatedClassification.created
AccessLogbyPatientsDateAccessLog.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​

MethodKey parametersReturn typeSortable?Sort order
allPatientsForSelf—FilterOptionsNo—
byIdsidsSortableFilterOptionsYesInput order
byIdentifiersForSelfidentifiersSortableFilterOptionsYesInput order
bySsinsForSelfssinsSortableFilterOptionsYesInput order
byDateOfBirthBetweenForSelffromDate, toDateSortableFilterOptionsYesDate of birth
byNameForSelfsearchStringFilterOptionsNo—
byGenderEducationProfessionForSelfgender, education?, profession?SortableFilterOptionsYesEducation, profession
byActiveForSelfactiveFilterOptionsNo—
byTelecomForSelfsearchStringSortableFilterOptionsYesTelecom number
byAddressPostalCodeHouseNumberForSelfsearchString, postalCode, houseNumber?SortableFilterOptionsYesAddress, postal code, house number
byAddressForSelfsearchStringSortableFilterOptionsYesAddress
byTagForSelftagType, tagCode?FilterOptionsNo—
note

byIds has no data-owner scoping — it returns a SortableFilterOptions (not Base) and works the same for all users. The ForDataOwner variants for the other methods also exist (e.g. byFuzzyNameForDataOwner accepts a dataOwnerId + searchString).

ContactFilters​

MethodKey parametersReturn typeSortable?Sort order
allContactsForSelf—FilterOptionsNo—
byFormIdsForSelfformIdsFilterOptionsNo—
byPatientsOpeningDateForSelfpatients, from?, to?, descending?SortableFilterOptionsYesopeningDate
byPatientSecretIdsOpeningDateForSelfsecretIds, from?, to?, descending?SortableFilterOptionsYesopeningDate
byIdentifiersForSelfidentifiersSortableFilterOptionsYesInput order
byCodeAndOpeningDateForSelfcodeType, codeCode?, startOfContactOpeningDate?, endOfContactOpeningDate?SortableFilterOptionsYesCode, openingDate
byTagAndOpeningDateForSelftagType, tagCode?, startOfContactOpeningDate?, endOfContactOpeningDate?SortableFilterOptionsYesTag, openingDate
byOpeningDateForSelfstartDate?, endDate?, descending?SortableFilterOptionsYesopeningDate
byServiceTagForSelftagType, tagCode?FilterOptionsNo—
byServiceCodeForSelfcodeType, codeCode?FilterOptionsNo—
byPatientsForSelfpatientsSortableFilterOptionsYesInput order
byPatientsSecretIdsForSelfsecretIdsSortableFilterOptionsYesInput order
byServiceIdsserviceIdsBaseSortableFilterOptionsYesInput order

ServiceFilters​

MethodKey parametersReturn typeSortable?Sort order
allServicesForSelf—FilterOptionsNo—
byIdentifiersForSelfidentifiersSortableFilterOptionsYesInput order
byCodeAndValueDateForSelfcodeType, codeCode?, startOfServiceValueDate?, endOfServiceValueDate?SortableFilterOptionsYesCode, valueDate
byTagAndValueDateForSelftagType, tagCode?, startOfServiceValueDate?, endOfServiceValueDate?SortableFilterOptionsYesTag, valueDate
byPatientsForSelfpatientsSortableFilterOptionsYesInput order
byPatientsSecretIdsForSelfsecretIdsSortableFilterOptionsYesInput order
byHealthElementIdFromSubContactForSelfhealthElementIdsSortableFilterOptionsYesInput order
byPatientsDateForSelfpatients, from?, to?, descending?SortableFilterOptionsYesvalueDate
byPatientSecretIdsDateForSelfsecretIds, from?, to?, descending?SortableFilterOptionsYesvalueDate
byIdsidsBaseSortableFilterOptionsYesInput order
byAssociationIdassociationIdBaseFilterOptionsNo—
byQualifiedLinklinkValues, linkQualification?BaseFilterOptionsNo—

HealthElementFilters​

MethodKey parametersReturn typeSortable?Sort order
allHealthElementsForSelf—FilterOptionsNo—
byIdentifiersForSelfidentifiersSortableFilterOptionsYesInput order
byCodeForSelfcodeType, codeCode?SortableFilterOptionsYesCode
byTagForSelftagType, tagCode?SortableFilterOptionsYesTag
byPatientsForSelfpatientsSortableFilterOptionsYesInput order
byPatientsSecretIdsForSelfsecretIdsSortableFilterOptionsYesInput order
byIdsidsBaseSortableFilterOptionsYesInput order
byPatientsOpeningDateForSelfpatients, from?, to?, descending?SortableFilterOptionsYesopeningDate
byPatientSecretIdsOpeningDateForSelfsecretIds, from?, to?, descending?SortableFilterOptionsYesopeningDate

DocumentFilters​

MethodKey parametersReturn typeSortable?Sort order
byPatientsCreatedForSelfpatients, from?, to?, descending?SortableFilterOptionsYescreated
byMessagesCreatedForSelfmessages, from?, to?, descending?SortableFilterOptionsYescreated
byOwningEntitySecretIdsCreatedForSelfsecretIds, from?, to?, descending?SortableFilterOptionsYescreated
byPatientsAndTypeForSelfdocumentType, patientsFilterOptionsNo—
byMessagesAndTypeForSelfdocumentType, messagesFilterOptionsNo—
byOwningEntitySecretIdsAndTypeForSelfdocumentType, secretIdsFilterOptionsNo—
byCodeForSelfcodeType, codeCode?SortableFilterOptionsYesCode
byTagForSelftagType, tagCode?SortableFilterOptionsYesTag

HealthcarePartyFilters​

Healthcare party filters are not data-owner scoped — they all return Base* types.

MethodKey parametersReturn typeSortable?Sort order
all—BaseFilterOptionsNo—
byIdentifiersidentifiersBaseFilterOptionsNo—
byCodecodeType, codeCode?BaseSortableFilterOptionsYesCode
byTagtagType, tagCode?BaseSortableFilterOptionsYesTag
byIdsidsSortableFilterOptionsYesInput order
byNamesearchString, descending?BaseSortableFilterOptionsYesName/speciality
byNationalIdentifiersearchString, descending?BaseSortableFilterOptionsYesSSIN
byParentIdparentIdBaseFilterOptionsNo—

Other entity filters​

UserFilters​

MethodKey parametersReturn typeSortable?
all—BaseFilterOptionsNo
byIdsidsBaseSortableFilterOptionsYes
byPatientIdpatientIdBaseFilterOptionsNo
byHealthcarePartyIdhealthcarePartyIdBaseFilterOptionsNo
byNameEmailOrPhonesearchStringBaseFilterOptionsNo

CodeFilters​

MethodKey parametersReturn typeSortable?
all—BaseFilterOptionsNo
byIdsidsBaseSortableFilterOptionsYes
byQualifiedLinklinkType, linkedId?BaseFilterOptionsNo
byRegionTypeCodeVersionregion, type?, code?, version?BaseFilterOptionsNo
byLanguageTypeLabelRegionlanguage, type, label?, region?BaseFilterOptionsNo
byLanguageTypesLabelRegionVersionlanguage, types, label, region?, version?BaseFilterOptionsNo

MessageFilters​

MethodKey parametersReturn typeSortable?
allMessagesForSelf—FilterOptionsNo
byTransportGuidForSelftransportGuidSortableFilterOptionsYes
fromAddressForSelfaddressFilterOptionsNo
toAddressForSelfaddressFilterOptionsNo
byPatientsSentDateForSelfpatients, from?, to?, descending?SortableFilterOptionsYes
byPatientSecretIdsSentDateForSelfsecretIds, from?, to?, descending?SortableFilterOptionsYes
byTransportGuidSentDateForSelftransportGuid, from, to, descending?SortableFilterOptionsYes
latestByTransportGuidForSelftransportGuidFilterOptionsNo
lifecycleBetweenForSelfstartTimestamp?, endTimestamp?, descendingFilterOptionsNo
byCodeForSelfcodeType, codeCode?SortableFilterOptionsYes
byTagForSelftagType, tagCode?SortableFilterOptionsYes
byInvoiceIdsinvoiceIdsBaseFilterOptionsNo
byParentIdsparentIdsBaseFilterOptionsNo

CalendarItemFilters​

MethodKey parametersReturn typeSortable?
byPatientsStartTimeForSelfpatients, from?, to?, descending?SortableFilterOptionsYes
byPatientSecretIdsStartTimeForSelfsecretIds, from?, to?, descending?SortableFilterOptionsYes
byPeriodAndAgendaagendaId, from, to, descending?BaseSortableFilterOptionsYes
byPeriodForSelffrom, toFilterOptionsNo
byRecurrenceIdrecurrenceIdFilterOptionsNo
lifecycleBetweenForSelfstartTimestamp?, endTimestamp?, descendingFilterOptionsNo

GroupFilters​

MethodKey parametersReturn typeSortable?
all—BaseFilterOptionsNo
bySuperGroupsuperGroupIdBaseFilterOptionsNo
withContentsuperGroupId, searchStringBaseSortableFilterOptionsYes

FormFilters​

MethodKey parametersReturn typeSortable?
byParentIdForSelfparentIdFilterOptionsNo
byPatientsOpeningDateForSelfpatients, from?, to?, descending?SortableFilterOptionsYes
byPatientSecretIdsOpeningDateForSelfsecretIds, from?, to?, descending?SortableFilterOptionsYes
byUniqueIduniqueId, descending?BaseSortableFilterOptionsYes

AccessLogFilters​

MethodKey parametersReturn typeSortable?
byPatientsDateForSelfpatients, from?, to?, descending?SortableFilterOptionsYes
byPatientSecretIdsDateForSelfsecretIds, from?, to?, descending?SortableFilterOptionsYes
byDatefrom?, to?, descending?BaseSortableFilterOptionsYes
byUserTypeDateuserId, accessType?, from?, descending?BaseSortableFilterOptionsYes

DeviceFilters​

MethodKey parametersReturn typeSortable?
all—BaseFilterOptionsNo
byResponsibleresponsibleIdBaseFilterOptionsNo
byIdsidsBaseSortableFilterOptionsYes

AgendaFilters​

MethodKey parametersReturn typeSortable?
all—BaseFilterOptionsNo
byUseruserIdBaseFilterOptionsNo
readableByUseruserIdBaseFilterOptionsNo
readableByUserRightsuserIdBaseFilterOptionsNo
byStringPropertypropertyId, propertyValueBaseFilterOptionsNo
byBooleanPropertypropertyId, propertyValueBaseFilterOptionsNo
byLongPropertypropertyId, propertyValueBaseFilterOptionsNo
byDoublePropertypropertyId, propertyValueBaseFilterOptionsNo
withPropertypropertyIdBaseFilterOptionsNo

TopicFilters​

MethodKey parametersReturn typeSortable?
allTopicsForSelf—FilterOptionsNo
byParticipantparticipantIdFilterOptionsNo

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.

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 }
}

2. Post-operative follow-up contacts​

Find all contacts for a patient in the past 30 days, excluding contacts tagged as "administrative".

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)
}

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).

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)
}

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.

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)
}

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).

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)
}