Skip to content

Commit

Permalink
fix(api-headless-cms): search with special chars in search term (#3557)
Browse files Browse the repository at this point in the history
  • Loading branch information
brunozoric committed Sep 28, 2023
1 parent a65f3b3 commit 770a185
Show file tree
Hide file tree
Showing 8 changed files with 333 additions and 113 deletions.
4 changes: 3 additions & 1 deletion jest.config.base.setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@

// Runs failed tests five times until they pass or until the max number of retries is exhausted.
// https://jestjs.io/docs/jest-object#jestretrytimesnumretries-options
jest.retryTimes(3);
if (process.env.CI === "true") {
jest.retryTimes(3);
}
28 changes: 21 additions & 7 deletions packages/api-elasticsearch/src/normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#_reserved_characters
*/

const specialCharactersToRemove = [`\\?`];

const specialCharacterToSpace = ["-"];

const specialCharacters = [
"\\\\",
"\\+",
// "\\-",
"\\=",
"\\&\\&",
"\\|\\|",
`\&`,
`\\|`,
">",
"<",
"\\!",
`\!`,
"\\(",
"\\)",
"\\{",
Expand All @@ -25,14 +27,19 @@ const specialCharacters = [
'\\"',
"\\~",
"\\*",
"\\?",
"\\:",
`\:`,
`\/`,
"\\#"
];

export const normalizeValue = (value: string) => {
let result = value || "";
if (!result) {
return result;
}
for (const character of specialCharactersToRemove) {
result = result.replace(new RegExp(character, "g"), "");
}
for (const character of specialCharacterToSpace) {
result = result.replace(new RegExp(character, "g"), " ");
}
Expand All @@ -44,6 +51,13 @@ export const normalizeValue = (value: string) => {
return result || "";
};

const hasSpecialChar = (value: string): boolean | null => {
if (!value) {
return null;
}
return value.match(/^([0-9a-zA-Z]+)$/i) === null;
};

export const normalizeValueWithAsterisk = (initial: string) => {
const value = normalizeValue(initial);
const results = value.split(" ");
Expand All @@ -53,14 +67,14 @@ export const normalizeValueWithAsterisk = (initial: string) => {
* If there is a / in the first word, do not put asterisk in front of it.
*/
const firstWord = results[0];
if (firstWord && firstWord.includes("/") === false) {
if (hasSpecialChar(firstWord) === false) {
result = `*${result}`;
}
/**
* If there is a / in the last word, do not put asterisk at the end of it.
*/
const lastWord = results[results.length - 1];
if (lastWord && lastWord.includes("/") === false) {
if (hasSpecialChar(lastWord) === false) {
result = `${result}*`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,7 @@ describe("filtering", () => {
fields
});

expect(resultNumber3).toHaveLength(0);

expect(resultNumber3).toMatchObject([]);
expect(resultNumber3).toHaveLength(19);
});

it("should filter by nested options variant colors", async () => {
Expand Down
172 changes: 170 additions & 2 deletions packages/api-headless-cms/__tests__/contentAPI/search.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { useFruitManageHandler } from "~tests/testHelpers/useFruitManageHandler";
import { setupContentModelGroup, setupContentModels } from "~tests/testHelpers/setup";
import { useCategoryManageHandler } from "~tests/testHelpers/useCategoryManageHandler";
import { toSlug } from "~/utils/toSlug";

describe("search", () => {
const categoryManager = useCategoryManageHandler({
path: "manage/en-US"
});
const fruitManager = useFruitManageHandler({
path: "manage/en-US"
});
Expand All @@ -13,6 +18,7 @@ describe("search", () => {
});

if (response.data.createFruit.error) {
console.log(JSON.stringify(response.data.createFruit.error, null, 2));
throw new Error(response.data.createFruit.error.message);
}
return response.data.createFruit.data;
Expand Down Expand Up @@ -41,11 +47,11 @@ describe("search", () => {

const setupFruits = async (input?: string[]) => {
const group = await setupContentModelGroup(fruitManager);
await setupContentModels(fruitManager, group, ["fruit"]);
await setupContentModels(fruitManager, group, ["fruit", "category"]);
return createFruits(input);
};

it.skip("should find record with dash in the middle of two words", async () => {
it("should find record with dash in the middle of two words", async () => {
await setupFruits();
const [response] = await listFruits({
where: {
Expand Down Expand Up @@ -331,4 +337,166 @@ describe("search", () => {
}
});
});

it("should find a record with special characters in the title", async () => {
await setupFruits();
const categories = {
apple: "A Tasty Fruit: Apple w/ Black Dots?",
banana: "A Not So Tasty Fruit: Banana w/ Yellow Dots",
orange: "Awesome Fruit: Orange w/ Leaves",
grape: "Wine - An Autumn Fruit: Grape w/ Seeds?",
tangerine: "An Autumn Fruit: Tangerine w/ Seeds?",
cleaning: "Clean Building Day | The Ultimate Cleaning Trick Tips!",
car: "2001 CarMaker Car type: SVO Reborn? - Burn Epi. 917"
};
const results: any[] = [];
for (const title of Object.values(categories)) {
const [result] = await categoryManager.createCategory({
data: {
title,
slug: toSlug(title)
}
});
results.push(result?.data?.createCategory?.data);
}
expect(results).toHaveLength(Object.values(categories).length);

const [initialResponse] = await categoryManager.listCategories({
sort: ["createdOn_ASC"]
});
expect(initialResponse).toMatchObject({
data: {
listCategories: {
data: expect.any(Array),
meta: {
totalCount: Object.values(categories).length,
hasMoreItems: false,
cursor: null
},
error: null
}
}
});

const [appleListResponse] = await categoryManager.listCategories({
where: {
title_contains: "tasty fruit: apple w/ black dots"
}
});
expect(appleListResponse).toMatchObject({
data: {
listCategories: {
data: [
{
title: categories.apple
}
],
meta: {
totalCount: 1,
hasMoreItems: false,
cursor: null
},
error: null
}
}
});

const [dotsListResponse] = await categoryManager.listCategories({
where: {
title_contains: "tasty fruit: w/ dots"
}
});
expect(dotsListResponse).toMatchObject({
data: {
listCategories: {
data: [
{
title: categories.banana
},
{
title: categories.apple
}
],
meta: {
totalCount: 2,
hasMoreItems: false,
cursor: null
},
error: null
}
}
});

const [questionMarksListResponse] = await categoryManager.listCategories({
where: {
title_contains: "autumn fruit: seeds?"
}
});
expect(questionMarksListResponse).toMatchObject({
data: {
listCategories: {
data: [
{
title: categories.tangerine
},
{
title: categories.grape
}
],
meta: {
totalCount: 2,
hasMoreItems: false,
cursor: null
},
error: null
}
}
});

const [cleaningListResponse] = await categoryManager.listCategories({
where: {
title_contains: "Clean Building Day | The Ultimate Cleaning Trick Tips!"
}
});
expect(cleaningListResponse).toMatchObject({
data: {
listCategories: {
data: [
{
title: categories.cleaning
}
],
meta: {
totalCount: 1,
hasMoreItems: false,
cursor: null
},
error: null
}
}
});

const [carListResponse] = await categoryManager.listCategories({
where: {
title_contains: "2001 CarMaker Car type: SVO Reborn? - Burn Epi. 917"
}
});
expect(carListResponse).toMatchObject({
data: {
listCategories: {
data: [
{
title: categories.car
}
],
meta: {
totalCount: 1,
hasMoreItems: false,
cursor: null
},
error: null
}
}
});
});
});
Loading

0 comments on commit 770a185

Please sign in to comment.