Skip to content

Commit

Permalink
[DUOS-2901] Add search bar to libraries (#2456)
Browse files Browse the repository at this point in the history
  • Loading branch information
aarohinadkarni authored Mar 13, 2024
1 parent 0f75062 commit 51da910
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 47 deletions.
4 changes: 2 additions & 2 deletions src/components/data_search/DatasetFilterList.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'];

Expand All @@ -31,7 +31,7 @@ export const DatasetFilterList = (props) => {
const filter = filterName.toLowerCase();
return (
<ListItem disablePadding key={filter}>
<ListItemButton sx={{ padding: '0' }} onClick={(event) => filterHandler(event, datasets, filter)}>
<ListItemButton sx={{ padding: '0' }} onClick={(event) => filterHandler(event, datasets, filter, searchRef.current.value)}>
<ListItemIcon>
<Checkbox checked={isFiltered(filter)} />
</ListItemIcon>
Expand Down
222 changes: 177 additions & 45 deletions src/components/data_search/DatasetSearchTable.js
Original file line number Diff line number Diff line change
@@ -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',
Expand Down Expand Up @@ -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 = [];
Expand Down Expand Up @@ -135,6 +220,11 @@ export const DatasetSearchTable = (props) => {
history.push(`/dar_application/${darDraft.referenceId}`);
};

const clearSearchRef = () => {
searchRef.current.value = '';
filterHandler(null, datasets, '', '');
}

useEffect(() => {
if (isEmpty(filtered)) {
return;
Expand Down Expand Up @@ -253,26 +343,68 @@ export const DatasetSearchTable = (props) => {
return (
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<TableHeaderSection icon={icon} title={title} description="Search, filter, and select datasets, then click 'Apply for Access' to request access" />
<Box sx={{ display: 'flex', flexDirection: 'row', paddingTop: '2em' }}>
<Box sx={{ width: '14%', padding: '0 1em' }}>
<DatasetFilterList datasets={datasets} filters={filters} filterHandler={filterHandler} />
<Box sx={{paddingTop: '2em', paddingLeft: '2em'}}>
<div className="right-header-section" style={Styles.RIGHT_HEADER_SECTION}>
<input
data-cy="search-bar"
type="text"
placeholder="Enter search terms"
style={{
width: '100%',
border: '1px solid #cecece',
backgroundColor: '#f3f6f7',
borderRadius: '5px',
height: '4rem',
paddingLeft: '2%',
fontFamily: 'Montserrat',
fontSize: '1.5rem'
}}
onChange={() => filterHandler(null, datasets, '', searchRef.current.value)}
ref={searchRef}
/>
<div/>
<Box sx={{ display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', paddingLeft: '1em', height: '4rem' }}>
<Button variant="contained" onClick={clearSearchRef} sx={{ width: '100px' }}>
Clear Search
</Button>
</Box>
</div>
</Box>
<Box sx={{display: 'flex', flexDirection: 'row', paddingTop: '2em'}}>
<Box sx={{width: '14%', padding: '0 1em'}}>
<DatasetFilterList datasets={datasets} filters={filters} filterHandler={filterHandler} searchRef={searchRef}/>
</Box>
<Box sx={{ width: '85%', padding: '0 1em' }}>
{
isEmpty(datasets) ?
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
<h1>No datasets registered for this library.</h1>
</Box>
:
<CollapsibleTable
data={tableData}
selected={selected}
selectHandler={selectHandler}
expandHandler={expandHandler}
collapseHandler={collapseHandler}
summary='faceted study search table'
/>
}
<Box sx={{width: '85%', padding: '0 1em'}}>
{(() => {
if (isEmpty(datasets)) {
return (
<Box sx={{
display: 'flex',
flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
<h1>No datasets registered for this library.</h1>
</Box>
);
} else if (isEmpty(filtered)) {
return (
<Box sx={{
display: 'flex',
flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
<h1>There are no datasets that fit these criteria.</h1>
</Box>
);
} else {
return (
<CollapsibleTable
data={tableData}
selected={selected}
selectHandler={selectHandler}
expandHandler={expandHandler}
collapseHandler={collapseHandler}
summary='faceted study search table'
/>
);
}
})()}
</Box>
</Box>
<Box sx={{ display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', padding: '2em 4em' }}>
Expand Down

0 comments on commit 51da910

Please sign in to comment.