You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by yj...@apache.org on 2021/01/28 01:49:36 UTC

[superset] branch master updated: chore(explore): Reorder dataset search results based on property relevance (#12770)

This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new c475f6b  chore(explore): Reorder dataset search results based on property relevance (#12770)
c475f6b is described below

commit c475f6b17318eb66170e853651b5e59f489c1b0a
Author: Nikola Gigić <ni...@gmail.com>
AuthorDate: Thu Jan 28 02:49:02 2021 +0100

    chore(explore): Reorder dataset search results based on property relevance (#12770)
---
 .../spec/javascripts/datasource/fixtures.tsx       | 93 ++++++++++++++++++++++
 .../explore/components/DatasourcePanel_spec.jsx    | 58 +++++++++++++-
 .../src/explore/components/DatasourcePanel.tsx     | 28 +++++--
 3 files changed, 168 insertions(+), 11 deletions(-)

diff --git a/superset-frontend/spec/javascripts/datasource/fixtures.tsx b/superset-frontend/spec/javascripts/datasource/fixtures.tsx
new file mode 100644
index 0000000..115b571
--- /dev/null
+++ b/superset-frontend/spec/javascripts/datasource/fixtures.tsx
@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export const columns = [
+  {
+    column_name: 'bootcamp_attend',
+    description: null,
+    expression: null,
+    filterable: true,
+    groupby: true,
+    id: 516,
+    is_dttm: false,
+    python_date_format: null,
+    type: 'DOUBLE PRECISION',
+    verbose_name: null,
+  },
+  {
+    column_name: 'calc_first_time_dev',
+    description: null,
+    expression:
+      'CASE WHEN is_first_dev_job = 0 THEN "No" WHEN is_first_dev_job = 1 THEN "Yes" END',
+    filterable: true,
+    groupby: true,
+    id: 477,
+    is_dttm: false,
+    python_date_format: null,
+    type: 'STRING',
+    verbose_name: null,
+  },
+  {
+    column_name: 'aaaaaaaaaaa',
+    description: null,
+    expression: null,
+    filterable: true,
+    groupby: true,
+    id: 516,
+    is_dttm: false,
+    python_date_format: null,
+    type: 'DOUBLE PRECISION',
+    verbose_name: null,
+  },
+];
+
+const metricsFiltered = {
+  certified: [
+    {
+      certification_details: null,
+      certified_by: 'user',
+      d3format: null,
+      description: null,
+      expression: '',
+      id: 56,
+      is_certified: true,
+      metric_name: 'metric_end_certified',
+      verbose_name: '',
+      warning_text: null,
+    },
+  ],
+  uncertified: [
+    {
+      certification_details: null,
+      certified_by: null,
+      d3format: null,
+      description: null,
+      expression: '',
+      id: 57,
+      is_certified: false,
+      metric_name: 'metric_end',
+      verbose_name: '',
+      warning_text: null,
+    },
+  ],
+};
+
+export const metrics = [
+  ...metricsFiltered.certified,
+  ...metricsFiltered.uncertified,
+];
diff --git a/superset-frontend/spec/javascripts/explore/components/DatasourcePanel_spec.jsx b/superset-frontend/spec/javascripts/explore/components/DatasourcePanel_spec.jsx
index 784e105..f9c0960 100644
--- a/superset-frontend/spec/javascripts/explore/components/DatasourcePanel_spec.jsx
+++ b/superset-frontend/spec/javascripts/explore/components/DatasourcePanel_spec.jsx
@@ -17,9 +17,11 @@
  * under the License.
  */
 import React from 'react';
-import { render, screen } from '@testing-library/react';
+import { Simulate } from 'react-dom/test-utils';
+import { render, screen, fireEvent } from '@testing-library/react';
 import { supersetTheme, ThemeProvider } from '@superset-ui/core';
 import DatasourcePanel from 'src/explore/components/DatasourcePanel';
+import { columns, metrics } from 'spec/javascripts/datasource/fixtures';
 
 describe('datasourcepanel', () => {
   const datasource = {
@@ -27,8 +29,8 @@ describe('datasourcepanel', () => {
     type: 'table',
     uid: '1__table',
     id: 1,
-    columns: [],
-    metrics: [],
+    columns,
+    metrics,
     database: {
       backend: 'mysql',
       name: 'main',
@@ -47,6 +49,14 @@ describe('datasourcepanel', () => {
     },
     actions: {},
   };
+
+  function search(value, input) {
+    fireEvent.change(input, {
+      target: { value },
+    });
+    Simulate.change(input);
+  }
+
   it('should render', () => {
     const { container } = render(
       <ThemeProvider theme={supersetTheme}>
@@ -66,4 +76,46 @@ describe('datasourcepanel', () => {
     expect(screen.getByText('Columns')).toBeTruthy();
     expect(screen.getByText('Metrics')).toBeTruthy();
   });
+
+  it('should render search results', () => {
+    const { container } = render(
+      <ThemeProvider theme={supersetTheme}>
+        <DatasourcePanel {...props} />
+      </ThemeProvider>,
+    );
+    const c = container.getElementsByClassName('option-label');
+
+    expect(c).toHaveLength(5);
+  });
+
+  it('should render 0 search results', () => {
+    const { container } = render(
+      <ThemeProvider theme={supersetTheme}>
+        <DatasourcePanel {...props} />
+      </ThemeProvider>,
+    );
+    const c = container.getElementsByClassName('option-label');
+    const searchInput = screen.getByPlaceholderText('Search Metrics & Columns');
+
+    search('sssssssss', searchInput);
+    setTimeout(() => {
+      expect(c).toHaveLength(0);
+    }, 201);
+  });
+
+  it('should render and sort search results', () => {
+    const { container } = render(
+      <ThemeProvider theme={supersetTheme}>
+        <DatasourcePanel {...props} />
+      </ThemeProvider>,
+    );
+    const c = container.getElementsByClassName('option-label');
+    const searchInput = screen.getByPlaceholderText('Search Metrics & Columns');
+
+    search('end', searchInput);
+    setTimeout(() => {
+      expect(c).toHaveLength(4);
+      expect(c[0].value).toBe('metric_end_certified');
+    }, 201);
+  });
 });
diff --git a/superset-frontend/src/explore/components/DatasourcePanel.tsx b/superset-frontend/src/explore/components/DatasourcePanel.tsx
index 7beebd2..143550c 100644
--- a/superset-frontend/src/explore/components/DatasourcePanel.tsx
+++ b/superset-frontend/src/explore/components/DatasourcePanel.tsx
@@ -117,30 +117,42 @@ export default function DataSourcePanel({
     setList({
       columns: matchSorter(columns, value, {
         keys: [
-          'verbose_name',
-          'column_name',
           {
-            key: 'description',
+            key: 'verbose_name',
             threshold: rankings.CONTAINS,
           },
           {
-            key: 'expression',
+            key: 'column_name',
             threshold: rankings.CONTAINS,
           },
+          {
+            key: item =>
+              [item.description, item.expression].map(
+                x => x?.replace(/[_\n\s]+/g, ' ') || '',
+              ),
+            threshold: rankings.CONTAINS,
+            maxRanking: rankings.CONTAINS,
+          },
         ],
         keepDiacritics: true,
       }),
       metrics: matchSorter(metrics, value, {
         keys: [
-          'verbose_name',
-          'metric_name',
           {
-            key: 'description',
+            key: 'verbose_name',
+            threshold: rankings.CONTAINS,
+          },
+          {
+            key: 'metric_name',
             threshold: rankings.CONTAINS,
           },
           {
-            key: 'expression',
+            key: item =>
+              [item.description, item.expression].map(
+                x => x?.replace(/[_\n\s]+/g, ' ') || '',
+              ),
             threshold: rankings.CONTAINS,
+            maxRanking: rankings.CONTAINS,
           },
         ],
         keepDiacritics: true,