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.

Data owner variants​

Most filter factories for EBAC (Entity Based Access Control) 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.

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.

Sortability changes in SDK 2.6.0

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.

// Function call
val filter = intersection(filterA, filterB, filterC)
// Infix shorthand
val filter2 = 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 filter2 = filterA or filterB

difference (set minus)​

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

val filter = difference(ofFilter, subtractingFilter)

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 intersection with very broad filters.

  • difference can also be costly if the subtracting filter is very broad. Even if the final number of entities returned by the difference is small, the backend may still need to execute the subtracting filter and collect a large number of entity ids to determine which ones to exclude.

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

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
byIdentifiersForSelfidentifiersFilterOptionsNo—
bySsinsForSelfssinsFilterOptionsNo—
byDateOfBirthBetweenForSelffromDate, toDateFilterOptionsNo—
byNameForSelfsearchStringFilterOptionsNo—
byGenderEducationProfessionForSelfgender, education?, profession?FilterOptionsNo—
byActiveForSelfactiveFilterOptionsNo—
byTelecomForSelfsearchStringFilterOptionsNo—
byAddressPostalCodeHouseNumberForSelfsearchString, postalCode, houseNumber?FilterOptionsNo—
byAddressForSelfsearchStringFilterOptionsNo—
byTagForSelftagType, tagCode?FilterOptionsNo—
note

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​

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

ServiceFilters​

MethodKey parametersReturn typeSortable?Sort order
allServicesForSelf—FilterOptionsNo—
byIdentifiersForSelfidentifiersFilterOptionsNo—
byCodeAndValueDateForSelfcodeType, codeCode?, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byTagAndValueDateForSelftagType, tagCode?, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byCodesAndValueDateForSelfcodeCodes, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byCodePrefixAndValueDateForSelfcodeType, codeCodePrefix, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byTagCodesAndValueDateForSelftagCodes, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byTagPrefixAndValueDateForSelftagType, tagCodePrefix, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byPatientsCodesAndValueDateForSelfpatients, codeCodes, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byPatientSecretIdsCodesAndValueDateForSelfsecretIds, codeCodes, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byPatientsCodePrefixAndValueDateForSelfpatients, codeType, codeCodePrefix, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byPatientSecretIdsCodePrefixAndValueDateForSelfsecretIds, codeType, codeCodePrefix, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byPatientsTagCodesAndValueDateForSelfpatients, tagCodes, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byPatientSecretIdsTagCodesAndValueDateForSelfsecretIds, tagCodes, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byPatientsTagPrefixAndValueDateForSelfpatients, tagType, tagCodePrefix, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byPatientSecretIdsTagPrefixAndValueDateForSelfsecretIds, tagType, tagCodePrefix, startOfServiceValueDate?, endOfServiceValueDate?FilterOptionsNo—
byPatientsForSelfpatientsFilterOptionsNo—
byPatientsSecretIdsForSelfsecretIdsFilterOptionsNo—
byHealthElementIdFromSubContactForSelfhealthElementIdsFilterOptionsNo—
byPatientsDateForSelfpatients, from?, to?, descending?SortableFilterOptionsYesvalueDate
byPatientSecretIdsDateForSelfsecretIds, from?, to?, descending?SortableFilterOptionsYesvalueDate
byIdsidsBaseSortableFilterOptionsYesInput order
byAssociationIdassociationIdBaseFilterOptionsNo—
byQualifiedLinklinkValues, linkQualification?BaseFilterOptionsNo—

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:

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

HealthElementFilters​

MethodKey parametersReturn typeSortable?Sort order
allHealthElementsForSelf—FilterOptionsNo—
byIdentifiersForSelfidentifiersFilterOptionsNo—
byCodeForSelfcodeType, codeCode?FilterOptionsNo—
byTagForSelftagType, tagCode?FilterOptionsNo—
byPatientsForSelfpatientsFilterOptionsNo—
byPatientsSecretIdsForSelfsecretIdsFilterOptionsNo—
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?FilterOptionsNo—

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
byTransportGuidForSelftransportGuidFilterOptionsNo
fromAddressForSelfaddressFilterOptionsNo
toAddressForSelfaddressFilterOptionsNo
byPatientsSentDateForSelfpatients, from?, to?, descending?SortableFilterOptionsYes
byPatientSecretIdsSentDateForSelfsecretIds, from?, to?, descending?SortableFilterOptionsYes
byTransportGuidSentDateForSelftransportGuid, from, to, descending?FilterOptionsNo
latestByTransportGuidForSelftransportGuidFilterOptionsNo
lifecycleBetweenForSelfstartTimestamp?, endTimestamp?, descendingFilterOptionsNo
byCodeForSelfcodeType, codeCode?FilterOptionsNo
byTagForSelftagType, tagCode?FilterOptionsNo
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

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.

MethodKey parametersReturn typeSortable?Sort order
all—BaseFilterOptionsNo—
byIdentifiersidentifiersBaseFilterOptionsNo—
byCodecodeType, codeCode?BaseSortableFilterOptionsYesCode
byTagtagType, tagCode?BaseSortableFilterOptionsYesTag
note

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.

MethodKey parametersReturn typeSortable?Sort order
allRelatedPersonsForSelf—FilterOptionsNo—
byIdentifiersForSelfidentifiersFilterOptionsNo—
byNameForSelfnameFilterOptionsNo—
byIdsidsBaseSortableFilterOptionsYesInput 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.

MethodKey parametersReturn typeSortable?Sort order
byIdsidsBaseSortableFilterOptionsYesInput order
byIdentifiersForSelfidentifiersFilterOptionsNo—
byTypeForSelftypeFilterOptionsNo—
afterDateForSelfdateSortableFilterOptionsYescreated

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 corresponding XFilters object 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.

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

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