8000 ENG-403 Selectivelly load refs for consolidated search by leite08 · Pull Request #3953 · metriport/metriport · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

ENG-403 Selectivelly load refs for consolidated search #3953

New issue < 8000 details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast overflow-auto" aria-label="Sign up for GitHub">

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 6, 2025

Conversation

leite08
Copy link
Member
@leite08 leite08 commented Jun 3, 2025

Dependencies

Description

Selectively load refs for consolidated search

Testing

  • Local
    • unit tests
    • search Condition related returns Practitioner
    • search DiagnosticReport related returns Organization
    • search Medication related returns Medication
    • search Medication returns Medication related ?
  • Staging
    • search Condition related returns Practitioner
    • search DiagnosticReport related returns Organization
    • search Medication related returns Medication
    • search Medication returns Medication related ?
  • Sandbox
    • none
  • Production
    • none

Release Plan

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added utilities for generating FHIR test resources and references, including medication and organization helpers, to enhance testing capabilities.
    • Introduced consolidated search and hydration improvements for FHIR resources, including reverse hydration for medication-related resources.
  • Bug Fixes

    • Fixed a typo in the AllergyIntolerance resource error message.
  • Refactor

    • Restructured FHIR resource search and hydration to use consolidated data search with refined filtering by resource and reference types.
    • Improved hydration logic to prevent redundant fetching and handle circular references more effectively.
    • Updated parameter naming for clarity in reference extraction functions without affecting behavior.
    • Enhanced formatting of reference display strings to include both reference and display fields.
  • Tests

    • Expanded and parameterized hydration test suites with realistic FHIR resource relationships.
    • Updated test fixtures and helper functions for consistency and coverage.
  • Style

    • Improved typing and function signatures for better clarity and type safety in test utilities and core functions.

Copy link
linear bot commented Jun 3, 2025

Copy link
coderabbitai bot commented Jun 3, 2025

"""

Walkthrough

This set of changes refactors and extends the logic for hydrating missing FHIR resource references by grouping and filtering missing references by resource type. It updates typing and parameter names for clarity, introduces new test utilities and fixtures, restructures test coverage with parameterized tests, and modifies consolidated search to use a new consolidated data search approach with hydration and reverse hydration. Several utility and test functions were added or updated for consistency and improved test data handling. Minor fixes include typo corrections, enhanced reference formatting, and adding ingestion date fields for OpenSearch indexing.

Changes

File(s) Change Summary
packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts Refactored hydrateMissingReferences to group and filter missing references by resource type, added helper functions, updated imports, and added iteration control; refactored searchPatientConsolidated to use consolidated data search with hydration and reverse hydration; added searchByIds and searchConsolidatedData helpers.
packages/core/src/command/consolidated/search/fhir-resource/tests/search-consolidated.test.ts Refactored and parameterized tests for hydration logic, updated fixtures, replaced resource chains, and improved test coverage and structure.
packages/core/src/command/consolidated/search/fhir-resource/tests/search-consolidated-setup.ts Added new test setup module with resource factories, hydration scenarios, and shared test fixtures including typed hydration mappings.
packages/core/src/external/fhir/shared/bundle.ts Updated getReferencesFromResources signature to accept resourcesToCheckRefs and optional sourceResources for flexible reference extraction and missing reference detection.
packages/core/src/command/consolidated/consolidated-filter.ts
packages/core/src/external/fhir/consolidated/consolidated.ts
packages/utils/src/consolidated/refs-from-consolidated.ts
packages/utils/src/fhir/fhir-converter/count-bundle-refs.ts
Changed parameter name in calls to getReferencesFromResources from resources to resourcesToCheckRefs to align with updated function signature.
packages/core/src/command/ai-brief/filter.ts Updated imports to explicitly include ResourceType and typed constant arrays for resource types.
packages/core/src/external/fhir/tests/medication-dispense.ts Added makeMedicationDispense utility function for generating mock MedicationDispense resources with default values and optional overrides.
packages/core/src/external/fhir/tests/medication-reference.ts Added makeMedicationReference utility function to generate medication reference strings with optional ID or random UUID.
packages/api/src/external/fhir/patient/tests/count-resources-on-s3.test.ts Fixed typo in error message string in test case from "AllergyIntollerances" to "AllergyIntolerances".
packages/core/src/fhir-to-cda/cda-templates/components/tests/make-diagnostic-report.ts Updated makeDiagnosticReport to accept and use optional patientId parameter passed to makeSubjectReference.
packages/core/src/fhir-to-cda/cda-templates/components/tests/make-observation.ts Updated makeObservation to accept and use optional patientId parameter passed to makeSubjectReference.
packages/core/src/fhir-to-cda/cda-templates/components/tests/make-organization.ts Updated makeOrganization to accept and use optional patientId parameter passed to makeSubjectReference.
packages/core/src/external/opensearch/fhir-ingestor.ts Added ingestedAt field with current ISO timestamp to indexed FHIR resources.
packages/core/src/external/opensearch/index-based-on-fhir.ts Added optional ingestedAt property to FhirIndexFields type with comment about future requirement.
packages/core/src/external/opensearch/lexical/fhir-searcher.ts Modified search method to return an object with isReturnAllResources flag and search results array.
packages/core/src/external/opensearch/lexical/query.ts Modified createLexicalSearchQuery to return { isReturnAllResources, searchQuery } and control query clauses based on empty or non-empty queries.
packages/core/src/external/opensearch/lexical/index-create.sh Updated index name to "consolidated-data-2", added ingestedAt field mapping, and set alias as write index.
packages/core/src/external/fhir/export/string/shared/reference.ts Modified formatReference to concatenate trimmed reference and display properties with a space, returning undefined if empty.

Sequence Diagram(s)

sequenceDiagram
    participant TestSuite
    participant hydrateMissingReferences
    participant groupByType
    participant SearchService

    TestSuite->>hydrateMissingReferences: Call with resources, searchService, iteration=1
    hydrateMissingReferences->>groupByType: Group resources by type
    hydrateMissingReferences->>SearchService: Fetch missing referenced resources filtered by type
    SearchService-->>hydrateMissingReferences: Return hydrated resources
    hydrateMissingReferences->>hydrateMissingReferences: Recursively call for missing references if iteration < max
    hydrateMissingReferences-->>TestSuite: Return fully hydrated resources
Loading

Possibly related PRs

  • metriport/metriport#3940: Directly related to the hydration logic and tests for hydrateMissingReferences in the same module, involving fixes and refactoring of hydration functionality.
  • metriport/metriport#3956: Related changes to consolidated search handling of empty queries and returning all resources, affecting hydration logic.
  • metriport/metriport#3866: Related modifications to consolidated FHIR bundle construction and management, connected to consolidated search and hydration features.
    """

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error code ERR_SSL_WRONG_VERSION_NUMBER
npm error errno ERR_SSL_WRONG_VERSION_NUMBER
npm error request to https://10.0.0.28:4873/punycode/-/punycode-2.3.1.tgz failed, reason: C0FCB4A62F7F0000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../deps/openssl/openssl/ssl/record/ssl3_record.c:354:
npm error
npm error A complete log of this run can be found in: /.npm/_logs/2025-06-04T23_40_45_236Z-debug-0.log


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e0a431 and 369c2ee.

