From 30dbc91d6824a41d4ee02b632462df175d6ffe9b Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Wed, 10 Feb 2021 15:38:24 +0000
Subject: [PATCH] New functionality for controlling, debugging and testing
 query caching.

---
 CHANGELOG.md                |  4 ++
 src/caosdb/common/models.py | 83 +++++++++++++++++++++++++++++++++----
 2 files changed, 80 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fd2bbfd2..8c47ddad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added ###
 
+* Added `cache` paramter to `execute_query` and `Query.execute` which indicates
+  whether server may use the cache for the query execution.
+* Added `cached` property to the `Query` class which indicates whether the
+  server used the cache for the execution of the last query.
 * Versioning support (experimental). The version db.Version class can
   represents particular entity versions and also the complete history of an
   entity.
diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py
index d857d5c9..5800e5cc 100644
--- a/src/caosdb/common/models.py
+++ b/src/caosdb/common/models.py
@@ -3611,6 +3611,23 @@ class ACL():
 
 
 class Query():
+    """Query
+
+    Attributes
+    ----------
+    q : str
+        The query string.
+    flags : dict of str
+        A dictionary of flags to be send with the query request.
+    messages : _Messages()
+        A container of messages included in the last query response.
+    cached : bool
+        indicates whether the server used the query cache for the execution of
+        this query.
+    results : int or Container
+        The number of results (when this was a count query) or the container
+        with the resulting entities.
+    """
 
     def putFlag(self, key, value=None):
         self.flags[key] = value
@@ -3626,10 +3643,12 @@ class Query():
     def __init__(self, q):
         self.flags = dict()
         self.messages = _Messages()
+        self.cached = None
 
         if isinstance(q, etree._Element):
             self.q = q.get("string")
             self.results = int(q.get("results"))
+            self.cached = q.get("cached").lower() == "true"
 
             for m in q:
                 if m.tag.lower() == 'warning' or m.tag.lower() == 'error':
@@ -3637,17 +3656,43 @@ class Query():
         else:
             self.q = q
 
-    def execute(self, unique=False, raise_exception_on_error=True,
-                **kwargs):
+    def execute(self, unique=False, raise_exception_on_error=True, cache=True):
+        """Execute a query (via a server-requests) and return the results.
+
+        Parameters
+        ----------
+
+        unique : bool
+            Whether the query is expected to have only one entity as result.
+            Defaults to False.
+        raise_exception_on_error : bool
+            Whether an exception should be raises when there are errors in the
+            resulting entities. Defaults to True.
+        cache : bool
+            Whether to use the query cache (equivalent to adding a "cache"
+            flag) to the Query object. Defaults to True.
+
+        Returns
+        -------
+        results : Container or integer
+            Returns an integer when it was a `COUNT` query. Otherwise, returns a
+            Container with the resulting entities.
+        """
         connection = get_connection()
-        query_dict = dict(self.flags)
+
+        flags = self.flags
+        if cache is False:
+            flags["cache"] = "false"
+        query_dict = dict(flags)
         query_dict["query"] = str(self.q)
+
         _log_request("GET Entity?" + str(query_dict), None)
         http_response = connection.retrieve(
             entity_uri_segments=["Entity"],
-            query_dict=query_dict, **kwargs)
+            query_dict=query_dict)
         cresp = Container._response_to_entities(http_response)
         self.results = cresp.query.results
+        self.cached = cresp.query.cached
 
         if self.q.lower().startswith('count') and len(cresp) == 0:
             # this was a count query
@@ -3672,8 +3717,32 @@ class Query():
         return cresp
 
 
-def execute_query(q, unique=False, raise_exception_on_error=True, flags=None,
-                  **kwargs):
+def execute_query(q, unique=False, raise_exception_on_error=True, cache=True, flags=None):
+    """Execute a query (via a server-requests) and return the results.
+
+    Parameters
+    ----------
+
+    q : str
+        The query string.
+    unique : bool
+        Whether the query is expected to have only one entity as result.
+        Defaults to False.
+    raise_exception_on_error : bool
+        Whether an exception should be raises when there are errors in the
+        resulting entities. Defaults to True.
+    cache : bool
+        Whether to use the query cache (equivalent to adding a "cache" flag).
+        Defaults to True.
+    flags : dict of str
+        Flags to be added to the request.
+
+    Returns
+    -------
+    results : Container or integer
+        Returns an integer when it was a `COUNT` query. Otherwise, returns a
+        Container with the resulting entities.
+    """
     query = Query(q)
 
     if flags is not None:
@@ -3681,7 +3750,7 @@ def execute_query(q, unique=False, raise_exception_on_error=True, flags=None,
 
     return query.execute(unique=unique,
                          raise_exception_on_error=raise_exception_on_error,
-                         **kwargs)
+                         cache=cache)
 
 
 class DropOffBox(list):
-- 
GitLab