Skip to content

feat: add search controller + search and reverseSearch methods #311

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions packages/core/server/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import urlAliasController from './url-alias';
import urlPatternController from './url-pattern';
import infoController from './info';
import coreController from './core';
import searchController from './search';

export default {
'url-alias': urlAliasController,
'url-pattern': urlPatternController,
info: infoController,
core: coreController,
search: searchController,
};
79 changes: 79 additions & 0 deletions packages/core/server/controllers/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Context } from 'koa';
import { UID } from '@strapi/strapi';
import { isContentTypeEnabled } from '../util/enabledContentTypes';
import { getMainField } from '../services/get-main-field';

/**
* Search controller
*/
export default {
search: async (ctx: Context & { params: { id: number } }) => {
const { q } = ctx.query;
const { id } = ctx.params;

Check warning on line 12 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (22)

'id' is assigned a value but never used

Check warning on line 12 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (20)

'id' is assigned a value but never used
let results = [];

Check failure on line 13 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (22)

'results' is never reassigned. Use 'const' instead

Check failure on line 13 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (20)

'results' is never reassigned. Use 'const' instead

const qStr = typeof q === 'string' ? q.trim() : '';
if (!qStr) {
ctx.throw(400, 'Missing or invalid query parameter "?q=" (must be a non-empty strin4g)');
return;
}

await Promise.all(
Object.entries(strapi.contentTypes).map(async ([uid, config]: [UID.CollectionType, any]) => {
const hasWT = isContentTypeEnabled(config);

Check warning on line 23 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Unsafe argument of type `any` assigned to a parameter of type `ContentType<ContentType>`

Check warning on line 23 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe argument of type `any` assigned to a parameter of type `ContentType<ContentType>`
if (!hasWT) return;

const mainField = await getMainField(uid);
if (!mainField) return;

const entries = await (strapi as any).documents(uid).findMany({

Check warning on line 29 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Unsafe call of an `any` typed value

Check warning on line 29 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Unsafe member access .documents on an `any` value

Check warning on line 29 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Unsafe call of an `any` typed value

Check warning on line 29 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Unsafe member access .findMany on an `any` value

Check warning on line 29 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Unsafe assignment of an `any` value

Check warning on line 29 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe call of an `any` typed value

Check warning on line 29 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .documents on an `any` value

Check warning on line 29 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe call of an `any` typed value

Check warning on line 29 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .findMany on an `any` value

Check warning on line 29 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
filters: {
[mainField]: { $containsi: qStr },
},
fields: [mainField, 'documentId'],
populate: {
url_alias: { fields: ['id'] },
},
});

if (!entries || entries.length === 0) return;

Check warning on line 39 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Unsafe member access .length on an `any` value

Check warning on line 39 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .length on an `any` value

const entriesWithContentType = entries.map((entry: any) => ({

Check warning on line 41 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Unsafe member access .map on an `any` value

Check warning on line 41 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Unsafe assignment of an `any` value

Check warning on line 41 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .map on an `any` value

Check warning on line 41 in packages/core/server/controllers/search.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
...entry,
contentType: uid,
}));

results.push(...entriesWithContentType);
}),
);

// @ts-ignore
ctx.body = results;
},
reverseSearch: async (ctx: Context & { params: { contentType: string; documentId: string } }) => {
const { contentType, documentId } = ctx.params;

if (typeof contentType !== 'string' || !(contentType in strapi.contentTypes)) {
ctx.throw(400, `Unknown or invalid content type: ${contentType}`);
return;
}

const mainField = await getMainField(contentType as UID.CollectionType);

const entry = await (strapi as any).documents(contentType as UID.CollectionType).findOne({
documentId,
fields: ['id', 'documentId', ...(mainField ? [mainField] : [])],
});

if (!entry) {
ctx.throw(404, 'Entry not found');
return;
}

ctx.body = {
id: entry.id,
documentId: entry.documentId,
...(mainField ? { [mainField]: entry[mainField] } : {}),
};
},
};
22 changes: 22 additions & 0 deletions packages/core/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,28 @@ export default {
policies: [],
},
},
/**
* Search routes
*/
{
method: 'GET',
path: '/search',
handler: 'search.search',
config: {
policies: [],
},
},
/**
* Reverse Search routes for a title or slug
*/
{
method: 'GET',
path: '/search/reverse/:contentType/:documentId',
handler: 'search.reverseSearch',
config: {
policies: [],
},
},
],
},
};
11 changes: 11 additions & 0 deletions packages/core/server/services/get-main-field.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { UID } from '@strapi/strapi';

export async function getMainField(uid: UID.CollectionType): Promise<string | null> {
const coreStoreSettings = await strapi.query('strapi::core-store').findMany({
where: { key: `plugin_content_manager_configuration_content_types::${uid}` },
});
if (!coreStoreSettings?.[0]) return null;

const value = JSON.parse(coreStoreSettings[0].value);
return value?.settings?.mainField ?? null;
}
Loading