📒 Files selected for processing (3)
  • packages/core/src/external/opensearch/fhir-ingestor.ts (2 hunks)
  • packages/core/src/external/opensearch/index-based-on-fhir.ts (1 hunks)
  • packages/core/src/external/opensearch/lexical/index-create.sh (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/core/src/external/opensearch/index-based-on-fhir.ts
  • packages/core/src/external/opensearch/fhir-ingestor.ts
  • packages/core/src/external/opensearch/lexical/index-create.sh
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class 8000 diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@leite08 leite08 force-pushed the 403-limit-refs-on-search-results branch from 4e19451 to e18989a Compare June 3, 2025 20:47
@leite08 leite08 marked this pull request as ready for review June 3, 2025 20:52
Copy link
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
packages/core/src/external/fhir/__tests__/medication-dispense.ts (1)

5-22: Field-precedence may unintentionally override caller-provided data

Because ...params is spread last, any extension, status, or even medicationReference supplied by the caller will silently replace the defaults defined earlier in the literal.

If that is intentional → great.
If you want the defaults to win when the caller omits a field (and only then), swap the order:

   resourceType: "MedicationDispense",
-  ...(params?.id ? { id: params.id } : { id: faker.string.uuid() }),
-  extension: [ /* default */ ],
-  status: "completed",
-  medicationReference,
-  ...params,
+  ...(params?.id ? { id: params.id } : { id: faker.string.uuid() }),
+  ...params,
+  extension: params?.extension ?? [
+    {
+      url: "https://public.metriport.com/fhir/StructureDefinition/doc-id-extension.json",
+      valueString: `${faker.string.uuid()}.xml`,
+    },
+  ],
+  status: params?.status ?? "completed",
+  medicationReference: params?.medicationReference ?? medicationReference,
packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated.test.ts (1)

192-206: Typo makes the code harder to scan

The variable resouinputResourcesces (lines 193 & 200) is a copy-paste artifact and hampers readability.

-      const resouinputResourcesces = [
+      const inputResources = [
         makeCondition({ recorder: makeReference(missingFirstLevel) }, patientId),
       ];
 ...
-        resources: resouinputResourcesces,
+        resources: inputResources,
 ...
-      expect(res).toEqual(resouinputResourcesces);
+      expect(res).toEqual(inputResources);
packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (2)

155-197: Consider refactoring the nested conditionals for better maintainability.

While the selective hydration logic is functionally correct, the nested structure with repetitive patterns could be improved for better readability and maintainability.

Consider extracting the hydration logic into a more declarative configuration:

-  const resourcesByType = groupBy(resources, "resourceType");
-  Object.entries(resourcesByType).forEach(([currentResourceType, resourcesOfCurrentType]) => {
-    const { missingReferences } = getReferencesFromResources({
-      resourcesToCheckRefs: resourcesOfCurrentType,
-      sourceResources: resources,
-    });
-
-    if (missingReferences.length < 1) return;
-
-    const missingRefsByType = groupBy(missingReferences, "type");
-
-    const missingPractitioners = get(missingRefsByType, "Practitioner");
-    if (missingPractitioners && missingPractitioners.length > 0) {
-      resourcesToHydrate.push(...missingPractitioners);
-    }
-
-    const missingOrgs = get(missingRefsByType, "Organization");
-    if (missingOrgs && missingOrgs.length > 0) {
-      resourcesToHydrate.push(...missingOrgs);
-    }
-
-    if (currentResourceType === "DiagnosticReport") {
-      const missingEncounters = get(missingRefsByType, "Encounter");
-      if (missingEncounters && missingEncounters.length > 0) {
-        resourcesToHydrate.push(...missingEncounters);
-      }
-      const missingObservations = get(missingRefsByType, "Observation");
-      if (missingObservations && missingObservations.length > 0) {
-        resourcesToHydrate.push(...missingObservations);
-      }
-    }
-
-    if (medRelatedResourceTypes.includes(currentResourceType as ResourceType)) {
-      medRelatedResourceTypes.forEach(medRelatedResourceType => {
-        const missingMedRelatedResources = get(missingRefsByType, medRelatedResourceType);
-        if (missingMedRelatedResources && missingMedRelatedResources.length > 0) {
-          resourcesToHydrate.push(...missingMedRelatedResources);
-        }
-      });
-    }
-  });
+  // Define hydration rules by resource type
+  const hydrationRules: Record<ResourceType, ResourceType[]> = {
+    // All resources can hydrate these common types
+    "*": ["Practitioner", "Organization"],
+    // Specialized hydration rules
+    DiagnosticReport: ["Encounter", "Observation"],
+    ...medRelatedResourceTypes.reduce((acc, type) => ({
+      ...acc,
+      [type]: medRelatedResourceTypes,
+    }), {}),
+  };
+
+  const resourcesByType = groupBy(resources, "resourceType");
+  Object.entries(resourcesByType).forEach(([currentResourceType, resourcesOfCurrentType]) => {
+    const { missingReferences } = getReferencesFromResources({
+      resourcesToCheckRefs: resourcesOfCurrentType,
+      sourceResources: resources,
+    });
+
+    if (missingReferences.length < 1) return;
+
+    const missingRefsByType = groupBy(missingReferences, "type");
+    
+    // Apply common hydration rules
+    hydrationRules["*"]?.forEach(refType => {
+      const missingRefs = missingRefsByType[refType];
+      if (missingRefs?.length > 0) {
+        resourcesToHydrate.push(...missingRefs);
+      }
+    });
+    
+    // Apply specialized hydration rules
+    const specializedRules = hydrationRules[currentResourceType as ResourceType];
+    specializedRules?.forEach(refType => {
+      const missingRefs = missingRefsByType[refType];
+      if (missingRefs?.length > 0) {
+        resourcesToHydrate.push(...missingRefs);
+      }
+    });
+  });

232-237: Consider removing the unnecessary generic getter function.

The get function doesn't provide additional value over TypeScript's built-in type safety for object property access.

-function get<K extends ResourceType, T extends ReferenceWithIdAndType<Resource>[]>(
-  obj: Dictionary<T>,
-  key: K
-): T | undefined {
-  return obj[key];
-}

Then update the usage to direct property access:

-    const missingPractitioners = get(missingRefsByType, "Practitioner");
+    const missingPractitioners = missingRefsByType["Practitioner"];
packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated-setup.ts (1)

149-158: Simplify the makeEncounter function implementation.

The function mutates the encounter object after creation and has redundant initialization logic.

 export function makeEncounter(
   params: Partial<Encounter> | undefined,
   patientId: string
 ): MarkRequired<Encounter, "id"> {
-  const encounter = makeEncounterImported(params, { patient: patientId });
-  if (!params?.participant) encounter.participant = [];
-  if (!params?.location) encounter.location = [];
-  if (!encounter.id) throw new Error("Encounter ID is required");
-  return { ...encounter, id: encounter.id };
+  const encounter = makeEncounterImported(
+    {
+      participant: [],
+      location: [],
+      ...params,
+    },
+    { patient: patientId }
+  );
+  if (!encounter.id) throw new Error("Encounter ID is required");
+  return encounter as MarkRequired<Encounter, "id">;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91379e2 and e18989a.

📒 Files selected for processing (15)
  • packages/api/src/external/fhir/patient/__tests__/count-resources-on-s3.test.ts (1 hunks)
  • packages/core/src/command/ai-brief/filter.ts (2 hunks)
  • packages/core/src/command/consolidated/consolidated-filter.ts (2 hunks)
  • packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated-setup.ts (1 hunks)
  • packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated.test.ts (5 hunks)
  • packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (4 hunks)
  • packages/core/src/external/fhir/__tests__/medication-dispense.ts (1 hunks)
  • packages/core/src/external/fhir/__tests__/medication-reference.ts (1 hunks)
  • packages/core/src/external/fhir/consolidated/consolidated.ts (1 hunks)
  • packages/core/src/external/fhir/shared/bundle.ts (1 hunks)
  • packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-diagnostic-report.ts (1 hunks)
  • packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-observation.ts (1 hunks)
  • packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-organization.ts (1 hunks)
  • packages/utils/src/consolidated/refs-from-consolidated.ts (1 hunks)
  • packages/utils/src/fhir/fhir-converter/count-bundle-refs.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.ts`: - Use the Onion Pattern to organize a package's code in layers - Try to use immutable code and avoid sharing state across different functions, objects, and systems - Try...

**/*.ts: - Use the Onion Pattern to organize a package's code in layers

  • Try to use immutable code and avoid sharing state across different functions, objects, and systems
  • Try to build code that's idempotent whenever possible
  • Prefer functional programming style functions: small, deterministic, 1 input, 1 output
  • Minimize coupling / dependencies
  • Avoid modifying objects received as parameter
  • Only add comments to code to explain why something was done, not how it works
  • Naming
    • classes, enums: PascalCase
    • constants, variables, functions: camelCase
    • file names: kebab-case
    • table and column names: snake_case
    • Use meaningful names, so whoever is reading the code understands what it means
    • Don’t use negative names, like notEnabled, prefer isDisabled
    • For numeric values, if the type doesn’t convey the unit, add the unit to the name
  • Typescript
    • Use types
    • Prefer const instead of let
    • Avoid any and casting from any to other types
    • Type predicates: only applicable to narrow down the type, not to force a complete type conversion
    • Prefer deconstructing parameters for functions instead of multiple parameters that might be of
      the same type
    • Don’t use null inside the app, only on code interacting with external interfaces/services,
      like DB and HTTP; convert to undefined before sending inwards into the code
    • Use async/await instead of .then()
    • Use the strict equality operator ===, don’t use abstract equality operator ==
    • When calling a Promise-returning function asynchronously (i.e., not awaiting), use .catch() to
      handle errors (see processAsyncError and emptyFunction depending on the case)
    • Date and Time
      • Always use buildDayjs() to create dayjs instances
      • Prefer dayjs.duration(...) to create duration consts and keep them as duration
  • Prefer Nullish Coalesce (??) than the OR operator (||) to provide a default value
  • Avoid creating arrow functions
  • Use truthy syntax instead of in - i.e., if (data.link) not if ('link' in data)
  • Error handling
    • Pass the original error as the new one’s cause so the stack trace is persisted
    • Error messages should have a static message - add dynamic data to MetriportError's additionalInfo prop
    • Avoid sending multiple events to Sentry for a single error
  • Global constants and variables
    • Move literals to constants declared after imports when possible (avoid magic numbers)
    • Avoid shared, global objects
  • Avoid using console.log and console.error in packages other than utils, infra and shared,
    and try to use out().log instead
  • Avoid multi-line logs
    • don't send objects as a second parameter to console.log() or out().log()
    • don't create multi-line strings when using JSON.stringify()
  • Use eslint to enforce code style
  • Use prettier to format code
  • max column length is 100 chars
  • multi-line comments use /** */
  • scripts: top-level comments go after the import
  • packages/utils/src/consolidated/refs-from-consolidated.ts
  • packages/core/src/command/ai-brief/filter.ts
  • packages/utils/src/fhir/fhir-converter/count-bundle-refs.ts
  • packages/core/src/external/fhir/__tests__/medication-reference.ts
  • packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-organization.ts
  • packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-observation.ts
  • packages/core/src/external/fhir/consolidated/consolidated.ts
  • packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-diagnostic-report.ts
  • packages/core/src/command/consolidated/consolidated-filter.ts
  • packages/core/src/external/fhir/__tests__/medication-dispense.ts
  • packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated-setup.ts
  • packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts
  • packages/core/src/external/fhir/shared/bundle.ts
  • packages/api/src/external/fhir/patient/__tests__/count-resources-on-s3.test.ts
  • packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated.test.ts
🧠 Learnings (1)
packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (1)
Learnt from: leite08
PR: metriport/metriport#3940
File: packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts:82-86
Timestamp: 2025-05-31T21:58:28.480Z
Learning: In packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts, the mutable array operations using push() on resourcesMutable and hydratedMutable (lines 82-86 and 100-103) have been explicitly accepted as exceptions to the immutability guidelines after previous discussion.
🧬 Code Graph Analysis (5)
packages/core/src/command/consolidated/consolidated-filter.ts (1)
packages/core/src/external/fhir/shared/bundle.ts (1)
  • getReferencesFromResources (73-98)
packages/core/src/external/fhir/__tests__/medication-dispense.ts (1)
packages/core/src/external/fhir/__tests__/medication-reference.ts (1)
  • makeMedicationReference (3-5)
packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated-setup.ts (7)
packages/core/src/domain/__tests__/patient.ts (1)
  • makePatient (56-66)
packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-condition.ts (1)
  • makeCondition (6-31)
packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-encounter.ts (1)
  • makePractitioner (5-11)
packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-organization.ts (1)
  • makeOrganization (5-15)
packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-observation.ts (1)
  • makeObservation (5-16)
packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-diagnostic-report.ts (1)
  • makeDiagnosticReport (5-24)
packages/core/src/external/fhir/__tests__/medication-dispense.ts (1)
  • makeMedicationDispense (5-22)
packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (3)
packages/core/src/external/fhir/shared/bundle.ts (2)
  • ReferenceWithIdAndType (54-55)
  • getReferencesFromResources (73-98)
packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated-setup.ts (2)
  • patientId (30-30)
  • cxId (28-28)
packages/core/src/external/opensearch/shared/id.ts (1)
  • getEntryId (9-11)
packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated.test.ts (5)
packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-condition.ts (1)
  • makeCondition (6-31)
packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated-setup.ts (7)
  • patientId (30-30)
  • genericHydration (40-63)
  • specializedHydration (91-147)
  • Entry (33-38)
  • patient (29-29)
  • makePractitionerWithOrg (160-168)
  • nonSpecializedHydration (65-89)
packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (1)
  • hydrateMissingReferences (140-230)
packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-organization.ts (1)
  • makeOrganization (5-15)
packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-encounter.ts (1)
  • makePractitioner (5-11)
🔇 Additional comments (20)
packages/api/src/external/fhir/patient/__tests__/count-resources-on-s3.test.ts (1)

131-131: LGTM! Typo correction.

Good catch fixing the typo from "AllergyIntollerances" to "AllergyIntolerances" in the error message.

packages/utils/src/consolidated/refs-from-consolidated.ts (1)

44-44: LGTM! Parameter renaming for clarity.

The parameter name change from resources to resourcesToCheckRefs improves clarity and aligns with the updated function signature in the shared bundle utilities.

packages/utils/src/fhir/fhir-converter/count-bundle-refs.ts (1)

95-95: LGTM! Consistent parameter renaming.

The parameter name change to resourcesToCheckRefs maintains consistency with the broader refactoring effort across the codebase.

packages/core/src/command/ai-brief/filter.ts (2)

1-8: LGTM! Improved type imports.

Adding the explicit ResourceType import improves type safety and supports the type annotations added below.


36-38: LGTM! Explicit type annotations.

Adding explicit ResourceType[] type annotations for the referenceResources and relevantResources arrays improves type safety and code clarity, following TypeScript best practices.

packages/core/src/external/fhir/consolidated/consolidated.ts (1)

99-99: LGTM! Con 8000 sistent parameter renaming.

The parameter name change to resourcesToCheckRefs in the hydration loop maintains consistency with the coordinated refactoring effort and improves code clarity.

packages/core/src/command/consolidated/consolidated-filter.ts (2)

116-118: LGTM! Parameter renaming aligns with updated function signature.

The parameter name change from resources to resourcesToCheckRefs correctly aligns with the updated getReferencesFromResources function signature, improving code clarity and consistency.


129-131: LGTM! Consistent parameter renaming maintained.

The second call site is correctly updated with the same parameter name change, maintaining consistency across the function.

packages/core/src/external/fhir/__tests__/medication-reference.ts (1)

1-5: Well-implemented test utility function.

The function follows coding guidelines correctly:

  • Uses camelCase for function and parameter names
  • Implements functional programming style (small, deterministic, single purpose)
  • Properly uses nullish coalesce operator (??)
  • Meaningful naming that clearly indicates its purpose
  • Consistent with other test utility patterns in the codebase
packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-organization.ts (2)

5-8: LGTM! Well-implemented optional parameter addition.

The optional patientId parameter is correctly typed and positioned, maintaining backward compatibility while enabling more specific test data generation.


11-11: LGTM! Proper parameter passing to makeSubjectReference.

The patientId parameter is correctly passed to makeSubjectReference, enabling context-aware subject reference generation.

packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-diagnostic-report.ts (2)

5-8: LGTM! Consistent implementation of optional patientId parameter.

The optional patientId parameter addition follows the same pattern as other test utility functions, maintaining consistency across the test suite while enabling more specific test data generation.


10-10: LGTM! Correct parameter usage.

The patientId parameter is properly passed to makeSubjectReference, aligning with the coordinated updates across test utilities.

packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-observation.ts (1)

5-12: 👍 Constructor extension looks good

The new patientId parameter propagates context to makeSubjectReference, enabling patient-scoped observations.
No functional or style issues spotted; call-sites just need to be updated to pass the extra argument (or rely on the new default).

packages/core/src/external/fhir/shared/bundle.ts (1)

84-87: ⚠️ Potential issue

Bug: flatMap splits UUIDs into characters — breaks missing-reference detection

Array.prototype.flatMap flattens iterables, so when the callback returns a string the string is treated as an array of chars:

["abc"].flatMap(x => x) // -> ['a','b','c']

Consequences:
sourceResourceIds becomes a set of single characters,
missingReferences is always the full list (nothing matches),
• Hydration will fetch far more resources than required.

Fix:

-  const sourceResourceIds = new Set(sourceResources.flatMap(r => r.id ?? []));
+  const sourceResourceIds = new Set(
+    sourceResources
+      .map(r => r.id)
+      .filter((id): id is string => typeof id === "string")
+  );

(similar change may be needed anywhere else you used flatMap + string)

⛔ Skipped due to learnings
Learnt from: leite08
PR: metriport/metriport#3797
File: packages/core/src/external/opensearch/text-ingestor-direct.ts:180-182
Timestamp: 2025-05-07T15:08:04.478Z
Learning: In JavaScript/TypeScript, when `flatMap()` encounters a string returned by the mapping function, it treats the string as a single element in the output array, not as an array of characters to be flattened.
packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated.test.ts (1)

254-260: Circular-reference test only confirms first fetch

The expectation getByIds_mock “called Times(1)” assumes second-level hydration never happens, but the setup queues a second mockResolvedValueOnce. Either:

  1. Remove the second mockResolvedValueOnce, or
  2. Expect 2 calls and assert that the second set of IDs matches org1.

This will keep the test meaningful even after internal changes.

packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (4)

1-1: LGTM!

The addition of ResourceType import is necessary for the new type-specific hydration logic.


27-32: Good constant definition following coding guidelines.

The medRelatedResourceTypes array properly groups medication-related resource types and follows the naming conventions.


153-153: Important guard condition added to prevent infinite recursion.

This iteration guard effectively prevents potential infinite loops during hydration, which is crucial for system stability.


199-204: LGTM! Clean implementation of ID extraction.

The logic correctly filters out patient IDs and transforms references into entry IDs for OpenSearch lookup.

Copy link
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated.test.ts (1)

8-9: Verify import source consistency.

The import of makePractitioner from the encounter test file seems inconsistent with its usage context. Consider importing it from a more appropriate shared utility module or the new setup module.

-import { makePractitioner } from "../../../../../fhir-to-cda/cda-templates/components/__tests__/make-encounter";
+import { makePractitioner } from "./search-consolidated-setup";
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e18989a and 839d18f.

📒 Files selected for processing (2)
  • packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated-setup.ts (1 hunks)
  • packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated.test.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/command/consolidated/search/fhir-resource/tests/search-consolidated-setup.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.ts`: - Use the Onion Pattern to organize a package's code in layers - Try to use immutable code and avoid sharing state across different functions, objects, and systems - Try...

**/*.ts: - Use the Onion Pattern to organize a package's code in layers

  • Try to use immutable code and avoid sharing state across different functions, objects, and systems
  • Try to build code that's idempotent whenever possible
  • Prefer functional programming style functions: small, deterministic, 1 input, 1 output
  • Minimize coupling / dependencies
  • Avoid modifying objects received as parameter
  • Only add comments to code to explain why something was done, not how it works
  • Naming
    • classes, enums: PascalCase
    • constants, variables, functions: camelCase
    • file names: kebab-case
    • table and column names: snake_case
    • Use meaningful names, so whoever is reading the code understands what it means
    • Don’t use negative names, like notEnabled, prefer isDisabled
    • For numeric values, if the type doesn’t convey the unit, add the unit to the name
  • Typescript
    • Use types
    • Prefer const instead of let
    • Avoid any and casting from any to other types
    • Type predicates: only applicable to narrow down the type, not to force a complete type conversion
    • Prefer deconstructing parameters for functions instead of multiple parameters that might be of
      the same type
    • Don’t use null inside the app, only on code interacting with external interfaces/services,
      like DB and HTTP; convert to undefined before sending inwards into the code
    • Use async/await instead of .then()
    • Use the strict equality operator ===, don’t use abstract equality operator ==
    • When calling a Promise-returning function asynchronously (i.e., not awaiting), use .catch() to
      handle errors (see processAsyncError and emptyFunction depending on the case)
    • Date and Time
      • Always use buildDayjs() to create dayjs instances
      • Prefer dayjs.duration(...) to create duration consts and keep them as duration
  • Prefer Nullish Coalesce (??) than the OR operator (||) to provide a default value
  • Avoid creating arrow functions
  • Use truthy syntax instead of in - i.e., if (data.link) not if ('link' in data)
  • Error handling
    • Pass the original error as the new one’s cause so the stack trace is persisted
    • Error messages should have a static message - add dynamic data to MetriportError's additionalInfo prop
    • Avoid sending multiple events to Sentry for a single error
  • Global constants and variables
    • Move literals to constants declared after imports when possible (avoid magic numbers)
    • Avoid shared, global objects
  • Avoid using console.log and console.error in packages other than utils, infra and shared,
    and try to use out().log instead
  • Avoid multi-line logs
    • don't send objects as a second parameter to console.log() or out().log()
    • don't create multi-line strings when using JSON.stringify()
  • Use eslint to enforce code style
  • Use prettier to format code
  • max column length is 100 chars
  • multi-line comments use /** */
  • scripts: top-level comments go after the import
  • packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated.test.ts
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated.test.ts (6)

49-84: Excellent parameterized test refactoring!

The parameterized test approach significantly improves maintainability and reduces code duplication. The generic function with proper typing is well-implemented and follows functional programming principles.


119-152: Good test organization for non-specialized hydration.

The parameterized approach is consistently applied here, maintaining good separation of concerns and test clarity.


154-189: Consistent parameterized test pattern for specialized hydration.

The test structure maintains consistency with the other parameterized test groups, promoting code reusability and maintainability.


252-270: Good refactor to organization-based circular references.

The change from encounter-location-practitioner chains to organization partOf cycles is more realistic and better tests the circular reference handling logic. The test structure is clear and well-organized.


321-360: Well-structured utility functions with proper typing.

The makeToEntryId and makeToGetByIdsResultEntry functions are well-implemented with proper error handling and TypeScript typing. They follow functional programming principles with clear input/output relationships.


272-317:

✅ Verification successful

Verify the expected resource count calculation.

The test expects 3 additional resources (line 296), but the logic suggests it should be 2 practitioners + 1 organization = 3 additional resources, which appears correct. However, please verify this calculation matches the actual hydration behavior.


🏁 Script executed:

#!/bin/bash
# Description: Verify the hydration logic for multiple references to the same resource
# Expected: Should find references to hydration logic and count calculations

# Search for hydration function implementation
ast-grep --pattern 'function hydrateMissingReferences($$$) {
  $$$
}'

# Search for any resource counting logic
rg -A 10 "hydrateMissingReferences.*resources" --type ts

Length of output: 9219


We need to locate the actual hydrateMissingReferences implementation to confirm deduplication and level-by-level fetch behavior.


🏁 Script executed:

#!/usr/bin/env bash
# Find where hydrateMissingReferences is defined
rg -n "hydrateMissingReferences" --type ts

Length of output: 2687


Gathering the implementation of hydrateMissingReferences for inspection…


🏁 Script executed:

#!/usr/bin/env bash
sed -n '130,260p' packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts

Length of output: 3937


Gathering maxHydrationAttempts value…


🏁 Script executed:

#!/usr/bin/env bash
rg -n "maxHydrationAttempts" -n packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts

Length of output: 211


Expected resource count is correct—no action needed.

The hydrateMissingReferences function first adds the two missing practitioners (iteration 1) and then the single organization (iteration 2), for a total of three additional resources, matching the test’s resources.length + 3 assertion.

@leite08 leite08 force-pushed the 403-limit-refs-on-search-results branch from 839d18f to 5050c2a Compare June 3, 2025 21:44
@leite08 leite08 changed the base branch from develop to eng-371-fix-hydration June 3, 2025 21:47
Copy link
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (1)

246-251: Consider improving the helper function's type safety and naming.

The get function provides type safety for dictionary access, but there are some potential improvements:

  1. Naming: The function name get is quite generic and could conflict with other utility functions. Consider a more specific name like getReferencesOfType or getTypedReferences.

  2. Return type consistency: The function returns T | undefined but the usage pattern suggests it will always return an array type. Consider constraining the generic type more specifically.

-function get<K extends ResourceType, T extends ReferenceWithIdAndType<Resource>[]>(
-  obj: Dictionary<T>,
-  key: K
-): T | undefined {
-  return obj[key];
-}
+function getReferencesOfType<K extends ResourceType>(
+  referencesGroupedByType: Dictionary<ReferenceWithIdAndType<Resource>[]>,
+  resourceType: K
+): ReferenceWithIdAndType<Resource>[] | undefined {
+  return referencesGroupedByType[resourceType];
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 839d18f and 5050c2a.

📒 Files selected for processing (15)
  • packages/api/src/external/fhir/patient/__tests__/count-resources-on-s3.test.ts (1 hunks)
  • packages/core/src/command/ai-brief/filter.ts (2 hunks)
  • packages/core/src/command/consolidated/consolidated-filter.ts (2 hunks)
  • packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated-setup.ts (1 hunks)
  • packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated.test.ts (5 hunks)
  • packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (6 hunks)
  • packages/core/src/external/fhir/__tests__/medication-dispense.ts (1 hunks)
  • packages/core/src/external/fhir/__tests__/medication-reference.ts (1 hunks)
  • packages/core/src/external/fhir/consolidated/consolidated.ts (1 hunks)
  • packages/core/src/external/fhir/shared/bundle.ts (1 hunks)
  • packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-diagnostic-report.ts (1 hunks)
  • packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-observation.ts (1 hunks)
  • packages/core/src/fhir-to-cda/cda-templates/components/__tests__/make-organization.ts (1 hunks)
  • packages/utils/src/consolidated/refs-from-consolidated.ts (1 hunks)
  • packages/utils/src/fhir/fhir-converter/count-bundle-refs.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (14)
  • packages/api/src/external/fhir/patient/tests/count-resources-on-s3.test.ts
  • packages/utils/src/fhir/fhir-converter/count-bundle-refs.ts
  • packages/utils/src/consolidated/refs-from-consolidated.ts
  • packages/core/src/command/consolidated/consolidated-filter.ts
  • packages/core/src/external/fhir/tests/medication-reference.ts
  • packages/core/src/fhir-to-cda/cda-templates/components/tests/make-observation.ts
  • packages/core/src/external/fhir/consolidated/consolidated.ts
  • packages/core/src/external/fhir/tests/medication-dispense.ts
  • packages/core/src/fhir-to-cda/cda-templates/components/tests/make-organization.ts
  • packages/core/src/command/ai-brief/filter.ts
  • packages/core/src/fhir-to-cda/cda-templates/components/tests/make-diagnostic-report.ts
  • packages/core/src/command/consolidated/search/fhir-resource/tests/search-consolidated.test.ts
  • packages/core/src/external/fhir/shared/bundle.ts
  • packages/core/src/command/consolidated/search/fhir-resource/tests/search-consolidated-setup.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.ts`: - Use the Onion Pattern to organize a package's code in layers - Try to use immutable code and avoid sharing state across different functions, objects, and systems - Try...

**/*.ts: - Use the Onion Pattern to organize a package's code in layers

  • Try to use immutable code and avoid sharing state across different functions, objects, and systems
  • Try to build code that's idempotent whenever possible
  • Prefer functional programming style functions: small, deterministic, 1 input, 1 output
  • Minimize coupling / dependencies
  • Avoid modifying objects received as parameter
  • Only add comments to code to explain why something was done, not how it works
  • Naming
    • classes, enums: PascalCase
    • constants, variables, functions: camelCase
    • file names: kebab-case
    • table and column names: snake_case
    • Use meaningful names, so whoever is reading the code understands what it means
    • Don’t use negative names, like notEnabled, prefer isDisabled
    • For numeric values, if the type doesn’t convey the unit, add the unit to the name
  • Typescript
    • Use types
    • Prefer const instead of let
    • Avoid any and casting from any to other types
    • Type predicates: only applicable to narrow down the type, not to force a complete type conversion
    • Prefer deconstructing parameters for functions instead of multiple parameters that might be of
      the same type
    • Don’t use null inside the app, only on code interacting with external interfaces/services,
      like DB and HTTP; convert to undefined before sending inwards into the code
    • Use async/await instead of .then()
    • Use the strict equality operator ===, don’t use abstract equality operator ==
    • When calling a Promise-returning function asynchronously (i.e., not awaiting), use .catch() to
      handle errors (see processAsyncError and emptyFunction depending on the case)
    • Date and Time
      • Always use buildDayjs() to create dayjs instances
      • Prefer dayjs.duration(...) to create duration consts and keep them as duration
  • Prefer Nullish Coalesce (??) than the OR operator (||) to provide a default value
  • Avoid creating arrow functions
  • Use truthy syntax instead of in - i.e., if (data.link) not if ('link' in data)
  • Error handling
    • Pass the original error as the new one’s cause so the stack trace is persisted
    • Error messages should have a static message - add dynamic data to MetriportError's additionalInfo prop
    • Avoid sending multiple events to Sentry for a single error
  • Global constants and variables
    • Move literals to constants declared after imports when possible (avoid magic numbers)
    • Avoid shared, global objects
  • Avoid using console.log and console.error in packages other than utils, infra and shared,
    and try to use out().log instead
  • Avoid multi-line logs
    • don't send objects as a second parameter to console.log() or out().log()
    • don't create multi-line strings when using JSON.stringify()
  • Use eslint to enforce code style
  • Use prettier to format code
  • max column length is 100 chars
  • multi-line comments use /** */
  • scripts: top-level comments go after the import
  • packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts
🧠 Learnings (1)
packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (1)
Learnt from: leite08
PR: metriport/metriport#3940
File: packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts:82-86
Timestamp: 2025-05-31T21:58:28.480Z
Learning: In packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts, the mutable array operations using push() on resourcesMutable and hydratedMutable (lines 82-86 and 100-103) have been explicitly accepted as exceptions to the immutability guidelines after previous discussion.
🧬 Code Graph Analysis (1)
packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (4)
packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated-setup.ts (3)
  • patient (29-29)
  • patientId (30-30)
  • cxId (28-28)
packages/shared/src/common/date.ts (1)
  • elapsedTimeFromNow (63-68)
packages/core/src/external/fhir/shared/bundle.ts (3)
  • buildBundleEntry (215-221)
  • ReferenceWithIdAndType (54-55)
  • getReferencesFromResources (73-98)
packages/core/src/external/opensearch/shared/id.ts (1)
  • getEntryId (9-11)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (7)
packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (7)

1-1: LGTM: Imports correctly added for the new functionality.

The new imports (ResourceType, Dictionary, groupBy, ReferenceWithIdAndType) are appropriate for the selective hydration functionality being implemented.

Also applies to: 6-6, 13-13


27-32: LGTM: Well-defined medication-related resource types constant.

The medRelatedResourceTypes constant is correctly defined and follows the naming conventions. It appropriately groups medication-related FHIR resource types for selective hydration.


101-106: LGTM: Function call updated correctly.

The function call to hydrateMissingReferences correctly passes the required parameters and the variable naming is consistent with the function's updated signature.


108-114: LGTM: Patient resource handling implemented correctly.

The logic correctly filters out Patient resources from the hydrated results and then re-adds the patient resource from the original patient domain object. This ensures Patient resources are not duplicated through the hydration process while maintaining the expected patient resource in the final result.


142-153: LGTM: Documentation updated appropriately.

The function documentation clearly explains the new behavior, including the exclusion of Patient resources from hydration and the new iteration parameter for controlling recursion depth.


213-218: LGTM: Updated variable names and logic flow.

The updated variable name resourcesToHydrate is more descriptive than the previous implementation, and the early return logic correctly handles cases where no resources need hydration.


167-211:

✅ Verification successful

Verify the selective hydration logic for correctness and completeness.

The refactored selective hydration logic implements a sophisticated approach by grouping resources by type and selectively hydrating references based on specific resource type combinations. However, there are several areas that need verification:

  1. DiagnosticReport-specific logic: Only DiagnosticReport resources hydrate Encounter and Observation references. Verify this is the intended behavior and not too restrictive.

  2. Medication-related hydration: The logic hydrates all medication-related references when the current resource is medication-related. This could potentially lead to extensive hydration chains.

  3. Missing resource types: Consider if other resource types (like Location, Device, etc.) should also be included in selective hydration scenarios.


🏁 Script executed:

#!/bin/bash
# Description: Verify the usage patterns of different resource types in FHIR references to validate the selective hydration logic.

echo "=== Checking DiagnosticReport references to Encounter and Observation ==="
ast-grep --pattern $'DiagnosticReport {
  $$$
  encounter: $$$
  $$$
}'

echo "=== Checking if other resource types reference Encounter/Observation ==="
rg -A 3 '"encounter":|"observation":' --type ts

echo "=== Checking medication resource reference patterns ==="
rg -A 5 'medication.*reference|reference.*medication' --type ts

Length of output: 33522


Selective hydration logic confirmed

A review of the codebase shows that:

  • Only DiagnosticReport resources actually reference Encounter and Observation fields and are explicitly hydrated for those types.
  • All medication-related resources are scoped to medRelatedResourceTypes, and there’s no indication of unintended or cascading hydration beyond what’s defined.
  • There are currently no Location or Device reference patterns in the code, so no additional types need to be added today.

No changes required. Revisit this logic if new resource types (e.g. Location, Device) are introduced.

leite08 added 2 commits June 4, 2025 13:12
Ref eng-403

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-403

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
@leite08 leite08 force-pushed the 403-limit-refs-on-search-results branch from 5050c2a to d29e1fd Compare June 4, 2025 20:12
leite08 added 2 commits June 4, 2025 15:49
Ref eng-403

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-403

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Copy link
9E88 @coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
packages/core/src/external/opensearch/lexical/fhir-searcher.ts (1)

44-55: Consider adding defensive error handling around the OpenSearch call

paginatedSearch may throw (e.g., network error, malformed query). Wrapping the request in a try / catch—similar to what getById already does—will prevent an unhandled rejection from bubbling up and give you a single, consistent place to log/capture the failure.

-    const response = await paginatedSearch<FhirSearchResult>({
+    let response;
+    try {
+      response = await paginatedSearch<FhirSearchResult>({
         client,
         indexName,
         searchRequest: searchQuery,
         mapResults,
-    });
+      });
+    } catch (error) {
+      const msg = "Error searching OpenSearch";
+      log(`${msg}: ${errorToString(error)}`);
+      capture.error(msg, { extra: { context: this.constructor.name, error: errorToString(error) } });
+      throw error;
+    }
packages/core/src/external/opensearch/lexical/query.ts (1)

27-28: Drop the superfluous ternary

isReturnAllResources can be assigned more directly, improving readability and satisfying the static-analysis warning.

-  const isReturnAllResources = actualQuery && actualQuery.length > 0 ? false : true;
+  const isReturnAllResources = !(actualQuery && actualQuery.length > 0);
🧰 Tools
🪛 Biome (1.9.4)

[error] 27-27: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (2)

190-199: Risk of oversize / malformed simple-query-string when injecting many IDs

query = \${simpleQueryStringPrefix} "${uniqueIds.join('" "')}"``
• Thousands of IDs will blow past OpenSearch’s default 32 kB query limit.
• IDs containing quotes/whitespace will corrupt the query.

Consider switching to an ids filter (preferred) or chunking IDs to stay below the limit.


331-336: Rename helper to avoid shadowing well-known lodash/get

A local util named get makes the reader think it’s lodash. Renaming clarifies intent and prevents accidental import clashes.

-function get<K extends ResourceType, T extends ReferenceWithIdAndType<Resource>[]>(
+function getFromDict<K extends ResourceType, T extends ReferenceWithIdAndType<Resource>[]>(
   obj: Dictionary<T>,
   key: K
 ): T | undefined {
   return obj[key];
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d29e1fd and 23c9436.

📒 Files selected for processing (7)
  • packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (6 hunks)
  • packages/core/src/external/fhir/export/string/shared/reference.ts (1 hunks)
  • packages/core/src/external/opensearch/fhir-ingestor.ts (2 hunks)
  • packages/core/src/external/opensearch/index-based-on-fhir.ts (1 hunks)
  • packages/core/src/external/opensearch/lexical/fhir-searcher.ts (2 hunks)
  • packages/core/src/external/opensearch/lexical/index-create.sh (3 hunks)
  • packages/core/src/external/opensearch/lexical/query.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.ts`: - Use the Onion Pattern to organize a package's code in layers - Try to use immutable code and avoid sharing state across different functions, objects, and systems - Try...

**/*.ts: - Use the Onion Pattern to organize a package's code in layers

  • Try to use immutable code and avoid sharing state across different functions, objects, and systems
  • Try to build code that's idempotent whenever possible
  • Prefer functional programming style functions: small, deterministic, 1 input, 1 output
  • Minimize coupling / dependencies
  • Avoid modifying objects received as parameter
  • Only add comments to code to explain why something was done, not how it works
  • Naming
    • classes, enums: PascalCase
    • constants, variables, functions: camelCase
    • file names: kebab-case
    • table and column names: snake_case
    • Use meaningful names, so whoever is reading the code understands what it means
    • Don’t use negative names, like notEnabled, prefer isDisabled
    • For numeric values, if the type doesn’t convey the unit, add the unit to the name
  • Typescript
    • Use types
    • Prefer const instead of let
    • Avoid any and casting from any to other types
    • Type predicates: only applicable to narrow down the type, not to force a complete type conversion
    • Prefer deconstructing parameters for functions instead of multiple parameters that might be of
      the same type
    • Don’t use null inside the app, only on code interacting with external interfaces/services,
      like DB and HTTP; convert to undefined before sending inwards into the code
    • Use async/await instead of .then()
    • Use the strict equality operator ===, don’t use abstract equality operator ==
    • When calling a Promise-returning function asynchronously (i.e., not awaiting), use .catch() to
      handle errors (see processAsyncError and emptyFunction depending on the case)
    • Date and Time
      • Always use buildDayjs() to create dayjs instances
      • Prefer dayjs.duration(...) to create duration consts and keep them as duration
  • Prefer Nullish Coalesce (??) than the OR operator (||) to provide a default value
  • Avoid creating arrow functions
  • Use truthy syntax instead of in - i.e., if (data.link) not if ('link' in data)
  • Error handling
    • Pass the original error as the new one’s cause so the stack trace is persisted
    • Error messages should have a static message - add dynamic data to MetriportError's additionalInfo prop
    • Avoid sending multiple events to Sentry for a single error
  • Global constants and variables
    • Move literals to constants declared after imports when possible (avoid magic numbers)
    • Avoid shared, global objects
  • Avoid using console.log and console.error in packages other than utils, infra and shared,
    and try to use out().log instead
  • Avoid multi-line logs
    • don't send objects as a second parameter to console.log() or out().log()
    • don't create multi-line strings when using JSON.stringify()
  • Use eslint to enforce code style
  • Use prettier to format code
  • max column length is 100 chars
  • multi-line comments use /** */
  • scripts: top-level comments go after the import
  • packages/core/src/external/opensearch/index-based-on-fhir.ts
  • packages/core/src/external/fhir/export/string/shared/reference.ts
  • packages/core/src/external/opensearch/fhir-ingestor.ts
  • packages/core/src/external/opensearch/lexical/query.ts
  • packages/core/src/external/opensearch/lexical/fhir-searcher.ts
  • packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts
🧠 Learnings (1)
packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts (2)
Learnt from: leite08
PR: metriport/metriport#3940
File: packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts:82-86
Timestamp: 2025-05-31T21:58:28.502Z
Learning: In packages/core/src/command/consolidated/search/fhir-resource/search-consolidated.ts, the mutable array operations using push() on resourcesMutable and hydratedMutable (lines 82-86 and 100-103) have been explicitly accepted as exceptions to the immutability guidelines after previous discussion.
Learnt from: leite08
PR: metriport/metriport#3814
File: packages/api/src/routes/internal/medical/patient-consolidated.ts:141-174
Timestamp: 2025-05-20T21:26:26.804Z
Learning: The functionality introduced in packages/api/src/routes/internal/medical/patient-consolidated.ts is planned to be refactored in downstream PR #3857, including improvements to error handling and validation.
🪛 Biome (1.9.4)
packages/core/src/external/opensearch/lexical/query.ts

[error] 27-27: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
🔇 Additional comments (6)
packages/core/src/external/opensearch/index-based-on-fhir.ts (1)

10-10: LGTM - Good incremental migration approach.

Adding the ingestionDate field as optional with a clear TODO for making it required after migration is a solid approach for production systems. This supports the consolidated search improvements while maintaining backward compatibility.

packages/core/src/external/opensearch/fhir-ingestor.ts (2)

3-3: LGTM - Correct date utility import.

Following the coding guidelines by using buildDayjs from shared utilities instead of direct dayjs usage.


269-269: LGTM - Proper ingestion timestamp implementation.

Correctly sets the ingestion date to current ISO timestamp, which aligns with the optional field added to FhirIndexFields type and supports the consolidated search improvements.

packages/core/src/external/opensearch/lexical/index-create.sh (2)

5-5: LGTM - Proper index versioning for breaking change.

The index name bump from "consolidated-data-1" to "consolidated-data-2" with alias update correctly handles the schema change for adding ingestionDate. The is_write_index: true designation properly configures the alias for write operations.

Ensure the index migration is coordinated with deployment to avoid data loss or downtime.

Also applies to: 46-48


27-29: LGTM - Correct field mapping for ingestion date.

The ingestionDate field mapping with type "date" correctly supports the ISO timestamp format being set in the application code.

packages/core/src/external/fhir/export/string/shared/reference.ts (1)

37-40:

❓ Verification inconclusive

LGTM - Improved reference formatting logic.

The new implementation is more robust by handling both reference and display fields, trimming whitespace, and filtering falsy values. This provides better reference representation for the consolidated search functionality.

Verify that the change in output format (joining reference and display with space) doesn't break existing consumers of this function:


🏁 Script executed:

#!/bin/bash
# Description: Find usages of formatReference to assess impact of format change
ast-grep --pattern 'formatReference($_)'

Length of output: 16880


🏁 Script executed:

#!/bin/bash
# Inspect the formatReference function signature and implementation context
rg -n "formatReference" -A5 packages/core/src/external/fhir/export/string/shared/reference.ts

Length of output: 648


Verify downstream handling of the new space-separated format

The updated formatReference now builds its return value by trimming and joining reference.reference and reference.display with a single space, returning undefined only if both are empty, and prefixing with "Label: " when isDebug is true. While this is more robust, it does change the literal output string.

I ran a search and found the definition plus ~70 call sites under:

  • packages/core/src/external/fhir/export/string/shared/reference.ts (implementation)
  • packages/core/src/external/fhir/export/string/resources/* (all resource files, e.g., document-reference.ts, procedure.ts, coverage.ts, etc.)

Please review these consumers to ensure:

  • No downstream formatting or parsing logic assumes the old delimiter or output shape.
  • Tests and documentation are updated to reflect "id display" (and "Label: id display") instead of the previous format.

leite08 added 4 commits June 4, 2025 16:16
Ref eng-403

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-403

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-403

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-403

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Copy link
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e3d4d2 and 3e0a431.

📒 Files selected for processing (4)
  • packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated-setup.ts (1 hunks)
  • packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated.test.ts (6 hunks)
  • packages/core/src/external/opensearch/lexical/query.ts (1 hunks)
  • packages/utils/src/consolidated/refs-from-consolidated.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/utils/src/consolidated/refs-from-consolidated.ts
  • packages/core/src/command/consolidated/search/fhir-resource/tests/search-consolidated.test.ts
  • packages/core/src/command/consolidated/search/fhir-resource/tests/search-consolidated-setup.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.ts`: - Use the Onion Pattern to organize a package's code in layers - Try to use immutable code and avoid sharing state across different functions, objects, and systems - Try...

**/*.ts: - Use the Onion Pattern to organize a package's code in layers

  • Try to use immutable code and avoid sharing state across different functions, objects, and systems
  • Try to build code that's idempotent whenever possible
  • Prefer functional programming style functions: small, deterministic, 1 input, 1 output
  • Minimize coupling / dependencies
  • Avoid modifying objects received as parameter
  • Only add comments to code to explain why something was done, not how it works
  • Naming
    • classes, enums: PascalCase
    • constants, variables, functions: camelCase
    • file names: kebab-case
    • table and column names: snake_case
    • Use meaningful names, so whoever is reading the code understands what it means
    • Don’t use negative names, like notEnabled, prefer isDisabled
    • For numeric values, if the type doesn’t convey the unit, add the unit to the name
  • Typescript
    • Use types
    • Prefer const instead of let
    • Avoid any and casting from any to other types
    • Type predicates: only applicable to narrow down the type, not to force a complete type conversion
    • Prefer deconstructing parameters for functions instead of multiple parameters that might be of
      the same type
    • Don’t use null inside the app, only on code interacting with external interfaces/services,
      like DB and HTTP; convert to undefined before sending inwards into the code
    • Use async/await instead of .then()
    • Use the strict equality operator ===, don’t use abstract equality operator ==
    • When calling a Promise-returning function asynchronously (i.e., not awaiting), use .catch() to
      handle errors (see processAsyncError and emptyFunction depending on the case)
    • Date and Time
      • Always use buildDayjs() to create dayjs instances
      • Prefer dayjs.duration(...) to create duration consts and keep them as duration
  • Prefer Nullish Coalesce (??) than the OR operator (||) to provide a default value
  • Avoid creating arrow functions
  • Use truthy syntax instead of in - i.e., if (data.link) not if ('link' in data)
  • Error handling
    • Pass the original error as the new one’s cause so the stack trace is persisted
    • Error messages should have a static message - add dynamic data to MetriportError's additionalInfo prop
    • Avoid sending multiple events to Sentry for a single error
  • Global constants and variables
    • Move literals to constants declared after imports when possible (avoid magic numbers)
    • Avoid shared, global objects
  • Avoid using console.log and console.error in packages other than utils, infra and shared,
    and try to use out().log instead
  • Avoid multi-line logs
    • don't send objects as a second parameter to console.log() or out().log()
    • don't create multi-line strings when using JSON.stringify()
  • Use eslint to enforce code style
  • Use prettier to format code
  • max column length is 100 chars
  • multi-line comments use /** */
  • scripts: top-level comments go after the import
  • packages/core/src/external/opensearch/lexical/query.ts
🧬 Code Graph Analysis (1)
packages/core/src/external/opensearch/lexical/query.ts (3)
packages/core/src/external/opensearch/index.ts (2)
  • OpenSearchRequestBody (36-36)
  • contentFieldName (1-1)
packages/core/src/external/opensearch/shared/query.ts (4)
  • simpleQueryStringPrefix (3-3)
  • cleanupQuery (53-56)
  • getGeneralParams (33-41)
  • getPatientFilters (43-45)
packages/core/src/command/consolidated/search/fhir-resource/__tests__/search-consolidated-setup.ts (2)
  • cxId (28-28)
  • patientId (30-30)
🪛 Biome (1.9.4)
packages/core/src/external/opensearch/lexical/query.ts

[error] 27-27: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
🔇 Additional comments (2)
packages/core/src/external/opensearch/lexical/query.ts (2)

20-23: LGTM! Function signature correctly returns structured result.

The updated return type properly separates the isReturnAllResources flag from the searchQuery, making the interface cleaner and more explicit.


29-79: LGTM! Fixed generalParams placement and implemented proper conditional logic.

The generalParams are now correctly spread inside searchQuery rather than at the top level, addressing the previous review feedback. The conditional logic using isReturnAllResources to include/exclude search clauses is well-structured and maintains the same OpenSearch query functionality while providing cleaner separation of concerns.

Ref eng-403

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Base automatically changed from eng-371-fix-hydration to develop June 6, 2025 23:48
@leite08 leite08 added this pull request to the merge queue Jun 6, 2025
Merged via the queue into develop with commit 0c00654 Jun 6, 2025
16 checks passed
@leite08 leite08 deleted the 403-limit-refs-on-search-results branch June 6, 2025 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
0