diff --git a/src/components/data_search/DatasetFilterList.js b/src/components/data_search/DatasetFilterList.js index f63ffa395..a45d7b538 100644 --- a/src/components/data_search/DatasetFilterList.js +++ b/src/components/data_search/DatasetFilterList.js @@ -10,7 +10,7 @@ import { Typography } from '@mui/material'; import { Checkbox } from '@mui/material'; export const DatasetFilterList = (props) => { - const { datasets, filters, filterHandler } = props; + const { datasets, filters, filterHandler, searchRef } = props; const accessManagementFilters = ['Controlled', 'Open', 'External']; @@ -31,7 +31,7 @@ export const DatasetFilterList = (props) => { const filter = filterName.toLowerCase(); return ( - filterHandler(event, datasets, filter)}> + filterHandler(event, datasets, filter, searchRef.current.value)}> diff --git a/src/components/data_search/DatasetSearchTable.js b/src/components/data_search/DatasetSearchTable.js index 0c3a649ab..b4ad05cad 100644 --- a/src/components/data_search/DatasetSearchTable.js +++ b/src/components/data_search/DatasetSearchTable.js @@ -1,15 +1,18 @@ import * as React from 'react'; import _ from 'lodash'; import { Box, Button, Link } from '@mui/material'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useCallback, useRef } from 'react'; import { groupBy, isEmpty } from 'lodash'; import CollapsibleTable from '../CollapsibleTable'; import TableHeaderSection from '../TableHeaderSection'; import DatasetExportButton from './DatasetExportButton'; -import { DAR, TerraDataRepo } from '../../libs/ajax'; +import { DAR, TerraDataRepo, DataSet } from '../../libs/ajax'; import { Config } from '../../libs/config'; import DatasetFilterList from './DatasetFilterList'; import { Notifications } from '../../libs/utils'; +import { Styles } from '../../libs/theme'; +import isEqual from 'lodash/isEqual'; + const studyTableHeader = [ 'Study Name', @@ -40,40 +43,122 @@ export const DatasetSearchTable = (props) => { const [selected, setSelected] = useState([]); const [exportableDatasets, setExportableDatasets] = useState({}); // datasetId -> snapshot const [tdrApiUrl, setTdrApiUrl] = useState(''); - + const searchRef = useRef(''); const isFiltered = (filter) => filters.indexOf(filter) > -1; - const filterHandler = (event, data, filter) => { + const assembleFullQuery = (searchTerm, filters) => { + const queryChunks = [ + { + 'match': { + '_type': 'dataset' + } + }, + { + 'exists': { + 'field': 'study' + } + } + ]; + + // do not apply search modifier if there is no searchTerm + if (searchTerm !== '') { + const searchModifier = [ + { + "multi_match": { + "query": searchTerm, + "type":"phrase_prefix", + "fields": [ + "datasetName", + "dataLocation", + "study.description", + "study.studyName", + "study.species", + "study.piName", + "study.dataCustodianEmail", + "study.dataTypes", + "dataUse.primary.code", + "dataUse.secondary.code", + "dac.dacName", + "datasetIdentifier" + ] + } + } + ]; + queryChunks.push(...searchModifier); + } + + var filterQuery = {}; + if (filters.length > 0) { + const shouldTerms = []; + + filters.forEach(term => { + shouldTerms.push({ + "term": { + "accessManagement": term + } + }); + }); + + if (shouldTerms.length > 0) { + filterQuery = [ + { + "bool": { + "should": shouldTerms + } + } + ]; + } + } + + // do not add filter subquery if no filters are applied + if (filters.length > 0) { + return { + 'from': 0, + 'size': 10000, + 'query': { + 'bool': { + 'must': queryChunks, + 'filter': filterQuery + } + } + }; + } else { + return { + 'from': 0, + 'size': 10000, + 'query': { + 'bool': { + 'must': queryChunks + } + } + }; + } + }; + + const filterHandler = (event, data, filter, searchTerm) => { var newFilters = []; - if (!isFiltered(filter)) { + if (!isFiltered(filter) && filter !== '') { newFilters = filters.concat(filter); } else { newFilters = filters.filter((f) => f !== filter); } setFilters(newFilters); - var newFiltered = []; - if (newFilters.length === 0) { - newFiltered = data; - } else { - newFiltered = data.filter((dataset) => { - // TODO: remove extra checks when openAccess property is deprecated - if (newFilters.includes('open') && (dataset.openAccess || dataset.accessManagement === 'open')) { - return true; - } - if (newFilters.includes('controlled') && ( - (!dataset.openAccess && dataset.accessManagement === undefined) || (dataset.openAccess === undefined && dataset.accessManagement === 'controlled') - )) { - return true; - } - if (newFilters.includes('external') && dataset.accessManagement === 'external') { - return true; - } - return false; - }); - } - setFiltered(newFiltered); + const fullQuery = assembleFullQuery(searchTerm, newFilters); + const search = async () => { + try { + await DataSet.searchDatasetIndex(fullQuery).then((filteredDatasets) => { + var newFiltered = datasets.filter(value => filteredDatasets.some(item => isEqual(item, value))); + setFiltered(newFiltered); + }); + } catch (error) { + Notifications.showError({ text: 'Failed to load Elasticsearch index' }); + } + }; + search(); }; + + const selectHandler = (event, data, selector) => { let idsToModify = []; @@ -136,6 +221,11 @@ export const DatasetSearchTable = (props) => { history.push(`/dar_application/${darDraft.referenceId}`); }; + const clearSearchRef = () => { + searchRef.current.value = ''; + filterHandler(null, datasets, '', ''); + } + useEffect(() => { if (isEmpty(filtered)) { return; @@ -253,26 +343,68 @@ export const DatasetSearchTable = (props) => { return ( - - - + +
+ filterHandler(null, datasets, '', searchRef.current.value)} + ref={searchRef} + /> +
+ + + +
+ + + + - - { - isEmpty(datasets) ? - -

No datasets registered for this library.

-
- : - - } + + {(() => { + if (isEmpty(datasets)) { + return ( + +

No datasets registered for this library.

+
+ ); + } else if (isEmpty(filtered)) { + return ( + +

There are no datasets that fit these criteria.

+
+ ); + } else { + return ( + + ); + } + })()}