diff --git a/CHANGELOG.md b/CHANGELOG.md
index 527a722732fcec5e05e2610f1ed77727a87425b6..81b19e4c41504ef350fb638f10b9195564c17109 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -52,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 * `etag` property for the `caosdb.Query` class. The etag allows to debug the
   caching and to decide whether the server has changed between queries.
 * function `_read_config_files` to read `pycaosdb.ini` files from different paths.
+* function `retrieve_substructure` that recursively adds connected entities.
 
 ### Changed ###
 
diff --git a/src/caosdb/common/datatype.py b/src/caosdb/common/datatype.py
index 5434f5b6556a13f65754eacc66cb32231366e5b3..3822c7497f9d2489344319edaf3c3aa64e8b2da6 100644
--- a/src/caosdb/common/datatype.py
+++ b/src/caosdb/common/datatype.py
@@ -62,10 +62,21 @@ def is_list_datatype(datatype):
 
 
 def is_reference(datatype):
-    """ returns whether the value is a reference
+    """Returns whether the value is a reference
 
     FILE and REFERENCE properties are examples, but also datatypes that are
-    RecordTypes
+    RecordTypes.
+
+    Parameters
+    ----------
+    datatype : str
+               The datatype to check.
+
+    Returns
+    -------
+    bool
+        True if the datatype is a not base datatype or a list of a base datatype.
+        Otherwise False is returned.
     """
 
     if datatype is None:
@@ -79,6 +90,39 @@ def is_reference(datatype):
     else:
         return True
 
+def get_referenced_recordtype(datatype):
+    """Return the record type of the referenced datatype.
+
+    Raises
+    ------
+    ValueError
+              In cases where datatype is not a reference, the list does not have
+              a referenced record type or the datatype is a FILE.
+
+    Parameters
+    ----------
+    datatype : str
+               The datatype to check.
+
+    Returns
+    -------
+    str
+       String containing the name of the referenced datatype.
+    """
+
+    if not is_reference(datatype):
+        raise ValueError("datatype must be a reference")
+
+    if is_list_datatype(datatype):
+        datatype = get_list_datatype(datatype)
+        if datatype is None:
+            raise ValueError("list does not have a list datatype")
+
+    if datatype == FILE:
+        raise ValueError("FILE references are not considered references with a record type")
+
+    return datatype
+
 
 def get_id_of_datatype(datatype):
     """ returns the id of a Record Type
diff --git a/src/caosdb/utils/plantuml.py b/src/caosdb/utils/plantuml.py
index 75b96ee3aa28ba916adc69e418de398abfe23356..be34b2604f3682bb71b48bbd73e00fe854b3af51 100644
--- a/src/caosdb/utils/plantuml.py
+++ b/src/caosdb/utils/plantuml.py
@@ -36,11 +36,24 @@ plantuml FILENAME.pu -> FILENAME.png
 import os
 
 import caosdb as db
+from caosdb.common.datatype import is_reference, get_referenced_recordtype
 
 REFERENCE = "REFERENCE"
 
 
 def get_description(description_str):
+    """Extract and format a description string from a record type or property.
+
+    Parameters
+    ----------
+    description_str : str
+                      The description string that is going to be formatted.
+
+    Returns
+    -------
+    str
+       The reformatted description ending in a line break.
+    """
     words = description_str.split()
     lines = []
     lines.append("")
@@ -211,15 +224,76 @@ package \"The property P references an instance of D\" <<Rectangle>> {
     return result
 
 
+def retrieve_substructure(start_record_types, depth, result_id_set=None, result_container=None, cleanup=True):
+    """Recursively retrieves CaosDB record types and properties, starting
+    from given initial types up to a specific depth.
+
+    Parameters
+    ----------
+    start_record_types : Iterable[db.Entity]
+                         Iterable with the entities to be displayed. Starting from these
+                         entities more entities will be retrieved.
+    depth : int
+            The maximum depth up to which to retriev sub entities.
+    result_id_set : set[int]
+                    Used by recursion. Filled with already visited ids.
+    result_container : db.Container
+                       Used by recursion. Filled with already visited entities.
+    cleanup : bool
+              Used by recursion. If True return the resulting result_container.
+              Don't return anything otherwise.
+
+    Returns
+    -------
+    db.Container
+                A container containing all the retrieved entites or None if cleanup is False.
+    """
+    # Initialize the id set and result container for level zero recursion depth:
+    if result_id_set is None:
+        result_id_set = set()
+    if result_container is None:
+        result_container = db.Container()
+
+    for entity in start_record_types:
+        entity.retrieve()
+        if entity.id not in result_id_set:
+            result_container.append(entity)
+        result_id_set.add(entity.id)
+        for prop in entity.properties:
+            if is_reference(prop.datatype) and prop.datatype != db.FILE and depth > 0:
+                rt = db.RecordType(name=get_referenced_recordtype(prop.datatype)).retrieve()
+                retrieve_substructure([rt], depth-1, result_id_set, result_container, False)
+
+            if prop.id not in result_id_set:
+                result_container.append(prop)
+                result_id_set.add(prop.id)
+
+        for parent in entity.parents:
+            rt = db.RecordType(id=parent.id).retrieve()
+            if parent.id not in result_id_set:
+                result_container.append(rt)
+            result_id_set.add(parent.id)
+            if depth > 0:
+                retrieve_substructure([rt], depth-1, result_id_set, result_container, False)
+
+    if cleanup:
+        return result_container
+    return None
+
+
 def to_graphics(recordtypes, filename):
-    """ calls recordtypes_to_plantuml_string(), saves result to file and
+    """Calls recordtypes_to_plantuml_string(), saves result to file and
     creates an svg image
 
-    plantuml needs to be installed
-    @params:
-    recordtypes: itrable with the record types to be displayed
-    filname: filename of the image (e.g. data_structure; data_structure.pu and
-    data_structure.svg will be created.
+    plantuml needs to be installed.
+
+    Parameters
+    ----------
+    recordtypes : Iterable[db.Entity]
+                  Iterable with the entities to be displayed.
+    filename : str
+               filename of the image without the extension(e.g. data_structure;
+               data_structure.pu and data_structure.svg will be created.)
     """
     pu = recordtypes_to_plantuml_string(recordtypes)