From b13ccb2b72f6c1af816fc1db865506be6ab09414 Mon Sep 17 00:00:00 2001 From: Abigail Watson Date: Thu, 18 Dec 2025 14:24:22 -0700 Subject: [PATCH 1/2] FHIR-53520: Add additional algorithm examples (deduplication, trends, interactions, care gaps, LLM) --- input/pagecontent/algorithms.md | 703 +++++++++++++++++++++++++++++++- 1 file changed, 702 insertions(+), 1 deletion(-) diff --git a/input/pagecontent/algorithms.md b/input/pagecontent/algorithms.md index da159fccd..7d8b98d3a 100644 --- a/input/pagecontent/algorithms.md +++ b/input/pagecontent/algorithms.md @@ -88,4 +88,705 @@ This algorithm is intended to act as a software agent that monitors a resource, 3. Consumption Rate Calculation: Compute utilization velocity and projected depletion time. 4. Alert Generation: Create graduated warning levels as resource approaches critical thresholds. 5. Predictive Projection: Forecast estimated time remaining based on current consumption pattern. -6. User Notification: Dispatch alerts indicating resource status and recommended actions. \ No newline at end of file +6. User Notification: Dispatch alerts indicating resource status and recommended actions. + +--- + +### Advanced Algorithms for PHR Systems + +#### Record Deduplication + +Deduplication identifies and merges duplicate resources imported from multiple healthcare systems. + +**Algorithm Definition:** + +Given sets R₁, R₂, ..., Rₙ of FHIR resources from n different sources, find all pairs (rᵢ, rⱼ) where rᵢ ∈ Rₖ, rⱼ ∈ Rₘ, k ≠ m, and similarity(rᵢ, rⱼ) > θ (threshold). + +**Similarity Function:** + +``` +similarity(r₁, r₂) = w₁·δ(code₁, code₂) + w₂·τ(date₁, date₂) + w₃·ν(value₁, value₂) +``` + +Where: +- δ = code similarity (1 if exact match, 0 otherwise) +- τ = temporal proximity (1 - |date₁ - date₂|/tolerance) +- ν = value similarity (varies by resource type) +- w₁ + w₂ + w₃ = 1 (weights) + +**Resource-Specific Rules:** + +| Resource | Key Fields | Date Tolerance | Threshold θ | +|----------|-----------|----------------|------------| +| Condition | code, onsetDateTime | ±30 days | 0.85 | +| MedicationStatement | medication.code, effectivePeriod | ±7 days | 0.90 | +| Observation (vitals) | code, effectiveDateTime | ±1 hour | 0.95 | +| Observation (labs) | code, effectiveDateTime | ±1 day | 0.90 | + +**Academic Pseudocode:** + +``` +ALGORITHM DeduplicateResources(R: Set of Resources) → Set of ResourcePairs +BEGIN + candidates ← ∅ + + FOR each r₁ ∈ R DO + FOR each r₂ ∈ R WHERE r₂ ≠ r₁ AND source(r₁) ≠ source(r₂) DO + // Exact identifier match + IF ∃ id ∈ identifiers(r₁) ∩ identifiers(r₂) THEN + candidates ← candidates ∪ {(r₁, r₂, 1.0)} + CONTINUE + END IF + + // Fuzzy matching + s ← CalculateSimilarity(r₁, r₂) + IF s > θ THEN + candidates ← candidates ∪ {(r₁, r₂, s)} + END IF + END FOR + END FOR + + RETURN SortBySimilarity(candidates) +END + +FUNCTION CalculateSimilarity(r₁, r₂: Resource) → ℝ +BEGIN + score ← 0 + IF SameCode(r₁, r₂) THEN score ← score + 0.4 + IF DateWithinTolerance(r₁, r₂) THEN score ← score + 0.3 + IF SameValue(r₁, r₂) THEN score ← score + 0.2 + IF SameContext(r₁, r₂) THEN score ← score + 0.1 + RETURN score +END +``` + +**JavaScript + MongoDB Implementation:** + +```javascript +async function findDuplicates(db, resourceType, threshold = 0.85) { + const pipeline = [ + { + $match: { resourceType: resourceType } + }, + { + $lookup: { + from: 'resources', + let: { + code: '$code.coding.code', + date: '$effectiveDateTime', + source: '$meta.source' + }, + pipeline: [ + { + $match: { + $expr: { + $and: [ + { $eq: ['$resourceType', resourceType] }, + { $ne: ['$meta.source', '$$source'] }, + { $in: ['$$code', '$code.coding.code'] } + ] + } + } + }, + { + $addFields: { + dateDiff: { + $abs: { + $subtract: [ + { $toDate: '$effectiveDateTime' }, + { $toDate: '$$date' } + ] + } + } + } + }, + { + $match: { + dateDiff: { $lt: 3600000 } // 1 hour in ms + } + } + ], + as: 'candidates' + } + }, + { + $unwind: '$candidates' + }, + { + $project: { + _id: 1, + resourceType: 1, + candidate: '$candidates', + similarity: { + $add: [ + 0.4, // code match + { $multiply: [0.3, + { $subtract: [1, + { $divide: ['$candidates.dateDiff', 86400000] } + ]} + ]} + ] + } + } + }, + { + $match: { + similarity: { $gte: threshold } + } + }, + { + $sort: { similarity: -1 } + } + ]; + + return await db.collection('resources').aggregate(pipeline).toArray(); +} +``` + +--- + +#### Trend Detection + +Identify clinically significant trends in time-series observations. + +**Mathematical Model:** + +For observations {(tᵢ, vᵢ)} where i = 1..n, fit linear regression: + +``` +v(t) = α + βt + ε +``` + +Where β (slope) indicates trend direction and magnitude. + +**Statistical Significance:** + +Trend is significant if: +1. p-value < 0.05 (null hypothesis: β = 0) +2. |β| > clinical threshold +3. R² > 0.5 (goodness of fit) + +**Academic Pseudocode:** + +``` +ALGORITHM DetectTrend(observations: Array, windowDays: Integer) → TrendResult +BEGIN + // Filter to time window + recent ← FILTER(observations, o.date > now() - windowDays) + + // Remove outliers (>3σ) + μ ← MEAN(recent.values) + σ ← STDDEV(recent.values) + cleaned ← FILTER(recent, |o.value - μ| ≤ 3σ) + + // Linear regression + n ← LENGTH(cleaned) + x ← [t₁, t₂, ..., tₙ] // timestamps as days + y ← [v₁, v₂, ..., vₙ] // values + + x̄ ← MEAN(x) + ȳ ← MEAN(y) + + // Calculate slope β + Σxy ← Σᵢ(xᵢ - x̄)(yᵢ - ȳ) + Σx² ← Σᵢ(xᵢ - x̄)² + + β ← Σxy / Σx² + α ← ȳ - β·x̄ + + // Calculate R² and p-value + SSR ← Σᵢ(α + β·xᵢ - ȳ)² + SST ← Σᵢ(yᵢ - ȳ)² + R² ← SSR / SST + + SE_β ← √(Σᵢ(yᵢ - α - β·xᵢ)² / ((n-2)·Σx²)) + t_stat ← β / SE_β + p_value ← 2·(1 - T_CDF(|t_stat|, n-2)) + + // Assess clinical significance + IF p_value < 0.05 AND |β| > THRESHOLD AND R² > 0.5 THEN + RETURN TrendResult( + significant: TRUE, + direction: SIGN(β), + rate: β, + confidence: R², + p_value: p_value + ) + ELSE + RETURN TrendResult(significant: FALSE) + END IF +END +``` + +**JavaScript Implementation:** + +```javascript +function analyzeTrend(observations, windowDays = 30, clinicalThreshold = 2) { + // Filter to time window + const cutoff = Date.now() - (windowDays * 86400000); + let recent = observations.filter(o => + new Date(o.effectiveDateTime).getTime() > cutoff + ); + + // Remove outliers + const values = recent.map(o => o.valueQuantity.value); + const mean = values.reduce((a,b) => a+b) / values.length; + const stdDev = Math.sqrt( + values.reduce((sq, v) => sq + Math.pow(v - mean, 2), 0) / values.length + ); + + recent = recent.filter(o => + Math.abs(o.valueQuantity.value - mean) <= 3 * stdDev + ); + + // Prepare data for regression + const n = recent.length; + const baseTime = new Date(recent[0].effectiveDateTime).getTime(); + const x = recent.map(o => + (new Date(o.effectiveDateTime).getTime() - baseTime) / 86400000 + ); + const y = recent.map(o => o.valueQuantity.value); + + // Calculate regression + const xMean = x.reduce((a,b) => a+b) / n; + const yMean = y.reduce((a,b) => a+b) / n; + + let sumXY = 0, sumX2 = 0; + for (let i = 0; i < n; i++) { + sumXY += (x[i] - xMean) * (y[i] - yMean); + sumX2 += Math.pow(x[i] - xMean, 2); + } + + const slope = sumXY / sumX2; + const intercept = yMean - slope * xMean; + + // Calculate R² + let SSR = 0, SST = 0; + for (let i = 0; i < n; i++) { + const predicted = intercept + slope * x[i]; + SSR += Math.pow(predicted - yMean, 2); + SST += Math.pow(y[i] - yMean, 2); + } + const rSquared = SSR / SST; + + // Simplified p-value (t-distribution) + let SSE = 0; + for (let i = 0; i < n; i++) { + SSE += Math.pow(y[i] - intercept - slope * x[i], 2); + } + const seBeta = Math.sqrt(SSE / ((n - 2) * sumX2)); + const tStat = slope / seBeta; + const pValue = 2 * (1 - tCDF(Math.abs(tStat), n - 2)); + + return { + significant: pValue < 0.05 && Math.abs(slope) > clinicalThreshold && rSquared > 0.5, + direction: slope > 0 ? 'increasing' : 'decreasing', + rate: slope, + confidence: rSquared, + pValue: pValue + }; +} +``` + +**Clinical Thresholds:** + +| Measurement | Concerning Trend | +|-------------|-----------------| +| Weight | >2 kg/week | +| Systolic BP | >10 mmHg/month | +| Resting HR | >10 bpm/month | +| Fasting glucose | >20 mg/dL/month | + +--- + +#### Medication Interaction Checker + +**Set Theory Formulation:** + +Given active medications M = {m₁, m₂, ..., mₙ} and interaction database D: M × M → {severity, mechanism}, find all pairs (mᵢ, mⱼ) where D(mᵢ, mⱼ) exists. + +**Academic Pseudocode:** + +``` +ALGORITHM CheckInteractions(medications: Set) → Array of Interactions +BEGIN + active ← FILTER(medications, m.status = 'active') + normalized ← MAP(active, m → NormalizeToRxNorm(m)) + + interactions ← ∅ + + FOR i ← 1 TO |normalized| - 1 DO + FOR j ← i + 1 TO |normalized| DO + result ← QueryInteractionDB(normalized[i], normalized[j]) + IF result ≠ NULL THEN + interactions ← interactions ∪ {result} + END IF + END FOR + END FOR + + RETURN SORT(interactions, BY severity DESC) +END +``` + +**JavaScript + External API:** + +```javascript +async function checkMedicationInteractions(medicationStatements) { + const active = medicationStatements.filter(m => m.status === 'active'); + + // Extract RxNorm codes + const rxnormCodes = active.map(m => { + const coding = m.medicationCodeableConcept?.coding?.find( + c => c.system === 'http://www.nlm.nih.gov/research/umls/rxnorm' + ); + return coding?.code; + }).filter(Boolean); + + const interactions = []; + + // Check all pairs + for (let i = 0; i < rxnormCodes.length - 1; i++) { + for (let j = i + 1; j < rxnormCodes.length; j++) { + const response = await fetch( + `https://rxnav.nlm.nih.gov/REST/interaction/interaction.json?` + + `rxcui=${rxnormCodes[i]}&rxcui=${rxnormCodes[j]}` + ); + + const data = await response.json(); + + if (data.interactionTypeGroup) { + data.interactionTypeGroup.forEach(group => { + group.interactionType.forEach(type => { + type.interactionPair.forEach(pair => { + interactions.push({ + medication1: pair.interactionConcept[0].minConceptItem.name, + medication2: pair.interactionConcept[1].minConceptItem.name, + severity: pair.severity, + description: pair.description + }); + }); + }); + }); + } + } + } + + return interactions.sort((a, b) => + severityRank(b.severity) - severityRank(a.severity) + ); +} + +function severityRank(severity) { + const ranks = { 'high': 3, 'moderate': 2, 'low': 1 }; + return ranks[severity?.toLowerCase()] || 0; +} +``` + +--- + +#### Care Gap Detection + +**Set-Based Definition:** + +Let G be guidelines, P be patient data. For each guideline g ∈ G: +- If g.applies(P) AND (last(P, g.procedure) = ∅ OR overdue(last(P, g.procedure), g.interval)) +- Then gap(g) = TRUE + +**Academic Pseudocode:** + +``` +ALGORITHM FindCareGaps(patient: Patient, guidelines: Set) → Set of CareGaps +BEGIN + gaps ← ∅ + + FOR EACH g ∈ guidelines DO + IF NOT AppliesTo(g, patient) THEN CONTINUE + + lastOccurrence ← FindLastOccurrence(patient, g.procedureCodes) + + IF lastOccurrence = NULL THEN + gaps ← gaps ∪ {CareGap( + guideline: g, + status: 'never_performed', + priority: 'high' + )} + ELSE IF IsOverdue(lastOccurrence.date, g.interval) THEN + gaps ← gaps ∪ {CareGap( + guideline: g, + status: 'overdue', + lastDate: lastOccurrence.date, + daysOverdue: DaysSince(lastOccurrence.date) - g.interval, + priority: CalculatePriority(daysOverdue) + )} + END IF + END FOR + + RETURN SORT(gaps, BY priority DESC, daysOverdue DESC) +END +``` + +**JavaScript + MongoDB:** + +```javascript +const GUIDELINES = [ + { + name: 'Colonoscopy', + appliesTo: p => p.age >= 45, + procedureCodes: ['73761001'], // SNOMED + intervalYears: 10 + }, + { + name: 'Mammogram', + appliesTo: p => p.sex === 'female' && p.age >= 40, + procedureCodes: ['71651007'], + intervalYears: 2 + }, + { + name: 'Flu Vaccine', + appliesTo: p => true, + procedureCodes: ['86198006'], + intervalYears: 1 + }, + { + name: 'Lipid Panel', + appliesTo: p => p.age >= 35, + procedureCodes: ['24331-1'], // LOINC + intervalYears: 5 + } +]; + +async function findCareGaps(db, patientId) { + const patient = await db.collection('Patient').findOne({ id: patientId }); + const age = calculateAge(patient.birthDate); + patient.age = age; + patient.sex = patient.gender; + + const gaps = []; + + for (const guideline of GUIDELINES) { + if (!guideline.appliesTo(patient)) continue; + + // Find last occurrence + const lastProcedure = await db.collection('Procedure').findOne({ + 'subject.reference': `Patient/${patientId}`, + 'code.coding.code': { $in: guideline.procedureCodes } + }, { + sort: { 'performedDateTime': -1 } + }); + + if (!lastProcedure) { + gaps.push({ + guideline: guideline.name, + status: 'never_performed', + priority: 'high' + }); + } else { + const lastDate = new Date(lastProcedure.performedDateTime); + const daysSince = (Date.now() - lastDate.getTime()) / 86400000; + const intervalDays = guideline.intervalYears * 365; + + if (daysSince > intervalDays) { + gaps.push({ + guideline: guideline.name, + status: 'overdue', + lastDate: lastDate, + daysOverdue: Math.floor(daysSince - intervalDays), + priority: daysSince > intervalDays * 1.5 ? 'high' : 'medium' + }); + } + } + } + + return gaps.sort((a, b) => { + if (a.priority !== b.priority) { + return a.priority === 'high' ? -1 : 1; + } + return (b.daysOverdue || 0) - (a.daysOverdue || 0); + }); +} + +function calculateAge(birthDate) { + const today = new Date(); + const birth = new Date(birthDate); + let age = today.getFullYear() - birth.getFullYear(); + const m = today.getMonth() - birth.getMonth(); + if (m < 0 || (m === 0 && today.getDate() < birth.getDate())) { + age--; + } + return age; +} +``` + +--- + +#### LLM-Based Data Summarization + +**Information Theory Foundation:** + +Maximize information preservation while minimizing text length: + +``` +argmax_S I(R; S) subject to |S| ≤ L +``` + +Where: +- R = original resources +- S = summary text +- I(R; S) = mutual information +- L = maximum summary length + +**Academic Pseudocode:** + +``` +ALGORITHM GenerateSummary(bundle: Bundle, summaryType: String) → String +BEGIN + // Extract key resources + conditions ← ExtractActiveConditions(bundle) + medications ← ExtractActiveMedications(bundle) + vitals ← ExtractRecentVitals(bundle, days: 30) + + // Convert to text normal form + conditionsText ← FormatConditions(conditions) + medsText ← FormatMedications(medications) + vitalsText ← FormatVitals(vitals) + + // Construct prompt with constraints + prompt ← CONSTRUCT_PROMPT( + type: summaryType, + conditions: conditionsText, + medications: medsText, + vitals: vitalsText, + constraints: [ + "Use plain language", + "Highlight concerning trends", + "Do not fabricate", + "Format as 2-3 paragraphs" + ] + ) + + // Generate with safety parameters + response ← LLM_API.generate( + prompt: prompt, + maxTokens: 500, + temperature: 0.3 // Low for factual content + ) + + // Validate against source + IF ContainsFabrication(response, bundle) THEN + RAISE ValidationError("Summary contains fabricated information") + END IF + + RETURN response +END +``` + +**JavaScript Implementation:** + +```javascript +async function generatePatientSummary(bundle, summaryType = 'brief') { + // Extract and normalize + const conditions = bundle.entry + .filter(e => e.resource.resourceType === 'Condition' && + e.resource.clinicalStatus?.coding?.[0]?.code === 'active') + .map(e => ({ + code: e.resource.code.coding[0].display, + onset: e.resource.onsetDateTime + })); + + const medications = bundle.entry + .filter(e => e.resource.resourceType === 'MedicationStatement' && + e.resource.status === 'active') + .map(e => e.resource.medicationCodeableConcept.text); + + const recentVitals = bundle.entry + .filter(e => { + if (e.resource.resourceType !== 'Observation') return false; + const date = new Date(e.resource.effectiveDateTime); + return (Date.now() - date.getTime()) < 30 * 86400000; + }) + .map(e => ({ + type: e.resource.code.coding[0].display, + value: e.resource.valueQuantity.value, + unit: e.resource.valueQuantity.unit, + date: e.resource.effectiveDateTime + })); + + // Build prompt + const prompt = ` +Generate a ${summaryType} clinical summary for this patient: + +Active Conditions: ${conditions.map(c => c.code).join(', ') || 'None documented'} +Current Medications: ${medications.join(', ') || 'None'} +Recent Vitals (last 30 days): ${formatVitals(recentVitals)} + +Guidelines: +- Use plain language suitable for patient understanding +- Highlight any concerning trends +- Do not fabricate information not present in the data +- Format as 2-3 paragraphs +- Include disclaimer about AI generation +`; + + // Call LLM API (example with OpenAI) + const response = await fetch('https://api.openai.com/v1/chat/completions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${process.env.OPENAI_API_KEY}` + }, + body: JSON.stringify({ + model: 'gpt-4', + messages: [ + { role: 'system', content: 'You are a medical summarization assistant.' }, + { role: 'user', content: prompt } + ], + max_tokens: 500, + temperature: 0.3 + }) + }); + + const data = await response.json(); + const summary = data.choices[0].message.content; + + // Validate (check for hallucinations) + if (containsFabricatedInfo(summary, bundle)) { + throw new Error('Summary contains information not in source data'); + } + + return summary + '\n\n*This summary was generated by AI and should be reviewed by a healthcare professional.*'; +} + +function formatVitals(vitals) { + return vitals.map(v => `${v.type}: ${v.value} ${v.unit}`).join('; '); +} + +function containsFabricatedInfo(summary, bundle) { + // Simple check: verify mentioned conditions/meds exist in bundle + const allText = JSON.stringify(bundle).toLowerCase(); + const summaryLower = summary.toLowerCase(); + + // Extract medical terms from summary (simplified) + const medicalTerms = summaryLower.match(/\b[a-z]{5,}\b/g) || []; + + // Check if unusual terms appear in summary but not in bundle + const suspicious = medicalTerms.filter(term => + !allText.includes(term) && isMedicalTerm(term) + ); + + return suspicious.length > 3; // Threshold for concern +} + +function isMedicalTerm(word) { + // Simplified check - in production, use medical ontology + const commonMedical = ['diabetes', 'hypertension', 'asthma', 'copd', + 'medication', 'blood', 'pressure', 'glucose']; + return commonMedical.includes(word); +} +``` + +**Safety Considerations:** +- Validate output against source data +- Include AI-generated disclaimers +- Log prompts and responses for audit +- Allow human review before sharing +- Never use for diagnostic decisions \ No newline at end of file From ba79e0e7158b5cbb47b575bc9e7758d994b7f8d4 Mon Sep 17 00:00:00 2001 From: Abigail Watson Date: Thu, 29 Jan 2026 20:39:48 -0600 Subject: [PATCH 2/2] FHIR-53519: Add query and filter parameter documentation to API page Add standard FHIR search parameters, PHR-specific filters (by data source, clinical relevance, verification status), bulk export filters, patient sharing preferences via Consent resource, response pagination, and selective field retrieval documentation. JIRA: https://jira.hl7.org/browse/FHIR-53519 Co-Authored-By: Claude Opus 4.5 --- input/pagecontent/api.md | 136 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/input/pagecontent/api.md b/input/pagecontent/api.md index 43af27cce..93240e5ed 100644 --- a/input/pagecontent/api.md +++ b/input/pagecontent/api.md @@ -28,4 +28,138 @@ GET /Bundle/$phr-export?patient=Patient/12345 POST /Bundle/$import ``` -Systems MUST post the API endpoints they use in the system's CapabilityStatement. +Systems MUST post the API endpoints they use in the system's CapabilityStatement. + +### Query and Filter Parameters + +PHR systems should support flexible filtering to allow patients and applications to retrieve specific subsets of data. + +#### Standard FHIR Search Parameters + +All standard FHIR search parameters apply. Common patterns for PHR queries: + +```http +# Resources modified since a date +GET /Observation?_lastUpdated=gt2025-01-01 + +# Resources within a date range +GET /Observation?date=ge2024-01-01&date=le2024-12-31 + +# Specific resource types +GET /Condition?patient=Patient/123 + +# By category +GET /Observation?category=vital-signs +GET /Observation?category=laboratory +GET /Observation?category=activity +``` + +#### PHR-Specific Filters + +##### By Data Source + +Filter by originating system: + +```http +GET /Observation?_source=urn:ehr:hospital-xyz +GET /Observation?_source=urn:device:fitbit +GET /Observation?_source=urn:phr:patient-entered +``` + +##### By Clinical Relevance + +Filter for active/current data (suitable for IPS generation): + +```http +GET /Condition?clinical-status=active +GET /MedicationStatement?status=active +GET /AllergyIntolerance?clinical-status=active +``` + +##### By Verification Status + +Distinguish verified vs unverified data: + +```http +GET /Condition?verification-status=confirmed +GET /Observation?_tag=clinician-verified +``` + +#### Bulk Export Filters + +When using the $phr-export operation: + +```http +# Export only specific resource types +GET /Patient/123/$phr-export?_type=Condition,MedicationStatement,AllergyIntolerance + +# Export data from a specific time period +GET /Patient/123/$phr-export?_since=2024-01-01&_until=2024-12-31 + +# Export only clinical data (exclude device/activity data) +GET /Patient/123/$phr-export?_profile=clinical + +# Export only for sharing (IPS-compatible subset) +GET /Patient/123/$phr-export?_profile=ips-compatible +``` + +#### Patient Sharing Preferences + +PHRs may implement patient-controlled sharing filters using Consent resources: + +```json +{ + "resourceType": "Consent", + "id": "sharing-preferences", + "status": "active", + "scope": { + "coding": [{ + "system": "http://terminology.hl7.org/CodeSystem/consentscope", + "code": "patient-privacy" + }] + }, + "provision": { + "type": "deny", + "provision": [ + { + "type": "permit", + "class": [ + {"code": "Condition"}, + {"code": "MedicationStatement"}, + {"code": "AllergyIntolerance"} + ] + } + ] + } +} +``` + +#### Response Pagination + +For large result sets: + +```http +GET /Observation?_count=100&_offset=0 +``` + +Response includes pagination links: + +```json +{ + "resourceType": "Bundle", + "type": "searchset", + "total": 1250, + "link": [ + {"relation": "self", "url": "...?_count=100&_offset=0"}, + {"relation": "next", "url": "...?_count=100&_offset=100"} + ] +} +``` + +#### Selective Field Retrieval + +Request only specific elements to reduce payload size: + +```http +GET /Observation?_elements=code,valueQuantity,effectiveDateTime +```