@@ -169,6 +169,7 @@ def search_by_id(
169169 page : int = 1 ,
170170 page_size : int = 10 ,
171171 async_mode : bool = False ,
172+ registry_event_type : str | None = None ,
172173 ) -> dict :
173174 """Search by identifier type and value (convenience method).
174175
@@ -179,6 +180,7 @@ def search_by_id(
179180 page: Page number (1-indexed)
180181 page_size: Records per page
181182 async_mode: If True, use async endpoint
183+ registry_event_type: Event type filter (BIRTH, DEATH, etc.)
182184
183185 Returns:
184186 SearchResponse or ACK dict
@@ -191,8 +193,56 @@ def search_by_id(
191193 record_type = record_type ,
192194 page = page ,
193195 page_size = page_size ,
196+ registry_event_type = registry_event_type ,
194197 )
195198
199+ def search_by_id_opencrvs (
200+ self ,
201+ identifier_type : str ,
202+ identifier_value : str ,
203+ event_type : str = "birth" ,
204+ page : int = 1 ,
205+ page_size : int = 10 ,
206+ async_mode : bool = False ,
207+ ) -> dict :
208+ """Search by identifier using OpenCRVS's non-standard format.
209+
210+ OpenCRVS doesn't support the standard DCI idtype-value query format.
211+ Instead, it requires an expression query with the identifier nested.
212+
213+ Args:
214+ identifier_type: Identifier type (UIN, BRN, MRN, DRN, etc.)
215+ identifier_value: Identifier value
216+ event_type: Registry event type (birth, death, etc.) - lowercase
217+ page: Page number (1-indexed)
218+ page_size: Records per page
219+ async_mode: If True, use async endpoint
220+
221+ Returns:
222+ SearchResponse or ACK dict
223+ """
224+ # Build OpenCRVS-style query for ID lookup
225+ # Format: { type: "BRN"|"UIN"|"DRN", value: "<identifier>" }
226+ query = {
227+ "type" : identifier_type ,
228+ "value" : identifier_value ,
229+ }
230+
231+ # Build envelope in OpenCRVS format
232+ envelope = self ._build_search_envelope_opencrvs (
233+ query = query ,
234+ query_type = "idtype-value" ,
235+ event_type = event_type ,
236+ page = page ,
237+ page_size = page_size ,
238+ )
239+
240+ if async_mode :
241+ return self ._make_request (ENDPOINT_ASYNC_SEARCH , envelope )
242+ else :
243+ endpoint = self .data_source .search_endpoint or ENDPOINT_SYNC_SEARCH
244+ return self ._make_request (endpoint , envelope )
245+
196246 def search_by_predicate (
197247 self ,
198248 predicate : str ,
@@ -231,49 +281,57 @@ def search_by_expression(
231281 registry_type : str | None = None ,
232282 registry_event_type : str | None = None ,
233283 async_mode : bool = False ,
284+ use_opencrvs_format : bool = False ,
234285 ) -> dict :
235286 """Search using expression query (e.g., date range filters).
236287
237- This supports DCI-compliant expression queries using ExpPredicate format.
288+ This supports both DCI-compliant expression queries and OpenCRVS's
289+ non-standard format.
238290
239291 Args:
240- expression: DCI ExpPredicateWithCondition list, e.g.:
241- [
242- {
243- "seq_num": 1,
244- "expression1": {
245- "attribute_name": "dateOfEvent",
246- "operator": "ge",
247- "attribute_value": "2020-01-01"
248- },
249- "condition": "and",
250- "expression2": {
251- "attribute_name": "dateOfEvent",
252- "operator": "le",
253- "attribute_value": "2026-02-10"
254- }
255- }
256- ]
292+ expression: Query expression. For standard DCI, a list of
293+ ExpPredicateWithCondition dicts. For OpenCRVS format, a dict
294+ of attribute filters (e.g., {"birthDate": {"type": "range", ...}}).
257295 record_type: PERSON, GROUP, etc.
258296 page: Page number (1-indexed)
259297 page_size: Records per page
260298 registry_type: Registry type (defaults to data source registry type)
261299 registry_event_type: Optional event type filter (e.g., "BIRTH", "DEATH")
262300 async_mode: If True, use async endpoint
301+ use_opencrvs_format: If True, wrap expression in OpenCRVS query
302+ structure and use OpenCRVS envelope format.
263303
264304 Returns:
265305 SearchResponse or ACK dict
266306 """
267- # Build envelope directly with expression query (bypass _parse_query)
268- envelope = self ._build_search_envelope (
269- query_type = QueryType .EXPRESSION ,
270- query = expression ,
271- registry_type = registry_type or self ._get_registry_type (),
272- registry_event_type = registry_event_type ,
273- record_type = record_type ,
274- page = page ,
275- page_size = page_size ,
276- )
307+ if use_opencrvs_format :
308+ # Wrap the caller's expression in OpenCRVS query structure
309+ opencrvs_query = {
310+ "type" : "ns:org:QueryType:expression" ,
311+ "value" : {
312+ "expression" : {
313+ "query" : expression ,
314+ }
315+ },
316+ }
317+ envelope = self ._build_search_envelope_opencrvs (
318+ query = opencrvs_query ,
319+ query_type = "expression" ,
320+ event_type = registry_event_type ,
321+ page = page ,
322+ page_size = page_size ,
323+ )
324+ else :
325+ # Standard DCI path (unchanged)
326+ envelope = self ._build_search_envelope (
327+ query_type = QueryType .EXPRESSION ,
328+ query = expression ,
329+ registry_type = registry_type or self ._get_registry_type (),
330+ registry_event_type = registry_event_type ,
331+ record_type = record_type ,
332+ page = page ,
333+ page_size = page_size ,
334+ )
277335
278336 if async_mode :
279337 return self ._make_request (ENDPOINT_ASYNC_SEARCH , envelope )
@@ -379,14 +437,10 @@ def _search_by_date_range_opencrvs(
379437 ) -> dict :
380438 """Search by date range using OpenCRVS's non-standard envelope format.
381439
382- OpenCRVS uses a custom format that differs from DCI spec:
383- - reg_type: lowercase event type (e.g., "birth") instead of registry type
384- - No reg_event_type field
385- - Requires consent object and locale field
386- - Expression query format: { type: "ns:org:QueryType:expression", value: { <attr>: { type: "range", ... } } }
440+ Builds an expression query with the correct OpenCRVS nesting:
441+ { type, value: { expression: { query: { <attr>: { type: "range", ... } } } } }
387442
388- This method exists for interoperability with OpenCRVS servers that don't
389- follow the standard DCI envelope format.
443+ Then delegates to _build_search_envelope_opencrvs for the envelope wrapper.
390444
391445 Args:
392446 start_date: Start date in ISO format (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ)
@@ -401,11 +455,26 @@ def _search_by_date_range_opencrvs(
401455 Returns:
402456 SearchResponse or ACK dict
403457 """
404- # Build envelope directly in OpenCRVS format (bypassing standard _build_search_envelope)
458+ # Build OpenCRVS-style expression query with correct nesting
459+ expression_query = {
460+ "type" : "ns:org:QueryType:expression" ,
461+ "value" : {
462+ "expression" : {
463+ "query" : {
464+ attribute_name : {
465+ "type" : "range" ,
466+ "gte" : start_date ,
467+ "lte" : end_date ,
468+ }
469+ }
470+ }
471+ },
472+ }
473+
474+ # Build envelope in OpenCRVS format (bypassing standard _build_search_envelope)
405475 envelope = self ._build_search_envelope_opencrvs (
406- start_date = start_date ,
407- end_date = end_date ,
408- attribute_name = attribute_name ,
476+ query = expression_query ,
477+ query_type = "expression" ,
409478 event_type = event_type ,
410479 page = page ,
411480 page_size = page_size ,
@@ -419,59 +488,43 @@ def _search_by_date_range_opencrvs(
419488
420489 def _build_search_envelope_opencrvs (
421490 self ,
422- start_date : str ,
423- end_date : str ,
424- attribute_name : str ,
491+ query : dict ,
492+ query_type : str ,
425493 event_type : str | None ,
426494 page : int ,
427495 page_size : int ,
428496 ) -> dict :
429497 """Build search envelope in OpenCRVS's non-standard format.
430498
431499 OpenCRVS expects:
432- - reg_type: lowercase event type (e.g., "birth"), NOT registry type
433- - No reg_event_type field
500+ - reg_type: "ns:org:RegistryType:Civil"
501+ - reg_event_type: lowercase event type (e.g., "birth")
434502 - Required consent object
435503 - Required locale field
436- - Expression query with range syntax
504+ - Pre-built query object (e.g., expression with nested query structure)
437505
438506 Args:
439- start_date: Start date
440- end_date: End date
441- attribute_name: Attribute to filter on
507+ query: Pre-built query object (e.g., expression query dict)
508+ query_type: Query type string (e.g., "expression", "idtype-value")
442509 event_type: Event type (BIRTH, DEATH, etc.)
443510 page: Page number
444511 page_size: Page size
445512
446513 Returns:
447- Envelope dict in OpenCRVS format
514+ Envelope dict in OpenCRVS format (no signature wrapper)
448515 """
449516 now = datetime .now (UTC )
450517 transaction_id = str (uuid .uuid4 ())
451518 reference_id = str (uuid .uuid4 ())
452519 message_id = str (uuid .uuid4 ())
453520
454- # OpenCRVS uses lowercase event type for reg_type (e.g., "birth" not "BIRTH" or "CRVS")
455- reg_type = (event_type or "birth" ).lower ()
456-
457- # Build OpenCRVS-style expression query
458- expression_query = {
459- "type" : "ns:org:QueryType:expression" ,
460- "value" : {
461- attribute_name : {
462- "type" : "range" ,
463- "gte" : start_date ,
464- "lte" : end_date ,
465- }
466- },
467- }
468-
469- # Build search criteria in OpenCRVS format (no reg_event_type)
521+ # Build search criteria in OpenCRVS format
470522 search_criteria = {
471523 "version" : "1.0.0" ,
472- "reg_type" : reg_type ,
473- "query_type" : "expression" ,
474- "query" : expression_query ,
524+ "reg_type" : RegistryType .CRVS .value ,
525+ "reg_event_type" : (event_type or "birth" ).lower (),
526+ "query_type" : query_type ,
527+ "query" : query ,
475528 "sort" : [
476529 {
477530 "attribute_name" : "createdAt" ,
0 commit comments