Returns the node with the given name if it exists either in the graph
        or in its database cache or `None` otherwise.

        Arguments:
            node_name (str): The name of the node to return.
            can_validate_and_load (bool): Whether `self._graph.get_authentic_node_name(node_name)`
                                          can be called to validate the node name and add the node
                                          to the graph if the node name is valid.
            external_id (Optional[str]): An optional external ID that is used only if there no node
                                         with the given name in the graph or in the cache and
                                         `can_validate_and_load` is `True`.

        Returns:
            The node with the given name if it exists either in the graph
            or in its database cache, `None` otherwise.

def get_node_by_name(self, node_name: str,
                         can_validate_and_load: bool = False,
                         external_id: Optional[str] = None) -> Optional[Node]:
        """
        Returns the node with the given name if it exists either in the graph
        or in its database cache or `None` otherwise.

        Arguments:
            node_name (str): The name of the node to return.
            can_validate_and_load (bool): Whether `self._graph.get_authentic_node_name(node_name)`
                                          can be called to validate the node name and add the node
                                          to the graph if the node name is valid.
            external_id (Optional[str]): An optional external ID that is used only if there no node
                                         with the given name in the graph or in the cache and
                                         `can_validate_and_load` is `True`.

        Returns:
            The node with the given name if it exists either in the graph
            or in its database cache, `None` otherwise.
        """
        node: Node = self._node_name_map.get(node_name)
        if node is not None:
            return node

        db_node: DBNode = self._graph.database.Node.find_by_name(node_name)
        if db_node is None:
            if can_validate_and_load:
                node_name = self._graph.get_authentic_node_name(node_name)
                if node_name is not None:
                    node = self._node_name_map.get(node_name)
                    if node is not None:
                        return node

                    db_node = self._graph.database.Node.find_by_name(node_name)
                    if db_node is None:
                        self._internal_add_node(node_name=node_name,
                                                external_id=external_id,
                                                are_neighbors_cached=False,
                                                add_to_cache=True)
                    else:
                        self._internal_add_node(node_name=db_node.name,
                                                external_id=db_node.external_id,
                                                are_neighbors_cached=db_node.are_neighbors_cached,
                                                add_to_cache=False)
            else:
                return None
        else:
            self._internal_add_node(node_name=db_node.name,
                                    external_id=db_node.external_id,
                                    are_neighbors_cached=db_node.are_neighbors_cached,
                                    add_to_cache=False)

        node = self._node_name_map.get(node_name)

        # Trying to load the cached neighbors of the created node from the database could
        # cause a very-very-very deep recursion, so don't even think about doing it here.

        return node