Repository: google/timesketch

Issue:
Add interactive histogram
**Is your feature request related to a problem? Please describe.**
It would be useful to have an interactive histogram that when clicked will add time rage filter for the query.

**Describe the solution you'd like**
Investigate if there is an open source library to help with the viz, and make sure it integrated with the filter system.


Discussion:

Resolving patch:
diff --git a/timesketch/api/v1/resources/explore.py b/timesketch/api/v1/resources/explore.py
--- a/timesketch/api/v1/resources/explore.py
+++ b/timesketch/api/v1/resources/explore.py
@@ -154,6 +154,12 @@ def post(self, sketch_id):
                     'min_doc_count': 0,
                     'size': len(sketch.timelines)
                 }
+            },
+            'count_over_time': {
+                'auto_date_histogram': {
+                    'field': 'datetime',
+                    'buckets': 50,
+                }
             }
         }
         if count:
@@ -228,6 +234,21 @@ def post(self, sketch_id):
             except ValueError as e:
                 abort(HTTP_STATUS_CODE_BAD_REQUEST, str(e))
 
+        # Get number of matching documents over time.
+        histogram_interval = result.get(
+            'aggregations', {}).get('count_over_time', {}).get('interval', '')
+        count_over_time = {
+            'data': {},
+            'interval': histogram_interval
+        }
+        try:
+            for bucket in result['aggregations']['count_over_time']['buckets']:
+                key = bucket.get('key')
+                if key:
+                    count_over_time['data'][key] = bucket.get('doc_count')
+        except KeyError:
+            pass
+
         # Get number of matching documents per index.
         count_per_index = {}
         try:
@@ -361,6 +382,7 @@ def post(self, sketch_id):
             'timeline_names': tl_names,
             'count_per_index': count_per_index,
             'count_per_timeline': count_per_timeline,
+            'count_over_time': count_over_time,
             'scroll_id': result.get('_scroll_id', ''),
             'search_node': search_node
         }
@@ -465,6 +487,8 @@ def get(self, sketch_id):
 class SearchHistoryTreeResource(resources.ResourceMixin, Resource):
     """Resource to get search history for a user."""
 
+    HISTORY_NODE_LIMIT = 250  # To prevent heap exhaustion during recursion.
+
     @login_required
     def get(self, sketch_id):
         """Handles GET request to the resource.
@@ -479,7 +503,8 @@ def get(self, sketch_id):
         tree = {}
         root_node = SearchHistory.query.filter_by(
             user=current_user, sketch=sketch).order_by(
-                SearchHistory.id).first()
+                SearchHistory.id.desc()).limit(self.HISTORY_NODE_LIMIT)[-1]
+
         last_node = SearchHistory.query.filter_by(
             user=current_user, sketch=sketch).order_by(
                 SearchHistory.id.desc()).first()


Test patch:
diff --git a/timesketch/api/v1/resources_test.py b/timesketch/api/v1/resources_test.py
--- a/timesketch/api/v1/resources_test.py
+++ b/timesketch/api/v1/resources_test.py
@@ -182,6 +182,7 @@ class ExploreResourceTest(BaseTest):
             'timeline_names': {'test': 'Timeline 1'},
             'count_per_index': {},
             'count_per_timeline': {},
+            'count_over_time': {'data': {}, 'interval': ''},
             'scroll_id': '',
             'search_node': {
                 'children': [],