Skip to content

Commit

Permalink
Feat/support nested field names (#1324)
Browse files Browse the repository at this point in the history
* feat(supportNestedFieldNames): Added solution described in ticket, unable to populate summary field using example described in ticket

* feat(supportNestedFieldNames): Ran linter

* feat(supportNestedFieldNames): Reverted auto formatting

* feat(supportNestedFieldNames): Added other changed file without auto formating

* feat(supportNestedFieldNames): Added nested search with lodash get to DiscoveryListView ln 130

* feat(supportNestedFieldNames): Updated files to remove unintentional auto formatting

* feat(supportNestedFieldNames): Refactored to use jsonpath

* feat(supportNestedFieldNames): Fixed issue with empty resourceFieldValues rendering in details view

* feat(supportNestedFieldNames): Updated parameter passed to search functionality so search will still work

* feat(supportNestedFieldNames): Updated parameter passed to search functionality to parse all array values

* feat(supportNestedFieldNames): Removed unneeded comments

* feat(supportNestedFieldNames): Updated discoverydetails logic to not render fields when array contains a single empty string

* feat(supportNestedFieldNames): Wrote function to parse array containing an array for displaying multiple values in a single place in the detail view

* feat(supportNestedFieldNames): Renamed function for clarity

* feat(supportNestedFieldNames): Reverted unneeded auto formatting from prettier and ran eslint

* feat(supportNestedFieldNames): Added jsonpath query for headerField in DiscoveryDetails.tsx

* feat(supportNestedFieldNames): Reverted unintentional auto formatting

* feat(supportNestedFieldNames): Added logic to formatSearchIndex so JSON Path values can be used in search in Discovery.tsx

* Update src/Discovery/DiscoveryListView.tsx

Co-authored-by: Mingfei Shao <2475897+mfshao@users.noreply.github.com>

* feat(supportNestedFieldNames): Removed console logs and added support for nested searchableFields

* feat(supportNestedFieldNames): Ran linter

* feat(supportNestedFieldNames): clarified comment

* Update src/Discovery/Discovery.tsx

Co-authored-by: Mingfei Shao <2475897+mfshao@users.noreply.github.com>

* Update src/Discovery/Discovery.tsx

Co-authored-by: Mingfei Shao <2475897+mfshao@users.noreply.github.com>

* Update src/Discovery/Discovery.tsx

Co-authored-by: Mingfei Shao <2475897+mfshao@users.noreply.github.com>

* use jsonpath to check if group is empty

* increase mds query limit

* update docs

* aggregation

* update for na fields

* fix test

* change concat char

---------

Co-authored-by: Mingfei Shao <2475897+mfshao@users.noreply.github.com>
Co-authored-by: Mingfei Shao <mshao1@uchicago.edu>
  • Loading branch information
3 people authored Jul 24, 2023
1 parent 258781b commit 87c7c04
Show file tree
Hide file tree
Showing 9 changed files with 244 additions and 78 deletions.
27 changes: 16 additions & 11 deletions docs/portal_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -433,26 +433,26 @@ Below is an example, with inline comments describing what each JSON block config
"public": true, // optional, defaults to true. If false, requires user to sign in before seeing the Discovery page
"features": {
"exportToWorkspace": { // configures the export to workspace feature. If enabled, the Discovery page data must contain a field which is a list of GUIDs for each study. See `manifestFieldName`
"enable": boolean,
"enableDownloadManifest": boolean, // enables a button which allows user to download a manifest file for gen3 client
"downloadManifestButtonText": string, // text to be displayed on the download manifest button
"manifestFieldName": string, // the field in the Discovery page data that contains the list of GUIDs that link to each study's data files.
"enable": true,
"enableDownloadManifest": true, // enables a button which allows user to download a manifest file for gen3 client
"downloadManifestButtonText": true, // text to be displayed on the download manifest button
"manifestFieldName": "__manifest", // the field in the Discovery page data that contains the list of GUIDs that link to each study's data files.
"documentationLinks": {
"gen3Client": string, // link to documentation about the gen3 client. Used for button tooltips
"gen3Workspaces": string, // link to documentation about gen3 workspaces. Used for button tooltips.
"gen3Client": "https://gen3-client", // link to documentation about the gen3 client. Used for button tooltips
"gen3Workspaces": "https://gen3-workspace-docs", // link to documentation about gen3 workspaces. Used for button tooltips.
}
},
"pageTitle": {
"enabled": true,
"text": "My Special Test Discovery Page"
},
"guidType": "discovery_metadata" // optional, default value is "discovery_metadata", allows for displaying only select mds records on the discovery page; by changing the _guid_type on the mds records and this setting to match
"guidType": "discovery_metadata", // optional, default value is "discovery_metadata", allows for displaying only select mds records on the discovery page; by changing the _guid_type on the mds records and this setting to match
"search": {
"searchBar": {
"enabled": true,
"inputSubtitle": "Search Bar", // optional, subtitle of search bar
"placeholder": "Search studies by keyword", // optional, placeholder text of search input
"searchableTextFields": ["study", "age", "publication"] // optional, list of properties in data to make searchable
"searchableTextFields": ["study", "age", "publication", "minimal_info.study_title"] // optional, list of properties in data to make searchable
// if not present, only fields visible in the table will be searchable
},
"tagSearchDropdown": { // optional, config section for searchable tags
Expand Down Expand Up @@ -494,6 +494,11 @@ Below is an example, with inline comments describing what each JSON block config
"field": "study_id",
"type": "count" // count of rows in data where `field` is non-empty
},
{
"name": "Accession Numbers",
"field": "minimal_info.dbgap_accession", // JSONPath syntax field name for nested fields
"type": "count"
},
{
"name": "Total Subjects",
"field": "_subjects_count",
Expand Down Expand Up @@ -529,7 +534,7 @@ Below is an example, with inline comments describing what each JSON block config
},
{
"name": "dbGaP Accession Number",
"field": "study_id"
"field": "minimal_info.dbgap_accession" // JSONPath syntax field name for nested fields
},
{
"name": "Commons",
Expand Down Expand Up @@ -583,8 +588,8 @@ Below is an example, with inline comments describing what each JSON block config
"includeIfNotAvailable": false
},
{
"name": "Project ID",
"field": "project_id",
"name": "Investigator Names",
"field": "citation.investigators[*].full_name", // JSONPath syntax field name for nested fields with array
"contentType": "string",
"includeIfNotAvailable": false
}
Expand Down
116 changes: 116 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"isomorphic-fetch": "^3.0.0",
"jest-fetch-mock": "^2.1.1",
"js-search": "^2.0.0",
"jsonpath": "^1.1.1",
"jszip": "^3.1.5",
"less": "^3.11.1",
"less-loader": "^4.1.0",
Expand Down
31 changes: 22 additions & 9 deletions src/Discovery/Discovery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, {
useState, useEffect, ReactNode, useMemo,
} from 'react';
import * as JsSearch from 'js-search';
import jsonpath from 'jsonpath';
import {
Tag, Popover, Space, Collapse, Button, Dropdown, Pagination, Tooltip,
} from 'antd';
Expand Down Expand Up @@ -116,10 +117,13 @@ export const renderFieldContent = (content: any, contentType: 'string' | 'paragr
return content;
case 'number':
if (Array.isArray(content)) {
return content.join(', ');
return content.map((v) => v.toLocaleString()).join('; ');
}
return content.toLocaleString();
case 'paragraphs':
if (Array.isArray(content)) {
return content.join('\n').split('\n').map((paragraph, i) => <p key={i}>{paragraph}</p>);
}
return content.split('\n').map((paragraph, i) => <p key={i}>{paragraph}</p>);
case 'link':
return (
Expand Down Expand Up @@ -266,6 +270,14 @@ const Discovery: React.FunctionComponent<Props> = (props: Props) => {
filterState],
);

const formatSearchIndex = (index: String) => {
// Removes [*] wild cards used by JSON Path and converts to array
const wildCardStringRegex = new RegExp(/\[\*\]/, 'g');
const indexWithoutWildcards = index.replace(wildCardStringRegex, '');
const indexArr = indexWithoutWildcards.split('.');
return indexArr;
};

useEffect(() => {
// Load studies into JS Search.
const search = new JsSearch.Search(config.minimalFieldMapping.uid);
Expand All @@ -279,17 +291,20 @@ const Discovery: React.FunctionComponent<Props> = (props: Props) => {
const searchableFields = config.features.search.searchBar.searchableTextFields;
if (searchableFields) {
searchableFields.forEach((field) => {
search.addIndex(field);
const formattedFields = formatSearchIndex(field);
search.addIndex(formattedFields);
});
} else {
config.studyColumns.forEach((column) => {
if (!column.contentType || column.contentType === 'string') {
search.addIndex(column.field);
const studyColumnFieldsArr = formatSearchIndex(column.field);
search.addIndex(studyColumnFieldsArr);
}
});
// Also enable search over preview field if present
if (config.studyPreviewField) {
search.addIndex(config.studyPreviewField.field);
const studyPreviewFieldArr = formatSearchIndex(config.studyPreviewField.field);
search.addIndex(studyPreviewFieldArr);
}
}
// ---
Expand Down Expand Up @@ -333,10 +348,10 @@ const Discovery: React.FunctionComponent<Props> = (props: Props) => {
textWrap: 'word-break',
width: column.width,
render: (_, record) => {
let value = record[column.field];
let value = jsonpath.query(record, `$.${column.field}`);
let renderedCell: undefined | string | ReactNode;

if (!value) {
if (!value || value.length === 0 || value.every((val) => val === '')) {
if (column.errorIfNotAvailable !== false) {
throw new Error(`Configuration error: Could not find field ${column.field} in record ${JSON.stringify(record)}. Check the 'study_columns' section of the Discovery config.`);
}
Expand All @@ -350,9 +365,7 @@ const Discovery: React.FunctionComponent<Props> = (props: Props) => {
? config.features.search.searchBar.searchableTextFields.indexOf(column.field) !== -1
: !column.contentType || column.contentType === 'string';
if (columnIsSearchable && props.searchTerm) {
if (Array.isArray(value)) {
value = value.join(', ');
}
value = value.join(', '); // "value" will always be an array from jsonpath.query()
renderedCell = highlightSearchTerm(value, props.searchTerm).highlighted;
} else if (column.hrefValueFromField) {
renderedCell = <a href={`//${record[column.hrefValueFromField]}`} target='_blank' rel='noreferrer'>{renderFieldContent(value, column.contentType, config)}</a>;
Expand Down
Loading

0 comments on commit 87c7c04

Please sign in to comment.