Added aggregations support to views
authorThierry Florac <tflorac@ulthar.net>
Mon, 11 Jan 2021 17:03:44 +0100
changeset 175 17aaf1ec3390
parent 174 812bfc53bf5c
child 176 75df6b9c434f
Added aggregations support to views
src/pyams_content_es/shared/view/__init__.py
--- a/src/pyams_content_es/shared/view/__init__.py	Mon Jan 11 16:48:43 2021 +0100
+++ b/src/pyams_content_es/shared/view/__init__.py	Mon Jan 11 17:03:44 2021 +0100
@@ -14,15 +14,15 @@
 
 from datetime import datetime
 
-from elasticsearch_dsl import Q, Search
+from elasticsearch_dsl import A, Q, Search
 from pyramid.threadlocal import get_current_registry
 from pyramid_es import get_client
 
 from pyams_catalog.query import CatalogResultSet
 from pyams_content.features.search import ISearchFolder
 from pyams_content.shared.common import IGNORED_CONTENT_TYPES
-from pyams_content.shared.view.interfaces import IViewQuery, \
-    IViewQueryEsParamsExtension, IViewQueryFilterExtension, IViewUserQuery, IWfView, RELEVANCE_ORDER
+from pyams_content.shared.view.interfaces import IViewQuery, IViewQueryEsParamsExtension, \
+    IViewQueryFilterExtension, IViewUserQuery, IWfView, RELEVANCE_ORDER
 from pyams_content.shared.view.portlet import SEARCH_EXCLUDED_ITEMS
 from pyams_sequence.interfaces import ISequentialIntIds
 from pyams_utils.adapter import ContextAdapter, adapter_config
@@ -63,7 +63,8 @@
         if content_path is not None:
             params &= Q('term', **{'parent_ids': content_path})
         # check content types
-        params &= Q('bool', must_not=Q('terms', **{'content_type': tuple(IGNORED_CONTENT_TYPES.keys())}))
+        params &= Q('bool',
+                    must_not=Q('terms', **{'content_type': tuple(IGNORED_CONTENT_TYPES.keys())}))
         content_types = view.get_content_types(context)
         if content_types:
             params &= Q('terms', **{'content_type': content_types})
@@ -73,7 +74,9 @@
             params &= Q('terms', **{'data_type': data_types})
         return params
 
-    def get_results(self, context, sort_index, reverse, limit, request=None):
+    def get_results(self, context, sort_index, reverse, limit,
+                    request=None, aggregates=None):
+        aggregations = {}
         registry = request.registry
         client = get_client(request)
         params = self.get_params(context, request)
@@ -85,6 +88,9 @@
                 .params(request_timeout=30) \
                 .query(params) \
                 .source(['internal_id'])
+            if aggregates:
+                for agg in aggregates:
+                    search.aggs.bucket(agg['name'], A(agg['type'], **agg['params']))
             # Define sort order
             sort_values = []
             if (not sort_index) or (sort_index == RELEVANCE_ORDER):
@@ -108,12 +114,15 @@
             else:
                 search = search[:999]
             # Get query results
-            items = CatalogResultSet([result.internal_id for result in search])
-            total_count = search.count()
-        for name, adapter in sorted(registry.getAdapters((self.context,), IViewQueryFilterExtension),
+            results = search.execute()
+            items = CatalogResultSet([result.internal_id for result in results.hits])
+            aggregations = results.aggregations
+            total_count = results.hits.total
+        for name, adapter in sorted(registry.getAdapters((self.context,),
+                                                         IViewQueryFilterExtension),
                                     key=lambda x: x[1].weight):
             items = adapter.filter(context, items, request)
-        return unique_iter(items), total_count
+        return unique_iter(items), total_count, aggregations
 
 
 #
@@ -171,3 +180,14 @@
                                                       'header.*^3.0'
                                                   ]}) | \
                       Q('simple_query_string', **{'query': fulltext})
+
+
+@adapter_config(name='content_type', context=EsSearchFolderQuery, provides=IViewUserQuery)
+class EsSearchFolderContentTypeQuery(ContextAdapter):
+    """Search folder content-type query"""
+
+    @staticmethod
+    def get_user_params(request):
+        content_type = request.params.get('content_type')
+        if content_type:
+            yield Q('term', **{'content_type': content_type})