Skip to content

Commit 3b046c1

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

File tree

1 file changed

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

1 file changed

+79
-10
lines changed

invenio_records_resources/services/records/results.py

+79-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,77 @@ def expand(self, identity, hit):
510505
dict_merge(results, d)
511506

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

0 commit comments

Comments
 (0)