From 66f11ae0cde887dc29a00688af1b2968b7f6cba3 Mon Sep 17 00:00:00 2001 From: ChelseaKR Date: Thu, 16 Jan 2025 19:29:29 -0800 Subject: [PATCH 1/3] enhance query readability --- backend/src/domain/search/searchTrainings.ts | 63 ++++++++++---------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/backend/src/domain/search/searchTrainings.ts b/backend/src/domain/search/searchTrainings.ts index f0dd7c814..e51834b51 100644 --- a/backend/src/domain/search/searchTrainings.ts +++ b/backend/src/domain/search/searchTrainings.ts @@ -1,6 +1,6 @@ import NodeCache from "node-cache"; // import * as Sentry from "@sentry/node"; -import {SearchTrainings} from "../types"; +import { SearchTrainings } from "../types"; import {credentialEngineAPI} from "../../credentialengine/CredentialEngineAPI"; import {credentialEngineUtils} from "../../credentialengine/CredentialEngineUtils"; import {CTDLResource} from "../credentialengine/CredentialEngine"; @@ -327,26 +327,6 @@ function buildQuery(params: { const isZipCode = zipcodes.lookup(params.searchQuery); const isCounty = Object.keys(zipcodeJson.byCounty).includes(params.searchQuery); - /* const miles = params.miles; - const zipcode = params.zipcode;*/ - /* - let zipcodesList: string[] | zipcodes.ZipCode[] = [] - - if (isZipCode) { - zipcodesList = [params.searchQuery] - } else if (isCounty) { - zipcodesList = zipcodeJson.byCounty[params.searchQuery as keyof typeof zipcodeJson.byCounty] - } - - if (params.county) { - zipcodesList = zipcodeJson.byCounty[params.county as keyof typeof zipcodeJson.byCounty] - } - - if (miles && miles > 0 && zipcode) { - const zipcodesInRadius = zipcodes.radius(zipcode, miles); - zipcodesList = zipcodesInRadius; - }*/ - const queryParts = params.searchQuery.split('+').map(part => part.trim()); const hasMultipleParts = queryParts.length > 1; const [ownedByPart, trainingPart] = queryParts; @@ -354,23 +334,48 @@ function buildQuery(params: { let termGroup: TermGroup = { "search:operator": "search:orTerms", ...(isSOC || isCIP || !!isZipCode || isCounty ? undefined : { - "ceterms:name": { "search:value": params.searchQuery, "search:matchType": "search:contains" }, - "ceterms:description": { "search:value": params.searchQuery, "search:matchType": "search:contains" }, - "ceterms:ownedBy": { "ceterms:name": { "search:value": params.searchQuery, "search:matchType": "search:contains" } } + "ceterms:name": { + "search:value": params.searchQuery, + "search:matchType": "search:contains" + }, + "ceterms:description": { + "search:value": params.searchQuery, + "search:matchType": "search:contains" + }, + "ceterms:ownedBy": { + "ceterms:name": { + "search:value": params.searchQuery, + "search:matchType": "search:contains" + } + } }), "ceterms:occupationType": isSOC ? { - "ceterms:codedNotation": { "search:value": params.searchQuery, "search:matchType": "search:startsWith" } + "ceterms:codedNotation": { + "search:value": params.searchQuery, + "search:matchType": "search:startsWith" + } } : undefined, "ceterms:instructionalProgramType": isCIP ? { - "ceterms:codedNotation": { "search:value": params.searchQuery, "search:matchType": "search:startsWith" } + "ceterms:codedNotation": { + "search:value": params.searchQuery, + "search:matchType": "search:startsWith" + } } : undefined }; if (hasMultipleParts) { termGroup = { "search:operator": "search:andTerms", - "ceterms:ownedBy": { "ceterms:name": { "search:value": ownedByPart, "search:matchType": "search:contains" } }, - "ceterms:name": { "search:value": trainingPart, "search:matchType": "search:contains" } + "ceterms:ownedBy": { + "ceterms:name": { + "search:value": ownedByPart, + "search:matchType": "search:contains" + } + }, + "ceterms:name": { + "search:value": trainingPart, + "search:matchType": "search:contains" + } }; } @@ -452,8 +457,6 @@ async function transformCertificateToTraining( } } - - function packageResults(page: number, limit: number, results: TrainingResult[], totalResults: number): TrainingData { const totalPages = Math.ceil(totalResults / limit); const hasPreviousPage = page > 1; From 9f86bb40bd2cebc7268a3b7586a87642bc655d06 Mon Sep 17 00:00:00 2001 From: ChelseaKR Date: Thu, 16 Jan 2025 19:42:29 -0800 Subject: [PATCH 2/3] feat: include and prioritize exact matches while retaining broader contains matches for learning opportunity profile ceterms:name an ceterms:description --- backend/src/domain/search/searchTrainings.ts | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/backend/src/domain/search/searchTrainings.ts b/backend/src/domain/search/searchTrainings.ts index e51834b51..bb85febfc 100644 --- a/backend/src/domain/search/searchTrainings.ts +++ b/backend/src/domain/search/searchTrainings.ts @@ -334,14 +334,14 @@ function buildQuery(params: { let termGroup: TermGroup = { "search:operator": "search:orTerms", ...(isSOC || isCIP || !!isZipCode || isCounty ? undefined : { - "ceterms:name": { - "search:value": params.searchQuery, - "search:matchType": "search:contains" - }, - "ceterms:description": { - "search:value": params.searchQuery, - "search:matchType": "search:contains" - }, + "ceterms:name": [ + { "search:value": params.searchQuery, "search:matchType": "search:exact" }, + { "search:value": params.searchQuery, "search:matchType": "search:contains" }, + ], + "ceterms:description": [ + { "search:value": params.searchQuery, "search:matchType": "search:exact" }, + { "search:value": params.searchQuery, "search:matchType": "search:contains" }, + ], "ceterms:ownedBy": { "ceterms:name": { "search:value": params.searchQuery, @@ -355,11 +355,11 @@ function buildQuery(params: { "search:matchType": "search:startsWith" } } : undefined, - "ceterms:instructionalProgramType": isCIP ? { - "ceterms:codedNotation": { - "search:value": params.searchQuery, - "search:matchType": "search:startsWith" - } + "ceterms:instructionalProgramType": isCIP ? { + "ceterms:codedNotation": { + "search:value": params.searchQuery, + "search:matchType": "search:startsWith" + } } : undefined }; From 7a3f6dc30d93d7ce971a46775be40b9fcb8ffe23 Mon Sep 17 00:00:00 2001 From: ChelseaKR Date: Thu, 16 Jan 2025 20:01:55 -0800 Subject: [PATCH 3/3] more readability enhancement --- backend/src/domain/search/searchTrainings.ts | 36 ++++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/backend/src/domain/search/searchTrainings.ts b/backend/src/domain/search/searchTrainings.ts index bb85febfc..9cf78ccd0 100644 --- a/backend/src/domain/search/searchTrainings.ts +++ b/backend/src/domain/search/searchTrainings.ts @@ -1,24 +1,24 @@ import NodeCache from "node-cache"; // import * as Sentry from "@sentry/node"; import { SearchTrainings } from "../types"; -import {credentialEngineAPI} from "../../credentialengine/CredentialEngineAPI"; -import {credentialEngineUtils} from "../../credentialengine/CredentialEngineUtils"; -import {CTDLResource} from "../credentialengine/CredentialEngine"; -import {getLocalExceptionCounties} from "../utils/getLocalExceptionCounties"; -import {DataClient} from "../DataClient"; -import {getHighlight} from "../utils/getHighlight"; -import {TrainingData, TrainingResult} from "../training/TrainingResult"; +import { credentialEngineAPI } from "../../credentialengine/CredentialEngineAPI"; +import { credentialEngineUtils } from "../../credentialengine/CredentialEngineUtils"; +import { CTDLResource } from "../credentialengine/CredentialEngine"; +import { getLocalExceptionCounties } from "../utils/getLocalExceptionCounties"; +import { DataClient } from "../DataClient"; +import { getHighlight } from "../utils/getHighlight"; +import { TrainingData, TrainingResult } from "../training/TrainingResult"; import zipcodeJson from "../utils/zip-county.json"; import zipcodes, {ZipCode} from "zipcodes"; import { convertZipCodeToCounty } from "../utils/convertZipCodeToCounty"; -import {DeliveryType} from "../DeliveryType"; -import {normalizeCipCode} from "../utils/normalizeCipCode"; -import {normalizeSocCode} from "../utils/normalizeSocCode"; +import { DeliveryType } from "../DeliveryType"; +import { normalizeCipCode } from "../utils/normalizeCipCode"; +import { normalizeSocCode } from "../utils/normalizeSocCode"; // Initializing a simple in-memory cache const cache = new NodeCache({ stdTTL: 300, checkperiod: 120 }); -const fetchAllCerts = async (query: object, offset = 0, limit = 10): Promise<{ allCerts: CTDLResource[]; totalResults: number }> => { +const searchTrainingPrograms = async (query: object, offset = 0, limit = 10): Promise<{ allCerts: CTDLResource[]; totalResults: number }> => { try { console.log(`FETCHING RECORD with offset ${offset} and limit ${limit}`); const response = await credentialEngineAPI.getResults(query, offset, limit); @@ -34,19 +34,19 @@ const fetchAllCerts = async (query: object, offset = 0, limit = 10): Promise<{ a }; -const fetchAllCertsInBatches = async (query: object, batchSize = 100) => { +const searchTrainingProgramsInBatches = async (query: object, batchSize = 100) => { const allCerts: CTDLResource[] = []; - const initialResponse = await fetchAllCerts(query, 0, batchSize); + const initialResponse = await searchTrainingPrograms(query, 0, batchSize); const totalResults = initialResponse.totalResults; allCerts.push(...initialResponse.allCerts); const fetchBatch = async (offset: number) => { try { - const response = await fetchAllCerts(query, offset, batchSize); + const response = await searchTrainingPrograms(query, offset, batchSize); return response.allCerts; } catch (error) { console.error(`Error fetching batch at offset ${offset}:`, error); - return []; // Skip this batch + return []; } }; @@ -263,7 +263,7 @@ export const searchTrainingsFactory = (dataClient: DataClient): SearchTrainings // If unfiltered results are not in cache, fetch the results if (!unFilteredResults) { console.log(`Fetching results for query: ${params.searchQuery}`); - const { allCerts } = await fetchAllCertsInBatches(query); + const { allCerts } = await searchTrainingProgramsInBatches(query); unFilteredResults = await Promise.all( allCerts.map((certificate) => transformCertificateToTraining(dataClient, certificate, params.searchQuery) @@ -355,8 +355,8 @@ function buildQuery(params: { "search:matchType": "search:startsWith" } } : undefined, - "ceterms:instructionalProgramType": isCIP ? { - "ceterms:codedNotation": { + "ceterms:instructionalProgramType": isCIP ? + {"ceterms:codedNotation": { "search:value": params.searchQuery, "search:matchType": "search:startsWith" }