Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DUOS-2901] Add search bar to libraries #2456

Merged
merged 13 commits into from
Mar 13, 2024
Merged
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": {
aarohinadkarni marked this conversation as resolved.
Show resolved Hide resolved
"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 @@ -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;
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
Loading