Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support case-insensitive search for all views #161

Draft
wants to merge 6 commits into
base: dev-ignore-case-searching
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 22 additions & 101 deletions frontend/src/pages/cohorts/CohortsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,112 +15,33 @@ import {
} from "../../shared/helpers";
import RecordsList from "../../components/RecordsList";
import { useParams } from "react-router-dom";
import { buildSearchValRegexString } from "../../utils/stringBuilders";

function cohortFilterWhereVariables(parsedSearchVals: string[]): CohortWhere[] {
if (parsedSearchVals.length > 1) {
const whereVariables: CohortWhere[] = [
{ cohortId_IN: parsedSearchVals },
{
hasCohortCompleteCohortCompletes_SOME: {
type_IN: parsedSearchVals,
return parsedSearchVals.flatMap((searchVal) => [
{ cohortId_MATCHES: buildSearchValRegexString(searchVal) },
{
hasCohortSampleSamples_SOME: {
hasMetadataSampleMetadata_SOME: {
primaryId_MATCHES: buildSearchValRegexString(searchVal),
},
},
{
hasCohortCompleteCohortCompletes_SOME: {
projectTitle_IN: parsedSearchVals,
},
},
{
hasCohortCompleteCohortCompletes_SOME: {
projectSubtitle_IN: parsedSearchVals,
},
},
{
hasCohortCompleteCohortCompletes_SOME: {
status_IN: parsedSearchVals,
},
},
{
hasCohortCompleteCohortCompletes_SOME: {
date_IN: parsedSearchVals,
},
},
{
hasCohortSampleSamples_SOME: {
hasMetadataSampleMetadata_SOME: {
primaryId_IN: parsedSearchVals,
},
},
},
];

// Enable fuzzy search for these fields instead of exact match
// because their value type in the DB is a string
parsedSearchVals.forEach((val) => {
whereVariables.push({
hasCohortCompleteCohortCompletes_SOME: {
endUsers_CONTAINS: val,
},
});
whereVariables.push({
hasCohortCompleteCohortCompletes_SOME: {
pmUsers_CONTAINS: val,
},
});
});

return whereVariables;
}

if (parsedSearchVals.length === 1) {
return [
{ cohortId_CONTAINS: parsedSearchVals[0] },
{
hasCohortCompleteCohortCompletes_SOME: {
type_CONTAINS: parsedSearchVals[0],
},
},
...[
"type",
"endUsers",
"pmUsers",
"projectTitle",
"projectSubtitle",
"status",
"date",
].map((cohortCompleteFieldName) => ({
hasCohortCompleteCohortCompletes_SOME: {
[cohortCompleteFieldName + "_MATCHES"]:
buildSearchValRegexString(searchVal),
},
{
hasCohortCompleteCohortCompletes_SOME: {
endUsers_CONTAINS: parsedSearchVals[0],
},
},
{
hasCohortCompleteCohortCompletes_SOME: {
pmUsers_CONTAINS: parsedSearchVals[0],
},
},
{
hasCohortCompleteCohortCompletes_SOME: {
projectTitle_CONTAINS: parsedSearchVals[0],
},
},
{
hasCohortCompleteCohortCompletes_SOME: {
projectSubtitle_CONTAINS: parsedSearchVals[0],
},
},
{
hasCohortCompleteCohortCompletes_SOME: {
status_CONTAINS: parsedSearchVals[0],
},
},
{
hasCohortCompleteCohortCompletes_SOME: {
date_CONTAINS: parsedSearchVals[0],
},
},
{
hasCohortSampleSamples_SOME: {
hasMetadataSampleMetadata_SOME: {
primaryId_CONTAINS: parsedSearchVals[0],
},
},
},
];
}

return [];
})),
]);
}

interface ICohortsPageProps {
Expand Down
44 changes: 12 additions & 32 deletions frontend/src/pages/patients/PatientsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from "../../shared/helpers";
import { getUserEmail } from "../../utils/getUserEmail";
import { openLoginPopup } from "../../utils/openLoginPopup";
import { buildSearchValRegexString } from "../../utils/stringBuilders";

// Mirror the field types in the CRDB, where CMO_ID is stored without the "C-" prefix
export type PatientIdsTriplet = {
Expand All @@ -31,41 +32,20 @@ export type PatientIdsTriplet = {
function patientFilterWhereVariables(
parsedSearchVals: string[]
): PatientWhere[] {
if (parsedSearchVals.length > 1) {
return [
{
patientAliasesIsAlias_SOME: {
value_IN: parsedSearchVals,
},
},
{
hasSampleSamples_SOME: {
hasMetadataSampleMetadata_SOME: {
cmoSampleName_IN: parsedSearchVals,
},
},
return parsedSearchVals.flatMap((searchVal) => [
{
patientAliasesIsAlias_SOME: {
value_MATCHES: buildSearchValRegexString(searchVal),
},
];
}

if (parsedSearchVals.length === 1) {
return [
{
patientAliasesIsAlias_SOME: {
value_CONTAINS: parsedSearchVals[0],
},
{
hasSampleSamples_SOME: {
hasMetadataSampleMetadata_SOME: {
cmoSampleName_MATCHES: buildSearchValRegexString(searchVal),
},
},
{
hasSampleSamples_SOME: {
hasMetadataSampleMetadata_SOME: {
cmoSampleName_CONTAINS: parsedSearchVals[0],
},
},
},
];
}

return [];
},
]);
}

function addCDashToCMOId(cmoId: string): string {
Expand Down
108 changes: 7 additions & 101 deletions frontend/src/pages/requests/RequestsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,13 @@ import {
} from "../../shared/helpers";
import RecordsList from "../../components/RecordsList";
import { useParams } from "react-router-dom";
import { buildSearchValRegexString } from "../../utils/stringBuilders";

function requestFilterWhereVariables(
parsedSearchVals: string[]
): RequestWhere[] {
if (parsedSearchVals.length > 1) {
// search term 08944_b 16167

// these are just the string names of the members of RequestWhere but we want to be able to make these usable..
var typenames = [
return parsedSearchVals.flatMap((searchVal) =>
[
"igoProjectId",
"igoRequestId",
"projectManagerName",
Expand All @@ -35,102 +33,10 @@ function requestFilterWhereVariables(
"qcAccessEmails",
"dataAccessEmails",
"otherContactEmails",
];
var queryTerms = typenames.map((tn) => {
return `${tn}` + "_MATCHES";
});

// query terms looks like: [
// "igoProjectId_MATCHES",
// "igoRequestId_MATCHES",
// "projectManagerName_MATCHES",
// "investigatorName_MATCHES",
// "piEmail_MATCHES",
// "dataAnalystName_MATCHES",
// "dataAnalystEmail_MATCHES",
// "genePanel_MATCHES",
// "labHeadName_MATCHES",
// "labHeadEmail_MATCHES",
// "qcAccessEmails_MATCHES",
// "dataAccessEmails_MATCHES",
// "otherContactEmails_MATCHES"
// ]

// goal is to build a list of the above query terms for every parsed search value entered by the user...
// ex: [ {igoProjectId_MATCHES: searchTerm1}, {igoProjectId_MATCHES: searchTerm2}, {igoRequestId_MATCHES: searchTerm1}, {igoRequestId_MATCHES: searchTerm2}, etc]

let compiledQuery = new Array();

for (var i = 0; i < queryTerms.length; i++) {
for (var j = 0; j < parsedSearchVals.length; j++) {
console.log("query term....", queryTerms[i]);
let val = parsedSearchVals[j];
// `${queryTerms[i]}`: val
let m = `\{ ${queryTerms[i]} \: \"${val}\" \}`;
compiledQuery.push(m);
}
}

console.log("\n\nCOMPILED QUERY TERMS!");
console.log(queryTerms);

console.log("compiled query", compiledQuery);

// this type of WHERE clause means we're only allow exact matches and not using the regex pattern matching
return [
{ igoProjectId_IN: parsedSearchVals },
{ igoRequestId_IN: parsedSearchVals },
{ projectManagerName_IN: parsedSearchVals },
{ investigatorName_IN: parsedSearchVals },
{ investigatorEmail_IN: parsedSearchVals },
{ piEmail_IN: parsedSearchVals },
{ dataAnalystName_IN: parsedSearchVals },
{ dataAnalystEmail_IN: parsedSearchVals },
{ genePanel_IN: parsedSearchVals },
{ labHeadName_IN: parsedSearchVals },
{ labHeadEmail_IN: parsedSearchVals },
{ qcAccessEmails_IN: parsedSearchVals },
{ dataAccessEmails_IN: parsedSearchVals },
{ otherContactEmails_IN: parsedSearchVals },
];
}

/// we want to be able to match on a regex OR match on CONTAINS...
if (parsedSearchVals.length === 1) {
return [
{ igoProjectId_CONTAINS: parsedSearchVals[0] },
{ igoRequestId_CONTAINS: parsedSearchVals[0] },
{ projectManagerName_CONTAINS: parsedSearchVals[0] },
{ investigatorName_CONTAINS: parsedSearchVals[0] },
{ investigatorEmail_CONTAINS: parsedSearchVals[0] },
{ piEmail_CONTAINS: parsedSearchVals[0] },
{ dataAnalystName_CONTAINS: parsedSearchVals[0] },
{ dataAnalystEmail_CONTAINS: parsedSearchVals[0] },
{ genePanel_CONTAINS: parsedSearchVals[0] },
{ labHeadName_CONTAINS: parsedSearchVals[0] },
{ labHeadEmail_CONTAINS: parsedSearchVals[0] },
{ qcAccessEmails_CONTAINS: parsedSearchVals[0] },
{ dataAccessEmails_CONTAINS: parsedSearchVals[0] },
{ otherContactEmails_CONTAINS: parsedSearchVals[0] },
// matches
{ igoProjectId_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ igoRequestId_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ projectManagerName_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ investigatorName_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ investigatorEmail_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ piEmail_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ dataAnalystName_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ dataAnalystEmail_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ genePanel_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ labHeadName_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ labHeadEmail_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ qcAccessEmails_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ dataAccessEmails_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
{ otherContactEmails_MATCHES: `(?i)${parsedSearchVals[0]}.*` },
];
}

return [];
].map((fieldName) => ({
[fieldName + "_MATCHES"]: buildSearchValRegexString(searchVal),
}))
);
}

export default function RequestsPage() {
Expand Down
Loading