From d810e12db60a83070b77ae2c43239670e5721d5f Mon Sep 17 00:00:00 2001 From: Nils Holle Date: Wed, 2 Jul 2025 13:53:20 +0200 Subject: [PATCH 1/2] Fix performance issues when fetching object list with SampleDB --- sampledbapi/objects.py | 55 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/sampledbapi/objects.py b/sampledbapi/objects.py index fede618..1c4d738 100644 --- a/sampledbapi/objects.py +++ b/sampledbapi/objects.py @@ -25,13 +25,16 @@ class Object(SampleDBObject): schema: Optional[dict] = None data: Optional[dict] = None - def __init__(self, d: Dict): + def __init__(self, d: Dict, users_cache: Optional[Dict[int, users.User]] = None): """Initialize a new instrument from dictionary.""" super().__init__(d) if "utc_datetime" in d: self.version_datetime = utils.str2datetime(d['utc_datetime']) if "user_id" in d: - self.version_editor = users.get(d['user_id']) + if users_cache is not None and d['user_id'] in users_cache: + self.version_editor = users_cache[d['user_id']] + else: + self.version_editor = users.get(d['user_id']) if "data" in d: self.data = utils.convert_json(d['data']) @@ -259,7 +262,24 @@ def get_location_occurences(self) -> List[LocationOccurence]: List: `See here. `__ """ - return [LocationOccurence(i) for i in get_data(f"objects/{self.object_id}/locations")] + # Get the raw location occurrence data first + locations_data = get_data(f"objects/{self.object_id}/locations") + + # Collect unique user IDs from the location occurrences + user_ids = set() + for loc_data in locations_data: + if "responsible_user" in loc_data: + user_ids.add(loc_data["responsible_user"]) + if "user" in loc_data: + user_ids.add(loc_data["user"]) + + # Fetch all users once and create a cache + users_cache = {} + for user_id in user_ids: + users_cache[user_id] = users.get(user_id) + + # Create LocationOccurence instances with the users cache + return [LocationOccurence(i, users_cache) for i in locations_data] def get_location_occurence(self, location_id: int) -> LocationOccurence: """ @@ -449,7 +469,22 @@ def get_list(q: str = "", action_id: int = -1, action_type: str = "", if name_only: pars["name_only"] = "true" - return [Object(o) for o in get_data("objects", pars)] + # Get the raw object data first + objects_data = get_data("objects", pars) + + # Collect unique user IDs from the objects + user_ids = set() + for obj_data in objects_data: + if "user_id" in obj_data: + user_ids.add(obj_data["user_id"]) + + # Fetch all users once and create a cache + users_cache = {} + for user_id in user_ids: + users_cache[user_id] = users.get(user_id) + + # Create Object instances with the users cache + return [Object(o, users_cache) for o in objects_data] else: raise TypeError() @@ -517,15 +552,21 @@ class LocationOccurence(SampleDBObject): description: Optional[str] = None utc_datetime: Optional[datetime] = None - def __init__(self, d: Dict): + def __init__(self, d: Dict, users_cache: Optional[Dict[int, users.User]] = None): """Initialize a new instrument from dictionary.""" super().__init__(d) if "location" in d: self.location = locations.get(d["location"]) if "responsible_user" in d: - self.responsible_user = users.get(d["responsible_user"]) + if users_cache is not None and d["responsible_user"] in users_cache: + self.responsible_user = users_cache[d["responsible_user"]] + else: + self.responsible_user = users.get(d["responsible_user"]) if "user" in d: - self.user = users.get(d["user"]) + if users_cache is not None and d["user"] in users_cache: + self.user = users_cache[d["user"]] + else: + self.user = users.get(d["user"]) if "utc_datetime" in d: self.utc_datetime = datetime.strptime( d["utc_datetime"], '%Y-%m-%dT%H:%M:%S.%f') From 5110a8a4e77fd622c407fbf31d47f11bf5e1e67c Mon Sep 17 00:00:00 2001 From: Malte Deckers Date: Fri, 18 Oct 2024 11:44:14 +0200 Subject: [PATCH 2/2] Split related objects functions by id and type --- sampledbapi/objects.py | 62 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/sampledbapi/objects.py b/sampledbapi/objects.py index 1c4d738..e6492ea 100644 --- a/sampledbapi/objects.py +++ b/sampledbapi/objects.py @@ -95,13 +95,65 @@ def get_related_objects(self): Two lists containing referenced objects and referencing objects. """ - related_objects = get_data(f"objects/{self.object_id}/related_objects") - referenced_object_ids = related_objects['referenced_objects'] - referencing_object_ids = related_objects['referencing_objects'] - referenced_objects = list(map(get, map(lambda dic: dic['object_id'], referenced_object_ids))) - referencing_objects = list(map(get, map(lambda dic: dic['object_id'], referencing_object_ids))) + referenced_object_ids, referencing_object_ids = self.get_related_object_ids() + referenced_objects = list(map(get, referenced_object_ids)) + referencing_objects = list(map(get, referencing_object_ids)) return referenced_objects, referencing_objects + def get_referencing_objects(self): + """Gets objects referencing an object. + + Returns: + A lists containing the referencing objects. + + """ + referencing_object_ids = self.get_referencing_object_ids() + referencing_objects = list(map(get, referencing_object_ids)) + return referencing_objects + + def get_referenced_objects(self): + """Gets objects referenced by an object. + + Returns: + A lists containing the referenced objects. + + """ + referenced_object_ids = self.get_referenced_object_ids() + referenced_objects = list(map(get, referenced_object_ids)) + return referenced_objects + + def get_related_object_ids(self): + """Gets IDs of objects related to an object. + + Returns: + Two lists containing IDs of referenced objects and referencing objects. + + """ + related_objects = get_data(f"objects/{self.object_id}/related_objects") + referenced_object_ids = map(lambda dic: dic['object_id'], related_objects['referenced_objects']) + referencing_object_ids = map(lambda dic: dic['object_id'], related_objects['referencing_objects']) + return referenced_object_ids, referencing_object_ids + + def get_referencing_object_ids(self): + """Gets IDs of objects referencing an object. + + Returns: + A lists containing the IDs of the referencing objects. + + """ + _, referencing_object_ids = self.get_related_object_ids() + return referencing_object_ids + + def get_referenced_object_ids(self): + """Gets IDs of objects referenced by an object. + + Returns: + A lists containing the IDs of the referenced objects. + + """ + referenced_object_ids, _ = self.get_related_object_ids() + return referenced_object_ids + def get_public(self) -> bool: """Get whether or not the object is public.