From e46c1be5407c9082da954adba4d1353969c20751 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Sun, 26 May 2024 12:14:14 -0400 Subject: [PATCH 01/73] Add changelog --- CHANGELOG-replace-searchkit.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGELOG-replace-searchkit.md diff --git a/CHANGELOG-replace-searchkit.md b/CHANGELOG-replace-searchkit.md new file mode 100644 index 0000000000..57920aba48 --- /dev/null +++ b/CHANGELOG-replace-searchkit.md @@ -0,0 +1 @@ +- Replace searchkit. \ No newline at end of file From 08a8643a88a541a7e12d01b1c68065c73b76843d Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 30 May 2024 13:16:43 -0400 Subject: [PATCH 02/73] Install elastic-builder --- context/package-lock.json | 51 +++++++++++++++++++++++++++++++++++++++ context/package.json | 1 + 2 files changed, 52 insertions(+) diff --git a/context/package-lock.json b/context/package-lock.json index edf910ad2a..02da0bfcbe 100644 --- a/context/package-lock.json +++ b/context/package-lock.json @@ -41,6 +41,7 @@ "d3": "^7.9.0", "d3-array": "^3.2.4", "date-fns": "^3.6.0", + "elastic-builder": "^2.29.0", "fast-deep-equal": "^3.1.3", "immer": "^10.0.4", "lineupjsx": "^4.6.0", @@ -18019,6 +18020,21 @@ "node": ">=0.10.0" } }, + "node_modules/elastic-builder": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/elastic-builder/-/elastic-builder-2.29.0.tgz", + "integrity": "sha512-PanRadFQs/nnGV3sJOlZ9eGeSzbVBtbbtpx1bqep64EYALIaTudSEW9Eqo4gp5rWqH4VOTMFV6vPFz26o9L0KQ==", + "dependencies": { + "lodash.has": "^4.5.2", + "lodash.hasin": "^4.5.2", + "lodash.head": "^4.0.1", + "lodash.isempty": "^4.4.0", + "lodash.isnil": "^4.0.0", + "lodash.isobject": "^3.0.2", + "lodash.isstring": "^4.0.1", + "lodash.omit": "^4.5.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.741", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.741.tgz", @@ -25921,11 +25937,46 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" }, + "node_modules/lodash.has": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", + "integrity": "sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g==" + }, + "node_modules/lodash.hasin": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.hasin/-/lodash.hasin-4.5.2.tgz", + "integrity": "sha512-AFAitwTSq1Ka/1J9uBaVxpLBP5OI3INQvkl4wKcgIYxoA0S3aqO1QWXHR9aCcOrWtPFqP7GzlFncZfe0Jz0kNw==" + }, + "node_modules/lodash.head": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.head/-/lodash.head-4.0.1.tgz", + "integrity": "sha512-cw2auMZNAJp4bAAbG0CITl68eE0j4SkbIPEskppHhIyQ2SFQWSrcoH6mnpcFPgsOqSI/lcrnCHKlz8r54D4jCA==" + }, + "node_modules/lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==" + }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" + }, + "node_modules/lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", diff --git a/context/package.json b/context/package.json index 4c41cd6da5..9b9e3c32cd 100644 --- a/context/package.json +++ b/context/package.json @@ -34,6 +34,7 @@ "d3": "^7.9.0", "d3-array": "^3.2.4", "date-fns": "^3.6.0", + "elastic-builder": "^2.29.0", "fast-deep-equal": "^3.1.3", "immer": "^10.0.4", "lineupjsx": "^4.6.0", From 4c6b90a0919d501430aba0e1866a111cfc93724b Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 30 May 2024 13:17:16 -0400 Subject: [PATCH 03/73] Type for es endpoint in context --- context/app/static/js/components/Contexts.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/context/app/static/js/components/Contexts.tsx b/context/app/static/js/components/Contexts.tsx index d145905bc9..6b0b40e0f3 100644 --- a/context/app/static/js/components/Contexts.tsx +++ b/context/app/static/js/components/Contexts.tsx @@ -66,6 +66,7 @@ export const FlaskDataContext = createContext('FlaskDataCo export const useFlaskDataContext = () => useContext(FlaskDataContext); interface AppContextType { + elasticsearchEndpoint: string; assetsEndpoint: string; groupsToken: string; workspacesToken: string; From 8ef242b090219c1bb9e9559c2d77054b094e9944 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 30 May 2024 13:25:00 -0400 Subject: [PATCH 04/73] Add basic search implementation --- .../static/js/components/search/Search.tsx | 91 +++++++++++++++++++ .../app/static/js/components/search/index.ts | 3 + .../app/static/js/components/search/store.ts | 26 ++++++ 3 files changed, 120 insertions(+) create mode 100644 context/app/static/js/components/search/Search.tsx create mode 100644 context/app/static/js/components/search/index.ts create mode 100644 context/app/static/js/components/search/store.ts diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx new file mode 100644 index 0000000000..4dface6001 --- /dev/null +++ b/context/app/static/js/components/search/Search.tsx @@ -0,0 +1,91 @@ +import React, { useMemo } from 'react'; +import useSWR from 'swr'; +import { SearchRequest, SearchResponseBody } from '@elastic/elasticsearch/lib/api/types'; +import esb from 'elastic-builder'; + +import { fetcher } from 'js/helpers/swr'; +import { getAuthHeader } from 'js/helpers/functions'; +import { useAppContext } from 'js/components/Contexts'; +import { SearchStoreProvider, useSearchStore, SearchStoreState } from './store'; + +function useAuthHeader() { + const { groupsToken } = useAppContext(); + return useMemo(() => getAuthHeader(groupsToken), [groupsToken]); +} + +interface BuildSearchRequestInitArgs { + body: SearchRequest; + authHeader: { Authorization?: string }; +} + +function buildSearchRequestInit({ body, authHeader }: BuildSearchRequestInitArgs): RequestInit { + return { + method: 'POST', + body: JSON.stringify(body), + headers: { + 'Content-Type': 'application/json', + ...(authHeader ?? {}), + }, + }; +} + +function useRequestInit({ body }: { body: SearchRequest }) { + const authHeader = useAuthHeader(); + + return buildSearchRequestInit({ body, authHeader }); +} + +function buildQuery({ terms, size, sourceFields, sortField }: Omit) { + const query = esb + .requestBodySearch() + .size(size) + .source(sourceFields.size ? [...sourceFields] : false) + .sort(esb.sort(sortField.field, sortField.direction)); + + Object.entries(terms).forEach(([field, values]) => query.postFilter(esb.termsQuery(field, [...values]))); + + return query.toJSON(); +} + +function useSearch() { + const { endpoint, swrConfig, ...rest }: SearchStoreState = useSearchStore(); + + const query = buildQuery({ ...rest }); + + const requestInit = useRequestInit({ body: query }); + const { data, isLoading } = useSWR>( + { requestInit, url: endpoint }, + fetcher, + swrConfig, + ); + return { data, isLoading }; +} + +function Search() { + const { data, isLoading } = useSearch(); + + // eslint-disable-next-line no-console + console.log(data, isLoading); + return null; +} + +function SearchWrapper() { + const { elasticsearchEndpoint } = useAppContext(); + + return ( + + + + ); +} + +export default SearchWrapper; diff --git a/context/app/static/js/components/search/index.ts b/context/app/static/js/components/search/index.ts new file mode 100644 index 0000000000..517d0ee89b --- /dev/null +++ b/context/app/static/js/components/search/index.ts @@ -0,0 +1,3 @@ +import Search from './Search'; + +export default Search; diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts new file mode 100644 index 0000000000..fc401ea60f --- /dev/null +++ b/context/app/static/js/components/search/store.ts @@ -0,0 +1,26 @@ +import { createStoreImmer, createStoreContext } from 'js/helpers/zustand'; + +import { SWRConfiguration } from 'swr'; + +interface SortField { + field: string; + direction: 'asc' | 'desc'; +} + +export interface SearchStoreState { + terms: Record>; + sortField: SortField; + sourceFields: Set; + size: number; + endpoint: string; + swrConfig: SWRConfiguration; +} + +export const createStore = ({ initialState }: { initialState: SearchStoreState }) => + createStoreImmer(() => ({ + ...initialState, + })); + +const [SearchStoreProvider, useSearchStore, SearchStoreContext] = createStoreContext(createStore, 'Search'); + +export { SearchStoreProvider, useSearchStore, SearchStoreContext }; From 53cd5663edadce5deec1922fbee60e6aace9454d Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 30 May 2024 15:11:13 -0400 Subject: [PATCH 05/73] Display results --- .../search/Results/ResultsTable.tsx | 52 ++++++++++++++++ .../js/components/search/Results/index.ts | 3 + .../js/components/search/Results/style.ts | 59 +++++++++++++++++++ .../js/components/search/Results/utils.ts | 54 +++++++++++++++++ .../static/js/components/search/Search.tsx | 23 +++++--- .../app/static/js/components/search/store.ts | 6 +- .../app/static/js/components/search/types.ts | 3 + 7 files changed, 190 insertions(+), 10 deletions(-) create mode 100644 context/app/static/js/components/search/Results/ResultsTable.tsx create mode 100644 context/app/static/js/components/search/Results/index.ts create mode 100644 context/app/static/js/components/search/Results/style.ts create mode 100644 context/app/static/js/components/search/Results/utils.ts create mode 100644 context/app/static/js/components/search/types.ts diff --git a/context/app/static/js/components/search/Results/ResultsTable.tsx b/context/app/static/js/components/search/Results/ResultsTable.tsx new file mode 100644 index 0000000000..c074dfe5ac --- /dev/null +++ b/context/app/static/js/components/search/Results/ResultsTable.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; + +import { InternalLink } from 'js/shared-styles/Links'; +import { getByPath } from './utils'; +import { StyledTable, StyledTableBody, StyledTableRow, StyledTableCell } from './style'; +import { useSearch } from '../Search'; +import { useSearchStore } from '../store'; +import { HitDoc } from '../types'; + +function ResultCell({ hit, field }: { field: string; hit: SearchHit }) { + const source = hit?._source; + + if (!source) { + return ; + } + + const fieldValue = getByPath(source, field); + + return ( + + {field === 'hubmap_id' ? {fieldValue} : fieldValue} + + ); +} + +function ResultsTable() { + const { data } = useSearch(); + const { sourceFields } = useSearchStore(); + const hits = data?.hits?.hits; + + // TODO: Loading State + if (!hits) { + return null; + } + + return ( + + {hits.map((hit) => ( + + + {Object.keys(sourceFields).map((field) => ( + + ))} + + + ))} + + ); +} + +export default ResultsTable; diff --git a/context/app/static/js/components/search/Results/index.ts b/context/app/static/js/components/search/Results/index.ts new file mode 100644 index 0000000000..3ca3bed48d --- /dev/null +++ b/context/app/static/js/components/search/Results/index.ts @@ -0,0 +1,3 @@ +import ResultsTable from './ResultsTable'; + +export { ResultsTable }; diff --git a/context/app/static/js/components/search/Results/style.ts b/context/app/static/js/components/search/Results/style.ts new file mode 100644 index 0000000000..9bea8467ba --- /dev/null +++ b/context/app/static/js/components/search/Results/style.ts @@ -0,0 +1,59 @@ +import { styled } from '@mui/material/styles'; +import Table from '@mui/material/Table'; +import TableRow from '@mui/material/TableRow'; +import TableBody from '@mui/material/TableBody'; +import TableCell from '@mui/material/TableCell'; + +const StyledTable = styled(Table)(({ theme }) => ({ + backgroundColor: theme.palette.white.main, + boxShadow: theme.shadows[1], +})); + +const StyledTableBody = styled(TableBody)(({ theme }) => ({ + // NOTE: If we want to darken on hover, we need to give an explicit background to all rows. + // What looks white is actually transparent and brightness() has no effect. + backgroundColor: theme.palette.white.main, + + '&:hover': { + filter: theme.palette.white.hover, + }, + + // Material would apply this on TD, but we override, so there is no internal border above the highlight. + border: `1px solid ${theme.palette.divider}`, + + borderLeft: 'none', + borderRight: 'none', +})); + +const interPadding = `${16 * 0.6}px`; +const sidePadding = '64px'; + +const StyledTableRow = styled(TableRow)(({ theme }) => ({ + border: 0, + + '&.before-highlight td': { + paddingBottom: 0, + }, + '&.highlight td': { + paddingTop: interPadding, + paddingLeft: sidePadding, + paddingRight: sidePadding, + '& p': { + color: theme.palette.common.halfShadow, + margin: 0, + }, + }, +})); + +const StyledTableCell = styled(TableCell)({ + // Borders handled by tbody. + border: 'none', + // Elastic search injects when showing matches in context. + + em: { + fontWeight: 'bold', + fontStyle: 'normal', + }, +}); + +export { StyledTable, StyledTableRow, StyledTableBody, StyledTableCell }; diff --git a/context/app/static/js/components/search/Results/utils.ts b/context/app/static/js/components/search/Results/utils.ts new file mode 100644 index 0000000000..409e14d13d --- /dev/null +++ b/context/app/static/js/components/search/Results/utils.ts @@ -0,0 +1,54 @@ +import { get } from 'js/helpers/nodash'; +import { HitDoc, HitValues } from '../types'; + +const donorMetadataPath = 'mapped_metadata'; +const sampleMetdataPath = 'metadata'; + +const paths = { + donor: { + donor: donorMetadataPath, + }, + sample: { + sample: sampleMetdataPath, + donor: `donor.${donorMetadataPath}`, + }, + dataset: { + donor: `donor.${donorMetadataPath}`, + sample: `source_samples.${sampleMetdataPath}`, + dataset: 'metadata.metadata', + }, +}; + +const samplePaths = ['origin_samples', 'source_samples']; + +function matchSamplePath(fieldIdentifier: string) { + return samplePaths.reduce((matchedPath, path) => { + if (fieldIdentifier.startsWith(path)) { + return path; + } + return matchedPath; + }, ''); +} + +function getFieldFromHitFields(hitFields: HitDoc, identifier: string): HitValues { + const matchedSamplePath = matchSamplePath(identifier); + if (matchedSamplePath.length > 0) { + // source_samples and origin_samples are arrays and must be handled accordingly. + // TODO: Update design to reflect samples and datasets which have multiple origin samples with different organs. + return get(hitFields, [matchedSamplePath, '0', ...identifier.split('.').slice(1)].join('')); + } + + return get(hitFields, identifier); +} + +function getByPath(hitSource: HitDoc, field: string) { + const fieldValue = getFieldFromHitFields(hitSource, field); + + if (Array.isArray(fieldValue)) { + return fieldValue.join(' / '); + } + + return fieldValue; +} + +export { getByPath, getFieldFromHitFields, paths }; diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 4dface6001..7335c3a80f 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -7,6 +7,8 @@ import { fetcher } from 'js/helpers/swr'; import { getAuthHeader } from 'js/helpers/functions'; import { useAppContext } from 'js/components/Contexts'; import { SearchStoreProvider, useSearchStore, SearchStoreState } from './store'; +import { HitDoc } from './types'; +import { ResultsTable } from './Results'; function useAuthHeader() { const { groupsToken } = useAppContext(); @@ -39,7 +41,7 @@ function buildQuery({ terms, size, sourceFields, sortField }: Omit query.postFilter(esb.termsQuery(field, [...values]))); @@ -47,13 +49,13 @@ function buildQuery({ terms, size, sourceFields, sortField }: Omit() { +export function useSearch() { const { endpoint, swrConfig, ...rest }: SearchStoreState = useSearchStore(); const query = buildQuery({ ...rest }); const requestInit = useRequestInit({ body: query }); - const { data, isLoading } = useSWR>( + const { data, isLoading } = useSWR>( { requestInit, url: endpoint }, fetcher, swrConfig, @@ -62,11 +64,7 @@ function useSearch() { } function Search() { - const { data, isLoading } = useSearch(); - - // eslint-disable-next-line no-console - console.log(data, isLoading); - return null; + return ; } function SearchWrapper() { @@ -79,7 +77,14 @@ function SearchWrapper() { swrConfig: {}, terms: { 'entity_type.keyword': new Set(['Dataset']) }, sortField: { field: 'last_modified_timestamp', direction: 'desc' }, - sourceFields: new Set([]), + sourceFields: { + hubmap_id: { label: 'HuBMAP ID' }, + group_name: { label: 'Group' }, + assay_display_name: { label: 'Data Types' }, + 'origin_samples.mapped_organ': { label: 'Organ' }, + mapped_status: { label: 'Status' }, + last_modified_timestamp: { label: 'Last Modified' }, + }, size: 10, }} > diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index fc401ea60f..585aaf62f4 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -7,10 +7,14 @@ interface SortField { direction: 'asc' | 'desc'; } +interface SourceField { + label: string; +} + export interface SearchStoreState { terms: Record>; sortField: SortField; - sourceFields: Set; + sourceFields: Record; size: number; endpoint: string; swrConfig: SWRConfiguration; diff --git a/context/app/static/js/components/search/types.ts b/context/app/static/js/components/search/types.ts new file mode 100644 index 0000000000..15381d2070 --- /dev/null +++ b/context/app/static/js/components/search/types.ts @@ -0,0 +1,3 @@ +export type HitValues = string | number | string[] | number[]; + +export type HitDoc = Record; From c7eefaf96628d669abfa2efe9ebcf2cad21858a8 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Tue, 4 Jun 2024 16:06:29 -0400 Subject: [PATCH 06/73] Fix util path --- context/app/static/js/components/search/Results/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/app/static/js/components/search/Results/utils.ts b/context/app/static/js/components/search/Results/utils.ts index 409e14d13d..7851a94575 100644 --- a/context/app/static/js/components/search/Results/utils.ts +++ b/context/app/static/js/components/search/Results/utils.ts @@ -35,7 +35,7 @@ function getFieldFromHitFields(hitFields: HitDoc, identifier: string): HitValues if (matchedSamplePath.length > 0) { // source_samples and origin_samples are arrays and must be handled accordingly. // TODO: Update design to reflect samples and datasets which have multiple origin samples with different organs. - return get(hitFields, [matchedSamplePath, '0', ...identifier.split('.').slice(1)].join('')); + return get(hitFields, [matchedSamplePath, '0', ...identifier.split('.').slice(1)].join('.')); } return get(hitFields, identifier); From e2364eda36ef4103d0a0843e860a3a51794c40d1 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Tue, 4 Jun 2024 16:07:26 -0400 Subject: [PATCH 07/73] Add sorting --- .../search/Results/ResultsTable.tsx | 10 +++++ .../search/Results/SortingTableHead.tsx | 39 +++++++++++++++++++ .../js/components/search/Results/style.ts | 38 +++++++++++++++++- .../app/static/js/components/search/store.ts | 13 ++++++- 4 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 context/app/static/js/components/search/Results/SortingTableHead.tsx diff --git a/context/app/static/js/components/search/Results/ResultsTable.tsx b/context/app/static/js/components/search/Results/ResultsTable.tsx index c074dfe5ac..aea7711669 100644 --- a/context/app/static/js/components/search/Results/ResultsTable.tsx +++ b/context/app/static/js/components/search/Results/ResultsTable.tsx @@ -1,5 +1,7 @@ import React from 'react'; import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; +import TableRow from '@mui/material/TableRow'; +import TableHead from '@mui/material/TableHead'; import { InternalLink } from 'js/shared-styles/Links'; import { getByPath } from './utils'; @@ -7,6 +9,7 @@ import { StyledTable, StyledTableBody, StyledTableRow, StyledTableCell } from '. import { useSearch } from '../Search'; import { useSearchStore } from '../store'; import { HitDoc } from '../types'; +import SortingTableHead from './SortingTableHead'; function ResultCell({ hit, field }: { field: string; hit: SearchHit }) { const source = hit?._source; @@ -36,6 +39,13 @@ function ResultsTable() { return ( + + + {Object.entries(sourceFields).map(([field, { label }]) => ( + + ))} + + {hits.map((hit) => ( diff --git a/context/app/static/js/components/search/Results/SortingTableHead.tsx b/context/app/static/js/components/search/Results/SortingTableHead.tsx new file mode 100644 index 0000000000..71acb4ec57 --- /dev/null +++ b/context/app/static/js/components/search/Results/SortingTableHead.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import IconButton from '@mui/material/IconButton'; + +import { useSearchStore } from '../store'; + +import { ArrowUpOn, ArrowDownOn, ArrowDownOff, StyledHeaderCell } from './style'; + +export function OrderIcon({ direction }: { direction: 'asc' | 'desc' }) { + if (direction === 'asc') return ; + if (direction === 'desc') return ; +} + +function SortingTableHead({ field, label }: { field: string; label: string }) { + const { sortField, setSortField } = useSearchStore(); + + const { direction, field: currentSortField } = sortField; + + if (field !== currentSortField) { + return ( + + {label} + setSortField({ field, direction: 'desc' })}> + + + + ); + } + + return ( + + {label} + setSortField({ field, direction: direction === 'desc' ? 'asc' : 'desc' })}> + + + + ); +} + +export default SortingTableHead; diff --git a/context/app/static/js/components/search/Results/style.ts b/context/app/static/js/components/search/Results/style.ts index 9bea8467ba..baecfb9497 100644 --- a/context/app/static/js/components/search/Results/style.ts +++ b/context/app/static/js/components/search/Results/style.ts @@ -3,6 +3,10 @@ import Table from '@mui/material/Table'; import TableRow from '@mui/material/TableRow'; import TableBody from '@mui/material/TableBody'; import TableCell from '@mui/material/TableCell'; +import ArrowUpward from '@mui/icons-material/ArrowUpwardRounded'; +import ArrowDownward from '@mui/icons-material/ArrowDownwardRounded'; + +import { HeaderCell } from 'js/shared-styles/tables'; const StyledTable = styled(Table)(({ theme }) => ({ backgroundColor: theme.palette.white.main, @@ -56,4 +60,36 @@ const StyledTableCell = styled(TableCell)({ }, }); -export { StyledTable, StyledTableRow, StyledTableBody, StyledTableCell }; +const sharedArrowStyles = { + verticalAlign: 'text-top', + fontSize: '1.1rem', +}; + +const ArrowUpOn = styled(ArrowUpward)({ + ...sharedArrowStyles, +}); + +const ArrowDownOn = styled(ArrowDownward)({ + ...sharedArrowStyles, +}); + +const ArrowDownOff = styled(ArrowDownward)({ + ...sharedArrowStyles, + opacity: '12%', +}); + +const StyledHeaderCell = styled(HeaderCell)({ + cursor: 'pointer', + whiteSpace: 'nowrap', +}); + +export { + StyledTable, + StyledTableRow, + StyledTableBody, + StyledTableCell, + ArrowUpOn, + ArrowDownOn, + ArrowDownOff, + StyledHeaderCell, +}; diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index 585aaf62f4..c35638c583 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -20,9 +20,20 @@ export interface SearchStoreState { swrConfig: SWRConfiguration; } +export interface SearchStoreActions { + setSortField: (sortField: SortField) => void; +} + +export interface SearchStore extends SearchStoreState, SearchStoreActions {} + export const createStore = ({ initialState }: { initialState: SearchStoreState }) => - createStoreImmer(() => ({ + createStoreImmer((set) => ({ ...initialState, + setSortField: (sortField) => { + set((state) => { + state.sortField = sortField; + }); + }, })); const [SearchStoreProvider, useSearchStore, SearchStoreContext] = createStoreContext(createStore, 'Search'); From cf5b7e86afec1f1c73adfd7f025d6a7616b71531 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 6 Jun 2024 11:14:18 -0400 Subject: [PATCH 08/73] Get esfield from mapping --- .../static/js/components/search/Search.tsx | 3 +- .../js/components/search/buildTypesMap.ts | 39 + .../js/components/search/es-mapping.json | 19314 ++++++++++++++++ 3 files changed, 19355 insertions(+), 1 deletion(-) create mode 100644 context/app/static/js/components/search/buildTypesMap.ts create mode 100644 context/app/static/js/components/search/es-mapping.json diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 7335c3a80f..f3460d3830 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -9,6 +9,7 @@ import { useAppContext } from 'js/components/Contexts'; import { SearchStoreProvider, useSearchStore, SearchStoreState } from './store'; import { HitDoc } from './types'; import { ResultsTable } from './Results'; +import { getPortalESField } from './buildTypesMap'; function useAuthHeader() { const { groupsToken } = useAppContext(); @@ -42,7 +43,7 @@ function buildQuery({ terms, size, sourceFields, sortField }: Omit query.postFilter(esb.termsQuery(field, [...values]))); diff --git a/context/app/static/js/components/search/buildTypesMap.ts b/context/app/static/js/components/search/buildTypesMap.ts new file mode 100644 index 0000000000..ad39b194ee --- /dev/null +++ b/context/app/static/js/components/search/buildTypesMap.ts @@ -0,0 +1,39 @@ +import { get } from 'js/helpers/nodash'; +import esMapping from './es-mapping.json'; + +interface FieldType { + type?: string; +} + +type AdditionalFields = Record; + +interface FieldMapping extends FieldType { + fields?: AdditionalFields; + copy_to?: string[]; + properties?: Record; + [k: string]: unknown; +} + +interface Mappings { + mappings: { + properties: Record; + [k: string]: unknown; + }; +} + +export function getESField({ mappings, field }: { mappings: Mappings; field: string }) { + const fieldPaths = field.split('.'); + const mappingsPath = ['mappings', ...fieldPaths].join('.properties.'); + + const { fields }: FieldMapping = get(mappings, mappingsPath); + + if (fields?.keyword && fields?.keyword?.type === 'keyword') { + return `${field}.keyword`; + } + + return field; +} + +export function getPortalESField(field: string) { + return getESField({ mappings: esMapping, field }); +} diff --git a/context/app/static/js/components/search/es-mapping.json b/context/app/static/js/components/search/es-mapping.json new file mode 100644 index 0000000000..8c076db040 --- /dev/null +++ b/context/app/static/js/components/search/es-mapping.json @@ -0,0 +1,19314 @@ +{ + "mappings": { + "dynamic_templates": [ + { + "transposition_kit_number": { + "path_match": "*.transposition_kit_number", + "mapping": { + "type": "keyword" + } + } + }, + { + "library_adapter_sequence": { + "path_match": "*.library_adapter_sequence", + "mapping": { + "type": "keyword" + } + } + }, + { + "umi_offset": { + "path_match": "*.umi_offset", + "mapping": { + "type": "keyword" + } + } + }, + { + "umi_size": { + "path_match": "*.umi_size", + "mapping": { + "type": "keyword" + } + } + }, + { + "slide_id": { + "path_match": "*.slide_id", + "mapping": { + "type": "keyword" + } + } + }, + { + "sequencing_read_format": { + "path_match": "*.sequencing_read_format", + "mapping": { + "type": "keyword" + } + } + }, + { + "sample_indexing_set": { + "path_match": "*.sample_indexing_set", + "mapping": { + "type": "keyword" + } + } + }, + { + "barcode_offset": { + "path_match": "*.barcode_offset", + "mapping": { + "type": "keyword" + } + } + }, + { + "barcode_size": { + "path_match": "*.barcode_size", + "mapping": { + "type": "keyword" + } + } + }, + { + "map_every_string": { + "match_mapping_type": "string", + "mapping": { + "copy_to": "all_text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + { + "map_every_numeric": { + "match_mapping_type": "long", + "mapping": { + "type": "float" + } + } + } + ], + "date_detection": false, + "properties": { + "all_text": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "analyte_class": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "anatomy_0": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "anatomy_1": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "anatomy_2": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "anatomy_3": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "anatomy_4": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "anatomy_5": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ancestor_counts": { + "properties": { + "entity_type": { + "properties": { + "Dataset": { + "type": "float" + }, + "Donor": { + "type": "float" + }, + "Publication": { + "type": "float" + }, + "Sample": { + "type": "float" + } + } + } + } + }, + "ancestor_ids": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ancestors": { + "properties": { + "associated_collection": { + "properties": { + "contacts": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "creators": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "doi_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "last_modified_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "registered_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "title": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "contacts": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_principal_investigator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initia": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "contains_human_genetic_sequences": { + "type": "boolean" + }, + "contributors": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_principal_investigator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "creation_action": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_types": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dbgap_sra_experiment_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dbgap_study_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "doi_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "edam_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_data_product": { + "type": "boolean" + }, + "is_qa_qc": { + "type": "boolean" + }, + "rel_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "size": { + "type": "float" + }, + "type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "image_file_metadata": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "issue": { + "type": "float" + }, + "lab_dataset_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_donor_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_tissue_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "label": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "mapped_data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_last_modified_timestamp": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_metadata": { + "properties": { + "abo_blood_group_system": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "age_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "age_value": { + "type": "float" + }, + "apolipoprotein_e_phenotype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "blood_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "body_mass_index_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "body_mass_index_value": { + "type": "float" + }, + "cause_of_death": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "death_event": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ethnicity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "height_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "height_value": { + "type": "float" + }, + "kidney_donor_profile_index_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "kidney_donor_profile_index_value": { + "type": "float" + }, + "mechanism_of_injury": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "medical_history": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "other_anatomic_concept": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathology_note": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "race": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rh_blood_group": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rh_factor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sex": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "social_history": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "weight_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "weight_value": { + "type": "float" + } + } + }, + "mapped_organ": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_sample_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata": { + "properties": { + "PPID": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "Preservation_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "Preservation_media": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dag_provenance_list": { + "properties": { + "hash": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "origin": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "extra_metadata": { + "properties": { + "collectiontype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "edam_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_data_product": { + "type": "boolean" + }, + "is_qa_qc": { + "type": "boolean" + }, + "rel_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "size": { + "type": "float" + }, + "type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "health_status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "living_donor_data": { + "properties": { + "code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "concept_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "graph_version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept_preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "numeric_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "metadata": { + "properties": { + "ablation_distance_between_shots_x_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_x_value": { + "type": "float" + }, + "ablation_distance_between_shots_y_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_y_value": { + "type": "float" + }, + "ablation_frequency_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_frequency_value": { + "type": "float" + }, + "acquisition_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "amount_of_input_analyte_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "amount_of_input_analyte_value": { + "type": "float" + }, + "analysis_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "analyte_class": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "antibodies_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_value": { + "type": "float" + }, + "assay_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_input_entity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "barcode_offset": { + "type": "keyword" + }, + "barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "barcode_size": { + "type": "keyword" + }, + "bead_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_atac_cell_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_isolation_quality_metric_value": { + "type": "float" + }, + "bulk_rna_yield_units_per_tissue_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_yield_value": { + "type": "float" + }, + "bulk_transposition_input_number_nuclei": { + "type": "float" + }, + "capture_area_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "capture_batch_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "contributors_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_precision_bytes": { + "type": "float" + }, + "dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "desi_solvent": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "desi_solvent_flow_rate": { + "type": "float" + }, + "desi_solvent_flow_rate_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dual_count_start": { + "type": "float" + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "execution_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "expected_cell_count": { + "type": "float" + }, + "expected_entity_capture_count": { + "type": "float" + }, + "increment_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "increment_z_value": { + "type": "float" + }, + "intended_tile_overlap_percentage": { + "type": "float" + }, + "ion_mobility": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_batch_staining_done": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_image_preprocessing_required": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_staining_automated": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_targeted": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_technical_replicate": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_adapter_sequence": { + "type": "keyword" + }, + "library_average_fragment_size": { + "type": "float" + }, + "library_concentration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_concentration_value": { + "type": "float" + }, + "library_construction_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_creation_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield": { + "type": "float" + }, + "library_final_yield_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield_value": { + "type": "float" + }, + "library_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_input_amount_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_input_amount_value": { + "type": "float" + }, + "library_layout": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_output_amount_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_output_amount_value": { + "type": "float" + }, + "library_pcr_cycles": { + "type": "float" + }, + "library_pcr_cycles_for_sample_index": { + "type": "float" + }, + "library_preparation_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_area_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_area_value": { + "type": "float" + }, + "mass_analysis_polarity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mass_resolving_power": { + "type": "float" + }, + "mass_to_charge_range_high_value": { + "type": "float" + }, + "mass_to_charge_range_low_value": { + "type": "float" + }, + "mass_to_charge_resolving_power": { + "type": "float" + }, + "matrix_deposition_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_value": { + "type": "float" + }, + "max_y_height_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_y_height_value": { + "type": "float" + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_ionization_technique": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_scan_mode": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mz_range_high_value": { + "type": "float" + }, + "mz_range_low_value": { + "type": "float" + }, + "mz_resolving_power": { + "type": "float" + }, + "number_of_antibodies": { + "type": "float" + }, + "number_of_barcode_probes": { + "type": "float" + }, + "number_of_barcode_regions_per_barcode_probe": { + "type": "float" + }, + "number_of_channels": { + "type": "float" + }, + "number_of_cycles": { + "type": "float" + }, + "number_of_imaging_rounds": { + "type": "float" + }, + "number_of_input_cells_or_nuclei": { + "type": "float" + }, + "number_of_iterations_of_cdna_amplification": { + "type": "float" + }, + "number_of_pcr_cycles_for_indexing": { + "type": "float" + }, + "number_of_pre_amplification_pcr_cycles": { + "type": "float" + }, + "number_of_pseudocolors_per_channel": { + "type": "float" + }, + "number_of_readout_probes_per_channel": { + "type": "float" + }, + "number_of_sections": { + "type": "float" + }, + "number_of_spots": { + "type": "float" + }, + "operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "operator_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "overall_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "parent_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "permeabilization_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "permeabilization_time_value": { + "type": "float" + }, + "pi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pi_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_value": { + "type": "float" + }, + "pixel_size_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_x_value": { + "type": "float" + }, + "pixel_size_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_y_value": { + "type": "float" + }, + "polarity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_maldi_matrix": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_matrix": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_value": { + "type": "float" + }, + "processing_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "puck_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_value": { + "type": "float" + }, + "reagent_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_value": { + "type": "float" + }, + "resolution_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_y_value": { + "type": "float" + }, + "resolution_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_z_value": { + "type": "float" + }, + "rnaseq_assay_input": { + "type": "float" + }, + "rnaseq_assay_input_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rnaseq_assay_input_value": { + "type": "float" + }, + "rnaseq_assay_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_id": { + "type": "float" + }, + "sample_indexing_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_indexing_set": { + "type": "keyword" + }, + "sample_quality_metric": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_cell_number": { + "type": "float" + }, + "sc_isolation_enrichment": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_entity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_quality_metric": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_tissue_dissociation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "section_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "segment_data_format": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_batch_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_phix_percent": { + "type": "float" + }, + "sequencing_read_format": { + "type": "keyword" + }, + "sequencing_read_percent_q30": { + "type": "float" + }, + "sequencing_reagent_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "signal_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "slide_id": { + "type": "keyword" + }, + "source_project": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_value": { + "type": "float" + }, + "spot_size_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_size_value": { + "type": "float" + }, + "spot_spacing_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_spacing_value": { + "type": "float" + }, + "stain": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "stain_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "stain_technique": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "step_z_value": { + "type": "float" + }, + "tile_configuration": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tiled_image_columns": { + "type": "float" + }, + "tiled_image_count": { + "type": "float" + }, + "time_since_acquisition_instrument_calibration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "time_since_acquisition_instrument_calibration_value": { + "type": "float" + }, + "transposition_input": { + "type": "float" + }, + "transposition_kit_number": { + "type": "keyword" + }, + "transposition_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transposition_transposase_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "umi_offset": { + "type": "keyword" + }, + "umi_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "umi_size": { + "type": "keyword" + } + } + }, + "organ_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_donor_data": { + "properties": { + "code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "concept_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "graph_version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept_preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "numeric_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "pathologist_report": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "perfusion_solution": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "procedure_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_preservation_temperature": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_quality_criteria": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "thumbnail_file_abs_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "vital_state": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "next_revision_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "omap_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_other": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pages_or_article_num": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "portal_metadata_upload_files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "protocol_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "provider_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_status": { + "type": "boolean" + }, + "publication_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_venue": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "published_timestamp": { + "type": "float" + }, + "registered_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rui_location": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "submission_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "thumbnail_file": { + "properties": { + "file_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filename": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "title": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "visit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "assay_display_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_modality": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "associated_collection": { + "properties": { + "contacts": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "creators": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "doi_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "last_modified_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "registered_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "title": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "contacts": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_principal_investigator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initia": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orc_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "contains_human_genetic_sequences": { + "type": "boolean" + }, + "contributors": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_principal_investigator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orc_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orchid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "creation_action": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "creators": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_types": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "datasets": { + "properties": { + "contacts": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initia": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orc_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "contains_human_genetic_sequences": { + "type": "boolean" + }, + "contributors": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orc_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orchid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "creation_action": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_types": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dbgap_sra_experiment_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dbgap_study_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_subtype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "doi_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "index_version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_dataset_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "metadata": { + "properties": { + "dag_provenance_list": { + "properties": { + "hash": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "origin": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "extra_metadata": { + "properties": { + "collectiontype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "metadata": { + "properties": { + "_from_metadatatsv": { + "type": "boolean" + }, + "ablation_distance_between_shots_x_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_x_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_y_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_y_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_frequency_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_frequency_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "analyte_class": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "antibodies_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "collectiontype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "contributors_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_collection_mode": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_precision_bytes": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "donor_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dual_count_start": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "execution_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "expected_cell_count": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "increment_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "increment_z_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_targeted": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_technical_replicate": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "labeling": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_column_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_column_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_flow_rate_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_flow_rate_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_gradient": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_id_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_id_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_length_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_length_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_mobile_phase_a": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_mobile_phase_b": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_resin": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_temp_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_temp_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_adapter_sequence": { + "type": "keyword" + }, + "library_average_fragment_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_construction_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_layout": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_pcr_cycles": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_pcr_cycles_for_sample_index": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mass_resolving_power": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_y_height_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_y_height_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_scan_mode": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mz_range_high_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mz_range_low_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mz_resolving_power": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "number_of_antibodies": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "number_of_channels": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "number_of_cycles": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "number_of_imaging_rounds": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "number_of_sections": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "operator_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "overall_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pi_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_x_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_y_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "polarity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_maldi_matrix": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_search": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "puck_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "reagent_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_y_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_z_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rnaseq_assay_input": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rnaseq_assay_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_cell_number": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_enrichment": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_entity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_quality_metric": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_tissue_dissociation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "section_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "segment_data_format": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_phix_percent": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_read_format": { + "type": "keyword" + }, + "sequencing_read_percent_q30": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_reagent_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "signal_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "stain": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "step_z_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tissue_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transposition_input": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transposition_kit_number": { + "type": "keyword" + }, + "transposition_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transposition_transposase_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + } + } + }, + "next_revision_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "provider_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "published_timestamp": { + "type": "float" + }, + "registered_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "thumbnail_file": { + "properties": { + "file_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filename": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "title": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "dbgap_sra_experiment_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dbgap_study_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "descendant_counts": { + "properties": { + "dataset_type": { + "properties": { + "10X Multiome": { + "type": "float" + }, + "10X Multiome [Salmon + ArchR + Muon]": { + "type": "float" + }, + "2D Imaging Mass Cytometry": { + "type": "float" + }, + "2D Imaging Mass Cytometry [Image Pyramid]": { + "type": "float" + }, + "3D Imaging Mass Cytometry": { + "type": "float" + }, + "3D Imaging Mass Cytometry [Image Pyramid]": { + "type": "float" + }, + "ATACseq": { + "type": "float" + }, + "ATACseq [BWA + MACS2]": { + "type": "float" + }, + "ATACseq [Lab Processed]": { + "type": "float" + }, + "ATACseq [Salmon]": { + "type": "float" + }, + "ATACseq [SnapATAC]": { + "type": "float" + }, + "Auto-fluorescence": { + "type": "float" + }, + "Auto-fluorescence [Image Pyramid]": { + "type": "float" + }, + "CODEX": { + "type": "float" + }, + "CODEX [Cytokit + SPRM]": { + "type": "float" + }, + "Cell DIVE": { + "type": "float" + }, + "Cell DIVE [DeepCell + SPRM]": { + "type": "float" + }, + "DARTfish": { + "type": "float" + }, + "DESI": { + "type": "float" + }, + "DESI [Image Pyramid]": { + "type": "float" + }, + "Histology": { + "type": "float" + }, + "Histology [DeepCell + SPRM]": { + "type": "float" + }, + "Histology [Image Pyramid]": { + "type": "float" + }, + "Histology [Kaggle-1 Glomerulus Segmentation]": { + "type": "float" + }, + "LC-MS": { + "type": "float" + }, + "Light Sheet": { + "type": "float" + }, + "Light Sheet [Image Pyramid]": { + "type": "float" + }, + "MALDI": { + "type": "float" + }, + "MALDI [Image Pyramid]": { + "type": "float" + }, + "MIBI": { + "type": "float" + }, + "MIBI [DeepCell + SPRM]": { + "type": "float" + }, + "MUSIC": { + "type": "float" + }, + "Publication": { + "type": "float" + }, + "Publication [ancillary]": { + "type": "float" + }, + "RNAseq": { + "type": "float" + }, + "RNAseq [Lab Processed]": { + "type": "float" + }, + "RNAseq [Salmon]": { + "type": "float" + }, + "SIMS": { + "type": "float" + }, + "Slide-seq": { + "type": "float" + }, + "Slide-seq [Salmon]": { + "type": "float" + }, + "UNKNOWN": { + "type": "float" + }, + "Visium (no probes)": { + "type": "float" + }, + "Visium (no probes) [Salmon + Scanpy]": { + "type": "float" + }, + "WGS": { + "type": "float" + }, + "seqFish": { + "type": "float" + }, + "seqFish [Image Pyramid]": { + "type": "float" + }, + "seqFish [Lab Processed]": { + "type": "float" + } + } + }, + "entity_type": { + "properties": { + "Dataset": { + "type": "float" + }, + "Publication": { + "type": "float" + }, + "Sample": { + "type": "float" + } + } + } + } + }, + "descendant_ids": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "descendants": { + "properties": { + "associated_collection": { + "properties": { + "contacts": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "creators": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "doi_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "last_modified_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "registered_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "title": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "contacts": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_principal_investigator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initia": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orc_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "contains_human_genetic_sequences": { + "type": "boolean" + }, + "contributors": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_principal_investigator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orc_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orchid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "creation_action": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_types": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dbgap_sra_experiment_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dbgap_study_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "doi_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "edam_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_data_product": { + "type": "boolean" + }, + "is_qa_qc": { + "type": "boolean" + }, + "rel_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "size": { + "type": "float" + }, + "type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "image_file_metadata": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "issue": { + "type": "float" + }, + "lab_dataset_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_tissue_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "metadata": { + "properties": { + "PPID": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "Preservation_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "Preservation_media": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dag_provenance_list": { + "properties": { + "hash": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "origin": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "extra_metadata": { + "properties": { + "collectiontype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "file_row": { + "type": "float" + }, + "files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "edam_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_data_product": { + "type": "boolean" + }, + "is_qa_qc": { + "type": "boolean" + }, + "rel_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "size": { + "type": "float" + }, + "type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "files_info_alt_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "health_status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "histological_report": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata": { + "properties": { + "ablation_distance_between_shots_x_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_x_value": { + "type": "float" + }, + "ablation_distance_between_shots_y_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_y_value": { + "type": "float" + }, + "ablation_frequency_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_frequency_value": { + "type": "float" + }, + "acquisition_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "amount_of_input_analyte_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "amount_of_input_analyte_value": { + "type": "float" + }, + "analysis_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "analyte_class": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "antibodies_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_value": { + "type": "float" + }, + "assay_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_input_entity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "barcode_offset": { + "type": "keyword" + }, + "barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "barcode_size": { + "type": "keyword" + }, + "bead_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_atac_cell_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_isolation_quality_metric_value": { + "type": "float" + }, + "bulk_rna_yield_units_per_tissue_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_yield_value": { + "type": "float" + }, + "bulk_transposition_input_number_nuclei": { + "type": "float" + }, + "capture_area_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "capture_batch_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "contributors_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_collection_mode": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_precision_bytes": { + "type": "float" + }, + "dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "desi_solvent": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "desi_solvent_flow_rate": { + "type": "float" + }, + "desi_solvent_flow_rate_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dms": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dna_assay_input_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dna_assay_input_value": { + "type": "float" + }, + "dual_count_start": { + "type": "float" + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "execution_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "expected_cell_count": { + "type": "float" + }, + "expected_entity_capture_count": { + "type": "float" + }, + "gdna_fragmentation_quality_assurance": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "increment_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "increment_z_value": { + "type": "float" + }, + "intended_tile_overlap_percentage": { + "type": "float" + }, + "ion_mobility": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_batch_staining_done": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_image_preprocessing_required": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_staining_automated": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_targeted": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_technical_replicate": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "labeling": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_column_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_column_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_flow_rate_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_flow_rate_value": { + "type": "float" + }, + "lc_gradient": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_id_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_id_value": { + "type": "float" + }, + "lc_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_length_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_length_value": { + "type": "float" + }, + "lc_mobile_phase_a": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_mobile_phase_b": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_resin": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_temp_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_temp_value": { + "type": "float" + }, + "library_adapter_sequence": { + "type": "keyword" + }, + "library_average_fragment_size": { + "type": "float" + }, + "library_concentration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_concentration_value": { + "type": "float" + }, + "library_construction_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_construction_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_creation_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield": { + "type": "float" + }, + "library_final_yield_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield_value": { + "type": "float" + }, + "library_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_input_amount_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_input_amount_value": { + "type": "float" + }, + "library_layout": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_output_amount_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_output_amount_value": { + "type": "float" + }, + "library_pcr_cycles": { + "type": "float" + }, + "library_pcr_cycles_for_sample_index": { + "type": "float" + }, + "library_preparation_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_area_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_area_value": { + "type": "float" + }, + "mass_analysis_polarity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mass_resolving_power": { + "type": "float" + }, + "mass_to_charge_range_high_value": { + "type": "float" + }, + "mass_to_charge_range_low_value": { + "type": "float" + }, + "mass_to_charge_resolving_power": { + "type": "float" + }, + "matrix_deposition_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_value": { + "type": "float" + }, + "max_y_height_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_y_height_value": { + "type": "float" + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_ionization_technique": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_scan_mode": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mz_range_high_value": { + "type": "float" + }, + "mz_range_low_value": { + "type": "float" + }, + "mz_resolving_power": { + "type": "float" + }, + "non_global_files": { + "type": "float" + }, + "number_of_antibodies": { + "type": "float" + }, + "number_of_barcode_probes": { + "type": "float" + }, + "number_of_barcode_regions_per_barcode_probe": { + "type": "float" + }, + "number_of_channels": { + "type": "float" + }, + "number_of_cycles": { + "type": "float" + }, + "number_of_imaging_rounds": { + "type": "float" + }, + "number_of_input_cells_or_nuclei": { + "type": "float" + }, + "number_of_iterations_of_cdna_amplification": { + "type": "float" + }, + "number_of_pcr_cycles_for_indexing": { + "type": "float" + }, + "number_of_pre_amplification_pcr_cycles": { + "type": "float" + }, + "number_of_pseudocolors_per_channel": { + "type": "float" + }, + "number_of_readout_probes_per_channel": { + "type": "float" + }, + "number_of_sections": { + "type": "float" + }, + "number_of_spots": { + "type": "float" + }, + "operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "operator_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "overall_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "parent_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "permeabilization_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "permeabilization_time_value": { + "type": "float" + }, + "pi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pi_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_value": { + "type": "float" + }, + "pixel_size_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_x_value": { + "type": "float" + }, + "pixel_size_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_y_value": { + "type": "float" + }, + "polarity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_maldi_matrix": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_matrix": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_value": { + "type": "float" + }, + "processing_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_search": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "puck_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_value": { + "type": "float" + }, + "reagent_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_value": { + "type": "float" + }, + "resolution_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_y_value": { + "type": "float" + }, + "resolution_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_z_value": { + "type": "float" + }, + "rnaseq_assay_input": { + "type": "float" + }, + "rnaseq_assay_input_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rnaseq_assay_input_value": { + "type": "float" + }, + "rnaseq_assay_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_id": { + "type": "float" + }, + "sample_indexing_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_indexing_set": { + "type": "keyword" + }, + "sample_quality_metric": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_cell_number": { + "type": "float" + }, + "sc_isolation_enrichment": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_entity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_quality_metric": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_tissue_dissociation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "scan_direction": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "section_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "segment_data_format": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_batch_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_phix_percent": { + "type": "float" + }, + "sequencing_read_format": { + "type": "keyword" + }, + "sequencing_read_percent_q30": { + "type": "float" + }, + "sequencing_reagent_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "signal_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "slide_id": { + "type": "keyword" + }, + "source_project": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_value": { + "type": "float" + }, + "spatial_sampling_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spatial_target": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spatial_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_size_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_size_value": { + "type": "float" + }, + "spot_spacing_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_spacing_value": { + "type": "float" + }, + "stain": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "stain_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "stain_technique": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "step_z_value": { + "type": "float" + }, + "tile_configuration": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tiled_image_columns": { + "type": "float" + }, + "tiled_image_count": { + "type": "float" + }, + "time_since_acquisition_instrument_calibration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "time_since_acquisition_instrument_calibration_value": { + "type": "float" + }, + "transposition_input": { + "type": "float" + }, + "transposition_kit_number": { + "type": "keyword" + }, + "transposition_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transposition_reagent_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transposition_transposase_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "umi_offset": { + "type": "keyword" + }, + "umi_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "umi_size": { + "type": "keyword" + } + } + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "notes": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathologist_report": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathology_distance_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathology_distance_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "perfusion_solution": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_medium": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "procedure_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "quality_criteria": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_preservation_temperature": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_quality_criteria": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "storage_medium": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "storage_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "thumbnail_file_abs_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tissue_weight_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tissue_weight_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "vital_state": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "volume_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "volume_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "next_revision_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "omap_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_other": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pages_or_article_num": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "portal_metadata_upload_files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "previous_revision_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "protocol_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "provider_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_status": { + "type": "boolean" + }, + "publication_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_venue": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "published_timestamp": { + "type": "float" + }, + "registered_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rui_location": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "submission_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "thumbnail_file": { + "properties": { + "file_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filename": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "title": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "visit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_subtype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "doi_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "donor": { + "properties": { + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_donor_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "label": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "mapped_data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_last_modified_timestamp": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_metadata": { + "properties": { + "abo_blood_group_system": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "age_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "age_value": { + "type": "float" + }, + "apolipoprotein_e_phenotype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "blood_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "body_mass_index_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "body_mass_index_value": { + "type": "float" + }, + "cause_of_death": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "death_event": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ethnicity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "height_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "height_value": { + "type": "float" + }, + "kidney_donor_profile_index_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "kidney_donor_profile_index_value": { + "type": "float" + }, + "mechanism_of_injury": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "medical_history": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "other_anatomic_concept": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathology_note": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "race": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rh_blood_group": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rh_factor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sex": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "social_history": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "weight_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "weight_value": { + "type": "float" + } + } + }, + "metadata": { + "properties": { + "living_donor_data": { + "properties": { + "code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "concept_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "graph_version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept_preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "numeric_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "organ_donor_data": { + "properties": { + "code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "concept_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "graph_version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept_preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "numeric_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + } + } + }, + "portal_metadata_upload_files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "protocol_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "submission_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "edam_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_data_product": { + "type": "boolean" + }, + "is_qa_qc": { + "type": "boolean" + }, + "mapped_description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rel_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "size": { + "type": "float" + }, + "type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "image_file_metadata": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "immediate_ancestors": { + "properties": { + "associated_collection": { + "properties": { + "contacts": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "creators": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "doi_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "last_modified_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "registered_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "title": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "contacts": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_principal_investigator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initia": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "contains_human_genetic_sequences": { + "type": "boolean" + }, + "contributors": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_principal_investigator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "creation_action": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_types": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dbgap_sra_experiment_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dbgap_study_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "doi_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "edam_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_data_product": { + "type": "boolean" + }, + "is_qa_qc": { + "type": "boolean" + }, + "rel_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "size": { + "type": "float" + }, + "type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "image_file_metadata": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "issue": { + "type": "float" + }, + "lab_dataset_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_donor_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_tissue_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "label": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "metadata": { + "properties": { + "PPID": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "Preservation_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "Preservation_media": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dag_provenance_list": { + "properties": { + "hash": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "origin": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "extra_metadata": { + "properties": { + "collectiontype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "edam_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_data_product": { + "type": "boolean" + }, + "is_qa_qc": { + "type": "boolean" + }, + "rel_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "size": { + "type": "float" + }, + "type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "health_status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "living_donor_data": { + "properties": { + "code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "concept_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "graph_version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept_preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "numeric_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "metadata": { + "properties": { + "ablation_distance_between_shots_x_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_x_value": { + "type": "float" + }, + "ablation_distance_between_shots_y_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_y_value": { + "type": "float" + }, + "ablation_frequency_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_frequency_value": { + "type": "float" + }, + "acquisition_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "amount_of_input_analyte_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "amount_of_input_analyte_value": { + "type": "float" + }, + "analysis_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "analyte_class": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "antibodies_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_value": { + "type": "float" + }, + "assay_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_input_entity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "barcode_offset": { + "type": "keyword" + }, + "barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "barcode_size": { + "type": "keyword" + }, + "bead_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_atac_cell_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_isolation_quality_metric_value": { + "type": "float" + }, + "bulk_rna_yield_units_per_tissue_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_yield_value": { + "type": "float" + }, + "bulk_transposition_input_number_nuclei": { + "type": "float" + }, + "capture_area_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "capture_batch_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "contributors_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_precision_bytes": { + "type": "float" + }, + "dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "desi_solvent": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "desi_solvent_flow_rate": { + "type": "float" + }, + "desi_solvent_flow_rate_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dual_count_start": { + "type": "float" + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "execution_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "expected_cell_count": { + "type": "float" + }, + "expected_entity_capture_count": { + "type": "float" + }, + "increment_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "increment_z_value": { + "type": "float" + }, + "intended_tile_overlap_percentage": { + "type": "float" + }, + "ion_mobility": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_batch_staining_done": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_image_preprocessing_required": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_staining_automated": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_targeted": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_technical_replicate": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_adapter_sequence": { + "type": "keyword" + }, + "library_average_fragment_size": { + "type": "float" + }, + "library_concentration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_concentration_value": { + "type": "float" + }, + "library_construction_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_creation_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield": { + "type": "float" + }, + "library_final_yield_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield_value": { + "type": "float" + }, + "library_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_input_amount_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_input_amount_value": { + "type": "float" + }, + "library_layout": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_output_amount_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_output_amount_value": { + "type": "float" + }, + "library_pcr_cycles": { + "type": "float" + }, + "library_pcr_cycles_for_sample_index": { + "type": "float" + }, + "library_preparation_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_area_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_area_value": { + "type": "float" + }, + "mass_analysis_polarity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mass_resolving_power": { + "type": "float" + }, + "mass_to_charge_range_high_value": { + "type": "float" + }, + "mass_to_charge_range_low_value": { + "type": "float" + }, + "mass_to_charge_resolving_power": { + "type": "float" + }, + "matrix_deposition_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_value": { + "type": "float" + }, + "max_y_height_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_y_height_value": { + "type": "float" + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_ionization_technique": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_scan_mode": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mz_range_high_value": { + "type": "float" + }, + "mz_range_low_value": { + "type": "float" + }, + "mz_resolving_power": { + "type": "float" + }, + "number_of_antibodies": { + "type": "float" + }, + "number_of_barcode_probes": { + "type": "float" + }, + "number_of_barcode_regions_per_barcode_probe": { + "type": "float" + }, + "number_of_channels": { + "type": "float" + }, + "number_of_cycles": { + "type": "float" + }, + "number_of_imaging_rounds": { + "type": "float" + }, + "number_of_input_cells_or_nuclei": { + "type": "float" + }, + "number_of_iterations_of_cdna_amplification": { + "type": "float" + }, + "number_of_pcr_cycles_for_indexing": { + "type": "float" + }, + "number_of_pre_amplification_pcr_cycles": { + "type": "float" + }, + "number_of_pseudocolors_per_channel": { + "type": "float" + }, + "number_of_readout_probes_per_channel": { + "type": "float" + }, + "number_of_sections": { + "type": "float" + }, + "number_of_spots": { + "type": "float" + }, + "operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "operator_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "overall_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "parent_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "permeabilization_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "permeabilization_time_value": { + "type": "float" + }, + "pi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pi_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_value": { + "type": "float" + }, + "pixel_size_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_x_value": { + "type": "float" + }, + "pixel_size_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_y_value": { + "type": "float" + }, + "polarity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_maldi_matrix": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_matrix": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_value": { + "type": "float" + }, + "processing_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "puck_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_value": { + "type": "float" + }, + "reagent_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_value": { + "type": "float" + }, + "resolution_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_y_value": { + "type": "float" + }, + "resolution_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_z_value": { + "type": "float" + }, + "rnaseq_assay_input": { + "type": "float" + }, + "rnaseq_assay_input_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rnaseq_assay_input_value": { + "type": "float" + }, + "rnaseq_assay_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_id": { + "type": "float" + }, + "sample_indexing_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_indexing_set": { + "type": "keyword" + }, + "sample_quality_metric": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_cell_number": { + "type": "float" + }, + "sc_isolation_enrichment": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_entity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_quality_metric": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_tissue_dissociation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "section_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "segment_data_format": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_batch_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_phix_percent": { + "type": "float" + }, + "sequencing_read_format": { + "type": "keyword" + }, + "sequencing_read_percent_q30": { + "type": "float" + }, + "sequencing_reagent_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "signal_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "slide_id": { + "type": "keyword" + }, + "source_project": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_value": { + "type": "float" + }, + "spot_size_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_size_value": { + "type": "float" + }, + "spot_spacing_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_spacing_value": { + "type": "float" + }, + "stain": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "stain_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "stain_technique": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "step_z_value": { + "type": "float" + }, + "tile_configuration": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tiled_image_columns": { + "type": "float" + }, + "tiled_image_count": { + "type": "float" + }, + "time_since_acquisition_instrument_calibration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "time_since_acquisition_instrument_calibration_value": { + "type": "float" + }, + "transposition_input": { + "type": "float" + }, + "transposition_kit_number": { + "type": "keyword" + }, + "transposition_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transposition_transposase_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "umi_offset": { + "type": "keyword" + }, + "umi_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "umi_size": { + "type": "keyword" + } + } + }, + "organ_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_donor_data": { + "properties": { + "code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "concept_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "graph_version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept_preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "numeric_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "pathologist_report": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "perfusion_solution": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "procedure_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_preservation_temperature": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_quality_criteria": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "thumbnail_file_abs_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "vital_state": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "next_revision_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "omap_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_other": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pages_or_article_num": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "portal_metadata_upload_files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "protocol_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "provider_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_status": { + "type": "boolean" + }, + "publication_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_venue": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "published_timestamp": { + "type": "float" + }, + "registered_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rui_location": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "submission_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "thumbnail_file": { + "properties": { + "file_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filename": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "title": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "visit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "immediate_descendants": { + "properties": { + "associated_collection": { + "properties": { + "contacts": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "creators": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "doi_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "last_modified_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "registered_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "title": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "contacts": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_principal_investigator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initia": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orc_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "contains_human_genetic_sequences": { + "type": "boolean" + }, + "contributors": { + "properties": { + "affiliation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "display_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "first_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_contact": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_principal_investigator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "middle_name_or_initial": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orc_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orchid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "orcid_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "creation_action": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_types": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dbgap_sra_experiment_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dbgap_study_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "doi_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "edam_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_data_product": { + "type": "boolean" + }, + "is_qa_qc": { + "type": "boolean" + }, + "rel_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "size": { + "type": "float" + }, + "type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "image_file_metadata": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "issue": { + "type": "float" + }, + "lab_dataset_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_tissue_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "metadata": { + "properties": { + "PPID": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "Preservation_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "Preservation_media": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dag_provenance_list": { + "properties": { + "hash": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "origin": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "extra_metadata": { + "properties": { + "collectiontype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "file_row": { + "type": "float" + }, + "files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "edam_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_data_product": { + "type": "boolean" + }, + "is_qa_qc": { + "type": "boolean" + }, + "rel_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "size": { + "type": "float" + }, + "type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "files_info_alt_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "health_status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "histological_report": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata": { + "properties": { + "ablation_distance_between_shots_x_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_x_value": { + "type": "float" + }, + "ablation_distance_between_shots_y_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_y_value": { + "type": "float" + }, + "ablation_frequency_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_frequency_value": { + "type": "float" + }, + "acquisition_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "amount_of_input_analyte_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "amount_of_input_analyte_value": { + "type": "float" + }, + "analysis_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "analyte_class": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "antibodies_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_value": { + "type": "float" + }, + "assay_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_input_entity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "barcode_offset": { + "type": "keyword" + }, + "barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "barcode_size": { + "type": "keyword" + }, + "bead_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_atac_cell_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_isolation_quality_metric_value": { + "type": "float" + }, + "bulk_rna_yield_units_per_tissue_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_yield_value": { + "type": "float" + }, + "bulk_transposition_input_number_nuclei": { + "type": "float" + }, + "capture_area_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "capture_batch_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "contributors_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_collection_mode": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_precision_bytes": { + "type": "float" + }, + "dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "desi_solvent": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "desi_solvent_flow_rate": { + "type": "float" + }, + "desi_solvent_flow_rate_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dms": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dna_assay_input_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dna_assay_input_value": { + "type": "float" + }, + "dual_count_start": { + "type": "float" + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "execution_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "expected_cell_count": { + "type": "float" + }, + "expected_entity_capture_count": { + "type": "float" + }, + "gdna_fragmentation_quality_assurance": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "increment_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "increment_z_value": { + "type": "float" + }, + "intended_tile_overlap_percentage": { + "type": "float" + }, + "ion_mobility": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_batch_staining_done": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_image_preprocessing_required": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_staining_automated": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_targeted": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_technical_replicate": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "labeling": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_column_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_column_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_flow_rate_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_flow_rate_value": { + "type": "float" + }, + "lc_gradient": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_id_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_id_value": { + "type": "float" + }, + "lc_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_length_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_length_value": { + "type": "float" + }, + "lc_mobile_phase_a": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_mobile_phase_b": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_resin": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_temp_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_temp_value": { + "type": "float" + }, + "library_adapter_sequence": { + "type": "keyword" + }, + "library_average_fragment_size": { + "type": "float" + }, + "library_concentration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_concentration_value": { + "type": "float" + }, + "library_construction_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_construction_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_creation_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield": { + "type": "float" + }, + "library_final_yield_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield_value": { + "type": "float" + }, + "library_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_input_amount_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_input_amount_value": { + "type": "float" + }, + "library_layout": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_output_amount_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_output_amount_value": { + "type": "float" + }, + "library_pcr_cycles": { + "type": "float" + }, + "library_pcr_cycles_for_sample_index": { + "type": "float" + }, + "library_preparation_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_area_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_area_value": { + "type": "float" + }, + "mass_analysis_polarity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mass_resolving_power": { + "type": "float" + }, + "mass_to_charge_range_high_value": { + "type": "float" + }, + "mass_to_charge_range_low_value": { + "type": "float" + }, + "mass_to_charge_resolving_power": { + "type": "float" + }, + "matrix_deposition_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_value": { + "type": "float" + }, + "max_y_height_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_y_height_value": { + "type": "float" + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_ionization_technique": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_scan_mode": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mz_range_high_value": { + "type": "float" + }, + "mz_range_low_value": { + "type": "float" + }, + "mz_resolving_power": { + "type": "float" + }, + "non_global_files": { + "type": "float" + }, + "number_of_antibodies": { + "type": "float" + }, + "number_of_barcode_probes": { + "type": "float" + }, + "number_of_barcode_regions_per_barcode_probe": { + "type": "float" + }, + "number_of_channels": { + "type": "float" + }, + "number_of_cycles": { + "type": "float" + }, + "number_of_imaging_rounds": { + "type": "float" + }, + "number_of_input_cells_or_nuclei": { + "type": "float" + }, + "number_of_iterations_of_cdna_amplification": { + "type": "float" + }, + "number_of_pcr_cycles_for_indexing": { + "type": "float" + }, + "number_of_pre_amplification_pcr_cycles": { + "type": "float" + }, + "number_of_pseudocolors_per_channel": { + "type": "float" + }, + "number_of_readout_probes_per_channel": { + "type": "float" + }, + "number_of_sections": { + "type": "float" + }, + "number_of_spots": { + "type": "float" + }, + "operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "operator_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "overall_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "parent_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "permeabilization_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "permeabilization_time_value": { + "type": "float" + }, + "pi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pi_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_value": { + "type": "float" + }, + "pixel_size_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_x_value": { + "type": "float" + }, + "pixel_size_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_y_value": { + "type": "float" + }, + "polarity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_maldi_matrix": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_matrix": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_value": { + "type": "float" + }, + "processing_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_search": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "puck_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_value": { + "type": "float" + }, + "reagent_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_value": { + "type": "float" + }, + "resolution_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_y_value": { + "type": "float" + }, + "resolution_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_z_value": { + "type": "float" + }, + "rnaseq_assay_input": { + "type": "float" + }, + "rnaseq_assay_input_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rnaseq_assay_input_value": { + "type": "float" + }, + "rnaseq_assay_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_id": { + "type": "float" + }, + "sample_indexing_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_indexing_set": { + "type": "keyword" + }, + "sample_quality_metric": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_cell_number": { + "type": "float" + }, + "sc_isolation_enrichment": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_entity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_quality_metric": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_tissue_dissociation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "scan_direction": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "section_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "segment_data_format": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_batch_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_phix_percent": { + "type": "float" + }, + "sequencing_read_format": { + "type": "keyword" + }, + "sequencing_read_percent_q30": { + "type": "float" + }, + "sequencing_reagent_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "signal_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "slide_id": { + "type": "keyword" + }, + "source_project": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_value": { + "type": "float" + }, + "spatial_sampling_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spatial_target": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spatial_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_size_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_size_value": { + "type": "float" + }, + "spot_spacing_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_spacing_value": { + "type": "float" + }, + "stain": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "stain_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "stain_technique": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "step_z_value": { + "type": "float" + }, + "tile_configuration": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tiled_image_columns": { + "type": "float" + }, + "tiled_image_count": { + "type": "float" + }, + "time_since_acquisition_instrument_calibration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "time_since_acquisition_instrument_calibration_value": { + "type": "float" + }, + "transposition_input": { + "type": "float" + }, + "transposition_kit_number": { + "type": "keyword" + }, + "transposition_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transposition_reagent_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transposition_transposase_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "umi_offset": { + "type": "keyword" + }, + "umi_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "umi_size": { + "type": "keyword" + } + } + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "notes": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathologist_report": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathology_distance_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathology_distance_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "perfusion_solution": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_medium": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "procedure_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "quality_criteria": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_preservation_temperature": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_quality_criteria": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "storage_medium": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "storage_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "thumbnail_file_abs_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tissue_weight_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tissue_weight_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "vital_state": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "volume_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "volume_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "next_revision_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "omap_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_other": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pages_or_article_num": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "portal_metadata_upload_files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "previous_revision_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "protocol_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "provider_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_status": { + "type": "boolean" + }, + "publication_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_venue": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "published_timestamp": { + "type": "float" + }, + "registered_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rui_location": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "submission_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "thumbnail_file": { + "properties": { + "file_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filename": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "title": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "visit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "index_version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_component": { + "type": "boolean" + }, + "issue": { + "type": "float" + }, + "lab_dataset_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_donor_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_tissue_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "label": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "last_modified_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_user_sub": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_consortium": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_data_types": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_external_group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_last_modified_timestamp": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_metadata": { + "properties": { + "abo_blood_group_system": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "age_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "age_value": { + "type": "float" + }, + "apolipoprotein_e_phenotype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "blood_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "body_mass_index_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "body_mass_index_value": { + "type": "float" + }, + "cause_of_death": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "death_event": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ethnicity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "height_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "height_value": { + "type": "float" + }, + "kidney_donor_profile_index_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "kidney_donor_profile_index_value": { + "type": "float" + }, + "mechanism_of_injury": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "medical_history": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "other_anatomic_concept": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathology_note": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "race": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rh_blood_group": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rh_factor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sex": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "social_history": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "weight_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "weight_value": { + "type": "float" + } + } + }, + "mapped_organ": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_sample_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapper_metadata": { + "properties": { + "datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "size": { + "type": "float" + }, + "version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "metadata": { + "properties": { + "PPID": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "Preservation_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "Preservation_media": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dag_provenance_list": { + "properties": { + "hash": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "origin": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "extra_metadata": { + "properties": { + "collectiontype": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "file_row": { + "type": "float" + }, + "files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "edam_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_data_product": { + "type": "boolean" + }, + "is_qa_qc": { + "type": "boolean" + }, + "rel_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "size": { + "type": "float" + }, + "type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "files_info_alt_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "health_status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "histological_report": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "living_donor_data": { + "properties": { + "code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "concept_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "graph_version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept_preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "numeric_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "metadata": { + "properties": { + "ablation_distance_between_shots_x_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_x_value": { + "type": "float" + }, + "ablation_distance_between_shots_y_units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_distance_between_shots_y_value": { + "type": "float" + }, + "ablation_frequency_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ablation_frequency_value": { + "type": "float" + }, + "acquisition_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "acquisition_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "amount_of_input_analyte_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "amount_of_input_analyte_value": { + "type": "float" + }, + "analysis_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "analyte_class": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "antibodies_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "area_normalized_ion_dose_value": { + "type": "float" + }, + "assay_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_input_entity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "assay_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "barcode_offset": { + "type": "keyword" + }, + "barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "barcode_size": { + "type": "keyword" + }, + "bead_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bead_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_atac_cell_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_isolation_quality_metric_value": { + "type": "float" + }, + "bulk_rna_yield_units_per_tissue_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "bulk_rna_yield_value": { + "type": "float" + }, + "bulk_transposition_input_number_nuclei": { + "type": "float" + }, + "capture_area_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "capture_batch_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_offset": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cell_barcode_size": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "contributors_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_collection_mode": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_precision_bytes": { + "type": "float" + }, + "dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "desi_solvent": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "desi_solvent_flow_rate": { + "type": "float" + }, + "desi_solvent_flow_rate_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dms": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dna_assay_input_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "dna_assay_input_value": { + "type": "float" + }, + "dual_count_start": { + "type": "float" + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "execution_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "expected_cell_count": { + "type": "float" + }, + "expected_entity_capture_count": { + "type": "float" + }, + "gdna_fragmentation_quality_assurance": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "has_metadata": { + "type": "boolean" + }, + "increment_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "increment_z_value": { + "type": "float" + }, + "intended_tile_overlap_percentage": { + "type": "float" + }, + "ion_mobility": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_batch_staining_done": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_image_preprocessing_required": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_staining_automated": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_targeted": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "is_technical_replicate": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "labeling": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_column_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_column_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_flow_rate_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_flow_rate_value": { + "type": "float" + }, + "lc_gradient": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_id_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_id_value": { + "type": "float" + }, + "lc_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_length_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_length_value": { + "type": "float" + }, + "lc_mobile_phase_a": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_mobile_phase_b": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_resin": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_temp_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lc_temp_value": { + "type": "float" + }, + "library_adapter_sequence": { + "type": "keyword" + }, + "library_average_fragment_size": { + "type": "float" + }, + "library_concentration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_concentration_value": { + "type": "float" + }, + "library_construction_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_construction_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_creation_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield": { + "type": "float" + }, + "library_final_yield_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_final_yield_value": { + "type": "float" + }, + "library_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_input_amount_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_input_amount_value": { + "type": "float" + }, + "library_layout": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_output_amount_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "library_output_amount_value": { + "type": "float" + }, + "library_pcr_cycles": { + "type": "float" + }, + "library_pcr_cycles_for_sample_index": { + "type": "float" + }, + "library_preparation_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_area_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_area_value": { + "type": "float" + }, + "mass_analysis_polarity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mass_resolving_power": { + "type": "float" + }, + "mass_to_charge_range_high_value": { + "type": "float" + }, + "mass_to_charge_range_low_value": { + "type": "float" + }, + "mass_to_charge_resolving_power": { + "type": "float" + }, + "matrix_deposition_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_x_width_value": { + "type": "float" + }, + "max_y_height_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "max_y_height_value": { + "type": "float" + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_ionization_technique": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_scan_mode": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "ms_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mz_range_high_value": { + "type": "float" + }, + "mz_range_low_value": { + "type": "float" + }, + "mz_resolving_power": { + "type": "float" + }, + "non_global_files": { + "type": "float" + }, + "number_of_antibodies": { + "type": "float" + }, + "number_of_barcode_probes": { + "type": "float" + }, + "number_of_barcode_regions_per_barcode_probe": { + "type": "float" + }, + "number_of_channels": { + "type": "float" + }, + "number_of_cycles": { + "type": "float" + }, + "number_of_imaging_rounds": { + "type": "float" + }, + "number_of_input_cells_or_nuclei": { + "type": "float" + }, + "number_of_iterations_of_cdna_amplification": { + "type": "float" + }, + "number_of_pcr_cycles_for_indexing": { + "type": "float" + }, + "number_of_pre_amplification_pcr_cycles": { + "type": "float" + }, + "number_of_pseudocolors_per_channel": { + "type": "float" + }, + "number_of_readout_probes_per_channel": { + "type": "float" + }, + "number_of_sections": { + "type": "float" + }, + "number_of_spots": { + "type": "float" + }, + "operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "operator_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "overall_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "parent_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "permeabilization_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "permeabilization_time_value": { + "type": "float" + }, + "pi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pi_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_dwell_time_value": { + "type": "float" + }, + "pixel_size_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_x_value": { + "type": "float" + }, + "pixel_size_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pixel_size_y_value": { + "type": "float" + }, + "polarity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_model": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_instrument_vendor": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_maldi_matrix": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_matrix": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "primary_ion_current_value": { + "type": "float" + }, + "processing_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_search": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "puck_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "range_z_value": { + "type": "float" + }, + "reagent_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_x_value": { + "type": "float" + }, + "resolution_y_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_y_value": { + "type": "float" + }, + "resolution_z_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "resolution_z_value": { + "type": "float" + }, + "rnaseq_assay_input": { + "type": "float" + }, + "rnaseq_assay_input_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rnaseq_assay_input_value": { + "type": "float" + }, + "rnaseq_assay_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "roi_id": { + "type": "float" + }, + "sample_indexing_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_indexing_set": { + "type": "keyword" + }, + "sample_quality_metric": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_cell_number": { + "type": "float" + }, + "sc_isolation_enrichment": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_entity": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_quality_metric": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sc_isolation_tissue_dissociation": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "scan_direction": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "section_prep_protocols_io_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "segment_data_format": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_batch_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sequencing_phix_percent": { + "type": "float" + }, + "sequencing_read_format": { + "type": "keyword" + }, + "sequencing_read_percent_q30": { + "type": "float" + }, + "sequencing_reagent_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "signal_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "slide_id": { + "type": "keyword" + }, + "source_project": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_value": { + "type": "float" + }, + "spatial_sampling_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spatial_target": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spatial_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_size_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_size_value": { + "type": "float" + }, + "spot_spacing_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "spot_spacing_value": { + "type": "float" + }, + "stain": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "stain_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "stain_technique": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "step_z_value": { + "type": "float" + }, + "tile_configuration": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tiled_image_columns": { + "type": "float" + }, + "tiled_image_count": { + "type": "float" + }, + "time_since_acquisition_instrument_calibration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "time_since_acquisition_instrument_calibration_value": { + "type": "float" + }, + "transposition_input": { + "type": "float" + }, + "transposition_kit_number": { + "type": "keyword" + }, + "transposition_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transposition_reagent_kit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transposition_transposase_source": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "umi_offset": { + "type": "keyword" + }, + "umi_read": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "umi_size": { + "type": "keyword" + } + } + }, + "metadata_schema_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "notes": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_donor_data": { + "properties": { + "code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "concept_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "data_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "end_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "graph_version": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_code": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_concept_preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "grouping_sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "numeric_operator": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preferred_term": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sab": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "start_datetime": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "units": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "pathname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathologist_report": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathology_distance_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathology_distance_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "perfusion_solution": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_medium": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "preparation_protocol_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "procedure_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "quality_criteria": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_storage_duration_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_preservation_temperature": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_quality_criteria": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "storage_medium": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "storage_method": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "thumbnail_file_abs_path": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tissue_weight_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "tissue_weight_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "vital_state": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "volume_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "volume_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "next_revision_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "omap_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_other": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "origin_samples": { + "properties": { + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "image_file_metadata": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "lab_tissue_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "mapped_data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_last_modified_timestamp": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_metadata": { + "type": "object" + }, + "mapped_organ": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_sample_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata": { + "properties": { + "cold_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "health_status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathologist_report": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "perfusion_solution": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "procedure_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_preservation_temperature": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_quality_criteria": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "vital_state": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "organ": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_other": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "portal_metadata_upload_files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "protocol_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "submission_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "visit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "origin_samples_unique_mapped_organs": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pages_or_article_num": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pipeline": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "portal_metadata_upload_files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "previous_revision_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "processing_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "protocol_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "provider_info": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_status": { + "type": "boolean" + }, + "publication_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "publication_venue": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "published_timestamp": { + "type": "float" + }, + "raw_dataset_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "registered_doi": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rui_location": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "source_samples": { + "properties": { + "created_by_user_displayname": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_by_user_email": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "created_timestamp": { + "type": "float" + }, + "data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "entity_type": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "group_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "hubmap_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "lab_tissue_sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "last_modified_timestamp": { + "type": "float" + }, + "mapped_data_access_level": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_last_modified_timestamp": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_metadata": { + "type": "object" + }, + "mapped_organ": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "mapped_sample_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "metadata": { + "properties": { + "cold_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "cold_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "health_status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "organ_condition": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "pathologist_report": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "perfusion_solution": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "procedure_date": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_preservation_temperature": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_quality_criteria": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "specimen_tumor_distance_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "vital_state": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_unit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "warm_ischemia_time_value": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "organ": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "portal_metadata_upload_files": { + "properties": { + "description": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filepath": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "protocol_url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "rui_location": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "sample_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "submission_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "visit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "status": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "submission_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "thumbnail_file": { + "properties": { + "file_uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "filename": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + }, + "title": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "transformation_errors": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "uuid": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "visit": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + }, + "visualization": { + "type": "boolean" + }, + "vitessce-hints": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "copy_to": ["all_text"] + } + } + } +} From 898b776f5c8194e6fb1ec932745840a6e14c24be Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 6 Jun 2024 11:35:55 -0400 Subject: [PATCH 09/73] Refactor sort cell --- .../search/Results/ResultsTable.tsx | 60 ++++++++++++++++++- .../search/Results/SortingTableHead.tsx | 39 ------------ 2 files changed, 57 insertions(+), 42 deletions(-) delete mode 100644 context/app/static/js/components/search/Results/SortingTableHead.tsx diff --git a/context/app/static/js/components/search/Results/ResultsTable.tsx b/context/app/static/js/components/search/Results/ResultsTable.tsx index aea7711669..7d5c839792 100644 --- a/context/app/static/js/components/search/Results/ResultsTable.tsx +++ b/context/app/static/js/components/search/Results/ResultsTable.tsx @@ -2,14 +2,68 @@ import React from 'react'; import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; import TableRow from '@mui/material/TableRow'; import TableHead from '@mui/material/TableHead'; +import IconButton from '@mui/material/IconButton'; import { InternalLink } from 'js/shared-styles/Links'; import { getByPath } from './utils'; -import { StyledTable, StyledTableBody, StyledTableRow, StyledTableCell } from './style'; +import { + StyledTable, + StyledTableBody, + StyledTableRow, + StyledTableCell, + ArrowUpOn, + ArrowDownOn, + ArrowDownOff, + StyledHeaderCell, +} from './style'; import { useSearch } from '../Search'; import { useSearchStore } from '../store'; import { HitDoc } from '../types'; -import SortingTableHead from './SortingTableHead'; + +type SortDirection = 'asc' | 'desc'; + +export function OrderIcon({ + direction, + isCurrentSortField, +}: { + direction: SortDirection; + isCurrentSortField: boolean; +}) { + if (!isCurrentSortField) return ; + if (direction === 'asc') return ; + if (direction === 'desc') return ; +} + +export function getSortOrder({ + direction, + isCurrentSortField, +}: { + direction: SortDirection; + isCurrentSortField: boolean; +}) { + if (!isCurrentSortField) { + return 'desc'; + } + + return direction === 'desc' ? 'asc' : 'desc'; +} + +function SortHeaderCell({ field, label }: { field: string; label: string }) { + const { sortField, setSortField } = useSearchStore(); + + const { direction, field: currentSortField } = sortField; + + const isCurrentSortField = field === currentSortField; + + return ( + + {label} + setSortField({ direction: getSortOrder({ direction, isCurrentSortField }), field })}> + + + + ); +} function ResultCell({ hit, field }: { field: string; hit: SearchHit }) { const source = hit?._source; @@ -42,7 +96,7 @@ function ResultsTable() { {Object.entries(sourceFields).map(([field, { label }]) => ( - + ))} diff --git a/context/app/static/js/components/search/Results/SortingTableHead.tsx b/context/app/static/js/components/search/Results/SortingTableHead.tsx deleted file mode 100644 index 71acb4ec57..0000000000 --- a/context/app/static/js/components/search/Results/SortingTableHead.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import IconButton from '@mui/material/IconButton'; - -import { useSearchStore } from '../store'; - -import { ArrowUpOn, ArrowDownOn, ArrowDownOff, StyledHeaderCell } from './style'; - -export function OrderIcon({ direction }: { direction: 'asc' | 'desc' }) { - if (direction === 'asc') return ; - if (direction === 'desc') return ; -} - -function SortingTableHead({ field, label }: { field: string; label: string }) { - const { sortField, setSortField } = useSearchStore(); - - const { direction, field: currentSortField } = sortField; - - if (field !== currentSortField) { - return ( - - {label} - setSortField({ field, direction: 'desc' })}> - - - - ); - } - - return ( - - {label} - setSortField({ field, direction: direction === 'desc' ? 'asc' : 'desc' })}> - - - - ); -} - -export default SortingTableHead; From 6cf6194013867ccd7d1bd23e90947c98a78d1d56 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Fri, 7 Jun 2024 10:10:03 -0400 Subject: [PATCH 10/73] Add term facet --- .../search/Facets/FacetAccordion.tsx | 24 +++++ .../js/components/search/Facets/Facets.tsx | 19 ++++ .../js/components/search/Facets/TermFacet.tsx | 92 +++++++++++++++++++ .../js/components/search/Facets/style.ts | 81 ++++++++++++++++ .../static/js/components/search/Search.tsx | 29 ++++-- .../app/static/js/components/search/store.ts | 14 +++ 6 files changed, 253 insertions(+), 6 deletions(-) create mode 100644 context/app/static/js/components/search/Facets/FacetAccordion.tsx create mode 100644 context/app/static/js/components/search/Facets/Facets.tsx create mode 100644 context/app/static/js/components/search/Facets/TermFacet.tsx create mode 100644 context/app/static/js/components/search/Facets/style.ts diff --git a/context/app/static/js/components/search/Facets/FacetAccordion.tsx b/context/app/static/js/components/search/Facets/FacetAccordion.tsx new file mode 100644 index 0000000000..01de771b15 --- /dev/null +++ b/context/app/static/js/components/search/Facets/FacetAccordion.tsx @@ -0,0 +1,24 @@ +import React, { PropsWithChildren } from 'react'; +import Typography from '@mui/material/Typography'; +import Accordion from '@mui/material/Accordion'; + +import { InnerAccordionDetails, InnerAccordionSummary, StyledExpandMoreIcon } from './style'; + +function FacetAccordion({ + title, + position, + children, +}: PropsWithChildren<{ title: string; position: 'inner' | 'outer' }>) { + return ( + + }> + + {title} + + + {children} + + ); +} + +export default FacetAccordion; diff --git a/context/app/static/js/components/search/Facets/Facets.tsx b/context/app/static/js/components/search/Facets/Facets.tsx new file mode 100644 index 0000000000..8041a887ca --- /dev/null +++ b/context/app/static/js/components/search/Facets/Facets.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import Paper from '@mui/material/Paper'; + +import { useSearchStore } from '../store'; +import { TermFacet } from './TermFacet'; + +export function Facets() { + const { terms } = useSearchStore(); + + return ( + + {Object.keys(terms).map((term) => ( + + ))} + + ); +} + +export default Facets; diff --git a/context/app/static/js/components/search/Facets/TermFacet.tsx b/context/app/static/js/components/search/Facets/TermFacet.tsx new file mode 100644 index 0000000000..57594a6264 --- /dev/null +++ b/context/app/static/js/components/search/Facets/TermFacet.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import Typography from '@mui/material/Typography'; +import IndeterminateCheckBoxOutlinedIcon from '@mui/icons-material/IndeterminateCheckBoxOutlined'; + +import { useSearch } from '../Search'; +import { useSearchStore } from '../store'; +import { + StyledCheckBoxBlankIcon, + StyledCheckBoxIcon, + StyledCheckbox, + StyledFormControlLabel, + StyledStack, + FormLabelText, +} from './style'; +import FacetAccordion from './FacetAccordion'; + +interface TermFacet { + label: string; + count: number; + active: boolean; + field: string; +} + +type LabelTransformations = ((label: string) => string)[]; + +interface TermLabelCount extends Omit { + labelTransformations?: LabelTransformations; +} + +export function transformLabel({ + label, + labelTransformations, +}: Required>) { + return labelTransformations.reduce((l, transformFn) => transformFn(l), label); +} + +export function TermLabelAndCount({ label, count, active, labelTransformations = [] }: TermLabelCount) { + return ( + + {transformLabel({ label, labelTransformations })} + {count} + + ); +} + +export function TermFacetItem({ active, label, count, field }: TermFacet) { + const { filterTerm } = useSearchStore(); + + return ( + } + name={`${label}-checkbox`} + color="primary" + icon={} + checkedIcon={} + onClick={() => filterTerm({ term: field, value: label })} + /> + } + label={} + /> + ); +} + +export function TermFacet({ field }: { field: string }) { + const { data } = useSearch(); + const { + terms: { [field]: term }, + } = useSearchStore(); + + const aggBuckets = data?.aggregations?.[field]?.buckets; + + if (!aggBuckets || !Array.isArray(aggBuckets)) { + return null; + } + + return ( + + {aggBuckets.map((bucket) => ( + + ))} + + ); +} diff --git a/context/app/static/js/components/search/Facets/style.ts b/context/app/static/js/components/search/Facets/style.ts new file mode 100644 index 0000000000..3cfe643f27 --- /dev/null +++ b/context/app/static/js/components/search/Facets/style.ts @@ -0,0 +1,81 @@ +import { styled } from '@mui/material/styles'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import CheckBoxIcon from '@mui/icons-material/CheckBox'; +import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; +import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Typography from '@mui/material/Typography'; +import Stack from '@mui/material/Stack'; + +const InnerAccordionDetails = styled(AccordionDetails)({ + flexDirection: 'column', + padding: '4px 10px 4px 16px', +}); + +const InnerAccordionSummary = styled(AccordionSummary)({ + justifyContent: 'left', + padding: '0px 16px 0px 16px', + '& > *': { + flexGrow: 'unset', + padding: 0, + margin: 0, + color: '#000', + }, + margin: 0, +}); + +const StyledExpandMoreIcon = styled(ExpandMoreIcon)(({ theme }) => ({ + fontSize: '1rem', + color: theme.palette.secondary.main, +})); + +const iconSize = { + fontSize: '1.2rem', +}; + +const StyledCheckBoxIcon = styled(CheckBoxIcon)({ + ...iconSize, +}); + +const StyledCheckBoxBlankIcon = styled(CheckBoxOutlineBlankIcon)({ + ...iconSize, +}); + +const StyledCheckbox = styled(Checkbox)({ + padding: '3px', +}); + +const StyledFormControlLabel = styled(FormControlLabel)({ + margin: '0px', + width: '100%', + 'span:nth-child(2)': { + flexGrow: 1, + }, + alignItems: 'start', +}); + +const StyledStack = styled(Stack)<{ $active: boolean }>(({ theme, $active }) => ({ + cursor: 'pointer', + p: { + fontSize: theme.typography.subtitle2.fontSize, + ...($active && { fontWeight: theme.typography.subtitle1.fontWeight }), + }, +})); + +const FormLabelText = styled(Typography)({ + marginRight: '2px', +}); + +export { + StyledCheckBoxBlankIcon, + StyledCheckBoxIcon, + StyledCheckbox, + StyledFormControlLabel, + InnerAccordionDetails, + InnerAccordionSummary, + StyledExpandMoreIcon, + StyledStack, + FormLabelText, +}; diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index f3460d3830..0d87e49c8f 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -1,7 +1,12 @@ import React, { useMemo } from 'react'; import useSWR from 'swr'; -import { SearchRequest, SearchResponseBody } from '@elastic/elasticsearch/lib/api/types'; +import { + SearchRequest, + SearchResponseBody, + AggregationsTermsAggregateBase, +} from '@elastic/elasticsearch/lib/api/types'; import esb from 'elastic-builder'; +import Stack from '@mui/material/Stack'; import { fetcher } from 'js/helpers/swr'; import { getAuthHeader } from 'js/helpers/functions'; @@ -10,6 +15,7 @@ import { SearchStoreProvider, useSearchStore, SearchStoreState } from './store'; import { HitDoc } from './types'; import { ResultsTable } from './Results'; import { getPortalESField } from './buildTypesMap'; +import Facets from './Facets/Facets'; function useAuthHeader() { const { groupsToken } = useAppContext(); @@ -45,18 +51,24 @@ function buildQuery({ terms, size, sourceFields, sortField }: Omit query.postFilter(esb.termsQuery(field, [...values]))); + Object.entries(terms).forEach(([field, values]) => { + const portalField = getPortalESField(field); + query.postFilter(esb.termsQuery(portalField, [...values])); + query.agg(esb.termsAggregation(field, portalField)); + }); return query.toJSON(); } -export function useSearch() { +type Aggregations = Record>; + +export function useSearch() { const { endpoint, swrConfig, ...rest }: SearchStoreState = useSearchStore(); const query = buildQuery({ ...rest }); const requestInit = useRequestInit({ body: query }); - const { data, isLoading } = useSWR>( + const { data, isLoading } = useSWR>( { requestInit, url: endpoint }, fetcher, swrConfig, @@ -65,7 +77,12 @@ export function useSearch() { } function Search() { - return ; + return ( + + + ; + + ); } function SearchWrapper() { @@ -76,7 +93,7 @@ function SearchWrapper() { initialState={{ endpoint: elasticsearchEndpoint, swrConfig: {}, - terms: { 'entity_type.keyword': new Set(['Dataset']) }, + terms: { entity_type: new Set(['Dataset']) }, sortField: { field: 'last_modified_timestamp', direction: 'desc' }, sourceFields: { hubmap_id: { label: 'HuBMAP ID' }, diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index c35638c583..a37a65849a 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -22,6 +22,7 @@ export interface SearchStoreState { export interface SearchStoreActions { setSortField: (sortField: SortField) => void; + filterTerm: ({ term, value }: { term: string; value: string }) => void; } export interface SearchStore extends SearchStoreState, SearchStoreActions {} @@ -34,6 +35,19 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } state.sortField = sortField; }); }, + filterTerm: ({ term, value }) => { + set((state) => { + const termSet = state?.terms?.[term]; + if (!termSet) { + return; + } + if (termSet.has(value)) { + termSet.delete(value); + } else { + termSet.add(value); + } + }); + }, })); const [SearchStoreProvider, useSearchStore, SearchStoreContext] = createStoreContext(createStore, 'Search'); From 05457df47a2a13a4e2e59aa450fcee06d375ace3 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Sun, 9 Jun 2024 21:15:17 -0400 Subject: [PATCH 11/73] Add hierarchical facet --- .../js/components/search/Facets/Facets.tsx | 7 +- .../js/components/search/Facets/TermFacet.tsx | 140 +++++++++++++++++- .../js/components/search/Facets/style.ts | 10 ++ .../static/js/components/search/Search.tsx | 30 +++- .../app/static/js/components/search/store.ts | 67 +++++++++ 5 files changed, 242 insertions(+), 12 deletions(-) diff --git a/context/app/static/js/components/search/Facets/Facets.tsx b/context/app/static/js/components/search/Facets/Facets.tsx index 8041a887ca..b9e318dbee 100644 --- a/context/app/static/js/components/search/Facets/Facets.tsx +++ b/context/app/static/js/components/search/Facets/Facets.tsx @@ -2,16 +2,19 @@ import React from 'react'; import Paper from '@mui/material/Paper'; import { useSearchStore } from '../store'; -import { TermFacet } from './TermFacet'; +import { TermFacet, HierarchicalTermFacet } from './TermFacet'; export function Facets() { - const { terms } = useSearchStore(); + const { terms, termz } = useSearchStore(); return ( {Object.keys(terms).map((term) => ( ))} + {Object.entries(termz).map(([parentField, { childTerm }]) => { + return ; + })} ); } diff --git a/context/app/static/js/components/search/Facets/TermFacet.tsx b/context/app/static/js/components/search/Facets/TermFacet.tsx index 57594a6264..6f497349f8 100644 --- a/context/app/static/js/components/search/Facets/TermFacet.tsx +++ b/context/app/static/js/components/search/Facets/TermFacet.tsx @@ -1,7 +1,12 @@ -import React from 'react'; +import React, { useState, useCallback } from 'react'; import Typography from '@mui/material/Typography'; import IndeterminateCheckBoxOutlinedIcon from '@mui/icons-material/IndeterminateCheckBoxOutlined'; +import Accordion from '@mui/material/Accordion'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import { AggregationsBuckets } from '@elastic/elasticsearch/lib/api/types'; +import { TooltipIconButton } from 'js/shared-styles/buttons/TooltipButton'; import { useSearch } from '../Search'; import { useSearchStore } from '../store'; import { @@ -11,19 +16,21 @@ import { StyledFormControlLabel, StyledStack, FormLabelText, + HierarchicalAccordionSummary, } from './style'; import FacetAccordion from './FacetAccordion'; -interface TermFacet { +interface CheckboxItem { label: string; count: number; active: boolean; - field: string; + onClick: () => void; + indeterminate?: boolean; } type LabelTransformations = ((label: string) => string)[]; -interface TermLabelCount extends Omit { +interface TermLabelCount extends Omit { labelTransformations?: LabelTransformations; } @@ -43,20 +50,19 @@ export function TermLabelAndCount({ label, count, active, labelTransformations = ); } -export function TermFacetItem({ active, label, count, field }: TermFacet) { - const { filterTerm } = useSearchStore(); - +function CheckboxFilterItem({ active, label, count, onClick, indeterminate = false }: CheckboxItem) { return ( } name={`${label}-checkbox`} color="primary" icon={} checkedIcon={} - onClick={() => filterTerm({ term: field, value: label })} + onClick={onClick} /> } label={} @@ -64,6 +70,16 @@ export function TermFacetItem({ active, label, count, field }: TermFacet) { ); } +interface TermFacet extends Omit { + field: string; +} + +export function TermFacetItem({ label, field, ...rest }: TermFacet) { + const { filterTerm } = useSearchStore(); + + return filterTerm({ term: field, value: label })} label={label} {...rest} />; +} + export function TermFacet({ field }: { field: string }) { const { data } = useSearch(); const { @@ -90,3 +106,111 @@ export function TermFacet({ field }: { field: string }) { ); } + +function buildExpandTooltip({ expanded, disabled }: { expanded: boolean; disabled: boolean }) { + if (disabled) { + return undefined; + } + + return expanded ? 'View Less' : 'View More'; +} + +export function HierarchicalFacetParent({ childValues, field, label, ...rest }: TermFacet & { childValues: string[] }) { + const { filterHierarchicalParentTerm } = useSearchStore(); + + return ( + filterHierarchicalParentTerm({ term: field, value: label, childValues })} + label={label} + {...rest} + /> + ); +} + +export function HierarchicalTermFacetItem({ + field, + label, + childBuckets, + childField, + ...rest +}: TermFacet & { childField: string; childBuckets?: AggregationsBuckets<{ key: string; doc_count: number }> }) { + const [expanded, setExpanded] = useState(false); + const toggleExpanded = useCallback(() => { + setExpanded((prev) => !prev); + }, [setExpanded]); + + if (!childBuckets || !Array.isArray(childBuckets)) { + return null; + } + + const hasChildBuckets = childBuckets?.length; + const childValues = childBuckets.map((b) => b.key); + + return ( + + + + + } + > + + + + {childValues.map((v) => ( +
{v}
+ ))} +
+
+ ); +} + +export function HierarchicalTermFacet({ parentField, childField }: { parentField: string; childField: string }) { + const { data } = useSearch(); + + const { + termz: { + [parentField]: { values }, + }, + } = useSearchStore(); + + const parentBuckets = data?.aggregations?.[parentField]?.buckets; + + if (!parentBuckets || !Array.isArray(parentBuckets)) { + return null; + } + + return ( + + {parentBuckets.map((bucket) => ( + + ))} + + ); +} diff --git a/context/app/static/js/components/search/Facets/style.ts b/context/app/static/js/components/search/Facets/style.ts index 3cfe643f27..9d9c7372a0 100644 --- a/context/app/static/js/components/search/Facets/style.ts +++ b/context/app/static/js/components/search/Facets/style.ts @@ -68,6 +68,15 @@ const FormLabelText = styled(Typography)({ marginRight: '2px', }); +const HierarchicalAccordionSummary = styled(AccordionSummary)({ + margin: 0, + padding: 0, + '& > div': { + padding: 0, + margin: 0, + }, +}); + export { StyledCheckBoxBlankIcon, StyledCheckBoxIcon, @@ -78,4 +87,5 @@ export { StyledExpandMoreIcon, StyledStack, FormLabelText, + HierarchicalAccordionSummary, }; diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 0d87e49c8f..46c8a36690 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -44,7 +44,7 @@ function useRequestInit({ body }: { body: SearchRequest }) { return buildSearchRequestInit({ body, authHeader }); } -function buildQuery({ terms, size, sourceFields, sortField }: Omit) { +function buildQuery({ terms, termz, size, sourceFields, sortField }: Omit) { const query = esb .requestBodySearch() .size(size) @@ -57,10 +57,33 @@ function buildQuery({ terms, size, sourceFields, sortField }: Omit { + if (!childTerm) { + return; + } + const { field: childField, values: childValues } = childTerm; + const parentPortalField = getPortalESField(field); + const childPortalField = getPortalESField(childField); + + if (values.size) { + query.postFilter(esb.termsQuery(parentPortalField, [...values])); + query.postFilter(esb.termsQuery(childPortalField, [...childValues])); + } + query.agg(esb.termsAggregation(field, parentPortalField).agg(esb.termsAggregation(childField, childPortalField))); + }); + return query.toJSON(); } -type Aggregations = Record>; +interface Bucket { + key: string; + doc_count: number; +} + +type Aggregations = Record< + string, + AggregationsTermsAggregateBase>>> +>; export function useSearch() { const { endpoint, swrConfig, ...rest }: SearchStoreState = useSearchStore(); @@ -94,6 +117,9 @@ function SearchWrapper() { endpoint: elasticsearchEndpoint, swrConfig: {}, terms: { entity_type: new Set(['Dataset']) }, + termz: { + dataset_type: { values: new Set([]), childTerm: { field: 'assay_display_name', values: new Set([]) } }, + }, sortField: { field: 'last_modified_timestamp', direction: 'desc' }, sourceFields: { hubmap_id: { label: 'HuBMAP ID' }, diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index a37a65849a..00e1404c64 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -11,8 +11,19 @@ interface SourceField { label: string; } +interface ChildTerm { + values: Set; + field: string; +} + +interface Term { + values: Set; + childTerm: ChildTerm; +} + export interface SearchStoreState { terms: Record>; + termz: Record; sortField: SortField; sourceFields: Record; size: number; @@ -23,6 +34,16 @@ export interface SearchStoreState { export interface SearchStoreActions { setSortField: (sortField: SortField) => void; filterTerm: ({ term, value }: { term: string; value: string }) => void; + filterHierarchicalParentTerm: ({ + term, + value, + childValues, + }: { + term: string; + value: string; + childValues: string[]; + }) => void; + filterHierarchicalChildTerm: ({ parentTerm, value }: { parentTerm: string; value: string }) => void; } export interface SearchStore extends SearchStoreState, SearchStoreActions {} @@ -48,6 +69,52 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } } }); }, + filterHierarchicalParentTerm: ({ term, value, childValues }) => { + set((state) => { + const termState = state?.termz?.[term]; + + if (!termState) { + return; + } + + const { values, childTerm } = termState; + + if (values.has(value)) { + values.delete(value); + + if (childTerm) { + childTerm.values = new Set([]); + } + } else { + values.add(value); + + if (childTerm) { + childTerm.values = new Set(childValues); + } + } + }); + }, + filterHierarchicalChildTerm: ({ parentTerm, value }) => { + set((state) => { + const termState = state?.termz?.[parentTerm]; + + if (!termState) { + return; + } + + const childValues = termState?.childTerm?.values; + + if (!childValues) { + return; + } + + if (childValues.has(value)) { + childValues.delete(value); + } else { + childValues.add(value); + } + }); + }, })); const [SearchStoreProvider, useSearchStore, SearchStoreContext] = createStoreContext(createStore, 'Search'); From a20a996e4b624651bed73dc02121bdf565da129d Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 10 Jun 2024 09:37:26 -0400 Subject: [PATCH 12/73] Fix hierarchical facet --- .../js/components/search/Facets/Facets.tsx | 4 +- .../js/components/search/Facets/TermFacet.tsx | 41 +++++++++++++++-- .../static/js/components/search/Search.tsx | 17 ++++--- .../app/static/js/components/search/store.ts | 46 +++++++++---------- 4 files changed, 71 insertions(+), 37 deletions(-) diff --git a/context/app/static/js/components/search/Facets/Facets.tsx b/context/app/static/js/components/search/Facets/Facets.tsx index b9e318dbee..39778e4f0f 100644 --- a/context/app/static/js/components/search/Facets/Facets.tsx +++ b/context/app/static/js/components/search/Facets/Facets.tsx @@ -12,8 +12,8 @@ export function Facets() { {Object.keys(terms).map((term) => ( ))} - {Object.entries(termz).map(([parentField, { childTerm }]) => { - return ; + {Object.entries(termz).map(([parentField, { childField }]) => { + return ; })} ); diff --git a/context/app/static/js/components/search/Facets/TermFacet.tsx b/context/app/static/js/components/search/Facets/TermFacet.tsx index 6f497349f8..cfe525e256 100644 --- a/context/app/static/js/components/search/Facets/TermFacet.tsx +++ b/context/app/static/js/components/search/Facets/TermFacet.tsx @@ -127,18 +127,43 @@ export function HierarchicalFacetParent({ childValues, field, label, ...rest }: ); } +export function HierarchicalFacetChild({ parentValue, field, label, ...rest }: TermFacet & { parentValue: string }) { + const { filterHierarchicalChildTerm } = useSearchStore(); + + return ( + filterHierarchicalChildTerm({ parentTerm: field, value: label, parentValue })} + label={label} + {...rest} + /> + ); +} + export function HierarchicalTermFacetItem({ field, label, childBuckets, + parentField, childField, ...rest -}: TermFacet & { childField: string; childBuckets?: AggregationsBuckets<{ key: string; doc_count: number }> }) { +}: TermFacet & { + parentField: string; + childField: string; + childBuckets?: AggregationsBuckets<{ key: string; doc_count: number }>; +}) { const [expanded, setExpanded] = useState(false); const toggleExpanded = useCallback(() => { setExpanded((prev) => !prev); }, [setExpanded]); + const { + termz: { + [parentField]: { + values: { [label]: childState }, + }, + }, + } = useSearchStore(); + if (!childBuckets || !Array.isArray(childBuckets)) { return null; } @@ -175,8 +200,15 @@ export function HierarchicalTermFacetItem({ - {childValues.map((v) => ( -
{v}
+ {childBuckets.map(({ key, doc_count }) => ( + ))}
@@ -205,9 +237,10 @@ export function HierarchicalTermFacet({ parentField, childField }: { parentField label={bucket.key} count={bucket.doc_count} key={bucket.key} - active={values.has(bucket.key)} + active={bucket.key in values} field={parentField} childField={childField} + parentField={parentField} childBuckets={bucket[childField]?.buckets} /> ))} diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 46c8a36690..22bf9df8f2 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -57,17 +57,20 @@ function buildQuery({ terms, termz, size, sourceFields, sortField }: Omit { - if (!childTerm) { + Object.entries(termz).forEach(([field, { values, childField }]) => { + if (!childField) { return; } - const { field: childField, values: childValues } = childTerm; const parentPortalField = getPortalESField(field); const childPortalField = getPortalESField(childField); - if (values.size) { - query.postFilter(esb.termsQuery(parentPortalField, [...values])); - query.postFilter(esb.termsQuery(childPortalField, [...childValues])); + if (Object.keys(values).length) { + query.postFilter(esb.termsQuery(parentPortalField, Object.keys(values))); + + const childValues = Object.values(values) + .map((v) => [...v]) + .flat(); + query.postFilter(esb.termsQuery(childPortalField, childValues)); } query.agg(esb.termsAggregation(field, parentPortalField).agg(esb.termsAggregation(childField, childPortalField))); }); @@ -118,7 +121,7 @@ function SearchWrapper() { swrConfig: {}, terms: { entity_type: new Set(['Dataset']) }, termz: { - dataset_type: { values: new Set([]), childTerm: { field: 'assay_display_name', values: new Set([]) } }, + dataset_type: { values: {}, childField: 'assay_display_name' }, }, sortField: { field: 'last_modified_timestamp', direction: 'desc' }, sourceFields: { diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index 00e1404c64..698ef714e5 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -11,19 +11,14 @@ interface SourceField { label: string; } -interface ChildTerm { - values: Set; - field: string; -} - -interface Term { - values: Set; - childTerm: ChildTerm; +interface HierarchicalTerm { + values: Record>; + childField: string; } export interface SearchStoreState { terms: Record>; - termz: Record; + termz: Record; sortField: SortField; sourceFields: Record; size: number; @@ -43,7 +38,15 @@ export interface SearchStoreActions { value: string; childValues: string[]; }) => void; - filterHierarchicalChildTerm: ({ parentTerm, value }: { parentTerm: string; value: string }) => void; + filterHierarchicalChildTerm: ({ + parentTerm, + parentValue, + value, + }: { + parentTerm: string; + parentValue: string; + value: string; + }) => void; } export interface SearchStore extends SearchStoreState, SearchStoreActions {} @@ -77,24 +80,16 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } return; } - const { values, childTerm } = termState; - - if (values.has(value)) { - values.delete(value); + const { values } = termState; - if (childTerm) { - childTerm.values = new Set([]); - } + if (value in values) { + delete values[value]; } else { - values.add(value); - - if (childTerm) { - childTerm.values = new Set(childValues); - } + values[value] = new Set(childValues); } }); }, - filterHierarchicalChildTerm: ({ parentTerm, value }) => { + filterHierarchicalChildTerm: ({ parentTerm, parentValue, value }) => { set((state) => { const termState = state?.termz?.[parentTerm]; @@ -102,7 +97,7 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } return; } - const childValues = termState?.childTerm?.values; + const childValues = termState?.values?.[parentValue]; if (!childValues) { return; @@ -110,6 +105,9 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } if (childValues.has(value)) { childValues.delete(value); + if (childValues.size === 0) { + delete termState.values[parentValue]; + } } else { childValues.add(value); } From 9e24f030a61061e158e50da89fe4501cda64eb94 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 10 Jun 2024 12:07:10 -0400 Subject: [PATCH 13/73] Add free text search --- .../static/js/components/search/Search.tsx | 29 +++++++++++++++---- .../static/js/components/search/SearchBar.tsx | 29 +++++++++++++++++++ .../app/static/js/components/search/store.ts | 8 +++++ 3 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 context/app/static/js/components/search/SearchBar.tsx diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 22bf9df8f2..8616897ea2 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -7,6 +7,7 @@ import { } from '@elastic/elasticsearch/lib/api/types'; import esb from 'elastic-builder'; import Stack from '@mui/material/Stack'; +import Box from '@mui/material/Box'; import { fetcher } from 'js/helpers/swr'; import { getAuthHeader } from 'js/helpers/functions'; @@ -16,6 +17,7 @@ import { HitDoc } from './types'; import { ResultsTable } from './Results'; import { getPortalESField } from './buildTypesMap'; import Facets from './Facets/Facets'; +import SearchBar from './SearchBar'; function useAuthHeader() { const { groupsToken } = useAppContext(); @@ -44,13 +46,25 @@ function useRequestInit({ body }: { body: SearchRequest }) { return buildSearchRequestInit({ body, authHeader }); } -function buildQuery({ terms, termz, size, sourceFields, sortField }: Omit) { +function buildQuery({ + terms, + termz, + size, + search, + searchFields, + sourceFields, + sortField, +}: Omit) { const query = esb .requestBodySearch() .size(size) .source(Object.keys(sourceFields).length ? Object.keys(sourceFields) : false) .sort(esb.sort(getPortalESField(sortField.field), sortField.direction)); + if (search.length) { + query.query(esb.simpleQueryStringQuery(search).fields(searchFields)); + } + Object.entries(terms).forEach(([field, values]) => { const portalField = getPortalESField(field); query.postFilter(esb.termsQuery(portalField, [...values])); @@ -104,10 +118,13 @@ export function useSearch() { function Search() { return ( - - - ; - + + + + + + + ); } @@ -117,6 +134,8 @@ function SearchWrapper() { return ( ) => { + e.preventDefault(); + setSearch(input.match(/^\s*HBM\S+\s*$/i) ? `"${input}"` : input); + }; + return ( +
+ ) => { + setInput(event.target.value); + }} + /> + + ); +} + +export default SearchBar; diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index 698ef714e5..11c3a5920c 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -17,6 +17,8 @@ interface HierarchicalTerm { } export interface SearchStoreState { + search: string; + searchFields: string[]; terms: Record>; termz: Record; sortField: SortField; @@ -27,6 +29,7 @@ export interface SearchStoreState { } export interface SearchStoreActions { + setSearch: (search: string) => void; setSortField: (sortField: SortField) => void; filterTerm: ({ term, value }: { term: string; value: string }) => void; filterHierarchicalParentTerm: ({ @@ -54,6 +57,11 @@ export interface SearchStore extends SearchStoreState, SearchStoreActions {} export const createStore = ({ initialState }: { initialState: SearchStoreState }) => createStoreImmer((set) => ({ ...initialState, + setSearch: (search) => { + set((state) => { + state.search = search; + }); + }, setSortField: (sortField) => { set((state) => { state.sortField = sortField; From cfd23bb649546944c23e1059cdf1af9ef9c694e1 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 10 Jun 2024 13:38:17 -0400 Subject: [PATCH 14/73] Add search scrolling --- .../js/components/search/Facets/TermFacet.tsx | 12 +- .../search/Results/ResultsTable.tsx | 48 ++++--- .../static/js/components/search/Search.tsx | 47 +------ .../components/search/useScrollSearchHits.ts | 133 ++++++++++++++++++ 4 files changed, 174 insertions(+), 66 deletions(-) create mode 100644 context/app/static/js/components/search/useScrollSearchHits.ts diff --git a/context/app/static/js/components/search/Facets/TermFacet.tsx b/context/app/static/js/components/search/Facets/TermFacet.tsx index cfe525e256..116b163ffb 100644 --- a/context/app/static/js/components/search/Facets/TermFacet.tsx +++ b/context/app/static/js/components/search/Facets/TermFacet.tsx @@ -81,12 +81,12 @@ export function TermFacetItem({ label, field, ...rest }: TermFacet) { } export function TermFacet({ field }: { field: string }) { - const { data } = useSearch(); + const { aggregations } = useSearch(); const { terms: { [field]: term }, } = useSearchStore(); - const aggBuckets = data?.aggregations?.[field]?.buckets; + const aggBuckets = aggregations?.[field]?.buckets; if (!aggBuckets || !Array.isArray(aggBuckets)) { return null; @@ -216,7 +216,7 @@ export function HierarchicalTermFacetItem({ } export function HierarchicalTermFacet({ parentField, childField }: { parentField: string; childField: string }) { - const { data } = useSearch(); + const { aggregations } = useSearch(); const { termz: { @@ -224,7 +224,11 @@ export function HierarchicalTermFacet({ parentField, childField }: { parentField }, } = useSearchStore(); - const parentBuckets = data?.aggregations?.[parentField]?.buckets; + if (!aggregations) { + return null; + } + + const parentBuckets = aggregations?.[parentField]?.buckets; if (!parentBuckets || !Array.isArray(parentBuckets)) { return null; diff --git a/context/app/static/js/components/search/Results/ResultsTable.tsx b/context/app/static/js/components/search/Results/ResultsTable.tsx index 7d5c839792..02a156dcb4 100644 --- a/context/app/static/js/components/search/Results/ResultsTable.tsx +++ b/context/app/static/js/components/search/Results/ResultsTable.tsx @@ -3,6 +3,9 @@ import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; import TableRow from '@mui/material/TableRow'; import TableHead from '@mui/material/TableHead'; import IconButton from '@mui/material/IconButton'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import Typography from '@mui/material/Typography'; import { InternalLink } from 'js/shared-styles/Links'; import { getByPath } from './utils'; @@ -82,9 +85,8 @@ function ResultCell({ hit, field }: { field: string; hit: SearchHit }) { } function ResultsTable() { - const { data } = useSearch(); + const { searchHits: hits, loadMore, totalHitsCount } = useSearch(); const { sourceFields } = useSearchStore(); - const hits = data?.hits?.hits; // TODO: Loading State if (!hits) { @@ -92,24 +94,32 @@ function ResultsTable() { } return ( - - - - {Object.entries(sourceFields).map(([field, { label }]) => ( - - ))} - - - {hits.map((hit) => ( - - - {Object.keys(sourceFields).map((field) => ( - + + + + + {Object.entries(sourceFields).map(([field, { label }]) => ( + ))} - - - ))} - + + + {hits.map((hit) => ( + + + {Object.keys(sourceFields).map((field) => ( + + ))} + + + ))} +
+ + + {hits.length} Results Shown | {totalHitsCount} Total Results + + ); } diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 8616897ea2..b2330ffde9 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -1,16 +1,9 @@ -import React, { useMemo } from 'react'; -import useSWR from 'swr'; -import { - SearchRequest, - SearchResponseBody, - AggregationsTermsAggregateBase, -} from '@elastic/elasticsearch/lib/api/types'; +import React from 'react'; +import { AggregationsTermsAggregateBase } from '@elastic/elasticsearch/lib/api/types'; import esb from 'elastic-builder'; import Stack from '@mui/material/Stack'; import Box from '@mui/material/Box'; -import { fetcher } from 'js/helpers/swr'; -import { getAuthHeader } from 'js/helpers/functions'; import { useAppContext } from 'js/components/Contexts'; import { SearchStoreProvider, useSearchStore, SearchStoreState } from './store'; import { HitDoc } from './types'; @@ -18,33 +11,7 @@ import { ResultsTable } from './Results'; import { getPortalESField } from './buildTypesMap'; import Facets from './Facets/Facets'; import SearchBar from './SearchBar'; - -function useAuthHeader() { - const { groupsToken } = useAppContext(); - return useMemo(() => getAuthHeader(groupsToken), [groupsToken]); -} - -interface BuildSearchRequestInitArgs { - body: SearchRequest; - authHeader: { Authorization?: string }; -} - -function buildSearchRequestInit({ body, authHeader }: BuildSearchRequestInitArgs): RequestInit { - return { - method: 'POST', - body: JSON.stringify(body), - headers: { - 'Content-Type': 'application/json', - ...(authHeader ?? {}), - }, - }; -} - -function useRequestInit({ body }: { body: SearchRequest }) { - const authHeader = useAuthHeader(); - - return buildSearchRequestInit({ body, authHeader }); -} +import { useScrollSearchHits } from './useScrollSearchHits'; function buildQuery({ terms, @@ -107,13 +74,7 @@ export function useSearch() { const query = buildQuery({ ...rest }); - const requestInit = useRequestInit({ body: query }); - const { data, isLoading } = useSWR>( - { requestInit, url: endpoint }, - fetcher, - swrConfig, - ); - return { data, isLoading }; + return useScrollSearchHits({ query, endpoint, swrConfig }); } function Search() { diff --git a/context/app/static/js/components/search/useScrollSearchHits.ts b/context/app/static/js/components/search/useScrollSearchHits.ts new file mode 100644 index 0000000000..afb81d5f32 --- /dev/null +++ b/context/app/static/js/components/search/useScrollSearchHits.ts @@ -0,0 +1,133 @@ +import { useCallback, useMemo } from 'react'; +import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite'; +import { SWRConfiguration } from 'swr'; +import { SearchRequest, SearchResponseBody } from '@elastic/elasticsearch/lib/api/types'; + +import { fetcher } from 'js/helpers/swr'; +import { getAuthHeader } from 'js/helpers/functions'; +import { useAppContext } from 'js/components/Contexts'; +import { SWRError } from 'js/helpers/swr/errors'; + +function useAuthHeader() { + const { groupsToken } = useAppContext(); + return useMemo(() => getAuthHeader(groupsToken), [groupsToken]); +} + +interface BuildSearchRequestInitArgs { + body: SearchRequest; + authHeader: { Authorization?: string }; +} + +function buildSearchRequestInit({ body, authHeader }: BuildSearchRequestInitArgs): RequestInit { + return { + method: 'POST', + body: JSON.stringify(body), + headers: { + 'Content-Type': 'application/json', + ...(authHeader ?? {}), + }, + }; +} + +function useBuildRequestInit() { + const authHeader = useAuthHeader(); + + return useCallback(({ body }: { body: SearchRequest }) => buildSearchRequestInit({ body, authHeader }), [authHeader]); +} + +function getTotalHitsCount(results?: SearchResponseBody) { + const total = results?.hits?.total; + if (typeof total === 'number') { + return total; + } + return total?.value; +} + +// Get the sort array from the last hit. https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html#search-after. +function getSearchAfterSort(hits: SearchResponseBody['hits']['hits']) { + const { sort } = hits.slice(-1)[0]; + return sort; +} + +function getCombinedHits(pagesResults: SearchResponseBody[]) { + const hasData = pagesResults.length > 0; + + if (!hasData) { + return { totalHitsCount: undefined, searchHits: [] }; + } + + return { + totalHitsCount: getTotalHitsCount(pagesResults[0]), + aggregations: pagesResults[0]?.aggregations, + searchHits: pagesResults.map((d) => d?.hits?.hits).flat(), + }; +} + +export function useScrollSearchHits({ + query, + endpoint, + swrConfig, +}: { + query: SearchRequest; + endpoint: string; + swrConfig: SWRConfiguration; +}) { + const buildRequestInit = useBuildRequestInit(); + const getKey: SWRInfiniteKeyLoader = useCallback( + (pageIndex: number, previousPageData: SearchResponseBody) => { + const previousPageHits = previousPageData?.hits?.hits ?? []; + + if (previousPageData && !previousPageHits.length) return null; + // First page, we return the key array unmodified. + if (pageIndex === 0) + return { requestInit: buildRequestInit({ body: { ...query, track_total_hits: true } }), url: endpoint }; + + // Subsequent pages, we add the search after param to the query. + const searchAfterSort = getSearchAfterSort(previousPageHits); + return { + requestInit: buildRequestInit({ body: { ...query, track_total_hits: true, search_after: searchAfterSort } }), + url: endpoint, + }; + }, + [query, buildRequestInit, endpoint], + ); + + const { data, error, isLoading, isValidating, size, setSize } = useSWRInfinite< + SearchResponseBody, + SWRError + >( + getKey, + // TODO: revisit to fix types/make keys more type-safe + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument + fetcher, + { + fallbackData: [], + revalidateAll: false, + revalidateFirstPage: false, + keepPreviousData: true, + ...swrConfig, + }, + ); + + const { searchHits, totalHitsCount, aggregations } = getCombinedHits(data ?? []); + + const isReachingEnd = searchHits.length === 0 || searchHits.length === totalHitsCount; + + const loadMore = useCallback(() => { + if (isReachingEnd || isLoading || isValidating) { + return; + } + setSize(size + 1).catch(console.error); + }, [size, setSize, isReachingEnd, isLoading, isValidating]); + + return { + aggregations, + searchHits, + error, + isLoading, + setSize, + loadMore, + totalHitsCount, + isReachingEnd, + }; +} From 793d3e0680e90879bd6de9919dae8c396b6585c9 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 10 Jun 2024 13:46:59 -0400 Subject: [PATCH 15/73] Fix alignment of results count --- .../static/js/components/search/Results/ResultsTable.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/context/app/static/js/components/search/Results/ResultsTable.tsx b/context/app/static/js/components/search/Results/ResultsTable.tsx index 02a156dcb4..dc4bda0672 100644 --- a/context/app/static/js/components/search/Results/ResultsTable.tsx +++ b/context/app/static/js/components/search/Results/ResultsTable.tsx @@ -116,9 +116,11 @@ function ResultsTable() { - - {hits.length} Results Shown | {totalHitsCount} Total Results - + + + {hits.length} Results Shown | {totalHitsCount} Total Results + + ); } From a792014262093ac3cdc757aa0a1fc0bdbb72ca1f Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 10 Jun 2024 13:52:40 -0400 Subject: [PATCH 16/73] Add indeterminate state to term facet --- .../app/static/js/components/search/Facets/TermFacet.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/context/app/static/js/components/search/Facets/TermFacet.tsx b/context/app/static/js/components/search/Facets/TermFacet.tsx index 116b163ffb..d0d1d4ce8f 100644 --- a/context/app/static/js/components/search/Facets/TermFacet.tsx +++ b/context/app/static/js/components/search/Facets/TermFacet.tsx @@ -197,7 +197,13 @@ export function HierarchicalTermFacetItem({ } > - + 0 && !childValues.every((v) => childState?.has(v))} + /> {childBuckets.map(({ key, doc_count }) => ( From 63d38c150988c5239b91cf7e8febc773dc452e1e Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 10 Jun 2024 16:59:15 -0400 Subject: [PATCH 17/73] Add highlighting --- .../search/Results/ResultsTable.tsx | 32 +- .../js/components/search/Results/style.ts | 63 +-- .../static/js/components/search/Search.tsx | 2 +- context/package-lock.json | 371 ++++++++++++++++-- context/package.json | 2 + 5 files changed, 402 insertions(+), 68 deletions(-) diff --git a/context/app/static/js/components/search/Results/ResultsTable.tsx b/context/app/static/js/components/search/Results/ResultsTable.tsx index dc4bda0672..721d1eefa2 100644 --- a/context/app/static/js/components/search/Results/ResultsTable.tsx +++ b/context/app/static/js/components/search/Results/ResultsTable.tsx @@ -6,6 +6,8 @@ import IconButton from '@mui/material/IconButton'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Typography from '@mui/material/Typography'; +import DOMPurify from 'isomorphic-dompurify'; +import parse from 'html-react-parser'; import { InternalLink } from 'js/shared-styles/Links'; import { getByPath } from './utils'; @@ -84,6 +86,15 @@ function ResultCell({ hit, field }: { field: string; hit: SearchHit }) { ); } +function HighlightRow({ colSpan, highlight }: { colSpan: number } & Required>) { + const sanitizedHighlight = DOMPurify.sanitize(Object.values(highlight).join(' ... ')); + return ( + + {parse(sanitizedHighlight)} + + ); +} + function ResultsTable() { const { searchHits: hits, loadMore, totalHitsCount } = useSearch(); const { sourceFields } = useSearchStore(); @@ -103,15 +114,18 @@ function ResultsTable() { ))} - {hits.map((hit) => ( - - - {Object.keys(sourceFields).map((field) => ( - - ))} - - - ))} + + {hits.map((hit) => ( + + + {Object.keys(sourceFields).map((field) => ( + + ))} + + {hit?.highlight && } + + ))} + - - - {hits.length} Results Shown | {totalHitsCount} Total Results - - + ); } diff --git a/context/app/static/js/components/search/Results/ResultsTiles.tsx b/context/app/static/js/components/search/Results/ResultsTiles.tsx new file mode 100644 index 0000000000..e381ebe282 --- /dev/null +++ b/context/app/static/js/components/search/Results/ResultsTiles.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; +import Box from '@mui/material/Box'; + +import { Entity } from 'js/components/types'; +import EntityTile, { tileWidth } from 'js/components/entity-tile/EntityTile'; +import { getTileDescendantCounts } from 'js/components/entity-tile/EntityTile/utils'; +import { capitalizeString } from 'js/helpers/functions'; +import TileGrid from 'js/shared-styles/tiles/TileGrid'; +import { useSearch } from '../Search'; +import ViewMoreResults from './ViewMoreResults'; + +function Tile({ hit }: { hit: SearchHit> }) { + if (!(hit?._source?.hubmap_id && hit?._source.uuid)) { + return null; + } + + return ( + + ); +} + +function ResultsTiles() { + const { searchHits: hits } = useSearch(); + + return ( + + + {hits.map((hit) => ( + + ))} + + + + ); +} + +export default ResultsTiles; diff --git a/context/app/static/js/components/search/Results/ViewMoreResults.tsx b/context/app/static/js/components/search/Results/ViewMoreResults.tsx new file mode 100644 index 0000000000..e7f2daa89e --- /dev/null +++ b/context/app/static/js/components/search/Results/ViewMoreResults.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import Button from '@mui/material/Button'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; + +import { useSearch } from '../Search'; + +function ViewMoreResults() { + const { searchHits: hits, loadMore, totalHitsCount } = useSearch(); + + return ( + <> + + + + {hits.length} Results Shown | {totalHitsCount} Total Results + + + + ); +} + +export default ViewMoreResults; diff --git a/context/app/static/js/components/search/Results/utils.ts b/context/app/static/js/components/search/Results/utils.ts index 7851a94575..62684ee439 100644 --- a/context/app/static/js/components/search/Results/utils.ts +++ b/context/app/static/js/components/search/Results/utils.ts @@ -1,5 +1,5 @@ import { get } from 'js/helpers/nodash'; -import { HitDoc, HitValues } from '../types'; +import { Entity } from 'js/components/types'; const donorMetadataPath = 'mapped_metadata'; const sampleMetdataPath = 'metadata'; @@ -30,18 +30,19 @@ function matchSamplePath(fieldIdentifier: string) { }, ''); } -function getFieldFromHitFields(hitFields: HitDoc, identifier: string): HitValues { +// TODO: Return type should be a union of Entity leaf types. +function getFieldFromHitFields(hitFields: Partial, identifier: string) { const matchedSamplePath = matchSamplePath(identifier); if (matchedSamplePath.length > 0) { // source_samples and origin_samples are arrays and must be handled accordingly. // TODO: Update design to reflect samples and datasets which have multiple origin samples with different organs. - return get(hitFields, [matchedSamplePath, '0', ...identifier.split('.').slice(1)].join('.')); + return get(hitFields, [matchedSamplePath, '0', ...identifier.split('.').slice(1)].join('.'), ''); } - return get(hitFields, identifier); + return get(hitFields, identifier, ''); } -function getByPath(hitSource: HitDoc, field: string) { +function getByPath(hitSource: Partial, field: string) { const fieldValue = getFieldFromHitFields(hitSource, field); if (Array.isArray(fieldValue)) { diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 526bce05c2..c3b0f55511 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -16,13 +16,14 @@ import { RangeConfig, FACETS, } from './store'; -import { HitDoc } from './types'; import Results from './Results'; import { getPortalESField } from './buildTypesMap'; import Facets from './Facets/Facets'; import SearchBar from './SearchBar'; import { useScrollSearchHits } from './useScrollSearchHits'; import FilterChips from './Facets/FilterChips'; +import { Entity } from '../types'; +import { DefaultSearchViewSwitch } from './SearchViewSwitch'; type Filters = Record; @@ -63,7 +64,7 @@ function buildQuery({ const query = esb .requestBodySearch() .size(size) - .source(sourceFields.length ? sourceFields : false) + .source([...new Set(Object.values(sourceFields).flat())]) .sort(esb.sort(getPortalESField(sortField.field), sortField.direction)); if (search.length) { @@ -188,7 +189,7 @@ export function useSearch() { const query = buildQuery({ ...rest }); - return useScrollSearchHits({ query, endpoint, swrConfig }); + return useScrollSearchHits, Aggregations>({ query, endpoint, swrConfig }); } type FacetOption = TermConfig | HierarchicalTermConfig | RangeConfig; @@ -227,11 +228,13 @@ type SearchConfig = Pick< facets: FacetGroups; }; -function buildInitialSearchState({ facets, swrConfig = {}, ...rest }: SearchConfig) { +function buildInitialSearchState({ facets, sourceFields, swrConfig = {}, ...rest }: SearchConfig) { return { search: '', ...buildFacets({ facetGroups: facets }), swrConfig, + sourceFields, + view: Object.keys(sourceFields)[0], ...rest, }; } @@ -304,7 +307,12 @@ const facetGroups: FacetGroups = { function Search() { return ( - + + + + + + @@ -320,16 +328,26 @@ const searchConfig = { searchFields: ['all_text', 'description'], // TODO: figure out how to make assertion unnecessary. sortField: { field: 'last_modified_timestamp', direction: 'desc' as const }, - sourceFields: [ - 'hubmap_id', - 'group_name', - 'assay_display_name', - 'origin_samples.mapped_organ', - 'mapped_status', - 'last_modified_timestamp', - ], + sourceFields: { + table: [ + 'hubmap_id', + 'group_name', + 'assay_display_name', + 'origin_samples_unique_mapped_organs', + 'mapped_status', + 'last_modified_timestamp', + ], + tile: [ + 'hubmap_id', + 'uuid', + 'last_modified_timestamp', + 'descendant_counts.entity_type', + 'thumbnail_file.file_uuid', + 'origin_samples_unique_mapped_organs', + ], + }, facets: facetGroups, - size: 10, + size: 18, }; function SearchWrapper({ config }: { config: Omit }) { diff --git a/context/app/static/js/components/search/SearchViewSwitch.tsx b/context/app/static/js/components/search/SearchViewSwitch.tsx new file mode 100644 index 0000000000..fe62a6d3ae --- /dev/null +++ b/context/app/static/js/components/search/SearchViewSwitch.tsx @@ -0,0 +1,51 @@ +import React, { useCallback } from 'react'; +import ListRoundedIcon from '@mui/icons-material/ListRounded'; +import GridOnRoundedIcon from '@mui/icons-material/GridOnRounded'; +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; +import { Button, SvgIcon } from '@mui/material'; + +import { TooltipToggleButton } from 'js/shared-styles/buttons'; +import { SecondaryBackgroundTooltip } from 'js/shared-styles/tooltips'; +import { useSearchStore } from './store'; + +function SearchViewSwitch({ views }: { views: { label: string; icon: typeof SvgIcon }[] }) { + const { view, setView } = useSearchStore(); + + const handleChange = useCallback( + (event: React.MouseEvent, v: string) => { + setView(v); + }, + [setView], + ); + + return ( + + {views.map(({ label, icon: Icon }) => ( + + + + ))} + + ); +} + +const defaultViews = [ + { label: 'Table', icon: ListRoundedIcon }, + { label: 'Tile', icon: GridOnRoundedIcon }, +]; + +function DefaultSearchViewSwitch() { + return ; +} +export default SearchViewSwitch; + +export { DefaultSearchViewSwitch }; diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index e0c2758b48..0f5030f251 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -51,11 +51,14 @@ export interface FacetsState { ranges: Record; } +type SourceFields = Record; + export interface SearchStoreState extends FacetsState { search: string; searchFields: string[]; sortField: SortField; - sourceFields: string[]; + sourceFields: SourceFields; + view: string; size: number; endpoint: string; swrConfig?: SWRConfiguration; @@ -63,6 +66,7 @@ export interface SearchStoreState extends FacetsState { export interface SearchStoreActions { setSearch: (search: string) => void; + setView: (view: string) => void; setSortField: (sortField: SortField) => void; filterTerm: ({ term, value }: { term: string; value: string }) => void; filterHierarchicalParentTerm: ({ @@ -96,6 +100,11 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } state.search = search; }); }, + setView: (view) => { + set((state) => { + state.view = view; + }); + }, setSortField: (sortField) => { set((state) => { state.sortField = sortField; diff --git a/context/app/static/js/components/search/types.ts b/context/app/static/js/components/search/types.ts deleted file mode 100644 index 15381d2070..0000000000 --- a/context/app/static/js/components/search/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type HitValues = string | number | string[] | number[]; - -export type HitDoc = Record; From 243eca1215c03ac321cad897ddb35cba978d9510 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 27 Jun 2024 12:34:13 -0400 Subject: [PATCH 33/73] Add tiles sort --- .../search/Results/ResultsTiles.tsx | 68 ++++++++++++++++++- .../static/js/components/search/Search.tsx | 3 + .../app/static/js/components/search/store.ts | 2 +- 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/context/app/static/js/components/search/Results/ResultsTiles.tsx b/context/app/static/js/components/search/Results/ResultsTiles.tsx index e381ebe282..132d82baea 100644 --- a/context/app/static/js/components/search/Results/ResultsTiles.tsx +++ b/context/app/static/js/components/search/Results/ResultsTiles.tsx @@ -1,6 +1,11 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; import Box from '@mui/material/Box'; +import MenuItem from '@mui/material/MenuItem'; +import FormControl from '@mui/material/FormControl'; +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import Check from '@mui/icons-material/Check'; import { Entity } from 'js/components/types'; import EntityTile, { tileWidth } from 'js/components/entity-tile/EntityTile'; @@ -9,6 +14,66 @@ import { capitalizeString } from 'js/helpers/functions'; import TileGrid from 'js/shared-styles/tiles/TileGrid'; import { useSearch } from '../Search'; import ViewMoreResults from './ViewMoreResults'; +import { useSearchStore } from '../store'; +import { getFieldLabel } from '../labelMap'; + +function TilesSortSelect() { + const { + sortField, + setSortField, + sourceFields: { table: tableFields }, + } = useSearchStore(); + + const handleChange = useCallback( + (event: SelectChangeEvent) => { + const selectedField = event.target.value; + setSortField({ field: selectedField, direction: selectedField === 'last_modified_timestamp' ? 'desc' : 'asc' }); + }, + [setSortField], + ); + + return ( + + + + ); +} function Tile({ hit }: { hit: SearchHit> }) { if (!(hit?._source?.hubmap_id && hit?._source.uuid)) { @@ -42,4 +107,5 @@ function ResultsTiles() { ); } +export { TilesSortSelect }; export default ResultsTiles; diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index c3b0f55511..c9e63644f4 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -24,6 +24,7 @@ import { useScrollSearchHits } from './useScrollSearchHits'; import FilterChips from './Facets/FilterChips'; import { Entity } from '../types'; import { DefaultSearchViewSwitch } from './SearchViewSwitch'; +import { TilesSortSelect } from './Results/ResultsTiles'; type Filters = Record; @@ -305,12 +306,14 @@ const facetGroups: FacetGroups = { }; function Search() { + const { view } = useSearchStore(); return ( + {view === 'tile' && } diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index 0f5030f251..8b36d02390 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -2,7 +2,7 @@ import { createStoreImmer, createStoreContext } from 'js/helpers/zustand'; import { SWRConfiguration } from 'swr'; -interface SortField { +export interface SortField { field: string; direction: 'asc' | 'desc'; } From 00d1594687e677941ccc3adfbb0a5a6cfe797b98 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 27 Jun 2024 15:44:33 -0400 Subject: [PATCH 34/73] Add selection and dropdown menus --- .../js/components/search/Results/ResultsTable.tsx | 8 ++++++-- .../app/static/js/components/search/Search.tsx | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/context/app/static/js/components/search/Results/ResultsTable.tsx b/context/app/static/js/components/search/Results/ResultsTable.tsx index 74387c44e5..808d316c0c 100644 --- a/context/app/static/js/components/search/Results/ResultsTable.tsx +++ b/context/app/static/js/components/search/Results/ResultsTable.tsx @@ -9,6 +9,8 @@ import parse from 'html-react-parser'; import { InternalLink } from 'js/shared-styles/Links'; import { Entity } from 'js/components/types'; +import SelectableHeaderCell from 'js/shared-styles/tables/SelectableHeaderCell'; +import SelectableRowCell from 'js/shared-styles/tables/SelectableRowCell'; import { getByPath } from './utils'; import { StyledTable, @@ -102,7 +104,7 @@ function ResultsTable() { } = useSearchStore(); // TODO: Loading State - if (!hits) { + if (!hits.length) { return null; } @@ -111,6 +113,7 @@ function ResultsTable() { + h._id)} disabled={false} /> {tableFields.map((field) => ( ))} @@ -120,11 +123,12 @@ function ResultsTable() { {hits.map((hit) => ( + {tableFields.map((field) => ( ))} - {hit?.highlight && } + {hit?.highlight && } ))} diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index c9e63644f4..633a934e9f 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -6,6 +6,8 @@ import Box from '@mui/material/Box'; import { produce } from 'immer'; import { useAppContext } from 'js/components/Contexts'; +import SelectableTableProvider from 'js/shared-styles/tables/SelectableTableProvider'; +import WorkspacesDropdownMenu from 'js/components/workspaces/WorkspacesDropdownMenu'; import { SearchStoreProvider, useSearchStore, @@ -25,6 +27,7 @@ import FilterChips from './Facets/FilterChips'; import { Entity } from '../types'; import { DefaultSearchViewSwitch } from './SearchViewSwitch'; import { TilesSortSelect } from './Results/ResultsTiles'; +import MetadataMenu from '../searchPage/MetadataMenu'; type Filters = Record; @@ -313,6 +316,8 @@ function Search() { + + {view === 'tile' && } @@ -344,6 +349,8 @@ const searchConfig = { 'hubmap_id', 'uuid', 'last_modified_timestamp', + 'entity_type', + 'mapped_data_types', 'descendant_counts.entity_type', 'thumbnail_file.file_uuid', 'origin_samples_unique_mapped_organs', @@ -357,9 +364,11 @@ function SearchWrapper({ config }: { config: Omit }) { const { elasticsearchEndpoint } = useAppContext(); return ( - - - + + + + + ); } From 573697ad477549b08414af899dcca70270dd1607 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 27 Jun 2024 16:18:28 -0400 Subject: [PATCH 35/73] Add header --- .../static/js/components/search/Search.tsx | 82 ++++++++++++++----- 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 633a934e9f..3b3e5dc094 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -1,13 +1,18 @@ import React from 'react'; import { AggregationsTermsAggregateBase } from '@elastic/elasticsearch/lib/api/types'; import esb from 'elastic-builder'; +import { produce } from 'immer'; +import { styled } from '@mui/material/styles'; import Stack from '@mui/material/Stack'; import Box from '@mui/material/Box'; -import { produce } from 'immer'; +import Typography from '@mui/material/Typography'; +import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; import { useAppContext } from 'js/components/Contexts'; import SelectableTableProvider from 'js/shared-styles/tables/SelectableTableProvider'; import WorkspacesDropdownMenu from 'js/components/workspaces/WorkspacesDropdownMenu'; +import { entityIconMap } from 'js/shared-styles/icons/entityIconMap'; + import { SearchStoreProvider, useSearchStore, @@ -308,25 +313,60 @@ const facetGroups: FacetGroups = { ], }; -function Search() { +const EntityIcon = styled(SvgIcon)({ + fontSize: '2.5rem', +}); + +interface TypeProps { + type: keyof Pick; +} + +function Header({ type }: TypeProps) { + return ( + + + + {type}s + + + ); +} + +function Bar({ type }: TypeProps) { const { view } = useSearchStore(); + return ( - - - - - - - - {view === 'tile' && } - - - - - - - - + + + + + + + {view === 'tile' && } + + + ); +} + +function Body() { + return ( + + + + + + + ); +} + +function Search({ type }: TypeProps) { + return ( + +
+ + + + ); @@ -363,10 +403,12 @@ const searchConfig = { function SearchWrapper({ config }: { config: Omit }) { const { elasticsearchEndpoint } = useAppContext(); + const type = 'Dataset'; + return ( - + - + ); From 763f9a0bfdcd83fd9b63b22c4c178a108f754a29 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 27 Jun 2024 16:28:05 -0400 Subject: [PATCH 36/73] Delete cells search --- .../static/js/components/Routes/Routes.jsx | 9 -- .../static/js/pages/search/CellsSearch.jsx | 88 ------------------- 2 files changed, 97 deletions(-) delete mode 100644 context/app/static/js/pages/search/CellsSearch.jsx diff --git a/context/app/static/js/components/Routes/Routes.jsx b/context/app/static/js/components/Routes/Routes.jsx index a3bd52be10..ffdfec057c 100644 --- a/context/app/static/js/components/Routes/Routes.jsx +++ b/context/app/static/js/components/Routes/Routes.jsx @@ -12,7 +12,6 @@ const Sample = lazy(() => import('js/pages/Sample')); const Collection = lazy(() => import('js/pages/Collection')); const Home = lazy(() => import('js/pages/Home/Home')); const Search = lazy(() => import('js/components/search')); -const CellsSearch = lazy(() => import('js/pages/search/CellsSearch')); const DevSearch = lazy(() => import('js/pages/search/DevSearch')); const Diversity = lazy(() => import('js/pages/Diversity')); const Preview = lazy(() => import('js/pages/Preview')); @@ -128,14 +127,6 @@ function Routes({ flaskData }) { ); } - if (urlPath.startsWith('/cells-search')) { - return ( - - - - ); - } - if (urlPath.startsWith('/dev-search')) { return ( diff --git a/context/app/static/js/pages/search/CellsSearch.jsx b/context/app/static/js/pages/search/CellsSearch.jsx deleted file mode 100644 index 1911ce16b6..0000000000 --- a/context/app/static/js/pages/search/CellsSearch.jsx +++ /dev/null @@ -1,88 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import SearchDatasetTutorial from 'js/components/tutorials/SearchDatasetTutorial'; -import { useAppContext } from 'js/components/Contexts'; -import { getDefaultQuery } from 'js/helpers/functions'; -import SearchWrapper from 'js/components/searchPage/SearchWrapper'; -import { donorConfig, sampleConfig, datasetConfig, fieldsToHighlight } from 'js/components/searchPage/config'; -import { listFilter } from 'js/components/searchPage/utils'; -import SearchNote from 'js/components/searchPage/SearchNote'; -import Results from 'js/components/searchPage/Results'; -import { SearchHeader } from './style'; - -function Search({ title }) { - const { elasticsearchEndpoint, groupsToken } = useAppContext(); - - const hiddenFilters = [ - listFilter('ancestor_ids', 'Ancestor ID'), - listFilter('entity_type', 'Entity Type'), - listFilter('descendant_ids', 'Descendant ID'), - ]; - - const filtersByType = { - donor: { ...donorConfig.filters, '': hiddenFilters }, - sample: { ...sampleConfig.filters, '': hiddenFilters }, - dataset: { ...datasetConfig.filters, '': hiddenFilters }, - }; - - const resultFieldsByType = { - donor: donorConfig.fields, - sample: sampleConfig.fields, - dataset: datasetConfig.fields, - }; - - const searchParams = new URLSearchParams(window.location.search); - const typeParam = 'entity_type[0]'; - const type = (searchParams.get(typeParam) || '').toLowerCase(); - if (!(type in resultFieldsByType)) { - throw Error( - `Unexpected URL param "${typeParam}=${type}"; Should be one of {${Object.keys(resultFieldsByType).join(', ')}}`, - ); - } - - const resultFields = resultFieldsByType[type]; - const searchProps = { - // Prefix for details links: - detailsUrlPrefix: `/browse/${type || 'dataset'}/`, - // Search results field which will be appended to detailsUrlPrefix: - idField: 'uuid', - // Search results fields to display in table: - resultFields, - // Default hitsPerPage is 10: - hitsPerPage: 18, - // Entity type - type, - // Sidebar facet configuration: - filters: filtersByType[type], - queryFields: ['all_text', ...fieldsToHighlight], - isLoggedIn: Boolean(groupsToken), - apiUrl: elasticsearchEndpoint, - groupsToken, - defaultQuery: getDefaultQuery(), - }; - - const wrappedSearch = ; - - return ( - <> - - [Preview] {title} - - {type === 'dataset' && } - - {wrappedSearch} - - ); -} - -Search.propTypes = { - title: PropTypes.string.isRequired, - groupsToken: PropTypes.string, -}; - -Search.defaultProps = { - groupsToken: '', -}; - -export default Search; From 5a24e1733f8b7fbc3d0448b1aa2cc8b2f56080cd Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 27 Jun 2024 18:48:14 -0400 Subject: [PATCH 37/73] Add configs for each entity --- context/app/routes_main.py | 21 +-- .../static/js/components/Routes/Routes.jsx | 5 +- .../static/js/components/search/Search.tsx | 111 +----------- .../app/static/js/components/search/store.ts | 1 + context/app/static/js/pages/search/S.tsx | 166 ++++++++++++++++++ 5 files changed, 180 insertions(+), 124 deletions(-) create mode 100644 context/app/static/js/pages/search/S.tsx diff --git a/context/app/routes_main.py b/context/app/routes_main.py index b1cbbe7a06..fe8eb7fe07 100644 --- a/context/app/routes_main.py +++ b/context/app/routes_main.py @@ -42,28 +42,13 @@ def ccf_eui(): ) -@blueprint.route('/search') -@blueprint.route('/cells-search') -def search(): - entity_type = request.args.get('entity_type[0]') - title = f'{entity_type}s' if entity_type else 'Search' - flask_data = { - **get_default_flask_data(), - 'title': title, - } - return render_template( - 'base-pages/react-content.html', - title=title, - flask_data=flask_data, - ) - - -@blueprint.route('/test-search/') +@blueprint.route('/search/') def test_search(type): if type not in ['donors', 'samples', 'datasets']: abort(404) - title = f'{type.capitalize()} Test Search' + title = f'{type.capitalize()} Search' flask_data = { + 'type': type, **get_default_flask_data(), 'title': title, } diff --git a/context/app/static/js/components/Routes/Routes.jsx b/context/app/static/js/components/Routes/Routes.jsx index ffdfec057c..13d1f8aa51 100644 --- a/context/app/static/js/components/Routes/Routes.jsx +++ b/context/app/static/js/components/Routes/Routes.jsx @@ -11,8 +11,8 @@ const Dataset = lazy(() => import('js/pages/Dataset')); const Sample = lazy(() => import('js/pages/Sample')); const Collection = lazy(() => import('js/pages/Collection')); const Home = lazy(() => import('js/pages/Home/Home')); -const Search = lazy(() => import('js/components/search')); const DevSearch = lazy(() => import('js/pages/search/DevSearch')); +const Search = lazy(() => import('js/pages/search/S')); const Diversity = lazy(() => import('js/pages/Diversity')); const Preview = lazy(() => import('js/pages/Preview')); const Publications = lazy(() => import('js/pages/Publications')); @@ -54,6 +54,7 @@ function Routes({ flaskData }) { geneSymbol, cell_type: cellId, tutorialName, + type, } = flaskData; const urlPath = window.location.pathname; const url = window.location.href; @@ -122,7 +123,7 @@ function Routes({ flaskData }) { if (urlPath.startsWith('/search')) { return ( - + ); } diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 3b3e5dc094..643d83b528 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -232,7 +232,7 @@ function buildFacets({ facetGroups }: { facetGroups: FacetGroups }) { type SearchConfig = Pick< SearchStoreState, - 'searchFields' | 'sourceFields' | 'endpoint' | 'swrConfig' | 'sortField' | 'size' + 'searchFields' | 'sourceFields' | 'endpoint' | 'swrConfig' | 'sortField' | 'size' | 'type' > & { facets: FacetGroups; }; @@ -248,71 +248,6 @@ function buildInitialSearchState({ facets, sourceFields, swrConfig = {}, ...rest }; } -const facetConfigs = { - dataset_type: { field: 'dataset_type', childField: 'assay_display_name', type: FACETS.hierarchical }, - mapped_status: { field: 'mapped_status', childField: 'mapped_data_access_level', type: FACETS.hierarchical }, - 'donor.mapped_metadata.age_value': { field: 'donor.mapped_metadata.age_value', min: 0, max: 100, type: FACETS.range }, - 'donor.mapped_metadata.body_mass_index_value': { - field: 'donor.mapped_metadata.body_mass_index_value', - min: 0, - max: 50, - type: FACETS.range, - }, -}; - -const facetGroups: FacetGroups = { - 'Dataset Metadata': [ - facetConfigs.dataset_type, - { - field: 'origin_samples_unique_mapped_organs', - type: FACETS.term, - }, - { - field: 'analyte_class', - type: FACETS.term, - }, - { - field: 'source_samples.sample_category', - type: FACETS.term, - }, - facetConfigs.mapped_status, - ], - 'Dataset Processing': [ - { - field: 'processing', - type: FACETS.term, - }, - { - field: 'pipeline', - type: FACETS.term, - }, - { - field: 'visualization', - type: FACETS.term, - }, - { - field: 'processing_type', - type: FACETS.term, - }, - { - field: 'assay_modality', - type: FACETS.term, - }, - ], - 'Donor Metadata': [ - { - field: 'donor.mapped_metadata.sex', - type: FACETS.term, - }, - facetConfigs['donor.mapped_metadata.age_value'], - { - field: 'donor.mapped_metadata.race', - type: FACETS.term, - }, - facetConfigs['donor.mapped_metadata.body_mass_index_value'], - ], -}; - const EntityIcon = styled(SvgIcon)({ fontSize: '2.5rem', }); @@ -348,7 +283,7 @@ function Bar({ type }: TypeProps) { ); } -function Body() { +function Body({ facetGroups }: { facetGroups: FacetGroups }) { return ( @@ -359,63 +294,31 @@ function Body() { ); } -function Search({ type }: TypeProps) { +function Search({ type, facetGroups }: TypeProps & { facetGroups: FacetGroups }) { return (
- + ); } -const searchConfig = { - searchFields: ['all_text', 'description'], - // TODO: figure out how to make assertion unnecessary. - sortField: { field: 'last_modified_timestamp', direction: 'desc' as const }, - sourceFields: { - table: [ - 'hubmap_id', - 'group_name', - 'assay_display_name', - 'origin_samples_unique_mapped_organs', - 'mapped_status', - 'last_modified_timestamp', - ], - tile: [ - 'hubmap_id', - 'uuid', - 'last_modified_timestamp', - 'entity_type', - 'mapped_data_types', - 'descendant_counts.entity_type', - 'thumbnail_file.file_uuid', - 'origin_samples_unique_mapped_organs', - ], - }, - facets: facetGroups, - size: 18, -}; - function SearchWrapper({ config }: { config: Omit }) { const { elasticsearchEndpoint } = useAppContext(); - const type = 'Dataset'; + const { type, facets } = config; return ( - + ); } -function DatasetsSearch() { - return ; -} - -export default DatasetsSearch; +export default SearchWrapper; diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index 8b36d02390..b405403d90 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -62,6 +62,7 @@ export interface SearchStoreState extends FacetsState { size: number; endpoint: string; swrConfig?: SWRConfiguration; + type: 'Donor' | 'Sample' | 'Dataset'; } export interface SearchStoreActions { diff --git a/context/app/static/js/pages/search/S.tsx b/context/app/static/js/pages/search/S.tsx new file mode 100644 index 0000000000..9a593f0463 --- /dev/null +++ b/context/app/static/js/pages/search/S.tsx @@ -0,0 +1,166 @@ +import React from 'react'; + +import Search from 'js/components/search'; +import { FACETS } from 'js/components/search/store'; +import { EntityWithType, isDonor } from 'js/components/types'; + +const sharedConfig = { + searchFields: ['all_text', 'description'], + // TODO: figure out how to make assertion unnecessary. + sortField: { field: 'last_modified_timestamp', direction: 'desc' as const }, + size: 18, +}; + +const sharedTileFields = [ + 'hubmap_id', + 'uuid', + 'last_modified_timestamp', + 'entity_type', + 'descendant_counts.entity_type', +]; + +const sharedAffiliationFilters = [ + { field: 'group_name', type: FACETS.term }, + { field: 'created_by_user_displayname', type: FACETS.term }, +]; + +function makeDonorMetadataFilters(e: EntityWithType) { + const isDonorEntity = isDonor(e); + + const pathPrefix = isDonorEntity ? '' : 'donor.'; + return [ + { field: `${pathPrefix}mapped_metadata.sex`, type: FACETS.term }, + { field: `${pathPrefix}mapped_metadata.age_value`, min: 0, max: 100, type: FACETS.range }, + { field: `${pathPrefix}mapped_metadata.race`, type: FACETS.term }, + { field: `${pathPrefix}mapped_metadata.body_mass_index_value`, min: 0, max: 50, type: FACETS.range }, + ]; +} + +const donorFacetGroups = { + 'Donor Metadata': makeDonorMetadataFilters({ entity_type: 'Dataset' }), + Affiliation: sharedAffiliationFilters, +}; + +const donorConfig = { + ...sharedConfig, + sourceFields: { + table: [ + 'hubmap_id', + 'group_name', + 'mapped_metadata.age_value', + 'mapped_metadata.body_mass_index_value', + 'mapped_metadata.sex', + 'mapped_metadata.race', + 'last_modified_timestamp', + ], + tile: [...sharedTileFields, 'mapped_metadata.age_unit'], + }, + facets: donorFacetGroups, + // TODO: figure out how to make assertion unnecessary. + type: 'Donor' as const, +}; + +const sampleFacetGroups = { + 'Sample Metadata': [ + { + field: 'origin_samples_unique_mapped_organs', + type: FACETS.term, + }, + { + field: 'sample_category', + type: FACETS.term, + }, + ], + 'Donor Metadata': makeDonorMetadataFilters({ entity_type: 'Dataset' }), + Affiliation: sharedAffiliationFilters, +}; + +const sampleConfig = { + ...sharedConfig, + sourceFields: { + table: [ + 'hubmap_id', + 'group_name', + 'sample_category', + 'origin_samples_unique_mapped_organs', + 'last_modified_timestamp', + ], + tile: [...sharedTileFields, 'origin_samples_unique_mapped_organs'], + }, + facets: sampleFacetGroups, + // TODO: figure out how to make assertion unnecessary. + type: 'Sample' as const, +}; + +const datasetFacetGroups = { + 'Dataset Metadata': [ + { field: 'dataset_type', childField: 'assay_display_name', type: FACETS.hierarchical }, + { + field: 'origin_samples_unique_mapped_organs', + type: FACETS.term, + }, + { + field: 'analyte_class', + type: FACETS.term, + }, + { + field: 'source_samples.sample_category', + type: FACETS.term, + }, + { field: 'mapped_status', childField: 'mapped_data_access_level', type: FACETS.hierarchical }, + ], + 'Dataset Processing': [ + { + field: 'processing', + type: FACETS.term, + }, + { + field: 'pipeline', + type: FACETS.term, + }, + { + field: 'visualization', + type: FACETS.term, + }, + { + field: 'processing_type', + type: FACETS.term, + }, + { + field: 'assay_modality', + type: FACETS.term, + }, + ], + 'Donor Metadata': makeDonorMetadataFilters({ entity_type: 'Dataset' }), + Affiliation: [{ field: 'mapped_consortium', type: FACETS.term }, ...sharedAffiliationFilters], +}; + +const datasetConfig = { + ...sharedConfig, + sourceFields: { + table: [ + 'hubmap_id', + 'group_name', + 'assay_display_name', + 'origin_samples_unique_mapped_organs', + 'mapped_status', + 'last_modified_timestamp', + ], + tile: [...sharedTileFields, 'thumbnail_file.file_uuid', 'origin_samples_unique_mapped_organs'], + }, + facets: datasetFacetGroups, + // TODO: figure out how to make assertion unnecessary. + type: 'Dataset' as const, +}; + +const configs = { + donors: donorConfig, + samples: sampleConfig, + datasets: datasetConfig, +}; + +function S({ type }: { type: 'donors' | 'samples' | 'datasets' }) { + const config = configs[type]; + return ; +} +export default S; From 97d44acff233fb9b87cbb7ce728968efe40819c5 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 27 Jun 2024 18:54:58 -0400 Subject: [PATCH 38/73] Fix missing organs --- context/app/static/js/components/search/Results/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/app/static/js/components/search/Results/utils.ts b/context/app/static/js/components/search/Results/utils.ts index 62684ee439..b993c636c7 100644 --- a/context/app/static/js/components/search/Results/utils.ts +++ b/context/app/static/js/components/search/Results/utils.ts @@ -19,7 +19,7 @@ const paths = { }, }; -const samplePaths = ['origin_samples', 'source_samples']; +const samplePaths = ['origin_samples.', 'source_samples.']; function matchSamplePath(fieldIdentifier: string) { return samplePaths.reduce((matchedPath, path) => { From 2689f7e144d3ce0257ee5288da41562f33b7a2c5 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 27 Jun 2024 19:37:03 -0400 Subject: [PATCH 39/73] Add support for default queries --- .../static/js/components/search/Search.tsx | 13 +++++++++--- .../app/static/js/components/search/store.ts | 2 ++ context/app/static/js/pages/search/S.tsx | 20 +++++++++++++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 643d83b528..e899eb6b3b 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -69,6 +69,7 @@ function buildQuery({ searchFields, sourceFields, sortField, + defaultQuery, }: Omit) { const query = esb .requestBodySearch() @@ -76,8 +77,14 @@ function buildQuery({ .source([...new Set(Object.values(sourceFields).flat())]) .sort(esb.sort(getPortalESField(sortField.field), sortField.direction)); - if (search.length) { - query.query(esb.simpleQueryStringQuery(search).fields(searchFields)).highlight(esb.highlight(searchFields)); + const hasTextQuery = search.length > 0; + + const freeTextQueries = hasTextQuery ? [esb.simpleQueryStringQuery(search).fields(searchFields)] : []; + const defaultQueries = defaultQuery ? [defaultQuery] : []; + query.query(esb.boolQuery().must([...defaultQueries, ...freeTextQueries])); + + if (hasTextQuery) { + query.highlight(esb.highlight(searchFields)); } const termFilters = Object.entries(terms).reduce((acc, [field, { values }]) => { @@ -232,7 +239,7 @@ function buildFacets({ facetGroups }: { facetGroups: FacetGroups }) { type SearchConfig = Pick< SearchStoreState, - 'searchFields' | 'sourceFields' | 'endpoint' | 'swrConfig' | 'sortField' | 'size' | 'type' + 'searchFields' | 'sourceFields' | 'endpoint' | 'swrConfig' | 'sortField' | 'size' | 'type' | 'defaultQuery' > & { facets: FacetGroups; }; diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index b405403d90..18aa3221fb 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -1,3 +1,4 @@ +import esb from 'elastic-builder'; import { createStoreImmer, createStoreContext } from 'js/helpers/zustand'; import { SWRConfiguration } from 'swr'; @@ -54,6 +55,7 @@ export interface FacetsState { type SourceFields = Record; export interface SearchStoreState extends FacetsState { + defaultQuery?: esb.Query; search: string; searchFields: string[]; sortField: SortField; diff --git a/context/app/static/js/pages/search/S.tsx b/context/app/static/js/pages/search/S.tsx index 9a593f0463..7adf5ef06e 100644 --- a/context/app/static/js/pages/search/S.tsx +++ b/context/app/static/js/pages/search/S.tsx @@ -1,8 +1,10 @@ import React from 'react'; +import esb from 'elastic-builder'; import Search from 'js/components/search'; import { FACETS } from 'js/components/search/store'; import { EntityWithType, isDonor } from 'js/components/types'; +import { getPortalESField } from 'js/components/search/buildTypesMap'; const sharedConfig = { searchFields: ['all_text', 'description'], @@ -36,8 +38,19 @@ function makeDonorMetadataFilters(e: EntityWithType) { ]; } +function buildDefaultQuery(type: 'Dataset' | 'Donor' | 'Sample') { + return { + defaultQuery: esb + .boolQuery() + .must([ + esb.termsQuery(getPortalESField('entity_type'), [type]), + esb.boolQuery().mustNot([esb.existsQuery('next_revision_uuid'), esb.existsQuery('sub_status')]), + ]), + }; +} + const donorFacetGroups = { - 'Donor Metadata': makeDonorMetadataFilters({ entity_type: 'Dataset' }), + 'Donor Metadata': makeDonorMetadataFilters({ entity_type: 'Donor' }), Affiliation: sharedAffiliationFilters, }; @@ -56,6 +69,7 @@ const donorConfig = { tile: [...sharedTileFields, 'mapped_metadata.age_unit'], }, facets: donorFacetGroups, + ...buildDefaultQuery('Donor'), // TODO: figure out how to make assertion unnecessary. type: 'Donor' as const, }; @@ -71,7 +85,7 @@ const sampleFacetGroups = { type: FACETS.term, }, ], - 'Donor Metadata': makeDonorMetadataFilters({ entity_type: 'Dataset' }), + 'Donor Metadata': makeDonorMetadataFilters({ entity_type: 'Sample' }), Affiliation: sharedAffiliationFilters, }; @@ -88,6 +102,7 @@ const sampleConfig = { tile: [...sharedTileFields, 'origin_samples_unique_mapped_organs'], }, facets: sampleFacetGroups, + ...buildDefaultQuery('Sample'), // TODO: figure out how to make assertion unnecessary. type: 'Sample' as const, }; @@ -149,6 +164,7 @@ const datasetConfig = { tile: [...sharedTileFields, 'thumbnail_file.file_uuid', 'origin_samples_unique_mapped_organs'], }, facets: datasetFacetGroups, + ...buildDefaultQuery('Dataset'), // TODO: figure out how to make assertion unnecessary. type: 'Dataset' as const, }; From eedfadafae9d2ead951c1b7fdc4e2d7b2bac46d8 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Sun, 30 Jun 2024 19:22:09 -0400 Subject: [PATCH 40/73] Sync url state --- .../static/js/components/search/Search.tsx | 125 ++++++++++++++---- .../static/js/components/search/SearchBar.tsx | 4 +- .../app/static/js/components/search/store.ts | 93 +++++++++++-- context/package-lock.json | 3 +- context/package.json | 3 +- 5 files changed, 187 insertions(+), 41 deletions(-) diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index e899eb6b3b..ae60768f86 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { AggregationsTermsAggregateBase } from '@elastic/elasticsearch/lib/api/types'; import esb from 'elastic-builder'; import { produce } from 'immer'; @@ -7,7 +7,8 @@ import Stack from '@mui/material/Stack'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; - +import { parse } from 'qs'; +import merge from 'deepmerge'; import { useAppContext } from 'js/components/Contexts'; import SelectableTableProvider from 'js/shared-styles/tables/SelectableTableProvider'; import WorkspacesDropdownMenu from 'js/components/workspaces/WorkspacesDropdownMenu'; @@ -22,6 +23,13 @@ import { TermConfig, RangeConfig, FACETS, + rangeHasValues, + termHasValues, + hierarchicalTermHasValues, + convertState, + SearchURLState, + Term, + HierarchicalTerm, } from './store'; import Results from './Results'; import { getPortalESField } from './buildTypesMap'; @@ -81,53 +89,54 @@ function buildQuery({ const freeTextQueries = hasTextQuery ? [esb.simpleQueryStringQuery(search).fields(searchFields)] : []; const defaultQueries = defaultQuery ? [defaultQuery] : []; + query.query(esb.boolQuery().must([...defaultQueries, ...freeTextQueries])); if (hasTextQuery) { query.highlight(esb.highlight(searchFields)); } - const termFilters = Object.entries(terms).reduce((acc, [field, { values }]) => { + const termFilters = Object.entries(terms).reduce((acc, [field, term]) => { return produce(acc, (draft) => { + const { values } = term; const portalField = getPortalESField(field); - if (values.size) { + if (termHasValues(term)) { draft[portalField] = esb.termsQuery(portalField, [...values]); } }); }, {}); - const rangeFilters = Object.entries(ranges).reduce((acc, [field, { values, min, max }]) => { + const rangeFilters = Object.entries(ranges).reduce((acc, [field, range]) => { return produce(acc, (draft) => { + const { values } = range; const portalField = getPortalESField(field); - if (values.min !== min || values.max !== max) { + if (rangeHasValues(range)) { draft[portalField] = esb.rangeQuery(portalField).gte(values.min).lte(values.max); } }); }, {}); - const hierarchicalFilters = Object.entries(hierarchicalTerms).reduce( - (acc, [field, { values, childField }]) => { - return produce(acc, (draft) => { - if (!childField) { - return acc; - } - const parentPortalField = getPortalESField(field); - const childPortalField = getPortalESField(childField); + const hierarchicalFilters = Object.entries(hierarchicalTerms).reduce((acc, [field, hierarchicalTerm]) => { + return produce(acc, (draft) => { + const { values, childField } = hierarchicalTerm; + if (!childField) { + return acc; + } + const parentPortalField = getPortalESField(field); + const childPortalField = getPortalESField(childField); - if (Object.keys(values).length) { - draft[parentPortalField] = esb.termsQuery(parentPortalField, Object.keys(values)); + if (hierarchicalTermHasValues(hierarchicalTerm)) { + draft[parentPortalField] = esb.termsQuery(parentPortalField, Object.keys(values)); - const childValues = Object.values(values) - .map((v) => [...v]) - .flat(); - draft[childPortalField] = esb.termsQuery(childPortalField, childValues); - } - return draft; - }); - }, - {}, - ); + const childValues = Object.values(values) + .map((v) => [...v]) + .flat(); + draft[childPortalField] = esb.termsQuery(childPortalField, childValues); + } + return draft; + }); + }, {}); const allFilters = { ...termFilters, ...rangeFilters, ...hierarchicalFilters }; @@ -202,7 +211,6 @@ type Aggregations = Record< export function useSearch() { const { endpoint, swrConfig = {}, ...rest }: SearchStoreState = useSearchStore(); - const query = buildQuery({ ...rest }); return useScrollSearchHits, Aggregations>({ query, endpoint, swrConfig }); @@ -314,14 +322,73 @@ function Search({ type, facetGroups }: TypeProps & { facetGroups: FacetGroups }) ); } +const mergeTerms = (termState: Record, termURLState: Record>) => { + return Object.entries(termState).reduce>((acc, [k, v]) => { + return produce(acc, (draft) => { + const urlStateValues = termURLState?.[k]?.values ?? []; + draft[k] = { ...v, values: new Set([...v.values, ...urlStateValues]) }; + }); + }, {}); +}; + +const mergeHierarchicalTerms = ( + termState: Record, + termURLState: Record>, +) => { + return { ...termState, ...termURLState }; +}; + +const options = { + customMerge: (key: string) => { + if (key === 'terms') { + return mergeTerms; + } + if (key === 'hierarchicalTerms') { + return mergeHierarchicalTerms; + } + return undefined; + }, +}; + +function useInitialURLState() { + const [hasLoadedURLState, setHasLoadedURLState] = useState(false); + const [initialUrlState, setInitialUrlState] = useState>({ terms: {} }); + + useEffect(() => { + const searchParams: Partial = parse(window.location.search, { ignoreQueryPrefix: true }); + + if (Object.keys(searchParams).length) { + setInitialUrlState(convertState(searchParams)); + } + setHasLoadedURLState(true); + }, []); + + return { initialUrlState, hasLoadedURLState }; +} + function SearchWrapper({ config }: { config: Omit }) { const { elasticsearchEndpoint } = useAppContext(); - const { type, facets } = config; + const { search, sortField, terms, hierarchicalTerms, ranges, ...rest } = buildInitialSearchState({ + ...config, + endpoint: elasticsearchEndpoint, + }); + + const { initialUrlState, hasLoadedURLState } = useInitialURLState(); + + if (!hasLoadedURLState) { + return null; + } + + const initialState = { + ...merge({ search, sortField, terms, hierarchicalTerms, ranges }, initialUrlState, options), + ...rest, + }; + return ( - + diff --git a/context/app/static/js/components/search/SearchBar.tsx b/context/app/static/js/components/search/SearchBar.tsx index 8edbc5af42..70dddb84e9 100644 --- a/context/app/static/js/components/search/SearchBar.tsx +++ b/context/app/static/js/components/search/SearchBar.tsx @@ -4,9 +4,9 @@ import SearchBarComponent from 'js/shared-styles/inputs/SearchBar'; import { useSearchStore } from './store'; function SearchBar() { - const { setSearch } = useSearchStore(); + const { setSearch, search } = useSearchStore(); - const [input, setInput] = useState(''); + const [input, setInput] = useState(search); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index 18aa3221fb..353b354b49 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -1,4 +1,6 @@ import esb from 'elastic-builder'; +import { produce } from 'immer'; +import { stringify } from 'qs'; import { createStoreImmer, createStoreContext } from 'js/helpers/zustand'; import { SWRConfiguration } from 'swr'; @@ -34,27 +36,27 @@ export interface RangeConfig extends FacetConfig { type: typeof FACETS.range; } -export interface Term extends TermConfig { - values: Set; +export interface Term> extends TermConfig { + values: V; } -export interface HierarchicalTerm extends HierarchicalTermConfig { - values: Record>; +export interface HierarchicalTerm> extends HierarchicalTermConfig { + values: Record; } interface Range extends RangeConfig { values: { min: number; max: number }; } -export interface FacetsState { - terms: Record; - hierarchicalTerms: Record; +export interface FacetsState> { + terms: Record>; + hierarchicalTerms: Record>; ranges: Record; } type SourceFields = Record; -export interface SearchStoreState extends FacetsState { +export interface SearchState extends FacetsState { defaultQuery?: esb.Query; search: string; searchFields: string[]; @@ -67,6 +69,9 @@ export interface SearchStoreState extends FacetsState { type: 'Donor' | 'Sample' | 'Dataset'; } +export type SearchStoreState = SearchState>; +export type SearchURLState = Partial>; + export interface SearchStoreActions { setSearch: (search: string) => void; setView: (view: string) => void; @@ -95,12 +100,79 @@ export interface SearchStoreActions { export interface SearchStore extends SearchStoreState, SearchStoreActions {} +export function termHasValues({ values }: Term) { + return values.size; +} + +export function hierarchicalTermHasValues({ values }: HierarchicalTerm) { + return Object.keys(values).length; +} + +export function rangeHasValues({ values, min, max }: Range) { + return values.min !== min || values.max !== max; +} + +export function convertState(state: SearchURLState) { + const { terms = {}, hierarchicalTerms = {} } = state; + + const t = Object.entries(terms).reduce>((acc, [k, v]) => { + return produce(acc, (draft) => { + draft[k] = { ...v, values: new Set(v.values) }; + }); + }, {}); + + const h = Object.entries(hierarchicalTerms).reduce>((acc, [k, v]) => { + return produce(acc, (draft) => { + draft[k] = { + ...v, + values: Object.entries(v.values).reduce>>((childAcc, [childKey, childValues]) => { + return produce(childAcc, (childDraft) => { + childDraft[childKey] = new Set(childValues); + }); + }, {}), + }; + }); + }, {}); + + return { ...state, terms: t, hierarchicalTerms: h }; +} + +function replaceURLSearchParams(state: SearchStoreState) { + const { search, sortField, terms, hierarchicalTerms, ranges } = state; + + const termsWithValues = Object.fromEntries(Object.entries(terms).filter(([_k, v]) => termHasValues(v))); + const hierarchicalTermsWithValues = Object.fromEntries( + Object.entries(hierarchicalTerms).filter(([_k, v]) => hierarchicalTermHasValues(v)), + ); + const rangesWithValues = Object.fromEntries(Object.entries(ranges).filter(([_k, v]) => rangeHasValues(v))); + + const urlState = { + search, + sortField, + terms: termsWithValues, + hierarchicalTerms: hierarchicalTermsWithValues, + ranges: rangesWithValues, + }; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const urlStateWithArrays: URLSearchParams = JSON.parse( + JSON.stringify(urlState, (_key, value: unknown) => (value instanceof Set ? [...value] : value)), + ); + + const urlCopy = new URL(String(window.location)); + urlCopy.search = stringify(urlStateWithArrays); + + // eslint-disable-next-line no-restricted-globals + history.pushState(null, '', urlCopy); +} + export const createStore = ({ initialState }: { initialState: SearchStoreState }) => createStoreImmer((set) => ({ ...initialState, setSearch: (search) => { set((state) => { state.search = search; + replaceURLSearchParams(state); }); }, setView: (view) => { @@ -111,6 +183,7 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } setSortField: (sortField) => { set((state) => { state.sortField = sortField; + replaceURLSearchParams(state); }); }, filterTerm: ({ term, value }) => { @@ -124,6 +197,7 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } } else { termSet.add(value); } + replaceURLSearchParams(state); }); }, filterHierarchicalParentTerm: ({ term, value, childValues }) => { @@ -141,6 +215,7 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } } else { values[value] = new Set(childValues); } + replaceURLSearchParams(state); }); }, filterHierarchicalChildTerm: ({ parentTerm, parentValue, value }) => { @@ -165,6 +240,7 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } } else { childValues.add(value); } + replaceURLSearchParams(state); }); }, filterRange: ({ field, min, max }) => { @@ -175,6 +251,7 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } return; } range.values = { min, max }; + replaceURLSearchParams(state); }); }, })); diff --git a/context/package-lock.json b/context/package-lock.json index aa26ba09a7..0508312785 100644 --- a/context/package-lock.json +++ b/context/package-lock.json @@ -41,6 +41,7 @@ "d3": "^7.9.0", "d3-array": "^3.2.4", "date-fns": "^3.6.0", + "deepmerge": "^4.3.1", "elastic-builder": "^2.29.0", "fast-deep-equal": "^3.1.3", "html-react-parser": "^5.1.10", @@ -50,7 +51,7 @@ "lz-string": "^1.5.0", "pretty-bytes": "^6.1.1", "prop-types": "^15.8.1", - "qs": "^6.12.0", + "qs": "^6.12.1", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", "react-device-detect": "^2.2.3", diff --git a/context/package.json b/context/package.json index 4f0022d45e..dd299d675f 100644 --- a/context/package.json +++ b/context/package.json @@ -34,6 +34,7 @@ "d3": "^7.9.0", "d3-array": "^3.2.4", "date-fns": "^3.6.0", + "deepmerge": "^4.3.1", "elastic-builder": "^2.29.0", "fast-deep-equal": "^3.1.3", "html-react-parser": "^5.1.10", @@ -43,7 +44,7 @@ "lz-string": "^1.5.0", "pretty-bytes": "^6.1.1", "prop-types": "^15.8.1", - "qs": "^6.12.0", + "qs": "^6.12.1", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", "react-device-detect": "^2.2.3", From 37df38bff5c1c14e32611f1c11035c3492b5622e Mon Sep 17 00:00:00 2001 From: John Conroy Date: Sun, 30 Jun 2024 22:02:22 -0400 Subject: [PATCH 41/73] Add redirect for old routes --- context/app/routes_main.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/context/app/routes_main.py b/context/app/routes_main.py index fe8eb7fe07..f29d2747b0 100644 --- a/context/app/routes_main.py +++ b/context/app/routes_main.py @@ -1,5 +1,5 @@ from flask import (render_template, current_app, abort, - session, request) + session, request, redirect, url_for) from .utils import get_default_flask_data, make_blueprint, get_organs @@ -43,7 +43,7 @@ def ccf_eui(): @blueprint.route('/search/') -def test_search(type): +def search(type): if type not in ['donors', 'samples', 'datasets']: abort(404) title = f'{type.capitalize()} Search' @@ -59,7 +59,14 @@ def test_search(type): ) -@blueprint.route('/dev-search') +@blueprint.route('/search') +def search_redirect(): + entity_type = request.args.get('entity_type[0]') + return redirect( + url_for('routes_main.search', type=f'{entity_type}s'.lower())) + + +@ blueprint.route('/dev-search') def dev_search(): title = 'Dev Search' flask_data = { @@ -73,7 +80,7 @@ def dev_search(): ) -@blueprint.route('/diversity') +@ blueprint.route('/diversity') def vis(): title = 'Donor Diversity' flask_data = { @@ -87,7 +94,7 @@ def vis(): ) -@blueprint.route('/collections') +@ blueprint.route('/collections') def collections(): flask_data = {**get_default_flask_data()} return render_template( @@ -97,7 +104,7 @@ def collections(): ) -@blueprint.route('/publications') +@ blueprint.route('/publications') def publications(): flask_data = {**get_default_flask_data()} return render_template( @@ -107,7 +114,7 @@ def publications(): ) -@blueprint.route('/my-lists') +@ blueprint.route('/my-lists') def my_lists(): flask_data = {**get_default_flask_data()} return render_template( @@ -117,7 +124,7 @@ def my_lists(): ) -@blueprint.route('/my-lists/') +@ blueprint.route('/my-lists/') def list_page(saved_list_uuid): flask_data = { **get_default_flask_data(), @@ -130,7 +137,7 @@ def list_page(saved_list_uuid): ) -@blueprint.route('/iframe/') +@ blueprint.route('/iframe/') def iframe_page(path): flask_data = { **get_default_flask_data(), @@ -143,7 +150,7 @@ def iframe_page(path): ) -@blueprint.route('/tutorials') +@ blueprint.route('/tutorials') def tutorials(): flask_data = { **get_default_flask_data(), @@ -155,7 +162,7 @@ def tutorials(): ) -@blueprint.route('/tutorials/') +@ blueprint.route('/tutorials/') def tutorial_detail(tutorial_name): flask_data = { **get_default_flask_data(), @@ -168,7 +175,7 @@ def tutorial_detail(tutorial_name): ) -@blueprint.route('/profile') +@ blueprint.route('/profile') def profile(): flask_data = {**get_default_flask_data()} return render_template( From 00f8e7f7513199d632194f966dc2b792e6c8d8f9 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Sun, 30 Jun 2024 22:47:58 -0400 Subject: [PATCH 42/73] Add tracking --- .../components/search/Facets/FilterChips.tsx | 18 +++++++-- .../js/components/search/Facets/TermFacet.tsx | 39 ++++++++++++++++--- .../search/Results/ResultsTable.tsx | 17 ++++++-- .../search/Results/ResultsTiles.tsx | 12 +++++- .../static/js/components/search/Search.tsx | 13 ++++++- .../static/js/components/search/SearchBar.tsx | 29 ++++++++++---- .../js/components/search/SearchViewSwitch.tsx | 10 ++++- .../app/static/js/components/search/store.ts | 1 + context/app/static/js/helpers/trackers.js | 3 +- 9 files changed, 116 insertions(+), 26 deletions(-) diff --git a/context/app/static/js/components/search/Facets/FilterChips.tsx b/context/app/static/js/components/search/Facets/FilterChips.tsx index dca7a47a3b..6cc7cd3cbc 100644 --- a/context/app/static/js/components/search/Facets/FilterChips.tsx +++ b/context/app/static/js/components/search/Facets/FilterChips.tsx @@ -1,12 +1,24 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import Chip, { ChipProps } from '@mui/material/Chip'; import Stack from '@mui/material/Stack'; +import { trackEvent } from 'js/helpers/trackers'; import { useSearchStore } from '../store'; import { getFieldLabel } from '../labelMap'; -function FilterChip(props: ChipProps) { - return ; +function FilterChip({ onDelete, label, ...props }: ChipProps & { onDelete: () => void }) { + const { analyticsCategory } = useSearchStore(); + + const handleDelete = useCallback(() => { + onDelete(); + trackEvent({ + category: analyticsCategory, + action: 'Unselect Facet Chip', + label, + }); + }, [onDelete, label, analyticsCategory]); + + return ; } function FilterChips() { diff --git a/context/app/static/js/components/search/Facets/TermFacet.tsx b/context/app/static/js/components/search/Facets/TermFacet.tsx index 783c20d261..482e1b6880 100644 --- a/context/app/static/js/components/search/Facets/TermFacet.tsx +++ b/context/app/static/js/components/search/Facets/TermFacet.tsx @@ -7,6 +7,7 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { AggregationsBuckets } from '@elastic/elasticsearch/lib/api/types'; import { TooltipIconButton } from 'js/shared-styles/buttons/TooltipButton'; +import { trackEvent } from 'js/helpers/trackers'; import { useSearch } from '../Search'; import { useSearchStore } from '../store'; import { @@ -24,6 +25,7 @@ import { getFieldLabel } from '../labelMap'; interface CheckboxItem { label: string; count: number; + title: string; active: boolean; onClick: () => void; indeterminate?: boolean; @@ -31,7 +33,7 @@ interface CheckboxItem { type LabelTransformations = ((label: string) => string)[]; -interface TermLabelCount extends Omit { +interface TermLabelCount extends Omit { labelTransformations?: LabelTransformations; } @@ -51,7 +53,20 @@ export function TermLabelAndCount({ label, count, active, labelTransformations = ); } -function CheckboxFilterItem({ active, label, count, onClick, indeterminate = false }: CheckboxItem) { +function CheckboxFilterItem({ active, label, title, count, onClick, indeterminate = false }: CheckboxItem) { + const { analyticsCategory } = useSearchStore(); + + const handleClick = useCallback(() => { + onClick(); + + const facetAction = active ? 'Unselect' : 'Select'; + trackEvent({ + category: analyticsCategory, + action: `${facetAction} Facet`, + label: `${title}: ${label}`, + }); + }, [active, analyticsCategory, title, label, onClick]); + return ( } checkedIcon={} - onClick={onClick} + onClick={handleClick} /> } label={} @@ -78,7 +93,11 @@ interface TermFacet extends Omit { export function TermFacetItem({ label, field, ...rest }: TermFacet) { const { filterTerm } = useSearchStore(); - return filterTerm({ term: field, value: label })} label={label} {...rest} />; + const handleClick = useCallback(() => { + filterTerm({ term: field, value: label }); + }, [filterTerm, field, label]); + + return ; } export function TermFacet({ field }: { field: string }) { @@ -93,8 +112,9 @@ export function TermFacet({ field }: { field: string }) { return null; } + const title = getFieldLabel(field); return ( - + {aggBuckets.map((bucket) => ( ))} @@ -146,6 +167,7 @@ export function HierarchicalTermFacetItem({ childBuckets, parentField, childField, + title, ...rest }: TermFacet & { parentField: string; @@ -202,6 +224,7 @@ export function HierarchicalTermFacetItem({ childValues={childValues} label={label} field={field} + title={title} {...rest} indeterminate={childState?.size > 0 && !childValues.every((v) => childState?.has(v))} /> @@ -215,6 +238,7 @@ export function HierarchicalTermFacetItem({ count={doc_count} parentValue={label} active={childState?.has(key)} + title={title} /> ))} @@ -241,8 +265,10 @@ export function HierarchicalTermFacet({ field: parentField, childField }: { fiel return null; } + const title = getFieldLabel(parentField); + return ( - + {parentBuckets.map((bucket) => ( ))} diff --git a/context/app/static/js/components/search/Results/ResultsTable.tsx b/context/app/static/js/components/search/Results/ResultsTable.tsx index 808d316c0c..74adeca6d9 100644 --- a/context/app/static/js/components/search/Results/ResultsTable.tsx +++ b/context/app/static/js/components/search/Results/ResultsTable.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; import TableRow from '@mui/material/TableRow'; import TableHead from '@mui/material/TableHead'; @@ -11,6 +11,7 @@ import { InternalLink } from 'js/shared-styles/Links'; import { Entity } from 'js/components/types'; import SelectableHeaderCell from 'js/shared-styles/tables/SelectableHeaderCell'; import SelectableRowCell from 'js/shared-styles/tables/SelectableRowCell'; +import { trackEvent } from 'js/helpers/trackers'; import { getByPath } from './utils'; import { StyledTable, @@ -56,16 +57,26 @@ export function getSortOrder({ } function SortHeaderCell({ field, label }: { field: string; label: string }) { - const { sortField, setSortField } = useSearchStore(); + const { sortField, setSortField, analyticsCategory } = useSearchStore(); const { direction, field: currentSortField } = sortField; const isCurrentSortField = field === currentSortField; + const handleClick = useCallback(() => { + const newSortDirection = getSortOrder({ direction, isCurrentSortField }); + setSortField({ direction: newSortDirection, field }); + trackEvent({ + category: analyticsCategory, + action: `Sort Table View`, + label: `${label} ${newSortDirection}}`, + }); + }, [analyticsCategory, direction, field, isCurrentSortField, label, setSortField]); + return ( {label} - setSortField({ direction: getSortOrder({ direction, isCurrentSortField }), field })}> + diff --git a/context/app/static/js/components/search/Results/ResultsTiles.tsx b/context/app/static/js/components/search/Results/ResultsTiles.tsx index a2bb389c61..c9e1d7cc4b 100644 --- a/context/app/static/js/components/search/Results/ResultsTiles.tsx +++ b/context/app/static/js/components/search/Results/ResultsTiles.tsx @@ -12,6 +12,7 @@ import EntityTile, { tileWidth } from 'js/components/entity-tile/EntityTile'; import { getTileDescendantCounts } from 'js/components/entity-tile/EntityTile/utils'; import { capitalizeString } from 'js/helpers/functions'; import TileGrid from 'js/shared-styles/tiles/TileGrid'; +import { trackEvent } from 'js/helpers/trackers'; import { useSearch } from '../Search'; import ViewMoreResults from './ViewMoreResults'; import { useSearchStore } from '../store'; @@ -22,14 +23,21 @@ function TilesSortSelect() { sortField, setSortField, sourceFields: { table: tableFields }, + analyticsCategory, } = useSearchStore(); const handleChange = useCallback( (event: SelectChangeEvent) => { const selectedField = event.target.value; - setSortField({ field: selectedField, direction: selectedField === 'last_modified_timestamp' ? 'desc' : 'asc' }); + const direction = selectedField === 'last_modified_timestamp' ? 'desc' : 'asc'; + setSortField({ field: selectedField, direction }); + trackEvent({ + category: analyticsCategory, + action: `Sort Tile View`, + label: `${selectedField} ${direction}`, + }); }, - [setSortField], + [setSortField, analyticsCategory], ); return ( diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index ae60768f86..419c50495e 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -247,7 +247,15 @@ function buildFacets({ facetGroups }: { facetGroups: FacetGroups }) { type SearchConfig = Pick< SearchStoreState, - 'searchFields' | 'sourceFields' | 'endpoint' | 'swrConfig' | 'sortField' | 'size' | 'type' | 'defaultQuery' + | 'searchFields' + | 'sourceFields' + | 'endpoint' + | 'swrConfig' + | 'sortField' + | 'size' + | 'type' + | 'defaultQuery' + | 'analyticsCategory' > & { facets: FacetGroups; }; @@ -366,13 +374,14 @@ function useInitialURLState() { return { initialUrlState, hasLoadedURLState }; } -function SearchWrapper({ config }: { config: Omit }) { +function SearchWrapper({ config }: { config: Omit }) { const { elasticsearchEndpoint } = useAppContext(); const { type, facets } = config; const { search, sortField, terms, hierarchicalTerms, ranges, ...rest } = buildInitialSearchState({ ...config, endpoint: elasticsearchEndpoint, + analyticsCategory: `${type}s Search Page Interactions`, }); const { initialUrlState, hasLoadedURLState } = useInitialURLState(); diff --git a/context/app/static/js/components/search/SearchBar.tsx b/context/app/static/js/components/search/SearchBar.tsx index 70dddb84e9..2d1b381883 100644 --- a/context/app/static/js/components/search/SearchBar.tsx +++ b/context/app/static/js/components/search/SearchBar.tsx @@ -1,17 +1,32 @@ -import React, { useState } from 'react'; - +import React, { useCallback, useState } from 'react'; +import { trackSiteSearch, trackEvent } from 'js/helpers/trackers'; import SearchBarComponent from 'js/shared-styles/inputs/SearchBar'; import { useSearchStore } from './store'; function SearchBar() { - const { setSearch, search } = useSearchStore(); + const { setSearch, search, analyticsCategory } = useSearchStore(); const [input, setInput] = useState(search); - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - setSearch(input.match(/^\s*HBM\S+\s*$/i) ? `"${input}"` : input); - }; + const handleSubmit = useCallback( + (e: React.FormEvent) => { + e.preventDefault(); + + setSearch(input.match(/^\s*HBM\S+\s*$/i) ? `"${input}"` : input); + + const category = 'Free Text Search'; + + trackSiteSearch(input, category); + // TODO: Remove trackEvent, eventually? + trackEvent({ + category: analyticsCategory, + action: category, + label: input, + }); + }, + [analyticsCategory, input, setSearch], + ); + return (
, v: string) => { + trackEvent({ + category: analyticsCategory, + action: `Switch Search View`, + label: `${v}`, + }); setView(v); }, - [setView], + [setView, analyticsCategory], ); return ( diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index 353b354b49..d3902d6156 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -67,6 +67,7 @@ export interface SearchState extends FacetsState { endpoint: string; swrConfig?: SWRConfiguration; type: 'Donor' | 'Sample' | 'Dataset'; + analyticsCategory: string; } export type SearchStoreState = SearchState>; diff --git a/context/app/static/js/helpers/trackers.js b/context/app/static/js/helpers/trackers.js index f7c287acd8..a5a85200fc 100644 --- a/context/app/static/js/helpers/trackers.js +++ b/context/app/static/js/helpers/trackers.js @@ -140,9 +140,10 @@ function trackLink(href, type) { faro.api.pushEvent(type || 'Outbound Link Clicked', { href }); } -function trackSiteSearch(keyword) { +function trackSiteSearch(keyword, category) { tracker.trackSiteSearch({ keyword, + category, }); /* We currently call both trackEvent and trackSiteSearch: From 698f3408f288043f49a479691a6fe2eebe41b3f3 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Sun, 30 Jun 2024 22:58:13 -0400 Subject: [PATCH 43/73] Increase agg size --- context/app/static/js/components/search/Search.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 419c50495e..e73dc06819 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -44,6 +44,8 @@ import MetadataMenu from '../searchPage/MetadataMenu'; type Filters = Record; +const maxAggSize = 10000; + function buildFilterAggregation({ field, portalFields, @@ -148,7 +150,7 @@ function buildQuery({ const filterAggregation = buildFilterAggregation({ field, portalFields: [portalField], - aggregation: esb.termsAggregation(field, portalField), + aggregation: esb.termsAggregation(field, portalField).size(maxAggSize), filters: allFilters, }); @@ -182,7 +184,8 @@ function buildQuery({ portalFields: [parentPortalField, childPortalField], aggregation: esb .termsAggregation(field, parentPortalField) - .agg(esb.termsAggregation(childField, childPortalField)), + .size(maxAggSize) + .agg(esb.termsAggregation(childField, childPortalField).size(maxAggSize)), filters: allFilters, }); From b31397aa6060a92fcb79faa9bff4060d171501b1 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 1 Jul 2024 09:10:51 -0400 Subject: [PATCH 44/73] Track view more results --- .../search/Results/ViewMoreResults.tsx | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/context/app/static/js/components/search/Results/ViewMoreResults.tsx b/context/app/static/js/components/search/Results/ViewMoreResults.tsx index e7f2daa89e..a059416054 100644 --- a/context/app/static/js/components/search/Results/ViewMoreResults.tsx +++ b/context/app/static/js/components/search/Results/ViewMoreResults.tsx @@ -1,21 +1,35 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import Button from '@mui/material/Button'; import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; +import { trackEvent } from 'js/helpers/trackers'; import { useSearch } from '../Search'; +import { useSearchStore } from '../store'; function ViewMoreResults() { const { searchHits: hits, loadMore, totalHitsCount } = useSearch(); + const { analyticsCategory } = useSearchStore(); + + const resultsShown = `${hits.length} Results Shown | ${totalHitsCount} Total Results`; + + const handleClick = useCallback(() => { + loadMore(); + trackEvent({ + category: analyticsCategory, + action: 'View More Results', + label: resultsShown, + }); + }, [analyticsCategory, resultsShown, loadMore]); return ( <> - - {hits.length} Results Shown | {totalHitsCount} Total Results + {resultsShown} From d8576d0a40fef2cd97bf544b05e6c4ad527c1e34 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 1 Jul 2024 09:18:45 -0400 Subject: [PATCH 45/73] Handle last modied table cells --- .../js/components/search/Results/ResultsTable.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/context/app/static/js/components/search/Results/ResultsTable.tsx b/context/app/static/js/components/search/Results/ResultsTable.tsx index 74adeca6d9..cbade7b14e 100644 --- a/context/app/static/js/components/search/Results/ResultsTable.tsx +++ b/context/app/static/js/components/search/Results/ResultsTable.tsx @@ -6,6 +6,7 @@ import IconButton from '@mui/material/IconButton'; import Box from '@mui/material/Box'; import DOMPurify from 'isomorphic-dompurify'; import parse from 'html-react-parser'; +import { format } from 'date-fns/format'; import { InternalLink } from 'js/shared-styles/Links'; import { Entity } from 'js/components/types'; @@ -83,6 +84,17 @@ function SortHeaderCell({ field, label }: { field: string; label: string }) { ); } +function CellContent({ field, fieldValue }: { field: string; fieldValue: string }) { + switch (field) { + case 'hubmap_id': + return {fieldValue}; + case 'last_modified_timestamp': + return format(fieldValue, 'yyyy-MM-dd'); + default: + return fieldValue; + } +} + function ResultCell({ hit, field }: { field: string; hit: SearchHit> }) { const source = hit?._source; @@ -94,7 +106,7 @@ function ResultCell({ hit, field }: { field: string; hit: SearchHit - {field === 'hubmap_id' ? {fieldValue} : fieldValue} + ); } From 561303a277708a0910085d55454a4a08b7e40697 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 1 Jul 2024 15:01:02 -0400 Subject: [PATCH 46/73] Refactor filter state --- .../js/components/search/Facets/Facets.tsx | 12 +- .../components/search/Facets/FilterChips.tsx | 81 +++--- .../components/search/Facets/RangeFacet.tsx | 29 ++- .../js/components/search/Facets/TermFacet.tsx | 43 ++-- .../static/js/components/search/Search.tsx | 231 +++++++++--------- .../static/js/components/search/SearchBar.tsx | 15 +- .../app/static/js/components/search/store.ts | 153 ++++++------ 7 files changed, 311 insertions(+), 253 deletions(-) diff --git a/context/app/static/js/components/search/Facets/Facets.tsx b/context/app/static/js/components/search/Facets/Facets.tsx index ac721f68c1..389cd5f873 100644 --- a/context/app/static/js/components/search/Facets/Facets.tsx +++ b/context/app/static/js/components/search/Facets/Facets.tsx @@ -4,7 +4,7 @@ import Box from '@mui/material/Box'; import { TermFacet, HierarchicalTermFacet } from './TermFacet'; import RangeFacet from './RangeFacet'; import { FacetGroups } from '../Search'; -import { FACETS } from '../store'; +import { isHierarchicalFacet, isRangeFacet, isTermFacet } from '../store'; import FacetAccordion from './FacetAccordion'; export function Facets({ facetGroups }: { facetGroups: FacetGroups }) { @@ -13,14 +13,18 @@ export function Facets({ facetGroups }: { facetGroups: FacetGroups }) { {Object.entries(facetGroups).map(([k, v]) => ( {v.map((f) => { - if (f.type === FACETS.hierarchical) { + if (isHierarchicalFacet(f)) { return ; } - if (f.type === FACETS.range) { + if (isRangeFacet(f)) { return ; } - return ; + if (isTermFacet(f)) { + return ; + } + + return null; })} ))} diff --git a/context/app/static/js/components/search/Facets/FilterChips.tsx b/context/app/static/js/components/search/Facets/FilterChips.tsx index 6cc7cd3cbc..1dac7f7426 100644 --- a/context/app/static/js/components/search/Facets/FilterChips.tsx +++ b/context/app/static/js/components/search/Facets/FilterChips.tsx @@ -3,7 +3,17 @@ import Chip, { ChipProps } from '@mui/material/Chip'; import Stack from '@mui/material/Stack'; import { trackEvent } from 'js/helpers/trackers'; -import { useSearchStore } from '../store'; +import { + HierarchichalTermValues, + RangeValues, + TermValues, + isHierarchicalFacet, + isHierarchicalFilter, + isRangeFacet, + isRangeFilter, + isTermFilter, + useSearchStore, +} from '../store'; import { getFieldLabel } from '../labelMap'; function FilterChip({ onDelete, label, ...props }: ChipProps & { onDelete: () => void }) { @@ -22,44 +32,49 @@ function FilterChip({ onDelete, label, ...props }: ChipProps & { onDelete: () => } function FilterChips() { - const { terms, filterTerm, ranges, filterRange, hierarchicalTerms, filterHierarchicalChildTerm } = useSearchStore(); + const { filters, facets, filterTerm, filterRange, filterHierarchicalChildTerm } = useSearchStore(); return ( - {Object.values(terms).map((term) => - [...term.values].map((v) => ( - filterTerm({ term: term.field, value: v })} - /> - )), - )} - {Object.values(ranges).map(({ field, values, min, max }) => { - if (values.min === min && values.max === max) { - return null; - } - return ( - filterRange({ field, min, max })} - /> - ); - })} - {Object.values(hierarchicalTerms).map((term) => - Object.entries(term.values).map(([parent, children]) => { - return [...children].map((child) => ( + {Object.entries(filters).map(([field, v]: [string, RangeValues | HierarchichalTermValues | TermValues]) => { + if (isTermFilter(v)) { + return [...v.values].map((val) => ( - filterHierarchicalChildTerm({ parentTerm: term.field, parentValue: parent, value: child }) - } + label={`${getFieldLabel(field)}: ${val}`} + key={val} + onDelete={() => filterTerm({ term: field, value: val })} /> )); - }), - )} + } + const facetConfig = facets[field]; + + if (isRangeFilter(v) && isRangeFacet(facetConfig)) { + const { min, max } = facetConfig; + if (v.values.min === min && v.values.max === max) { + return null; + } + return ( + filterRange({ field, min, max })} + /> + ); + } + + if (isHierarchicalFilter(v) && isHierarchicalFacet(facetConfig)) { + return Object.entries(v.values).map(([parent, children]) => { + return [...children].map((child) => ( + filterHierarchicalChildTerm({ parentTerm: field, parentValue: parent, value: child })} + /> + )); + }); + } + return null; + })} ); } diff --git a/context/app/static/js/components/search/Facets/RangeFacet.tsx b/context/app/static/js/components/search/Facets/RangeFacet.tsx index 52656bbb68..9b2699768a 100644 --- a/context/app/static/js/components/search/Facets/RangeFacet.tsx +++ b/context/app/static/js/components/search/Facets/RangeFacet.tsx @@ -5,7 +5,7 @@ import Slider from '@mui/material/Slider'; import Box from '@mui/material/Box'; import Stack from '@mui/material/Stack'; import { useSearch } from '../Search'; -import { useSearchStore } from '../store'; +import { RangeConfig, RangeValues, isRangeFacet, isRangeFilter, useSearchStore } from '../store'; import FacetAccordion from './FacetAccordion'; import { getFieldLabel } from '../labelMap'; @@ -19,16 +19,14 @@ function buildBins({ buckets }: { buckets: HistogramBucket[] }) { return buckets.map((b) => ({ ...b, height: b.doc_count / maxCount })); } -function RangeFacet({ field }: { field: string }) { +function RangeFacet({ filter, field, facet }: { filter: RangeValues; field: string; facet: RangeConfig }) { const { aggregations } = useSearch(); - const { - ranges: { - [field]: { min, max, values }, - }, - filterRange, - } = useSearchStore(); + const { filterRange } = useSearchStore(); const theme = useTheme(); + const { values } = filter; + const { min, max } = facet; + const [selectedValues, setSelectedValues] = useState([min, max]); // Reset slider position when filter chip is deleted. @@ -111,4 +109,17 @@ function RangeFacet({ field }: { field: string }) { ); } -export default RangeFacet; +function FacetGuard({ field }: { field: string }) { + const { + filters: { [field]: filter }, + facets: { [field]: facet }, + } = useSearchStore(); + + if (!isRangeFilter(filter) || !isRangeFacet(facet)) { + return null; + } + + return ; +} + +export default FacetGuard; diff --git a/context/app/static/js/components/search/Facets/TermFacet.tsx b/context/app/static/js/components/search/Facets/TermFacet.tsx index 482e1b6880..951e0593f7 100644 --- a/context/app/static/js/components/search/Facets/TermFacet.tsx +++ b/context/app/static/js/components/search/Facets/TermFacet.tsx @@ -9,7 +9,7 @@ import { AggregationsBuckets } from '@elastic/elasticsearch/lib/api/types'; import { TooltipIconButton } from 'js/shared-styles/buttons/TooltipButton'; import { trackEvent } from 'js/helpers/trackers'; import { useSearch } from '../Search'; -import { useSearchStore } from '../store'; +import { isTermFilter, useSearchStore, TermValues, isHierarchicalFilter } from '../store'; import { StyledCheckBoxBlankIcon, StyledCheckBoxIcon, @@ -53,7 +53,7 @@ export function TermLabelAndCount({ label, count, active, labelTransformations = ); } -function CheckboxFilterItem({ active, label, title, count, onClick, indeterminate = false }: CheckboxItem) { +function CheckboxFilterItem({ active = false, label, title, count, onClick, indeterminate = false }: CheckboxItem) { const { analyticsCategory } = useSearchStore(); const handleClick = useCallback(() => { @@ -100,11 +100,8 @@ export function TermFacetItem({ label, field, ...rest }: TermFacet) { return ; } -export function TermFacet({ field }: { field: string }) { +function TermFacetContent({ filter, field }: { filter: TermValues; field: string }) { const { aggregations } = useSearch(); - const { - terms: { [field]: term }, - } = useSearchStore(); const aggBuckets = aggregations?.[field]?.[field]?.buckets; @@ -120,7 +117,7 @@ export function TermFacet({ field }: { field: string }) { label={bucket.key} count={bucket.doc_count} key={bucket.key} - active={term.values.has(bucket.key)} + active={filter.values.has(bucket.key)} field={field} title={title} /> @@ -129,6 +126,18 @@ export function TermFacet({ field }: { field: string }) { ); } +export function TermFacet({ field }: { field: string }) { + const { + filters: { [field]: filter }, + } = useSearchStore(); + + if (!isTermFilter(filter)) { + return null; + } + + return ; +} + function buildExpandTooltip({ expanded, disabled }: { expanded: boolean; disabled: boolean }) { if (disabled) { return undefined; @@ -180,17 +189,17 @@ export function HierarchicalTermFacetItem({ }, [setExpanded]); const { - hierarchicalTerms: { - [parentField]: { - values: { [label]: childState }, - }, - }, + filters: { [parentField]: filter }, } = useSearchStore(); - if (!childBuckets || !Array.isArray(childBuckets)) { + if (!childBuckets || !Array.isArray(childBuckets) || !isHierarchicalFilter(filter)) { return null; } + const { + values: { [label]: childState }, + } = filter; + const hasChildBuckets = childBuckets?.length; const childValues = childBuckets.map((b) => b.key); @@ -250,15 +259,15 @@ export function HierarchicalTermFacet({ field: parentField, childField }: { fiel const { aggregations } = useSearch(); const { - hierarchicalTerms: { - [parentField]: { values }, - }, + filters: { [parentField]: filter }, } = useSearchStore(); - if (!aggregations) { + if (!aggregations || !isHierarchicalFilter(filter)) { return null; } + const { values } = filter; + const parentBuckets = aggregations?.[parentField]?.[parentField]?.buckets; if (!parentBuckets || !Array.isArray(parentBuckets)) { diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index e73dc06819..a398de2796 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -18,18 +18,18 @@ import { SearchStoreProvider, useSearchStore, SearchStoreState, - FacetsState, HierarchicalTermConfig, TermConfig, RangeConfig, FACETS, - rangeHasValues, - termHasValues, - hierarchicalTermHasValues, - convertState, + filterHasValues, SearchURLState, - Term, - HierarchicalTerm, + FiltersType, + FacetsType, + isTermFilter, + isRangeFilter, + isHierarchicalFilter, + isHierarchicalFacet, } from './store'; import Results from './Results'; import { getPortalESField } from './buildTypesMap'; @@ -42,10 +42,10 @@ import { DefaultSearchViewSwitch } from './SearchViewSwitch'; import { TilesSortSelect } from './Results/ResultsTiles'; import MetadataMenu from '../searchPage/MetadataMenu'; -type Filters = Record; - const maxAggSize = 10000; +type FilterClauses = Record; + function buildFilterAggregation({ field, portalFields, @@ -55,7 +55,7 @@ function buildFilterAggregation({ field: string; portalFields: string[]; aggregation: esb.Aggregation; - filters: Filters; + filters: FilterClauses; }) { portalFields.forEach((f) => { if (f in filters) { @@ -71,11 +71,10 @@ function buildFilterAggregation({ } function buildQuery({ - terms, - hierarchicalTerms, - ranges, - size, + filters, + facets, search, + size, searchFields, sourceFields, sortField, @@ -98,98 +97,89 @@ function buildQuery({ query.highlight(esb.highlight(searchFields)); } - const termFilters = Object.entries(terms).reduce((acc, [field, term]) => { + const allFilters = Object.entries(filters).reduce((acc, [field, filter]) => { return produce(acc, (draft) => { - const { values } = term; - const portalField = getPortalESField(field); - if (termHasValues(term)) { - draft[portalField] = esb.termsQuery(portalField, [...values]); - } - }); - }, {}); - - const rangeFilters = Object.entries(ranges).reduce((acc, [field, range]) => { - return produce(acc, (draft) => { - const { values } = range; const portalField = getPortalESField(field); + const facetConfig = facets[field]; - if (rangeHasValues(range)) { - draft[portalField] = esb.rangeQuery(portalField).gte(values.min).lte(values.max); + if (isTermFilter(filter)) { + if (filterHasValues({ filter, facet: facetConfig })) { + draft[portalField] = esb.termsQuery(portalField, [...filter.values]); + } } - }); - }, {}); - const hierarchicalFilters = Object.entries(hierarchicalTerms).reduce((acc, [field, hierarchicalTerm]) => { - return produce(acc, (draft) => { - const { values, childField } = hierarchicalTerm; - if (!childField) { - return acc; + if (isRangeFilter(filter)) { + if (filterHasValues({ filter, facet: facetConfig })) { + draft[portalField] = esb.rangeQuery(portalField).gte(filter.values.min).lte(filter.values.max); + } } - const parentPortalField = getPortalESField(field); - const childPortalField = getPortalESField(childField); - if (hierarchicalTermHasValues(hierarchicalTerm)) { - draft[parentPortalField] = esb.termsQuery(parentPortalField, Object.keys(values)); + if (isHierarchicalFilter(filter) && isHierarchicalFacet(facetConfig)) { + if (filterHasValues({ filter, facet: facetConfig })) { + const childPortalField = getPortalESField(facetConfig.childField); - const childValues = Object.values(values) - .map((v) => [...v]) - .flat(); - draft[childPortalField] = esb.termsQuery(childPortalField, childValues); + draft[portalField] = esb.termsQuery(portalField, Object.keys(filter.values)); + + const childValues = Object.values(filter.values) + .map((v) => [...v]) + .flat(); + draft[childPortalField] = esb.termsQuery(childPortalField, childValues); + } } - return draft; }); }, {}); - const allFilters = { ...termFilters, ...rangeFilters, ...hierarchicalFilters }; - query.postFilter(esb.boolQuery().must(Object.values(allFilters))); - Object.keys(terms).forEach((field) => { - const portalField = getPortalESField(field); - - const filterAggregation = buildFilterAggregation({ - field, - portalFields: [portalField], - aggregation: esb.termsAggregation(field, portalField).size(maxAggSize), - filters: allFilters, - }); - - query.agg(filterAggregation); - }); - - Object.entries(ranges).forEach(([field, { min, max }]) => { + Object.values(facets).forEach((filter) => { + const { field } = filter; const portalField = getPortalESField(field); - const interval = Math.ceil((max - min) / 20); + if (filter.type === 'TERM') { + query.agg( + buildFilterAggregation({ + portalFields: [portalField], + aggregation: esb.termsAggregation(field, portalField).size(maxAggSize), + filters: { ...allFilters }, + field, + }), + ); + } - const filterAggregation = buildFilterAggregation({ - field, - portalFields: [portalField], - aggregation: esb.histogramAggregation(field, portalField, interval).extendedBounds(min, max), - filters: allFilters, - }); + if (filter.type === 'RANGE') { + const { min, max } = filter; + const interval = Math.ceil((max - min) / 20); + + query.agg( + buildFilterAggregation({ + portalFields: [portalField], + aggregation: esb.histogramAggregation(field, portalField, interval).extendedBounds(min, max), + filters: { ...allFilters }, + field, + }), + ); + } - query.agg(filterAggregation); - }); + if (filter.type === 'HIERARCHICAL') { + const { childField } = filter; + if (!childField) { + return; + } + const parentPortalField = getPortalESField(field); + const childPortalField = getPortalESField(childField); - Object.entries(hierarchicalTerms).forEach(([field, { childField }]) => { - if (!childField) { - return; + query.agg( + buildFilterAggregation({ + portalFields: [parentPortalField, childPortalField], + aggregation: esb + .termsAggregation(field, parentPortalField) + .size(maxAggSize) + .agg(esb.termsAggregation(childField, childPortalField).size(maxAggSize)), + filters: { ...allFilters }, + field, + }), + ); } - const parentPortalField = getPortalESField(field); - const childPortalField = getPortalESField(childField); - - const filterAggregation = buildFilterAggregation({ - field, - portalFields: [parentPortalField, childPortalField], - aggregation: esb - .termsAggregation(field, parentPortalField) - .size(maxAggSize) - .agg(esb.termsAggregation(childField, childPortalField).size(maxAggSize)), - filters: allFilters, - }); - - query.agg(filterAggregation); }); return query.toJSON(); @@ -226,25 +216,26 @@ export type FacetGroups = Record; function buildFacets({ facetGroups }: { facetGroups: FacetGroups }) { const allFacets = Object.values(facetGroups).flat(); - return allFacets.reduce( + return allFacets.reduce<{ filters: FiltersType; facets: FacetsType }>( (acc, curr) => { return produce(acc, (draft) => { if (curr.type === FACETS.term) { - draft.terms[curr.field] = { ...curr, values: new Set([]) }; + draft.filters[curr.field] = { values: new Set([]), type: curr.type }; } if (curr.type === FACETS.hierarchical) { - draft.hierarchicalTerms[curr.field] = { ...curr, values: {} }; + draft.filters[curr.field] = { values: {}, type: curr.type }; } if (curr.type === FACETS.range) { - draft.ranges[curr.field] = { ...curr, values: { min: curr.min, max: curr.max } }; + draft.filters[curr.field] = { values: { min: curr.min, max: curr.max }, type: curr.type }; } + draft.facets[curr.field] = curr; return draft; }); }, - { terms: {}, hierarchicalTerms: {}, ranges: {} }, + { filters: {}, facets: {} }, ); } @@ -333,29 +324,47 @@ function Search({ type, facetGroups }: TypeProps & { facetGroups: FacetGroups }) ); } -const mergeTerms = (termState: Record, termURLState: Record>) => { - return Object.entries(termState).reduce>((acc, [k, v]) => { +const mergeFilters = (filterState: FiltersType, filterURLState: FiltersType) => { + const z = Object.entries(filterState).reduce((acc, [k, v]) => { return produce(acc, (draft) => { - const urlStateValues = termURLState?.[k]?.values ?? []; - draft[k] = { ...v, values: new Set([...v.values, ...urlStateValues]) }; + const URLStateFilter = filterURLState?.[k]; + + if (isTermFilter(v)) { + const urlStateValues = URLStateFilter && isTermFilter(URLStateFilter) ? URLStateFilter.values : []; + draft[k] = { ...v, values: new Set([...v.values, ...urlStateValues]) }; + } + + if (isHierarchicalFilter(v)) { + const urlStateValues = + URLStateFilter && isHierarchicalFilter(URLStateFilter) ? URLStateFilter.values : {}; + draft[k] = { + ...v, + values: Object.fromEntries( + Object.entries(urlStateValues).map(([parentValue, childValues]) => [ + parentValue, + new Set([...childValues, ...urlStateValues[parentValue]]), + ]), + ), + }; + } + + if (isRangeFilter(v)) { + draft[k] = { + ...v, + values: URLStateFilter && isRangeFilter(URLStateFilter) ? URLStateFilter?.values : v.values, + }; + } + + return draft; }); }, {}); -}; - -const mergeHierarchicalTerms = ( - termState: Record, - termURLState: Record>, -) => { - return { ...termState, ...termURLState }; + return z; }; const options = { customMerge: (key: string) => { - if (key === 'terms') { - return mergeTerms; - } - if (key === 'hierarchicalTerms') { - return mergeHierarchicalTerms; + if (key === 'filters') { + return mergeFilters; } return undefined; }, @@ -363,13 +372,13 @@ const options = { function useInitialURLState() { const [hasLoadedURLState, setHasLoadedURLState] = useState(false); - const [initialUrlState, setInitialUrlState] = useState>({ terms: {} }); + const [initialUrlState, setInitialUrlState] = useState>({ filters: {} }); useEffect(() => { const searchParams: Partial = parse(window.location.search, { ignoreQueryPrefix: true }); if (Object.keys(searchParams).length) { - setInitialUrlState(convertState(searchParams)); + setInitialUrlState({ filters: {}, ...searchParams }); } setHasLoadedURLState(true); }, []); @@ -381,7 +390,7 @@ function SearchWrapper({ config }: { config: Omit> { + values: V; + type: typeof FACETS.term; +} + export interface HierarchicalTermConfig extends FacetConfig { childField: string; type: typeof FACETS.hierarchical; } +export interface HierarchichalTermValues> { + values: Record; + type: typeof FACETS.hierarchical; +} + export interface RangeConfig extends FacetConfig { min: number; max: number; type: typeof FACETS.range; } -export interface Term> extends TermConfig { - values: V; -} - -export interface HierarchicalTerm> extends HierarchicalTermConfig { - values: Record; +export interface RangeValues { + values: { + min: number; + max: number; + }; + type: typeof FACETS.range; } -interface Range extends RangeConfig { - values: { min: number; max: number }; -} +type Filter> = TermValues | HierarchichalTermValues | RangeValues; +type Facet = TermConfig | HierarchicalTermConfig | RangeConfig; -export interface FacetsState> { - terms: Record>; - hierarchicalTerms: Record>; - ranges: Record; -} +export type FiltersType> = Record>; +export type FacetsType = Record; type SourceFields = Record; -export interface SearchState extends FacetsState { +export interface SearchState { + filters: FiltersType; + facets: FacetsType; defaultQuery?: esb.Query; search: string; searchFields: string[]; @@ -101,58 +108,57 @@ export interface SearchStoreActions { export interface SearchStore extends SearchStoreState, SearchStoreActions {} -export function termHasValues({ values }: Term) { - return values.size; +export function isTermFilter>(filter: Filter): filter is TermValues { + return filter.type === 'TERM'; } -export function hierarchicalTermHasValues({ values }: HierarchicalTerm) { - return Object.keys(values).length; +export function isTermFacet(facet: Facet): facet is TermConfig { + return facet.type === 'TERM'; } -export function rangeHasValues({ values, min, max }: Range) { - return values.min !== min || values.max !== max; +export function isRangeFilter>(filter: Filter): filter is RangeValues { + return filter.type === 'RANGE'; } -export function convertState(state: SearchURLState) { - const { terms = {}, hierarchicalTerms = {} } = state; - - const t = Object.entries(terms).reduce>((acc, [k, v]) => { - return produce(acc, (draft) => { - draft[k] = { ...v, values: new Set(v.values) }; - }); - }, {}); - - const h = Object.entries(hierarchicalTerms).reduce>((acc, [k, v]) => { - return produce(acc, (draft) => { - draft[k] = { - ...v, - values: Object.entries(v.values).reduce>>((childAcc, [childKey, childValues]) => { - return produce(childAcc, (childDraft) => { - childDraft[childKey] = new Set(childValues); - }); - }, {}), - }; - }); - }, {}); - - return { ...state, terms: t, hierarchicalTerms: h }; +export function isRangeFacet(facet: Facet): facet is RangeConfig { + return facet.type === 'RANGE'; +} + +export function isHierarchicalFilter>(filter: Filter): filter is HierarchichalTermValues { + return filter.type === 'HIERARCHICAL'; +} + +export function isHierarchicalFacet(facet: Facet): facet is HierarchicalTermConfig { + return facet.type === 'HIERARCHICAL'; +} + +export function filterHasValues({ filter, facet }: { filter: Filter; facet: Facet }) { + if (isTermFilter(filter)) { + return filter.values.size; + } + + if (isHierarchicalFilter(filter)) { + return Object.keys(filter.values).length; + } + + if (isRangeFilter(filter) && isRangeFacet(facet)) { + return filter.values.min !== facet.min || filter.values.max !== facet.max; + } + + return false; } function replaceURLSearchParams(state: SearchStoreState) { - const { search, sortField, terms, hierarchicalTerms, ranges } = state; + const { search, sortField, filters, facets } = state; - const termsWithValues = Object.fromEntries(Object.entries(terms).filter(([_k, v]) => termHasValues(v))); - const hierarchicalTermsWithValues = Object.fromEntries( - Object.entries(hierarchicalTerms).filter(([_k, v]) => hierarchicalTermHasValues(v)), + const filtersWithValues = Object.fromEntries( + Object.entries(filters).filter(([k, v]) => filterHasValues({ filter: v, facet: facets[k] })), ); - const rangesWithValues = Object.fromEntries(Object.entries(ranges).filter(([_k, v]) => rangeHasValues(v))); const urlState = { search, sortField, - terms: termsWithValues, - hierarchicalTerms: hierarchicalTermsWithValues, - ranges: rangesWithValues, + filters: filtersWithValues, }; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment @@ -189,27 +195,29 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } }, filterTerm: ({ term, value }) => { set((state) => { - const termSet = state?.terms?.[term].values; - if (!termSet) { + const filter = state?.filters?.[term]; + + if (!isTermFilter(filter)) { return; } - if (termSet.has(value)) { - termSet.delete(value); + const { values } = filter; + + if (values.has(value)) { + values.delete(value); } else { - termSet.add(value); + values.add(value); } replaceURLSearchParams(state); }); }, filterHierarchicalParentTerm: ({ term, value, childValues }) => { set((state) => { - const termState = state?.hierarchicalTerms?.[term]; + const filter = state?.filters?.[term]; - if (!termState) { + if (!isHierarchicalFilter(filter)) { return; } - - const { values } = termState; + const { values } = filter; if (value in values) { delete values[value]; @@ -221,37 +229,38 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } }, filterHierarchicalChildTerm: ({ parentTerm, parentValue, value }) => { set((state) => { - const termState = state?.hierarchicalTerms?.[parentTerm]; + const filter = state?.filters?.[parentTerm]; - if (!termState) { + if (!isHierarchicalFilter(filter)) { return; } - const childValues = termState?.values?.[parentValue]; + const { values } = filter; - if (!childValues) { - return; - } + const childValues = values[parentValue] ?? new Set([]); if (childValues.has(value)) { childValues.delete(value); if (childValues.size === 0) { - delete termState.values[parentValue]; + delete filter.values[parentValue]; } } else { childValues.add(value); } + + filter.values[parentValue] = childValues; replaceURLSearchParams(state); }); }, filterRange: ({ field, min, max }) => { set((state) => { - const range = state?.ranges[field]; + const filter = state?.filters[field]; - if (!range) { + if (!isRangeFilter(filter)) { return; } - range.values = { min, max }; + + filter.values = { min, max }; replaceURLSearchParams(state); }); }, From 88fc038c602f1f1f72d9021aaa66f22f3d3f0ad7 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 1 Jul 2024 16:24:30 -0400 Subject: [PATCH 47/73] Fix hierarchical toggle --- context/app/static/js/components/search/store.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index 6772824522..8825c339ce 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -241,14 +241,16 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } if (childValues.has(value)) { childValues.delete(value); - if (childValues.size === 0) { - delete filter.values[parentValue]; - } } else { childValues.add(value); } - filter.values[parentValue] = childValues; + if (childValues.size === 0) { + delete filter.values[parentValue]; + } else { + filter.values[parentValue] = childValues; + } + replaceURLSearchParams(state); }); }, From 04febf01e6a59af0611d8c5e63e9cb46877de912 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 1 Jul 2024 16:44:53 -0400 Subject: [PATCH 48/73] Don't show view more button if hits reached --- .../js/components/search/Results/ViewMoreResults.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/context/app/static/js/components/search/Results/ViewMoreResults.tsx b/context/app/static/js/components/search/Results/ViewMoreResults.tsx index a059416054..916c7dbd9a 100644 --- a/context/app/static/js/components/search/Results/ViewMoreResults.tsx +++ b/context/app/static/js/components/search/Results/ViewMoreResults.tsx @@ -24,9 +24,11 @@ function ViewMoreResults() { return ( <> - + {hits.length !== totalHitsCount && ( + + )} {resultsShown} From 95f986bd13ee5f101d9e7ad90b487e13646c14ee Mon Sep 17 00:00:00 2001 From: John Conroy Date: Mon, 1 Jul 2024 18:32:30 -0400 Subject: [PATCH 49/73] Add expand for term facets --- .../js/components/search/Facets/TermFacet.tsx | 36 +++++++++++++++++-- .../static/js/components/search/Search.tsx | 27 ++++++++------ .../app/static/js/components/search/store.ts | 13 +++++++ 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/context/app/static/js/components/search/Facets/TermFacet.tsx b/context/app/static/js/components/search/Facets/TermFacet.tsx index 951e0593f7..6954bcb778 100644 --- a/context/app/static/js/components/search/Facets/TermFacet.tsx +++ b/context/app/static/js/components/search/Facets/TermFacet.tsx @@ -4,12 +4,13 @@ import IndeterminateCheckBoxOutlinedIcon from '@mui/icons-material/Indeterminate import Accordion from '@mui/material/Accordion'; import AccordionDetails from '@mui/material/AccordionDetails'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import Button from '@mui/material/Button'; import { AggregationsBuckets } from '@elastic/elasticsearch/lib/api/types'; import { TooltipIconButton } from 'js/shared-styles/buttons/TooltipButton'; import { trackEvent } from 'js/helpers/trackers'; import { useSearch } from '../Search'; -import { isTermFilter, useSearchStore, TermValues, isHierarchicalFilter } from '../store'; +import { isTermFilter, useSearchStore, TermValues, isHierarchicalFilter, isTermFacet } from '../store'; import { StyledCheckBoxBlankIcon, StyledCheckBoxIcon, @@ -100,10 +101,40 @@ export function TermFacetItem({ label, field, ...rest }: TermFacet) { return ; } +const smallAggSize = 5; +const maxAggSize = 10000; + +function FacetSizeButton({ field, hasMoreBuckets }: { field: string; hasMoreBuckets: boolean }) { + const { setTermSize, facets } = useSearchStore(); + + const facet = facets?.[field]; + + if (!isTermFacet(facet)) { + return null; + } + + if (facet?.size === smallAggSize && !hasMoreBuckets) { + return null; + } + + return ( + + ); +} + function TermFacetContent({ filter, field }: { filter: TermValues; field: string }) { const { aggregations } = useSearch(); - const aggBuckets = aggregations?.[field]?.[field]?.buckets; + const innerAggregations = aggregations?.[field]?.[field]; + const aggBuckets = innerAggregations?.buckets; + const hasMoreBuckets = Boolean(innerAggregations?.sum_other_doc_count); if (!aggBuckets || !Array.isArray(aggBuckets)) { return null; @@ -122,6 +153,7 @@ function TermFacetContent({ filter, field }: { filter: TermValues; field: string title={title} /> ))} + ); } diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index a398de2796..dd616bb8dc 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -30,6 +30,8 @@ import { isRangeFilter, isHierarchicalFilter, isHierarchicalFacet, + isTermFacet, + isRangeFacet, } from './store'; import Results from './Results'; import { getPortalESField } from './buildTypesMap'; @@ -131,23 +133,24 @@ function buildQuery({ query.postFilter(esb.boolQuery().must(Object.values(allFilters))); - Object.values(facets).forEach((filter) => { - const { field } = filter; + Object.values(facets).forEach((facet) => { + const { field } = facet; const portalField = getPortalESField(field); - if (filter.type === 'TERM') { + if (isTermFacet(facet)) { + const { size: facetSize } = facet; query.agg( buildFilterAggregation({ portalFields: [portalField], - aggregation: esb.termsAggregation(field, portalField).size(maxAggSize), + aggregation: esb.termsAggregation(field, portalField).size(facetSize ?? maxAggSize), filters: { ...allFilters }, field, }), ); } - if (filter.type === 'RANGE') { - const { min, max } = filter; + if (isRangeFacet(facet)) { + const { min, max } = facet; const interval = Math.ceil((max - min) / 20); query.agg( @@ -160,8 +163,8 @@ function buildQuery({ ); } - if (filter.type === 'HIERARCHICAL') { - const { childField } = filter; + if (isHierarchicalFacet(facet)) { + const { childField } = facet; if (!childField) { return; } @@ -187,10 +190,10 @@ function buildQuery({ interface OuterBucket { doc_count: number; + sum_other_doc_count?: number; } -interface InnerBucket { +interface InnerBucket extends OuterBucket { key: string; - doc_count: number; } type Aggregations = Record< @@ -221,17 +224,19 @@ function buildFacets({ facetGroups }: { facetGroups: FacetGroups }) { return produce(acc, (draft) => { if (curr.type === FACETS.term) { draft.filters[curr.field] = { values: new Set([]), type: curr.type }; + draft.facets[curr.field] = { ...curr, size: 5 }; } if (curr.type === FACETS.hierarchical) { draft.filters[curr.field] = { values: {}, type: curr.type }; + draft.facets[curr.field] = curr; } if (curr.type === FACETS.range) { draft.filters[curr.field] = { values: { min: curr.min, max: curr.max }, type: curr.type }; + draft.facets[curr.field] = curr; } - draft.facets[curr.field] = curr; return draft; }); }, diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index 8825c339ce..c4c7ce9be9 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -22,6 +22,7 @@ export interface FacetConfig { export interface TermConfig extends FacetConfig { type: typeof FACETS.term; + size?: number; } export interface TermValues> { @@ -84,6 +85,7 @@ export interface SearchStoreActions { setSearch: (search: string) => void; setView: (view: string) => void; setSortField: (sortField: SortField) => void; + setTermSize: ({ term, size }: { term: string; size: number }) => void; filterTerm: ({ term, value }: { term: string; value: string }) => void; filterHierarchicalParentTerm: ({ term, @@ -193,6 +195,17 @@ export const createStore = ({ initialState }: { initialState: SearchStoreState } replaceURLSearchParams(state); }); }, + setTermSize: ({ term, size }) => { + set((state) => { + const facet = state?.facets?.[term]; + + if (!isTermFacet(facet)) { + return; + } + + facet.size = size; + }); + }, filterTerm: ({ term, value }) => { set((state) => { const filter = state?.filters?.[term]; From 31965cb359e1c65ab4b8c9b0c74686db020582d7 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Tue, 8 Oct 2024 15:43:44 -0400 Subject: [PATCH 50/73] Update facets --- .../static/js/components/search/labelMap.ts | 30 +++++++++++-------- .../components/search/useScrollSearchHits.ts | 20 +++++-------- context/app/static/js/pages/search/S.tsx | 2 +- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/context/app/static/js/components/search/labelMap.ts b/context/app/static/js/components/search/labelMap.ts index 6da703cf11..5510ca05bb 100644 --- a/context/app/static/js/components/search/labelMap.ts +++ b/context/app/static/js/components/search/labelMap.ts @@ -1,24 +1,28 @@ const labelMap: Record = { - hubmap_id: 'HuBMAP ID', - group_name: 'Group', + age_value: 'Donor Age', + analyte_class: 'Analyte Class', assay_display_name: 'Data Types', - mapped_organ: 'Organ', - mapped_status: 'Status', + assay_modality: 'Assay Modalities', + body_mass_index_value: 'Donor BMI', + created_by_user_displayname: 'Registered By', dataset_type: 'Dataset Type', - last_modified_timestamp: 'Last Modified', entity_type: 'Entity Type', + group_name: 'Group', + hubmap_id: 'HuBMAP ID', + is_component: 'Component Dataset', + last_modified_timestamp: 'Last Modified', + mapped_consortium: 'Consortium', + mapped_organ: 'Organ', + mapped_status: 'Status', origin_samples_unique_mapped_organs: 'Organ', - analyte_class: 'Analyte Class', - sample_category: 'Sample Category', - processing: 'Processing', pipeline: 'Pipeline', - visualization: 'Visualization', + processing: 'Processing', processing_type: 'Processing Type', - assay_modality: 'Assay Modalities', - sex: 'Donor Sex', - age_value: 'Donor Age', race: 'Donor Race', - body_mass_index_value: 'Donor BMI', + raw_dataset_type: 'Dataset Type', + sample_category: 'Sample Category', + sex: 'Donor Sex', + visualization: 'Visualization Available', }; export function getFieldLabel(field: string) { diff --git a/context/app/static/js/components/search/useScrollSearchHits.ts b/context/app/static/js/components/search/useScrollSearchHits.ts index b7e89cb256..4899b83d86 100644 --- a/context/app/static/js/components/search/useScrollSearchHits.ts +++ b/context/app/static/js/components/search/useScrollSearchHits.ts @@ -95,19 +95,13 @@ export function useScrollSearchHits({ const { data, error, isLoading, isValidating, size, setSize } = useSWRInfinite< SearchResponseBody, SWRError - >( - getKey, - // TODO: revisit to fix types/make keys more type-safe - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument - fetcher, - { - fallbackData: [], - revalidateAll: false, - revalidateFirstPage: false, - keepPreviousData: true, - ...swrConfig, - }, - ); + >(getKey, fetcher, { + fallbackData: [], + revalidateAll: false, + revalidateFirstPage: false, + keepPreviousData: true, + ...swrConfig, + }); const { searchHits, totalHitsCount, aggregations } = getCombinedHits(data ?? []); diff --git a/context/app/static/js/pages/search/S.tsx b/context/app/static/js/pages/search/S.tsx index 7adf5ef06e..58c2eccf1f 100644 --- a/context/app/static/js/pages/search/S.tsx +++ b/context/app/static/js/pages/search/S.tsx @@ -109,7 +109,7 @@ const sampleConfig = { const datasetFacetGroups = { 'Dataset Metadata': [ - { field: 'dataset_type', childField: 'assay_display_name', type: FACETS.hierarchical }, + { field: 'raw_dataset_type', childField: 'assay_display_name', type: FACETS.hierarchical }, { field: 'origin_samples_unique_mapped_organs', type: FACETS.term, From ab8ce319168cc394f0bddf1abaead926bea82447 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Tue, 8 Oct 2024 16:59:52 -0400 Subject: [PATCH 51/73] Update labels, handle bools --- .../components/search/Facets/FilterChips.tsx | 3 +- .../js/components/search/Facets/TermFacet.tsx | 88 +++++++++++-------- .../search/fieldTransformationMap.ts | 39 ++++++++ 3 files changed, 91 insertions(+), 39 deletions(-) create mode 100644 context/app/static/js/components/search/fieldTransformationMap.ts diff --git a/context/app/static/js/components/search/Facets/FilterChips.tsx b/context/app/static/js/components/search/Facets/FilterChips.tsx index 1dac7f7426..e6fdc90d17 100644 --- a/context/app/static/js/components/search/Facets/FilterChips.tsx +++ b/context/app/static/js/components/search/Facets/FilterChips.tsx @@ -15,6 +15,7 @@ import { useSearchStore, } from '../store'; import { getFieldLabel } from '../labelMap'; +import transformValueLabel from '../fieldTransformationMap'; function FilterChip({ onDelete, label, ...props }: ChipProps & { onDelete: () => void }) { const { analyticsCategory } = useSearchStore(); @@ -40,7 +41,7 @@ function FilterChips() { if (isTermFilter(v)) { return [...v.values].map((val) => ( filterTerm({ term: field, value: val })} /> diff --git a/context/app/static/js/components/search/Facets/TermFacet.tsx b/context/app/static/js/components/search/Facets/TermFacet.tsx index 6954bcb778..c84b4f9aa3 100644 --- a/context/app/static/js/components/search/Facets/TermFacet.tsx +++ b/context/app/static/js/components/search/Facets/TermFacet.tsx @@ -22,6 +22,7 @@ import { } from './style'; import FacetAccordion from './FacetAccordion'; import { getFieldLabel } from '../labelMap'; +import transformValueLabel from '../fieldTransformationMap'; interface CheckboxItem { label: string; @@ -30,31 +31,34 @@ interface CheckboxItem { active: boolean; onClick: () => void; indeterminate?: boolean; + field: string; } -type LabelTransformations = ((label: string) => string)[]; - -interface TermLabelCount extends Omit { - labelTransformations?: LabelTransformations; +function getBucketKey(bucket: { key: string; key_as_string?: string; doc_count: number }) { + const { key, key_as_string } = bucket; + return key_as_string ?? key; } -export function transformLabel({ - label, - labelTransformations, -}: Required>) { - return labelTransformations.reduce((l, transformFn) => transformFn(l), label); -} +type TermLabelCount = Omit; -export function TermLabelAndCount({ label, count, active, labelTransformations = [] }: TermLabelCount) { +export function TermLabelAndCount({ label, count, active }: TermLabelCount) { return ( - {transformLabel({ label, labelTransformations })} + {label} {count} ); } -function CheckboxFilterItem({ active = false, label, title, count, onClick, indeterminate = false }: CheckboxItem) { +function CheckboxFilterItem({ + active = false, + label, + title, + count, + onClick, + indeterminate = false, + field, +}: CheckboxItem) { const { analyticsCategory } = useSearchStore(); const handleClick = useCallback(() => { @@ -82,7 +86,7 @@ function CheckboxFilterItem({ active = false, label, title, count, onClick, inde onClick={handleClick} /> } - label={} + label={} /> ); } @@ -98,7 +102,7 @@ export function TermFacetItem({ label, field, ...rest }: TermFacet) { filterTerm({ term: field, value: label }); }, [filterTerm, field, label]); - return ; + return ; } const smallAggSize = 5; @@ -143,16 +147,19 @@ function TermFacetContent({ filter, field }: { filter: TermValues; field: string const title = getFieldLabel(field); return ( - {aggBuckets.map((bucket) => ( - - ))} + {aggBuckets.map((bucket) => { + const key = getBucketKey(bucket); + return ( + + ); + })} ); @@ -185,6 +192,7 @@ export function HierarchicalFacetParent({ childValues, field, label, ...rest }: filterHierarchicalParentTerm({ term: field, value: label, childValues })} label={label} + field={field} {...rest} /> ); @@ -197,6 +205,7 @@ export function HierarchicalFacetChild({ parentValue, field, label, ...rest }: T filterHierarchicalChildTerm({ parentTerm: field, value: label, parentValue })} label={label} + field={field} {...rest} /> ); @@ -310,19 +319,22 @@ export function HierarchicalTermFacet({ field: parentField, childField }: { fiel return ( - {parentBuckets.map((bucket) => ( - - ))} + {parentBuckets.map((bucket) => { + const key = getBucketKey(bucket); + return ( + + ); + })} ); } diff --git a/context/app/static/js/components/search/fieldTransformationMap.ts b/context/app/static/js/components/search/fieldTransformationMap.ts new file mode 100644 index 0000000000..8e21d9eeeb --- /dev/null +++ b/context/app/static/js/components/search/fieldTransformationMap.ts @@ -0,0 +1,39 @@ +import { capitalizeString } from 'js/helpers/functions'; + +function mapProcessingType(label: string) { + if (label.toLowerCase() === 'hubmap') { + return 'HuBMAP'; + } + + return capitalizeString(label); +} + +const labelTransformationMap: Record string)[]> = { + analyte_class: [capitalizeString], + assay_modality: [capitalizeString], + is_component: [capitalizeString], + processing: [capitalizeString], + processing_type: [mapProcessingType], + sample_category: [capitalizeString], + visualization: [capitalizeString], +}; + +function getLabelTransformations(field: string) { + const exactMatch = labelTransformationMap?.[field]; + + const stem = field.split('.').pop(); + const stemMatch = stem && labelTransformationMap?.[stem]; + + return exactMatch ?? stemMatch ?? undefined; +} + +export function transformValueLabel({ label, field }: { label: string; field: string }) { + const labelTransformations = getLabelTransformations(field); + + if (!labelTransformations) { + return label; + } + return labelTransformations.reduce((l, transformFn) => transformFn(l), label); +} + +export default transformValueLabel; From 9d9707659f37741d780d170e7752de468299604b Mon Sep 17 00:00:00 2001 From: John Conroy Date: Tue, 8 Oct 2024 17:56:25 -0400 Subject: [PATCH 52/73] Style --- .../search/Facets/FacetAccordion.tsx | 23 ++++++--- .../js/components/search/Facets/Facets.tsx | 50 ++++++++++++------- .../js/components/search/Facets/style.ts | 15 +++--- .../static/js/components/search/Search.tsx | 2 +- 4 files changed, 59 insertions(+), 31 deletions(-) diff --git a/context/app/static/js/components/search/Facets/FacetAccordion.tsx b/context/app/static/js/components/search/Facets/FacetAccordion.tsx index b64cfb6a18..b4135a0028 100644 --- a/context/app/static/js/components/search/Facets/FacetAccordion.tsx +++ b/context/app/static/js/components/search/Facets/FacetAccordion.tsx @@ -2,21 +2,32 @@ import React, { PropsWithChildren } from 'react'; import Typography from '@mui/material/Typography'; import Accordion from '@mui/material/Accordion'; -import { InnerAccordionDetails, InnerAccordionSummary, StyledExpandMoreIcon } from './style'; +import { FacetAccordionSummary, FacetAccordionDetails, StyledExpandMoreIcon } from './style'; function FacetAccordion({ title, position, children, -}: PropsWithChildren<{ title: string; position: 'inner' | 'outer' }>) { + isFirst, + isLast, +}: PropsWithChildren<{ title: string; position: 'inner' | 'outer'; isFirst?: boolean; isLast?: boolean }>) { return ( - - } sx={{ width: '100%' }}> + ({ + width: '100%', + ...(position === 'outer' && !isLast && { borderBottom: `1px solid ${theme.palette.divider}` }), + })} + > + } $position={position}> {title} - - {children} + + {children} ); } diff --git a/context/app/static/js/components/search/Facets/Facets.tsx b/context/app/static/js/components/search/Facets/Facets.tsx index 389cd5f873..5666e70193 100644 --- a/context/app/static/js/components/search/Facets/Facets.tsx +++ b/context/app/static/js/components/search/Facets/Facets.tsx @@ -1,5 +1,6 @@ import React from 'react'; import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; import { TermFacet, HierarchicalTermFacet } from './TermFacet'; import RangeFacet from './RangeFacet'; @@ -9,25 +10,40 @@ import FacetAccordion from './FacetAccordion'; export function Facets({ facetGroups }: { facetGroups: FacetGroups }) { return ( - - {Object.entries(facetGroups).map(([k, v]) => ( - - {v.map((f) => { - if (isHierarchicalFacet(f)) { - return ; - } - if (isRangeFacet(f)) { - return ; - } + + ({ + alignItems: 'center', + boxShadow: theme.shadows[1], + padding: theme.spacing(1), + backgroundColor: theme.palette.white.main, + })} + > + {Object.entries(facetGroups).map(([k, v], i) => ( + + {v.map((f) => { + if (isHierarchicalFacet(f)) { + return ; + } + if (isRangeFacet(f)) { + return ; + } - if (isTermFacet(f)) { - return ; - } + if (isTermFacet(f)) { + return ; + } - return null; - })} - - ))} + return null; + })} + + ))} + ); } diff --git a/context/app/static/js/components/search/Facets/style.ts b/context/app/static/js/components/search/Facets/style.ts index f711aa5acd..545c8818ab 100644 --- a/context/app/static/js/components/search/Facets/style.ts +++ b/context/app/static/js/components/search/Facets/style.ts @@ -9,14 +9,15 @@ import FormControlLabel from '@mui/material/FormControlLabel'; import Typography from '@mui/material/Typography'; import Stack from '@mui/material/Stack'; -const InnerAccordionDetails = styled(AccordionDetails)({ +const FacetAccordionDetails = styled(AccordionDetails)({ flexDirection: 'column', - padding: '4px 10px 4px 16px', + padding: '4px 4px 4px 8px', }); -const InnerAccordionSummary = styled(AccordionSummary)({ +const FacetAccordionSummary = styled(AccordionSummary)(({ $position }: { $position: 'inner' | 'outer' }) => ({ + padding: $position === 'outer' ? '4px 0px' : 0, justifyContent: 'left', - padding: '0px 16px', + width: '100%', '& > *': { flexGrow: 'unset', padding: 0, @@ -24,7 +25,7 @@ const InnerAccordionSummary = styled(AccordionSummary)({ color: '#000', }, margin: 0, -}); +})); const StyledExpandMoreIcon = styled(ExpandMoreIcon)(({ theme }) => ({ fontSize: '1rem', @@ -82,8 +83,8 @@ export { StyledCheckBoxIcon, StyledCheckbox, StyledFormControlLabel, - InnerAccordionDetails, - InnerAccordionSummary, + FacetAccordionDetails, + FacetAccordionSummary, StyledExpandMoreIcon, StyledStack, FormLabelText, diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index dd616bb8dc..72b6cd196a 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -318,7 +318,7 @@ function Body({ facetGroups }: { facetGroups: FacetGroups }) { function Search({ type, facetGroups }: TypeProps & { facetGroups: FacetGroups }) { return ( - +
From 36671af7b411d16f9c99f96caa19c0a0d7f7311f Mon Sep 17 00:00:00 2001 From: John Conroy Date: Tue, 8 Oct 2024 18:40:40 -0400 Subject: [PATCH 53/73] Refactor field configurations --- .../components/search/Facets/FilterChips.tsx | 5 +- .../components/search/Facets/RangeFacet.tsx | 2 +- .../js/components/search/Facets/TermFacet.tsx | 7 +- .../search/Results/ResultsTable.tsx | 2 +- .../search/Results/ResultsTiles.tsx | 2 +- .../components/search/fieldConfigurations.ts | 69 +++++++++++++++++++ .../search/fieldTransformationMap.ts | 39 ----------- .../static/js/components/search/labelMap.ts | 37 ---------- 8 files changed, 78 insertions(+), 85 deletions(-) create mode 100644 context/app/static/js/components/search/fieldConfigurations.ts delete mode 100644 context/app/static/js/components/search/fieldTransformationMap.ts delete mode 100644 context/app/static/js/components/search/labelMap.ts diff --git a/context/app/static/js/components/search/Facets/FilterChips.tsx b/context/app/static/js/components/search/Facets/FilterChips.tsx index e6fdc90d17..1698502244 100644 --- a/context/app/static/js/components/search/Facets/FilterChips.tsx +++ b/context/app/static/js/components/search/Facets/FilterChips.tsx @@ -14,8 +14,7 @@ import { isTermFilter, useSearchStore, } from '../store'; -import { getFieldLabel } from '../labelMap'; -import transformValueLabel from '../fieldTransformationMap'; +import { getFieldLabel, getTransformedFieldalue } from '../fieldConfigurations'; function FilterChip({ onDelete, label, ...props }: ChipProps & { onDelete: () => void }) { const { analyticsCategory } = useSearchStore(); @@ -41,7 +40,7 @@ function FilterChips() { if (isTermFilter(v)) { return [...v.values].map((val) => ( filterTerm({ term: field, value: val })} /> diff --git a/context/app/static/js/components/search/Facets/RangeFacet.tsx b/context/app/static/js/components/search/Facets/RangeFacet.tsx index 9b2699768a..bc3462e84e 100644 --- a/context/app/static/js/components/search/Facets/RangeFacet.tsx +++ b/context/app/static/js/components/search/Facets/RangeFacet.tsx @@ -7,7 +7,7 @@ import Stack from '@mui/material/Stack'; import { useSearch } from '../Search'; import { RangeConfig, RangeValues, isRangeFacet, isRangeFilter, useSearchStore } from '../store'; import FacetAccordion from './FacetAccordion'; -import { getFieldLabel } from '../labelMap'; +import { getFieldLabel } from '../fieldConfigurations'; interface HistogramBucket { doc_count: number; diff --git a/context/app/static/js/components/search/Facets/TermFacet.tsx b/context/app/static/js/components/search/Facets/TermFacet.tsx index c84b4f9aa3..a4922854f7 100644 --- a/context/app/static/js/components/search/Facets/TermFacet.tsx +++ b/context/app/static/js/components/search/Facets/TermFacet.tsx @@ -21,8 +21,7 @@ import { HierarchicalAccordionSummary, } from './style'; import FacetAccordion from './FacetAccordion'; -import { getFieldLabel } from '../labelMap'; -import transformValueLabel from '../fieldTransformationMap'; +import { getFieldLabel, getTransformedFieldalue } from '../fieldConfigurations'; interface CheckboxItem { label: string; @@ -86,7 +85,9 @@ function CheckboxFilterItem({ onClick={handleClick} /> } - label={} + label={ + + } /> ); } diff --git a/context/app/static/js/components/search/Results/ResultsTable.tsx b/context/app/static/js/components/search/Results/ResultsTable.tsx index cbade7b14e..583b92f363 100644 --- a/context/app/static/js/components/search/Results/ResultsTable.tsx +++ b/context/app/static/js/components/search/Results/ResultsTable.tsx @@ -26,7 +26,7 @@ import { } from './style'; import { useSearch } from '../Search'; import { useSearchStore } from '../store'; -import { getFieldLabel } from '../labelMap'; +import { getFieldLabel } from '../fieldConfigurations'; import ViewMoreResults from './ViewMoreResults'; type SortDirection = 'asc' | 'desc'; diff --git a/context/app/static/js/components/search/Results/ResultsTiles.tsx b/context/app/static/js/components/search/Results/ResultsTiles.tsx index c9e1d7cc4b..cce38f313c 100644 --- a/context/app/static/js/components/search/Results/ResultsTiles.tsx +++ b/context/app/static/js/components/search/Results/ResultsTiles.tsx @@ -16,7 +16,7 @@ import { trackEvent } from 'js/helpers/trackers'; import { useSearch } from '../Search'; import ViewMoreResults from './ViewMoreResults'; import { useSearchStore } from '../store'; -import { getFieldLabel } from '../labelMap'; +import { getFieldLabel } from '../fieldConfigurations'; function TilesSortSelect() { const { diff --git a/context/app/static/js/components/search/fieldConfigurations.ts b/context/app/static/js/components/search/fieldConfigurations.ts new file mode 100644 index 0000000000..7e91d29c62 --- /dev/null +++ b/context/app/static/js/components/search/fieldConfigurations.ts @@ -0,0 +1,69 @@ +import { capitalizeString } from 'js/helpers/functions'; + +function mapProcessingType(label: string) { + if (label.toLowerCase() === 'hubmap') { + return 'HuBMAP'; + } + + return capitalizeString(label); +} + +const fieldConfigurationsMap: Record< + string, + { label?: string; valueTransformations?: ((label: string) => string)[]; valueSort?: 'asc' | 'dsc' | 'count' } +> = { + age_value: { label: 'Donor Age' }, + analyte_class: { label: 'Analyte Class', valueTransformations: [capitalizeString] }, + assay_display_name: { label: 'Data Types' }, + assay_modality: { label: 'Assay Modalities', valueTransformations: [capitalizeString] }, + body_mass_index_value: { label: 'Donor BMI' }, + created_by_user_displayname: { label: 'Registered By' }, + dataset_type: { label: 'Dataset Type' }, + entity_type: { label: 'Entity Type' }, + group_name: { label: 'Group' }, + hubmap_id: { label: 'HuBMAP ID' }, + is_component: { label: 'Component Dataset', valueTransformations: [capitalizeString] }, + last_modified_timestamp: { label: 'Last Modified' }, + mapped_consortium: { label: 'Consortium' }, + mapped_organ: { label: 'Organ' }, + mapped_status: { label: 'Status' }, + origin_samples_unique_mapped_organs: { label: 'Organ' }, + pipeline: { label: 'Pipeline', valueTransformations: [capitalizeString] }, + processing: { label: 'Processing' }, + processing_type: { label: 'Processing Type', valueTransformations: [mapProcessingType] }, + race: { label: 'Donor Race' }, + raw_dataset_type: { label: 'Dataset Type', valueSort: 'asc' }, + sample_category: { label: 'Sample Category', valueTransformations: [capitalizeString] }, + sex: { label: 'Donor Sex' }, + visualization: { label: 'Visualization Available', valueTransformations: [capitalizeString] }, +}; + +export function getFieldConfigurations(field: string) { + const exactMatch = fieldConfigurationsMap?.[field]; + + const stem = field.split('.').pop(); + const stemMatch = stem && fieldConfigurationsMap?.[stem]; + + return exactMatch ?? stemMatch ?? undefined; +} + +export function getTransformedFieldalue({ value, field }: { value: string; field: string }) { + const valueTransformations = getFieldConfigurations(field)?.valueTransformations; + + if (!valueTransformations) { + return value; + } + return valueTransformations.reduce((l, transformFn) => transformFn(l), value); +} + +export function getFieldLabel(field: string) { + const fieldLabel = getFieldConfigurations(field)?.label; + + return fieldLabel ?? field; +} + +export function getFieldValueSort(field: string) { + const valueSort = getFieldConfigurations(field)?.valueSort; + + return valueSort ?? 'count'; +} diff --git a/context/app/static/js/components/search/fieldTransformationMap.ts b/context/app/static/js/components/search/fieldTransformationMap.ts deleted file mode 100644 index 8e21d9eeeb..0000000000 --- a/context/app/static/js/components/search/fieldTransformationMap.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { capitalizeString } from 'js/helpers/functions'; - -function mapProcessingType(label: string) { - if (label.toLowerCase() === 'hubmap') { - return 'HuBMAP'; - } - - return capitalizeString(label); -} - -const labelTransformationMap: Record string)[]> = { - analyte_class: [capitalizeString], - assay_modality: [capitalizeString], - is_component: [capitalizeString], - processing: [capitalizeString], - processing_type: [mapProcessingType], - sample_category: [capitalizeString], - visualization: [capitalizeString], -}; - -function getLabelTransformations(field: string) { - const exactMatch = labelTransformationMap?.[field]; - - const stem = field.split('.').pop(); - const stemMatch = stem && labelTransformationMap?.[stem]; - - return exactMatch ?? stemMatch ?? undefined; -} - -export function transformValueLabel({ label, field }: { label: string; field: string }) { - const labelTransformations = getLabelTransformations(field); - - if (!labelTransformations) { - return label; - } - return labelTransformations.reduce((l, transformFn) => transformFn(l), label); -} - -export default transformValueLabel; diff --git a/context/app/static/js/components/search/labelMap.ts b/context/app/static/js/components/search/labelMap.ts deleted file mode 100644 index 5510ca05bb..0000000000 --- a/context/app/static/js/components/search/labelMap.ts +++ /dev/null @@ -1,37 +0,0 @@ -const labelMap: Record = { - age_value: 'Donor Age', - analyte_class: 'Analyte Class', - assay_display_name: 'Data Types', - assay_modality: 'Assay Modalities', - body_mass_index_value: 'Donor BMI', - created_by_user_displayname: 'Registered By', - dataset_type: 'Dataset Type', - entity_type: 'Entity Type', - group_name: 'Group', - hubmap_id: 'HuBMAP ID', - is_component: 'Component Dataset', - last_modified_timestamp: 'Last Modified', - mapped_consortium: 'Consortium', - mapped_organ: 'Organ', - mapped_status: 'Status', - origin_samples_unique_mapped_organs: 'Organ', - pipeline: 'Pipeline', - processing: 'Processing', - processing_type: 'Processing Type', - race: 'Donor Race', - raw_dataset_type: 'Dataset Type', - sample_category: 'Sample Category', - sex: 'Donor Sex', - visualization: 'Visualization Available', -}; - -export function getFieldLabel(field: string) { - const exactLabel = labelMap?.[field]; - - const stem = field.split('.').pop(); - const stemLabel = stem && labelMap?.[stem]; - - return exactLabel ?? stemLabel ?? field; -} - -export default labelMap; From 3cdf5558a61f0c7eec30b20cd185f8c2da1b3182 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Tue, 8 Oct 2024 19:58:24 -0400 Subject: [PATCH 54/73] Add agg sorting options --- .../js/components/search/Facets/TermFacet.tsx | 35 +++++++++++++++---- .../static/js/components/search/Search.tsx | 14 +++----- .../components/search/fieldConfigurations.ts | 2 +- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/context/app/static/js/components/search/Facets/TermFacet.tsx b/context/app/static/js/components/search/Facets/TermFacet.tsx index a4922854f7..6e328f07e6 100644 --- a/context/app/static/js/components/search/Facets/TermFacet.tsx +++ b/context/app/static/js/components/search/Facets/TermFacet.tsx @@ -9,7 +9,7 @@ import { AggregationsBuckets } from '@elastic/elasticsearch/lib/api/types'; import { TooltipIconButton } from 'js/shared-styles/buttons/TooltipButton'; import { trackEvent } from 'js/helpers/trackers'; -import { useSearch } from '../Search'; +import { useSearch, HierarchichalBucket, InnerBucket } from '../Search'; import { isTermFilter, useSearchStore, TermValues, isHierarchicalFilter, isTermFacet } from '../store'; import { StyledCheckBoxBlankIcon, @@ -21,7 +21,7 @@ import { HierarchicalAccordionSummary, } from './style'; import FacetAccordion from './FacetAccordion'; -import { getFieldLabel, getTransformedFieldalue } from '../fieldConfigurations'; +import { getFieldLabel, getFieldValueSort, getTransformedFieldalue } from '../fieldConfigurations'; interface CheckboxItem { label: string; @@ -33,11 +33,32 @@ interface CheckboxItem { field: string; } -function getBucketKey(bucket: { key: string; key_as_string?: string; doc_count: number }) { +type Bucket = HierarchichalBucket | InnerBucket; + +function getBucketKey(bucket: InnerBucket) { const { key, key_as_string } = bucket; return key_as_string ?? key; } +const sortFunctions: Record<'asc' | 'desc' | 'count', (a: Bucket, b: Bucket) => number> = { + count: ({ doc_count: a }, { doc_count: b }) => b - a, + asc: (a, b) => getBucketKey(a).localeCompare(getBucketKey(b)), + desc: (a, b) => getBucketKey(b).localeCompare(getBucketKey(a)), +}; + +function sortBuckets({ + field, + buckets, + sort, +}: { + field: string; + buckets: T[]; + sort?: 'asc' | 'desc' | 'count'; +}): T[] { + const sortFn = sortFunctions[sort ?? getFieldValueSort(field)]; + return buckets.sort(sortFn); +} + type TermLabelCount = Omit; export function TermLabelAndCount({ label, count, active }: TermLabelCount) { @@ -148,7 +169,7 @@ function TermFacetContent({ filter, field }: { filter: TermValues; field: string const title = getFieldLabel(field); return ( - {aggBuckets.map((bucket) => { + {sortBuckets({ field, buckets: aggBuckets }).map((bucket) => { const key = getBucketKey(bucket); return ( ; + childBuckets?: AggregationsBuckets; }) { const [expanded, setExpanded] = useState(false); const toggleExpanded = useCallback(() => { @@ -281,7 +302,7 @@ export function HierarchicalTermFacetItem({ /> - {childBuckets.map(({ key, doc_count }) => ( + {sortBuckets({ buckets: childBuckets, field: parentField }).map(({ key, doc_count }) => ( - {parentBuckets.map((bucket) => { + {sortBuckets({ buckets: parentBuckets, field: parentField }).map((bucket) => { const key = getBucketKey(bucket); return ( >>> - > ->; +export type HierarchichalBucket = InnerBucket & Partial>>; + +type Aggregations = Record>>; export function useSearch() { const { endpoint, swrConfig = {}, ...rest }: SearchStoreState = useSearchStore(); diff --git a/context/app/static/js/components/search/fieldConfigurations.ts b/context/app/static/js/components/search/fieldConfigurations.ts index 7e91d29c62..612d85d51f 100644 --- a/context/app/static/js/components/search/fieldConfigurations.ts +++ b/context/app/static/js/components/search/fieldConfigurations.ts @@ -10,7 +10,7 @@ function mapProcessingType(label: string) { const fieldConfigurationsMap: Record< string, - { label?: string; valueTransformations?: ((label: string) => string)[]; valueSort?: 'asc' | 'dsc' | 'count' } + { label?: string; valueTransformations?: ((label: string) => string)[]; valueSort?: 'asc' | 'desc' | 'count' } > = { age_value: { label: 'Donor Age' }, analyte_class: { label: 'Analyte Class', valueTransformations: [capitalizeString] }, From 9bd5ada3ddc0a03d3a6355b1a3b4809316b81d86 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Tue, 15 Oct 2024 14:34:32 -0400 Subject: [PATCH 55/73] Use history --- .../static/js/components/search/Search.tsx | 5 ++- .../app/static/js/components/search/store.ts | 8 ++-- context/package-lock.json | 41 +++++++++++-------- context/package.json | 1 + 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 6db7ba95ff..23ef58af57 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -9,11 +9,12 @@ import Typography from '@mui/material/Typography'; import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; import { parse } from 'qs'; import merge from 'deepmerge'; +import history from 'history/browser'; + import { useAppContext } from 'js/components/Contexts'; import SelectableTableProvider from 'js/shared-styles/tables/SelectableTableProvider'; import WorkspacesDropdownMenu from 'js/components/workspaces/WorkspacesDropdownMenu'; import { entityIconMap } from 'js/shared-styles/icons/entityIconMap'; - import { SearchStoreProvider, useSearchStore, @@ -376,7 +377,7 @@ function useInitialURLState() { const [initialUrlState, setInitialUrlState] = useState>({ filters: {} }); useEffect(() => { - const searchParams: Partial = parse(window.location.search, { ignoreQueryPrefix: true }); + const searchParams: Partial = parse(history.location.search, { ignoreQueryPrefix: true }); if (Object.keys(searchParams).length) { setInitialUrlState({ filters: {}, ...searchParams }); diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index c4c7ce9be9..b4a51a0730 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -1,7 +1,7 @@ import esb from 'elastic-builder'; import { stringify } from 'qs'; import { createStoreImmer, createStoreContext } from 'js/helpers/zustand'; - +import history from 'history/browser'; import { SWRConfiguration } from 'swr'; export interface SortField { @@ -168,11 +168,9 @@ function replaceURLSearchParams(state: SearchStoreState) { JSON.stringify(urlState, (_key, value: unknown) => (value instanceof Set ? [...value] : value)), ); - const urlCopy = new URL(String(window.location)); - urlCopy.search = stringify(urlStateWithArrays); + const { pathname } = history.location; - // eslint-disable-next-line no-restricted-globals - history.pushState(null, '', urlCopy); + history.push(`${pathname}?${stringify(urlStateWithArrays)}`); } export const createStore = ({ initialState }: { initialState: SearchStoreState }) => diff --git a/context/package-lock.json b/context/package-lock.json index 7ace495402..1a322cf384 100644 --- a/context/package-lock.json +++ b/context/package-lock.json @@ -46,6 +46,7 @@ "deepmerge": "^4.3.1", "elastic-builder": "^2.29.0", "fast-deep-equal": "^3.1.3", + "history": "^5.3.0", "html-react-parser": "^5.1.10", "immer": "^10.0.4", "isomorphic-dompurify": "^2.12.0", @@ -22013,23 +22014,11 @@ } }, "node_modules/history": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", - "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", - "dependencies": { - "invariant": "^2.2.1", - "loose-envify": "^1.2.0", - "resolve-pathname": "^2.2.0", - "value-equal": "^0.4.0", - "warning": "^3.0.0" - } - }, - "node_modules/history/node_modules/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", "dependencies": { - "loose-envify": "^1.0.0" + "@babel/runtime": "^7.7.6" } }, "node_modules/hls.js": { @@ -31410,6 +31399,18 @@ "react-dom": "^15.0.0-0 || ^16.0.0-0" } }, + "node_modules/searchkit/node_modules/history": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", + "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", + "dependencies": { + "invariant": "^2.2.1", + "loose-envify": "^1.2.0", + "resolve-pathname": "^2.2.0", + "value-equal": "^0.4.0", + "warning": "^3.0.0" + } + }, "node_modules/searchkit/node_modules/qs": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", @@ -31418,6 +31419,14 @@ "node": ">=0.6" } }, + "node_modules/searchkit/node_modules/warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/secure-json-parse": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", diff --git a/context/package.json b/context/package.json index 008af386cf..c4e2963735 100644 --- a/context/package.json +++ b/context/package.json @@ -39,6 +39,7 @@ "deepmerge": "^4.3.1", "elastic-builder": "^2.29.0", "fast-deep-equal": "^3.1.3", + "history": "^5.3.0", "html-react-parser": "^5.1.10", "immer": "^10.0.4", "isomorphic-dompurify": "^2.12.0", From 6bc0af0998e4ef077d58e2bd0911e01d8e7565d8 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Tue, 15 Oct 2024 14:37:01 -0400 Subject: [PATCH 56/73] Update variable name --- context/app/static/js/components/search/Search.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 23ef58af57..18ce811416 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -327,7 +327,7 @@ function Search({ type, facetGroups }: TypeProps & { facetGroups: FacetGroups }) } const mergeFilters = (filterState: FiltersType, filterURLState: FiltersType) => { - const z = Object.entries(filterState).reduce((acc, [k, v]) => { + const mergedFilters = Object.entries(filterState).reduce((acc, [k, v]) => { return produce(acc, (draft) => { const URLStateFilter = filterURLState?.[k]; @@ -360,7 +360,7 @@ const mergeFilters = (filterState: FiltersType, filterURLState: FiltersType Date: Tue, 15 Oct 2024 15:42:54 -0400 Subject: [PATCH 57/73] Add loading state --- .../js/components/search/Results/Results.tsx | 3 +- .../search/Results/ResultsTable.tsx | 30 ++++++++++++++----- ...tableRowCell.jsx => SelectableRowCell.tsx} | 27 +++++++---------- 3 files changed, 36 insertions(+), 24 deletions(-) rename context/app/static/js/shared-styles/tables/SelectableRowCell/{SelectableRowCell.jsx => SelectableRowCell.tsx} (62%) diff --git a/context/app/static/js/components/search/Results/Results.tsx b/context/app/static/js/components/search/Results/Results.tsx index 55ed270428..1020cc2493 100644 --- a/context/app/static/js/components/search/Results/Results.tsx +++ b/context/app/static/js/components/search/Results/Results.tsx @@ -16,11 +16,12 @@ function NoResults() { function Results() { const { searchHits: { length }, + isLoading, } = useSearch(); const { view } = useSearchStore(); - if (!length) { + if (!isLoading && !length) { return ; } diff --git a/context/app/static/js/components/search/Results/ResultsTable.tsx b/context/app/static/js/components/search/Results/ResultsTable.tsx index 583b92f363..e68795ed59 100644 --- a/context/app/static/js/components/search/Results/ResultsTable.tsx +++ b/context/app/static/js/components/search/Results/ResultsTable.tsx @@ -3,6 +3,7 @@ import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; import TableRow from '@mui/material/TableRow'; import TableHead from '@mui/material/TableHead'; import IconButton from '@mui/material/IconButton'; +import Skeleton from '@mui/material/Skeleton'; import Box from '@mui/material/Box'; import DOMPurify from 'isomorphic-dompurify'; import parse from 'html-react-parser'; @@ -120,29 +121,44 @@ function HighlightRow({ colSpan, highlight }: { colSpan: number } & Required ( + // eslint-disable-next-line react/no-array-index-key + + + {tableFields.map((field) => ( + + + + ))} + + )); +} - // TODO: Loading State - if (!hits.length) { - return null; - } +function ResultsTable() { + const { searchHits: hits, isLoading } = useSearch(); + + const { + sourceFields: { table: tableFields }, + } = useSearchStore(); return ( - h._id)} disabled={false} /> + h._id)} disabled={isLoading} /> {tableFields.map((field) => ( ))} + {isLoading && !hits?.length && } {hits.map((hit) => ( diff --git a/context/app/static/js/shared-styles/tables/SelectableRowCell/SelectableRowCell.jsx b/context/app/static/js/shared-styles/tables/SelectableRowCell/SelectableRowCell.tsx similarity index 62% rename from context/app/static/js/shared-styles/tables/SelectableRowCell/SelectableRowCell.jsx rename to context/app/static/js/shared-styles/tables/SelectableRowCell/SelectableRowCell.tsx index 291f1c7d53..13a64a289f 100644 --- a/context/app/static/js/shared-styles/tables/SelectableRowCell/SelectableRowCell.jsx +++ b/context/app/static/js/shared-styles/tables/SelectableRowCell/SelectableRowCell.tsx @@ -1,16 +1,23 @@ import React from 'react'; -import PropTypes from 'prop-types'; import Checkbox from '@mui/material/Checkbox'; import TableCell from '@mui/material/TableCell'; import { useSelectableTableStore } from 'js/shared-styles/tables/SelectableTableProvider'; -function SelectableRowCell({ rowKey, disabled }) { +function SelectableRowCell({ + rowKey, + disabled = false, + cellComponent: CellComponent = TableCell, +}: { + rowKey: string; + disabled?: boolean; + cellComponent?: React.FunctionComponent | typeof TableCell; +}) { const { toggleRow, selectedRows, tableLabel } = useSelectableTableStore(); return ( - + toggleRow(rowKey)} disabled={disabled} /> - + ); } -SelectableRowCell.propTypes = { - /** - Unique key representing the table. - */ - rowKey: PropTypes.string.isRequired, - disabled: PropTypes.bool, -}; - -SelectableRowCell.defaultProps = { - disabled: false, -}; - export default SelectableRowCell; From 323c88910f9ff6a0b07b62dc91072153a144dcc5 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Tue, 15 Oct 2024 15:44:03 -0400 Subject: [PATCH 58/73] Fix width --- context/app/static/js/components/search/Facets/Facets.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/app/static/js/components/search/Facets/Facets.tsx b/context/app/static/js/components/search/Facets/Facets.tsx index 5666e70193..ba1ca4dd03 100644 --- a/context/app/static/js/components/search/Facets/Facets.tsx +++ b/context/app/static/js/components/search/Facets/Facets.tsx @@ -10,7 +10,7 @@ import FacetAccordion from './FacetAccordion'; export function Facets({ facetGroups }: { facetGroups: FacetGroups }) { return ( - + ({ alignItems: 'center', From e287f3a0084da2c900ac17b2a63329cef6b7b542 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 17 Oct 2024 10:44:40 -0400 Subject: [PATCH 59/73] Add json validation --- .../static/js/components/search/Search.tsx | 7 ++- .../app/static/js/components/search/store.ts | 58 +++++++++++++++++-- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index 18ce811416..961bc147fd 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -7,7 +7,7 @@ import Stack from '@mui/material/Stack'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; -import { parse } from 'qs'; +import LZString from 'lz-string'; import merge from 'deepmerge'; import history from 'history/browser'; @@ -33,6 +33,7 @@ import { isHierarchicalFacet, isTermFacet, isRangeFacet, + parseURLState, } from './store'; import Results from './Results'; import { getPortalESField } from './buildTypesMap'; @@ -377,7 +378,9 @@ function useInitialURLState() { const [initialUrlState, setInitialUrlState] = useState>({ filters: {} }); useEffect(() => { - const searchParams: Partial = parse(history.location.search, { ignoreQueryPrefix: true }); + const searchParams = history?.location?.search + ? parseURLState(LZString.decompressFromEncodedURIComponent(history?.location?.search?.slice(1))) + : {}; if (Object.keys(searchParams).length) { setInitialUrlState({ filters: {}, ...searchParams }); diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index b4a51a0730..b3d3fe7c58 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -1,8 +1,9 @@ import esb from 'elastic-builder'; -import { stringify } from 'qs'; +import LZString from 'lz-string'; import { createStoreImmer, createStoreContext } from 'js/helpers/zustand'; import history from 'history/browser'; import { SWRConfiguration } from 'swr'; +import { z } from 'zod'; export interface SortField { field: string; @@ -17,7 +18,6 @@ export const FACETS = { export interface FacetConfig { field: string; - type: (typeof FACETS)[keyof typeof FACETS]; } export interface TermConfig extends FacetConfig { @@ -78,6 +78,53 @@ export interface SearchState { analyticsCategory: string; } +// Used to validate URL Search + +const rangeFilterSchema = z.object({ + values: z.object({ + min: z.number(), + max: z.number(), + }), + type: z.literal(FACETS.range), +}); + +const termFilterSchema = z.object({ + values: z.array(z.string()), + type: z.literal(FACETS.term), +}); + +const hierarchicalTermFilterSchema = z.object({ + values: z.record(z.string(), z.array(z.string())), + type: z.literal(FACETS.hierarchical), +}); + +const filtersSchema = z.record( + z.string(), + z.union([rangeFilterSchema, termFilterSchema, hierarchicalTermFilterSchema]), +); + +const sortFieldSchema = z.object({ + field: z.string(), + direction: z.union([z.literal('asc'), z.literal('desc')]), +}); + +const searchURLStateSchema = z + .object({ + search: z.string(), + sortField: sortFieldSchema, + filters: filtersSchema, + }) + .partial(); + +export function parseURLState(stateJSON: string) { + try { + const parsed = searchURLStateSchema.parse(JSON.parse(stateJSON)); + return parsed; + } catch (e) { + return {}; + } +} + export type SearchStoreState = SearchState>; export type SearchURLState = Partial>; @@ -163,14 +210,13 @@ function replaceURLSearchParams(state: SearchStoreState) { filters: filtersWithValues, }; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const urlStateWithArrays: URLSearchParams = JSON.parse( - JSON.stringify(urlState, (_key, value: unknown) => (value instanceof Set ? [...value] : value)), + const urlStateWithArrays: string = JSON.stringify(urlState, (_key, value: unknown) => + value instanceof Set ? [...value] : value, ); const { pathname } = history.location; - history.push(`${pathname}?${stringify(urlStateWithArrays)}`); + history.push(`${pathname}?${LZString.compressToEncodedURIComponent(urlStateWithArrays)}`); } export const createStore = ({ initialState }: { initialState: SearchStoreState }) => From 47a371d346406006a5946dde9d296d4175252d92 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 17 Oct 2024 11:08:21 -0400 Subject: [PATCH 60/73] Add function to build link --- .../DerivedDatasetsSection.tsx | 13 ++++++++++++- context/app/static/js/components/search/Search.tsx | 8 ++++---- context/app/static/js/components/search/store.ts | 12 ++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/context/app/static/js/components/detailPage/derivedEntities/DerivedDatasetsSection/DerivedDatasetsSection.tsx b/context/app/static/js/components/detailPage/derivedEntities/DerivedDatasetsSection/DerivedDatasetsSection.tsx index 650f6c370d..896d7ce0ff 100644 --- a/context/app/static/js/components/detailPage/derivedEntities/DerivedDatasetsSection/DerivedDatasetsSection.tsx +++ b/context/app/static/js/components/detailPage/derivedEntities/DerivedDatasetsSection/DerivedDatasetsSection.tsx @@ -4,6 +4,7 @@ import DerivedEntitiesSectionWrapper from 'js/components/detailPage/related-enti import RelatedEntitiesTabs from 'js/components/detailPage/related-entities/RelatedEntitiesTabs'; import RelatedEntitiesSectionActions from 'js/components/detailPage/related-entities/RelatedEntitiesSectionActions'; import { AllEntityTypes } from 'js/shared-styles/icons/entityIconMap'; +import { buildSearchLink } from 'js/components/search/store'; import { useDerivedDatasetsSection } from './hooks'; interface DerivedDatasetsSectionProps { @@ -21,7 +22,17 @@ function DerivedDatasetsSection({ uuid, entityType }: DerivedDatasetsSectionProp id="derived-data" title="Derived Data" action={ - + } > ) => { - const mergedFilters = Object.entries(filterState).reduce((acc, [k, v]) => { + const mergedFilters = Object.entries({ ...filterURLState, ...filterState }).reduce((acc, [k, v]) => { return produce(acc, (draft) => { const URLStateFilter = filterURLState?.[k]; - if (isTermFilter(v)) { + if (isTermFilter>(v)) { const urlStateValues = URLStateFilter && isTermFilter(URLStateFilter) ? URLStateFilter.values : []; draft[k] = { ...v, values: new Set([...v.values, ...urlStateValues]) }; } - if (isHierarchicalFilter(v)) { + if (isHierarchicalFilter>(v)) { const urlStateValues = URLStateFilter && isHierarchicalFilter(URLStateFilter) ? URLStateFilter.values : {}; draft[k] = { @@ -351,7 +351,7 @@ const mergeFilters = (filterState: FiltersType, filterURLState: FiltersType>(v)) { draft[k] = { ...v, values: URLStateFilter && isRangeFilter(URLStateFilter) ? URLStateFilter?.values : v.values, diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index b3d3fe7c58..bc73e80e1b 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -197,6 +197,18 @@ export function filterHasValues({ filter, facet }: { filter: Filter; facet: Face return false; } +export function buildSearchLink({ + entity, + filters, +}: { + entity: 'donors' | 'samples' | 'datasets'; + filters: FiltersType; +}) { + const filtersState = JSON.stringify({ filters }); + + return `/search/${entity}?${LZString.compressToEncodedURIComponent(filtersState)}`; +} + function replaceURLSearchParams(state: SearchStoreState) { const { search, sortField, filters, facets } = state; From c6a140feaeb8f1276cc2840394a030b32fb413b8 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 17 Oct 2024 11:27:53 -0400 Subject: [PATCH 61/73] Add search note --- .../static/js/components/search/Search.tsx | 6 +- .../js/components/search/SearchNote.tsx | 71 +++++++++++++++++++ .../components/search/fieldConfigurations.ts | 2 + .../app/static/js/components/search/store.ts | 2 +- 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 context/app/static/js/components/search/SearchNote.tsx diff --git a/context/app/static/js/components/search/Search.tsx b/context/app/static/js/components/search/Search.tsx index dfe715cb5b..0dc44d9a6d 100644 --- a/context/app/static/js/components/search/Search.tsx +++ b/context/app/static/js/components/search/Search.tsx @@ -45,6 +45,7 @@ import { Entity } from '../types'; import { DefaultSearchViewSwitch } from './SearchViewSwitch'; import { TilesSortSelect } from './Results/ResultsTiles'; import MetadataMenu from '../searchPage/MetadataMenu'; +import SearchNote from './SearchNote'; const maxAggSize = 10000; @@ -319,7 +320,10 @@ function Search({ type, facetGroups }: TypeProps & { facetGroups: FacetGroups })
- + + + + diff --git a/context/app/static/js/components/search/SearchNote.tsx b/context/app/static/js/components/search/SearchNote.tsx new file mode 100644 index 0000000000..0862dc4bf9 --- /dev/null +++ b/context/app/static/js/components/search/SearchNote.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import Typography from '@mui/material/Typography'; + +import { InternalLink } from 'js/shared-styles/Links'; +import useEntityData from 'js/hooks/useEntityData'; +import Skeleton from '@mui/material/Skeleton'; +import { Filter, isTermFilter, useSearchStore } from './store'; + +interface MessageProps { + label: string; + arg: string; +} + +function EntityMessage({ arg: uuid, label }: MessageProps) { + const [entity, isLoading] = useEntityData(uuid, ['hubmap_id', 'entity_type', 'mapped_data_types']); + if (!entity || isLoading) { + return ; + } + const { entity_type, hubmap_id } = entity; + const lcType = entity_type.toLowerCase(); + const dataTypes = (entity?.mapped_data_types ?? []).join('/'); + return ( + <> + {`${label} ${dataTypes} ${lcType} `} + {hubmap_id} + + ); +} + +/* +function CellTypeMessage({ arg: cellType, label }: MessageProps) { + return ( + <> + {label} {cellType} + + ); +} +*/ + +function getUUID(filter: Filter) { + if (!isTermFilter(filter)) { + return ''; + } + + const values = [...filter.values]; + + return values.length > 0 ? values[0] : ''; +} + +const paramNotes = [ + { filter: 'ancestor_ids', label: 'Derived from', Component: EntityMessage }, + { filter: 'descendant_ids', label: 'Ancestor of', Component: EntityMessage }, +]; + +function SearchNote() { + const { filters } = useSearchStore(); + const notesToDisplay = paramNotes.filter(({ filter }) => filters?.[filter]); + + if (notesToDisplay.length === 0) { + return null; + } + return ( + + {notesToDisplay.map(({ filter, label, Component }) => ( + + ))} + + ); +} + +export default SearchNote; diff --git a/context/app/static/js/components/search/fieldConfigurations.ts b/context/app/static/js/components/search/fieldConfigurations.ts index 612d85d51f..34b2fc01b5 100644 --- a/context/app/static/js/components/search/fieldConfigurations.ts +++ b/context/app/static/js/components/search/fieldConfigurations.ts @@ -13,12 +13,14 @@ const fieldConfigurationsMap: Record< { label?: string; valueTransformations?: ((label: string) => string)[]; valueSort?: 'asc' | 'desc' | 'count' } > = { age_value: { label: 'Donor Age' }, + ancestor_ids: { label: 'Ancestor ID' }, analyte_class: { label: 'Analyte Class', valueTransformations: [capitalizeString] }, assay_display_name: { label: 'Data Types' }, assay_modality: { label: 'Assay Modalities', valueTransformations: [capitalizeString] }, body_mass_index_value: { label: 'Donor BMI' }, created_by_user_displayname: { label: 'Registered By' }, dataset_type: { label: 'Dataset Type' }, + descendant_ids: { label: 'Descendant ID' }, entity_type: { label: 'Entity Type' }, group_name: { label: 'Group' }, hubmap_id: { label: 'HuBMAP ID' }, diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index bc73e80e1b..c355c09be4 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -54,7 +54,7 @@ export interface RangeValues { type: typeof FACETS.range; } -type Filter> = TermValues | HierarchichalTermValues | RangeValues; +export type Filter> = TermValues | HierarchichalTermValues | RangeValues; type Facet = TermConfig | HierarchicalTermConfig | RangeConfig; export type FiltersType> = Record>; From 4138e0b9c9590902722c08ce7e7b1b852a829b07 Mon Sep 17 00:00:00 2001 From: John Conroy Date: Thu, 17 Oct 2024 12:10:04 -0400 Subject: [PATCH 62/73] Update links --- .../Header/staticLinks/staticLinks.tsx | 13 ++- .../SupportAlert/SupportAlert.spec.tsx | 11 +- .../detailPage/SupportAlert/SupportAlert.tsx | 13 ++- .../DerivedDatasetsSection.tsx | 2 +- .../DerivedEntitiesSection.tsx | 12 ++- .../ProvTableDerivedLink.tsx | 32 ++++-- .../home/EntityCounts/EntityCounts.tsx | 10 +- .../static/js/components/home/Hero/Hero.tsx | 9 +- .../home/RecentEntities/RecentEntities.tsx | 6 +- .../PublicationRelatedEntities.tsx | 11 +- .../savedLists/NoItemsSaved/NoItemsSaved.jsx | 29 ++++- .../components/search/fieldConfigurations.ts | 2 +- .../app/static/js/components/search/store.ts | 8 +- .../AddDatasetsDialog/AddDatasetsDialog.tsx | 5 +- .../NewWorkspaceDialog/NewWorkspaceDialog.tsx | 17 ++- .../workspaces/workspaceMessaging.tsx | 18 +++- .../static/js/pages/Workspace/Workspace.tsx | 11 +- .../e2e/portal/dataset-search-tutorial.cy.js | 102 ------------------ 18 files changed, 168 insertions(+), 143 deletions(-) delete mode 100644 end-to-end/cypress/e2e/portal/dataset-search-tutorial.cy.js diff --git a/context/app/static/js/components/Header/staticLinks/staticLinks.tsx b/context/app/static/js/components/Header/staticLinks/staticLinks.tsx index 531f1665a1..1299617036 100644 --- a/context/app/static/js/components/Header/staticLinks/staticLinks.tsx +++ b/context/app/static/js/components/Header/staticLinks/staticLinks.tsx @@ -19,6 +19,7 @@ import ExternalImageIcon from 'js/shared-styles/icons/ExternalImageIcon'; import { entityIconMap } from 'js/shared-styles/icons/entityIconMap'; import { contactUsUrl } from 'js/shared-styles/Links/ContactUsLink'; import { DrawerTitle } from 'js/shared-styles/Drawer/styles'; +import { buildSearchLink } from 'js/components/search/store'; import AuthButton from '../AuthButton'; export const resourceLinks: DrawerSection[] = [ @@ -99,7 +100,9 @@ export const dataLinks: DrawerSection[] = [ { label: 'Datasets', description: 'Find datasets by dataset type, organs, pipelines and other metadata.', - href: '/search?entity_type[0]=Dataset', + href: buildSearchLink({ + entity_type: 'Dataset', + }), icon: , }, { @@ -158,13 +161,17 @@ export const dataLinks: DrawerSection[] = [ { label: 'Samples', description: 'Find samples by organ and other metadata, and discover derived datasets.', - href: '/search?entity_type[0]=Sample', + href: buildSearchLink({ + entity_type: 'Sample', + }), icon: , }, { label: 'Donors', description: 'Find donors by age, race and other metadata, and discover derived samples and datasets.', - href: '/search?entity_type[0]=Donor', + href: buildSearchLink({ + entity_type: 'Donor', + }), icon: , }, ], diff --git a/context/app/static/js/components/detailPage/SupportAlert/SupportAlert.spec.tsx b/context/app/static/js/components/detailPage/SupportAlert/SupportAlert.spec.tsx index 43d188750c..6e03ab7d48 100644 --- a/context/app/static/js/components/detailPage/SupportAlert/SupportAlert.spec.tsx +++ b/context/app/static/js/components/detailPage/SupportAlert/SupportAlert.spec.tsx @@ -1,10 +1,19 @@ import React from 'react'; import { render, screen } from 'test-utils/functions'; +import { buildSearchLink } from 'js/components/search/store'; import SupportAlert from './SupportAlert'; test('The alert links to parent dataset on search page', () => { const fakeUUID = 'abc123'; - const hrefToTest = `/search?descendant_ids[0]=${fakeUUID}&entity_type[0]=Dataset`; + const hrefToTest = buildSearchLink({ + entity_type: 'Dataset', + filters: { + descendant_ids: { + values: [fakeUUID], + type: 'TERM', + }, + }, + }); render(); expect(screen.getByText('the parent dataset')).toHaveAttribute('href', hrefToTest); diff --git a/context/app/static/js/components/detailPage/SupportAlert/SupportAlert.tsx b/context/app/static/js/components/detailPage/SupportAlert/SupportAlert.tsx index e34d4fbcd4..350ee7aba3 100644 --- a/context/app/static/js/components/detailPage/SupportAlert/SupportAlert.tsx +++ b/context/app/static/js/components/detailPage/SupportAlert/SupportAlert.tsx @@ -4,6 +4,7 @@ import { InternalLink } from 'js/shared-styles/Links'; import Typography from '@mui/material/Typography'; import { DetailPageAlert } from 'js/components/detailPage/style'; +import { buildSearchLink } from 'js/components/search/store'; interface SupportAlertProps { uuid: string; @@ -19,7 +20,17 @@ function SupportAlert({ uuid, isSupport }: SupportAlertProps) { “Support” entities provide derived, low-level data for visualizations. Navigate to{' '} - + the parent dataset {' '} for a view of this information in context. diff --git a/context/app/static/js/components/detailPage/derivedEntities/DerivedDatasetsSection/DerivedDatasetsSection.tsx b/context/app/static/js/components/detailPage/derivedEntities/DerivedDatasetsSection/DerivedDatasetsSection.tsx index 896d7ce0ff..f6bddbf0a6 100644 --- a/context/app/static/js/components/detailPage/derivedEntities/DerivedDatasetsSection/DerivedDatasetsSection.tsx +++ b/context/app/static/js/components/detailPage/derivedEntities/DerivedDatasetsSection/DerivedDatasetsSection.tsx @@ -24,7 +24,7 @@ function DerivedDatasetsSection({ uuid, entityType }: DerivedDatasetsSectionProp action={ } > diff --git a/context/app/static/js/components/detailPage/provenance/ProvTableDerivedLink/ProvTableDerivedLink.tsx b/context/app/static/js/components/detailPage/provenance/ProvTableDerivedLink/ProvTableDerivedLink.tsx index 68564dcdd4..200a941fdb 100644 --- a/context/app/static/js/components/detailPage/provenance/ProvTableDerivedLink/ProvTableDerivedLink.tsx +++ b/context/app/static/js/components/detailPage/provenance/ProvTableDerivedLink/ProvTableDerivedLink.tsx @@ -1,23 +1,37 @@ import React from 'react'; import { useTrackEntityPageEvent } from 'js/components/detailPage/useTrackEntityPageEvent'; +import { buildSearchLink } from 'js/components/search/store'; +import { ESEntityType } from 'js/components/types'; import { LinkButton } from './style'; interface ProvTableDerivedLinkProps { uuid: string; - type: string; + type: ESEntityType; } function ProvTableDerivedLink({ uuid, type }: ProvTableDerivedLinkProps) { const trackEntityPageEvent = useTrackEntityPageEvent(); - return ( - trackEntityPageEvent({ action: `Provenance / Table / View Derived ${type}s`, label: uuid })} - > - View Derived {type}s - - ); + + if (type === 'Sample' || type === 'Dataset') { + return ( + trackEntityPageEvent({ action: `Provenance / Table / View Derived ${type}s`, label: uuid })} + > + View Derived {type}s + + ); + } + return null; } export default ProvTableDerivedLink; diff --git a/context/app/static/js/components/home/EntityCounts/EntityCounts.tsx b/context/app/static/js/components/home/EntityCounts/EntityCounts.tsx index 9eaecbdb24..7b2ac3fa35 100644 --- a/context/app/static/js/components/home/EntityCounts/EntityCounts.tsx +++ b/context/app/static/js/components/home/EntityCounts/EntityCounts.tsx @@ -2,10 +2,14 @@ import React from 'react'; import EntityCount from 'js/components/home/EntityCount'; import { DatasetIcon, SampleIcon, DonorIcon, CollectionIcon, OrganIcon } from 'js/shared-styles/icons'; +import { buildSearchLink } from 'js/components/search/store'; import { useEntityCounts } from './hooks'; import { Background, FlexContainer, StyledSvgIcon } from './style'; -const entities = [ +const entities: { + icon: typeof DonorIcon; + entity_type: 'Donor' | 'Sample' | 'Dataset'; +}[] = [ { icon: DonorIcon, entity_type: 'Donor', @@ -37,7 +41,9 @@ function EntityCounts({ organsCount }: EntityCountsProps) { } count={entityCounts?.[entity_type]} label={`${entity_type}s`} - href={`/search?entity_type[0]=${entity_type}`} + href={buildSearchLink({ + entity_type, + })} /> ))} , - href: '/search?entity_type[0]=Dataset', + href: buildSearchLink({ + entity_type: 'Dataset', + }), }, { title: 'Explore molecules/cell types', @@ -57,7 +60,9 @@ const heroTabs = [ { title: 'Find datasets to download', icon: , - href: '/search?entity_type[0]=Dataset', + href: buildSearchLink({ + entity_type: 'Dataset', + }), }, ], bgColor: '#EAF0F8', // info-90 in figma diff --git a/context/app/static/js/components/home/RecentEntities/RecentEntities.tsx b/context/app/static/js/components/home/RecentEntities/RecentEntities.tsx index fedf286d9b..be6de094d9 100644 --- a/context/app/static/js/components/home/RecentEntities/RecentEntities.tsx +++ b/context/app/static/js/components/home/RecentEntities/RecentEntities.tsx @@ -4,6 +4,8 @@ import { useOrgan } from 'js/hooks/useOrgansApi'; import URLSvgIcon from 'js/shared-styles/icons/URLSvgIcon'; import Grid from '@mui/material/Grid'; import { format } from 'date-fns/format'; + +import { buildSearchLink } from 'js/components/search/store'; import { buildPublicationPanelProps } from 'js/components/publications/PublicationsPanelList/utils'; import { useRecentDatasetsQuery, @@ -44,7 +46,9 @@ function RecentDatasets() { entityName="Dataset" entities={recentDatasets} entityComponent={DatasetPanel} - viewAllLink="/search?entity_type[0]=Dataset" + viewAllLink={buildSearchLink({ + entity_type: 'Dataset', + })} /> ); } diff --git a/context/app/static/js/components/publications/PublicationRelatedEntities/PublicationRelatedEntities.tsx b/context/app/static/js/components/publications/PublicationRelatedEntities/PublicationRelatedEntities.tsx index 9aafee3f51..1c22c4b613 100644 --- a/context/app/static/js/components/publications/PublicationRelatedEntities/PublicationRelatedEntities.tsx +++ b/context/app/static/js/components/publications/PublicationRelatedEntities/PublicationRelatedEntities.tsx @@ -3,6 +3,7 @@ import React, { useState } from 'react'; import RelatedEntitiesSectionWrapper from 'js/components/detailPage/related-entities/RelatedEntitiesSectionWrapper'; import RelatedEntitiesTabs from 'js/components/detailPage/related-entities/RelatedEntitiesTabs'; import RelatedEntitiesSectionActions from 'js/components/detailPage/related-entities/RelatedEntitiesSectionActions'; +import { buildSearchLink } from 'js/components/search/store'; import { usePublicationsRelatedEntities } from './hooks'; interface PublicationRelatedEntitiesProps { @@ -21,7 +22,15 @@ function PublicationRelatedEntities({ uuid }: PublicationRelatedEntitiesProps) { iconTooltipText="HuBMAP data created or used by the publication." action={ } > diff --git a/context/app/static/js/components/savedLists/NoItemsSaved/NoItemsSaved.jsx b/context/app/static/js/components/savedLists/NoItemsSaved/NoItemsSaved.jsx index 16c0103a9e..26e23b7175 100644 --- a/context/app/static/js/components/savedLists/NoItemsSaved/NoItemsSaved.jsx +++ b/context/app/static/js/components/savedLists/NoItemsSaved/NoItemsSaved.jsx @@ -2,14 +2,37 @@ import React from 'react'; import { InternalLink } from 'js/shared-styles/Links'; import Description from 'js/shared-styles/sections/Description'; +import { buildSearchLink } from 'js/components/search/store'; function SearchPagesPrompt() { // Inserted in the middle of the message, so it shouldn't be capitalized. return ( <> - navigate to donors,{' '} - samples or{' '} - datasets search pages + navigate to{' '} + + donors + + ,{' '} + + samples + {' '} + or{' '} + + datasets + {' '} + search pages ); } diff --git a/context/app/static/js/components/search/fieldConfigurations.ts b/context/app/static/js/components/search/fieldConfigurations.ts index 34b2fc01b5..50e2ba8cbc 100644 --- a/context/app/static/js/components/search/fieldConfigurations.ts +++ b/context/app/static/js/components/search/fieldConfigurations.ts @@ -31,7 +31,7 @@ const fieldConfigurationsMap: Record< mapped_status: { label: 'Status' }, origin_samples_unique_mapped_organs: { label: 'Organ' }, pipeline: { label: 'Pipeline', valueTransformations: [capitalizeString] }, - processing: { label: 'Processing' }, + processing: { label: 'Processing', valueTransformations: [capitalizeString] }, processing_type: { label: 'Processing Type', valueTransformations: [mapProcessingType] }, race: { label: 'Donor Race' }, raw_dataset_type: { label: 'Dataset Type', valueSort: 'asc' }, diff --git a/context/app/static/js/components/search/store.ts b/context/app/static/js/components/search/store.ts index c355c09be4..e129954f1a 100644 --- a/context/app/static/js/components/search/store.ts +++ b/context/app/static/js/components/search/store.ts @@ -198,15 +198,15 @@ export function filterHasValues({ filter, facet }: { filter: Filter; facet: Face } export function buildSearchLink({ - entity, + entity_type, filters, }: { - entity: 'donors' | 'samples' | 'datasets'; - filters: FiltersType; + entity_type: 'Donor' | 'Dataset' | 'Sample'; + filters?: FiltersType; }) { const filtersState = JSON.stringify({ filters }); - return `/search/${entity}?${LZString.compressToEncodedURIComponent(filtersState)}`; + return `/search/${entity_type.toLowerCase()}s?${LZString.compressToEncodedURIComponent(filtersState)}`; } function replaceURLSearchParams(state: SearchStoreState) { diff --git a/context/app/static/js/components/workspaces/AddDatasetsDialog/AddDatasetsDialog.tsx b/context/app/static/js/components/workspaces/AddDatasetsDialog/AddDatasetsDialog.tsx index a8f498d171..146f6e236a 100644 --- a/context/app/static/js/components/workspaces/AddDatasetsDialog/AddDatasetsDialog.tsx +++ b/context/app/static/js/components/workspaces/AddDatasetsDialog/AddDatasetsDialog.tsx @@ -7,12 +7,15 @@ import Typography from '@mui/material/Typography'; import Step from 'js/shared-styles/surfaces/Step'; import { InternalLink } from 'js/shared-styles/Links'; import ErrorOrWarningMessages from 'js/shared-styles/alerts/ErrorOrWarningMessages'; +import { buildSearchLink } from 'js/components/search/store'; import { EditWorkspaceDialogContent } from '../EditWorkspaceDialog'; import { Workspace } from '../types'; import AddDatasetsTable from '../AddDatasetsTable'; import { useAddDatasetsDialog } from './hooks'; -const searchPageRoute = '/search?entity_type[0]=Dataset'; +const searchPageRoute = buildSearchLink({ + entity_type: 'Dataset', +}); function SearchPagePrompt() { return ( diff --git a/context/app/static/js/components/workspaces/NewWorkspaceDialog/NewWorkspaceDialog.tsx b/context/app/static/js/components/workspaces/NewWorkspaceDialog/NewWorkspaceDialog.tsx index f8bbe5150e..78f61acab7 100644 --- a/context/app/static/js/components/workspaces/NewWorkspaceDialog/NewWorkspaceDialog.tsx +++ b/context/app/static/js/components/workspaces/NewWorkspaceDialog/NewWorkspaceDialog.tsx @@ -16,6 +16,7 @@ import WorkspaceField from 'js/components/workspaces/WorkspaceField'; import { useLaunchWorkspaceStore } from 'js/stores/useWorkspaceModalStore'; import { useSelectItems } from 'js/hooks/useSelectItems'; import InternalLink from 'js/shared-styles/Links/InternalLink'; +import { buildSearchLink } from 'js/components/search/store'; import WorkspacesNoDatasetsAlert from 'js/components/workspaces/WorkspacesNoDatasetsAlert'; import { useWorkspaceTemplates } from './hooks'; @@ -45,15 +46,25 @@ const text = { {' '} Add datasets by HuBMAP ID below or navigate to the{' '} - dataset search page, select datasets and - follow steps to launch a workspace. + + dataset search page + + , select datasets and follow steps to launch a workspace. , ], all: [ 'To remove a dataset, select the dataset and press the delete button. If all datasets are removed, an empty workspace will be launched.', 'Once you navigate away from this page, all progress will be lost. You can copy IDs to your clipboard by selecting datasets in the table below and pressing the copy button. You can also save datasets to “My Lists”.',