Repository: learningequality/kolibri

Issue:
Catch and handle EmptyResultSet error when trying to cache metadata labels
EmptyResultSet

Sentry Issue: [KOLIBRI-BACKEND-2E9](https://learningequality.sentry.io/issues/4005137733/?referrer=github_integration)

```
EmptyResultSet: 
(16 additional frame(s) were not displayed)
...
  File "django/db/models/sql/query.py", line 233, in __str__
    sql, params = self.sql_with_params()
  File "django/db/models/sql/query.py", line 241, in sql_with_params
    return self.get_compiler(DEFAULT_DB_ALIAS).as_sql()
  File "django/db/models/sql/compiler.py", line 441, in as_sql
    where, w_params = self.compile(self.where) if self.where is not None else ("", [])
  File "django/db/models/sql/compiler.py", line 373, in compile
    sql, params = node.as_sql(self, self.connection)
  File "django/db/models/sql/where.py", line 97, in as_sql
    raise EmptyResultSet
```


Discussion:

Resolving patch:
diff --git a/kolibri/core/content/utils/search.py b/kolibri/core/content/utils/search.py
--- a/kolibri/core/content/utils/search.py
+++ b/kolibri/core/content/utils/search.py
@@ -9,6 +9,7 @@
 except ImportError:
     BitOr = None
 
+from django.core.exceptions import EmptyResultSet
 from django.db import connections
 from django.db.models import Aggregate
 from django.db.models import Case
@@ -39,6 +40,11 @@
 
 bitmask_fieldnames = {}
 
+empty_labels = {
+    "languages": [],
+    "channels": [],
+}
+
 
 for key, labels in metadata_lookup.items():
     bitmask_lookup = {}
@@ -57,6 +63,7 @@
             bitmask_fieldnames[bitmask_field_name].append(info)
         i += 64
     metadata_bitmasks[key] = bitmask_lookup
+    empty_labels[key] = []
 
 
 def _get_available_languages(base_queryset):
@@ -98,14 +105,17 @@ def template(self):
         )
 
 
-def get_available_metadata_labels(base_queryset):
+def get_available_metadata_labels(base_queryset):  # noqa: C901
     from kolibri.core.device.models import ContentCacheKey
 
     content_cache_key = ContentCacheKey.get_cache_key()
-    cache_key = "search-labels:{}:{}".format(
-        content_cache_key,
-        hashlib.md5(str(base_queryset.query).encode("utf8")).hexdigest(),
-    )
+    try:
+        cache_key = "search-labels:{}:{}".format(
+            content_cache_key,
+            hashlib.md5(str(base_queryset.query).encode("utf8")).hexdigest(),
+        )
+    except EmptyResultSet:
+        return empty_labels
     if cache_key not in cache:
         base_queryset = base_queryset.order_by()
         aggregates = {}


Test patch:
diff --git a/kolibri/core/content/test/test_search.py b/kolibri/core/content/test/test_search.py
--- a/kolibri/core/content/test/test_search.py
+++ b/kolibri/core/content/test/test_search.py
@@ -176,3 +176,13 @@ def test_labels(self, field, label):
         self.assertEqual(
             set(metadata_labels[field]), set(expected), "{} {}".format(field, label)
         )
+
+    @parameterized.expand(
+        field for field in (list(metadata_lookup.keys()) + ["channels", "languages"])
+    )
+    def test_labels_empty_queryset(self, field):
+        try:
+            labels = get_available_metadata_labels(ContentNode.objects.none())
+        except Exception as e:
+            self.fail("get_available_metadata_labels raised {}".format(e))
+        self.assertEqual(labels[field], [])