Skip to content

Commit

Permalink
comments on itis sorting functions
Browse files Browse the repository at this point in the history
  • Loading branch information
mauberti-bc committed Mar 5, 2024
1 parent 7410677 commit b5fd23e
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 32 deletions.
4 changes: 2 additions & 2 deletions api/src/services/itis-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ export class ItisService {
_sanitizeItisData = (data: ItisSolrSearchResponse[]): TaxonSearchResult[] => {
return data.map((item: ItisSolrSearchResponse) => {
const englishNames = item.commonNames?.filter((name) => name.split('$')[2] === 'English');
const commonNames = englishNames && englishNames.map((name) => name.split('$')[1])
const commonNames = englishNames?.map((name) => name.split('$')[1]);

return {
tsn: Number(item.tsn),
commonNames: commonNames,
commonNames: commonNames ?? [],
scientificName: item.scientificName,
rank: item.rank,
kingdom: item.kingdom
Expand Down
1 change: 1 addition & 0 deletions api/src/utils/itis.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO
62 changes: 32 additions & 30 deletions api/src/utils/itis.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
import { TaxonSearchResult } from '../services/taxonomy-service';

/**
* Sorts the ITIS response such that exact matches are at the start of the array of matching taxa
* Sorts the ITIS response by how strongly records match the search terms
*
* @param {ItisSolrSearchResponse[]} data
* @param {string[]} searchTerms
* @memberof ItisService
*/
export const sortExactMatches = (data: TaxonSearchResult[], searchTerms: string[]): TaxonSearchResult[] => {
const searchTermsLower = searchTerms.map((item) => item.toLowerCase());

// Prioritize taxa where any word in the scientific or common name matches any of the search terms
// Prioritize taxa where any word in the scientific or common name matches ANY of the search terms
// eg. ['Black', 'bear'] -> "Black" matches on "Black widow"
const contains = customSortContainsAnyMatchingSearchTerm(data, searchTermsLower);

// Prioritize records where any word in the scientific or common name matches the JOINED search terms
const someEquals = customSortContainsSearchTermsJoined(contains, searchTermsLower);
const containsAnyMatch = customSortContainsAnyMatchingSearchTerm(data, searchTermsLower);

// Prioritize taxa where either the scientific name or any common name CONTAINS the search terms joined
// eg. ['Black', 'bear'] -> "Black bear" matches on "American black bear"
const exactEquals = customSortContainsSearchTermsJoined(someEquals, searchTermsLower);
const containsAnyMatchJoined = customSortContainsSearchTermsJoinedExact(containsAnyMatch, searchTermsLower);

// Prioritize taxa where either the scientific name or any common name is EXACTLY EQUAL to the search terms joined
// eg. ['Wolf'] -> "Wolf" is prioritized over "Forest Wolf"
const exactlyEquals = customSortEqualsSearchTermsExact(containsAnyMatchJoined, searchTermsLower);

return exactEquals;
return exactlyEquals;
};

/**
* Sorts the ITIS response such that exact matches with search terms are first
* Sorts the ITIS response to prioritize records where any word in the scientific or
* common name matches ANY of the search terms
*
* @param {ItisSolrSearchResponse[]} data
* @param {string[]} searchTerms
* @memberof ItisService
*/
export const customSortContainsAnyMatchingSearchTerm = (
Expand All @@ -48,8 +52,7 @@ export const customSortContainsAnyMatchingSearchTerm = (
};

const checkForMatch = (item: TaxonSearchResult, searchTerms: string[]) => {
const commonNameWords = item.commonNames.flatMap((name: string) => name.toLowerCase().split(/\s+/)) ?? [];

const commonNameWords = item.commonNames?.flatMap((name: string) => name.toLowerCase().split(/\s+/));
const scientificNameWords = item.scientificName.toLowerCase().split(/\s+/);

// Check if any word in commonNames or scientificName matches any word in searchTerms
Expand All @@ -62,49 +65,53 @@ export const customSortContainsAnyMatchingSearchTerm = (
};

/**
* Sorts the ITIS response such that exact matches with search terms are first
* Sorts the ITIS response to prioritize records where either the scientific name or
* any common name CONTAINS the search terms joined
*
* @param {ItisSolrSearchResponse[]} data
* @param {string[]} searchTerms
* @memberof ItisService
*/
export const customSortContainsSearchTermsJoined = (
export const customSortContainsSearchTermsJoinedExact = (
data: TaxonSearchResult[],
searchTerms: string[]
): TaxonSearchResult[] => {
// Custom sorting function
const customSort = (a: TaxonSearchResult, b: TaxonSearchResult) => {
const aInReference = checkForMatch(a, searchTerms);
const bInReference = checkForMatch(b, searchTerms);

if (aInReference && !bInReference) {
return -1; // Place items from searchTerms before other items
} else if (!aInReference && bInReference) {
return 1; // Place other items after items from searchTerms
return 0; // Maintain the original order
} else {
return 0; // Maintain the original order if both are from searchTerms or both are not
return 0; // Maintain the original order
}
};

// Function to check if an item is a match with search terms
const checkForMatch = (item: TaxonSearchResult, searchTerms: string[]) => {
const commonNameWords = item.commonNames?.map((name) => name.toLowerCase());

// Lowercase scientificName and split into individual words
const scientificNameWords = item.scientificName.toLowerCase().split(/\s+/);
const commonNameWords = item.commonNames?.map((name: string) => name.toLowerCase());
const scientificNameWord = item.scientificName.toLowerCase();

return commonNameWords?.includes(searchTerms.join(' ')) || scientificNameWords.includes(searchTerms.join(' '));
// Add a space such that "Black bear" matches "American black bear" and not "Black Bearded"
return (
scientificNameWord === searchTerms.join(' ') ||
commonNameWords?.some((name) => `${name}${' '}`.includes(`${searchTerms.join(' ')}${' '}`))
);
};

return data.sort(customSort);
};

/**
* Sorts the ITIS response such that exact matches with search terms are first
* Sorts the ITIS response to prioritize taxa where either the scientific name or
* any common name is EXACTLY EQUAL to the search terms joined
*
* @param {ItisSolrSearchResponse[]} data
* @memberof ItisService
*/
export const customSortContainsSearchTermsJoined = (
export const customSortEqualsSearchTermsExact = (
data: TaxonSearchResult[],
searchTerms: string[]
): TaxonSearchResult[] => {
Expand All @@ -123,15 +130,10 @@ export const customSortContainsSearchTermsJoined = (

// Function to check if an item is a match with search terms
const checkForMatch = (item: TaxonSearchResult, searchTerms: string[]) => {
const commonNameWords = item.commonNames.map((name: string) => name.toLowerCase());

const commonNameWords = item.commonNames?.map((name: string) => name.toLowerCase());
const scientificNameWord = item.scientificName.toLowerCase();

// Add a space such that "Black bear" matches "American black bear" and not "Black Bearded"
return (
scientificNameWord === searchTerms.join(' ') ||
commonNameWords?.some((name) => `${name}${' '}`.includes(`${searchTerms.join(' ')}${' '}`))
);
return scientificNameWord === searchTerms.join(' ') || commonNameWords?.includes(searchTerms.join(' '));
};

return data.sort(customSort);
Expand Down

0 comments on commit b5fd23e

Please sign in to comment.