Skip to content

Commit 3502072

Browse files
committed
service: records: add MultiFieldsResolver
1 parent 89a11c2 commit 3502072

File tree

1 file changed

+78
-10
lines changed
  • invenio_records_resources/services/records

1 file changed

+78
-10
lines changed

invenio_records_resources/services/records/results.py

+78-10
Original file line numberDiff line numberDiff line change
@@ -447,11 +447,7 @@ def _find_fields(self, service, value):
447447
The `id` field used to match the resolved record is hardcoded,
448448
as in the `read_many` method.
449449
"""
450-
fields = []
451-
for field in self._fields:
452-
if field.has(service, value):
453-
fields.append(field)
454-
return fields
450+
return [field for field in self._fields if field.has(service, value)]
455451

456452
def _fetch_referenced(self, grouped_values, identity):
457453
"""Search and fetch referenced recs by ids."""
@@ -472,11 +468,10 @@ def _add_dereferenced_record(service, value, resolved_rec):
472468
_add_dereferenced_record(service, value, hit)
473469

474470
ghost_values = all_values - found_values
475-
if ghost_values:
476-
for value in ghost_values:
477-
# set dereferenced record to None. That will trigger eventually
478-
# the field.ghost_record() to be called
479-
_add_dereferenced_record(service, value, None)
471+
for value in ghost_values:
472+
# set dereferenced record to None. That will trigger eventually
473+
# the field.ghost_record() to be called
474+
_add_dereferenced_record(service, value, None)
480475

481476
def resolve(self, identity, hits):
482477
"""Collect field values and resolve referenced records."""
@@ -510,3 +505,76 @@ def expand(self, identity, hit):
510505
dict_merge(results, d)
511506

512507
return results
508+
509+
class MultiFieldsResolver(FieldsResolver):
510+
"""Resolve the reference record for each of the configured fields.
511+
512+
Given a list of fields referencing other records/objects,
513+
it fetches and returns the dereferenced record/obj.
514+
515+
This class supports resolution of nested fields and efficiently batches
516+
resolution requests to services.
517+
"""
518+
519+
def _collect_values(self, hits):
520+
"""Collect all field values to be expanded."""
521+
grouped_values = dict()
522+
523+
for hit in hits:
524+
for field in self._fields:
525+
try:
526+
value = dict_lookup(hit, field.field_name)
527+
if value is None:
528+
continue
529+
except KeyError:
530+
continue
531+
532+
# Ensure `get_value_service` can return multiple (v, service) tuples
533+
values_services = field.get_value_service(value)
534+
535+
if not isinstance(values_services, list):
536+
values_services = [values_services] # Ensure list format
537+
538+
for v, service in values_services:
539+
field.add_service_value(service, v)
540+
grouped_values.setdefault(service, set()).add(v)
541+
542+
return grouped_values
543+
544+
def expand(self, identity, hit):
545+
"""Expand and return the resolved fields for the given hit."""
546+
results = {}
547+
548+
for field in self._fields:
549+
try:
550+
value = dict_lookup(hit, field.field_name)
551+
if value is None:
552+
continue
553+
except KeyError:
554+
continue
555+
556+
# Ensure `get_value_service` supports lists of (value, service)
557+
values_services = field.get_value_service(value)
558+
resolved_recs = {}
559+
if isinstance(values_services, list):
560+
resolved_recs = []
561+
for v, service in values_services:
562+
resolved_rec = field.get_dereferenced_record(service, v)
563+
if resolved_rec:
564+
resolved = field.pick(identity, resolved_rec)
565+
if isinstance(resolved, list):
566+
resolved_recs.extend(resolved)
567+
else:
568+
resolved_recs.append(field.pick(identity, resolved_rec))
569+
else:
570+
v, service = values_services
571+
resolved_rec = field.get_dereferenced_record(service, v)
572+
if resolved_rec:
573+
resolved_recs = field.pick(identity, resolved_rec)
574+
if resolved_recs:
575+
# Maintain nested structure
576+
d = dict()
577+
dict_set(d, field.field_name, resolved_recs)
578+
dict_merge(results, d)
579+
580+
return results

0 commit comments

Comments
 (0)