Skip to main content

haste_fhir_search/
lib.rs

1use std::sync::Arc;
2
3use haste_fhir_client::{request::SearchRequest, url::Parameter};
4use haste_fhir_model::r4::generated::resources::{Resource, ResourceType, SearchParameter};
5use haste_fhir_operation_error::OperationOutcomeError;
6use haste_jwt::{ProjectId, ResourceId, TenantId, VersionId};
7use haste_repository::types::{FHIRMethod, SupportedFHIRVersions};
8use serde::Deserialize;
9
10pub mod config;
11pub mod elastic_search;
12pub mod indexing_conversion;
13pub mod memory;
14pub mod pg_search;
15pub mod project_parameters;
16mod query;
17pub mod search_parameter_cardinality;
18mod search_ranges;
19
20#[cfg(test)]
21mod proptests;
22
23/// The page size of a search that doesn't set `_count`.
24pub const DEFAULT_COUNT: u64 = 50;
25
26/// The largest page a search returns, whatever its `_count`. Elasticsearch's
27/// `index.max_result_window` defaults to the same 10k.
28pub const MAX_COUNT: u64 = 10_000;
29
30#[derive(Clone)]
31pub struct IndexResource {
32    pub id: ResourceId,
33    pub version_id: VersionId,
34
35    pub tenant: TenantId,
36    pub project: ProjectId,
37
38    pub fhir_method: FHIRMethod,
39    pub resource_type: ResourceType,
40    pub resource: Resource,
41
42    /// The repository's write sequence for this version. Versions of one
43    /// resource are ordered by it, which is what lets an index refuse to
44    /// replace a newer version with an older one replayed after it.
45    pub sequence: i64,
46}
47
48#[derive(Deserialize, Debug)]
49pub struct SearchEntry {
50    pub id: ResourceId,
51    pub resource_type: ResourceType,
52    pub version_id: VersionId,
53    pub project: ProjectId,
54}
55
56pub struct SearchReturn {
57    pub total: Option<i64>,
58    pub entries: Vec<SearchEntry>,
59}
60
61/// Parameters a resource must match at least one of, `ANDed` with the search's
62/// own parameters. Empty matches nothing. The server adds it on the caller's
63/// behalf (a `patient/` scope confining a search to one compartment); it
64/// never comes from a URL.
65#[derive(Debug, Clone)]
66pub struct AnyOf(pub Vec<Parameter>);
67
68#[derive(Debug, Default)]
69pub struct SearchOptions {
70    pub count_limit: Option<u64>,
71    pub any_of: Option<AnyOf>,
72}
73
74#[derive(Clone, Debug)]
75pub enum ParameterLevel {
76    Project,
77    System,
78}
79
80#[derive(Clone, Debug)]
81pub struct ResolvedParameter {
82    #[allow(dead_code)]
83    pub level: ParameterLevel,
84    pub search_parameter: Arc<SearchParameter>,
85}
86impl ResolvedParameter {
87    pub fn new(level: ParameterLevel, search_parameter: Arc<SearchParameter>) -> Self {
88        ResolvedParameter {
89            level,
90            search_parameter,
91        }
92    }
93}
94
95pub trait SearchParameterResolve: Send + Sync {
96    // Returns all search parameters for the given resource type, if any exist.
97    fn by_resource_type(
98        &self,
99        tenant: &TenantId,
100        project: &ProjectId,
101        resource_type: &ResourceType,
102    ) -> impl Future<Output = Result<Vec<ResolvedParameter>, OperationOutcomeError>> + Send;
103    // Returns the search parameter for the given resource type and code, if it exists.
104    fn by_name(
105        &self,
106        tenant: &TenantId,
107        project: &ProjectId,
108        resource_type: Option<&ResourceType>,
109        code: &str,
110    ) -> impl Future<Output = Result<Option<ResolvedParameter>, OperationOutcomeError>> + Send;
111    // Returns all search parameters, regardless of resource type.
112    fn all(
113        &self,
114        tenant: &TenantId,
115        project: &ProjectId,
116    ) -> impl Future<Output = Result<Vec<ResolvedParameter>, OperationOutcomeError>> + Send;
117    /// Drops any cached copy of the project's own parameters, so the next
118    /// lookup reads its indexed `SearchParameter`s again.
119    fn invalidate_project(
120        &self,
121        _tenant: &TenantId,
122        _project: &ProjectId,
123    ) -> impl Future<Output = ()> + Send {
124        std::future::ready(())
125    }
126}
127
128pub struct IndexFailure {
129    pub resource: IndexResource,
130    pub error: OperationOutcomeError,
131}
132
133pub struct IndexOutcome {
134    pub succeeded: usize,
135    pub failed: Vec<IndexFailure>,
136}
137
138pub trait SearchEngine: Send + Sync {
139    fn search(
140        &self,
141        fhir_version: &SupportedFHIRVersions,
142        tenant: &TenantId,
143        projects: &ProjectId,
144        search_request: &SearchRequest,
145        options: Option<SearchOptions>,
146    ) -> impl Future<Output = Result<SearchReturn, OperationOutcomeError>> + Send;
147
148    fn index(
149        &self,
150        fhir_version: SupportedFHIRVersions,
151        resource: Vec<IndexResource>,
152    ) -> impl Future<Output = Result<IndexOutcome, OperationOutcomeError>> + Send;
153
154    fn migrate(
155        &self,
156        fhir_version: &SupportedFHIRVersions,
157    ) -> impl Future<Output = Result<(), haste_fhir_operation_error::OperationOutcomeError>> + Send;
158}