diff --git a/packages/api/src/elastic/pages.ts b/packages/api/src/elastic/pages.ts index 40e4300e7..d6307007d 100644 --- a/packages/api/src/elastic/pages.ts +++ b/packages/api/src/elastic/pages.ts @@ -15,6 +15,7 @@ import { InFilter, LabelFilter, LabelFilterType, + NoFilter, ReadFilter, SortBy, SortOrder, @@ -208,6 +209,21 @@ const appendRecommendedBy = (body: SearchBody, recommendedBy: string): void => { }) } +const appendNoFilters = (body: SearchBody, noFilters: NoFilter[]): void => { + noFilters.forEach((filter) => { + body.query.bool.must_not.push({ + nested: { + path: filter.field, + query: { + exists: { + field: filter.field, + }, + }, + }, + }) + }) +} + export const createPage = async ( page: Page, ctx: PageContext @@ -389,6 +405,7 @@ export const searchPages = async ( matchFilters, ids, includeContent, + noFilters, } = args // default order is descending const sortOrder = sort?.order || SortOrder.DESCENDING @@ -484,6 +501,10 @@ export const searchPages = async ( }) } + if (noFilters) { + appendNoFilters(body, noFilters) + } + console.log('searching pages in elastic', JSON.stringify(body)) const response = await client.search, SearchBody>({ diff --git a/packages/api/src/elastic/types.ts b/packages/api/src/elastic/types.ts index bd238e7d6..39cdadba2 100644 --- a/packages/api/src/elastic/types.ts +++ b/packages/api/src/elastic/types.ts @@ -7,6 +7,7 @@ import { HasFilter, InFilter, LabelFilter, + NoFilter, ReadFilter, SortParams, } from '../utils/search' @@ -106,6 +107,16 @@ export interface SearchBody { } } } + | { + nested: { + path: string + query: { + exists: { + field: string + } + } + } + } )[] } } @@ -317,4 +328,5 @@ export interface PageSearchArgs { ids?: string[] recommendedBy?: string includeContent?: boolean + noFilters?: NoFilter[] } diff --git a/packages/api/src/utils/search.ts b/packages/api/src/utils/search.ts index 05c2cc3fe..b98ff016c 100644 --- a/packages/api/src/utils/search.ts +++ b/packages/api/src/utils/search.ts @@ -36,6 +36,7 @@ export interface SearchFilter { matchFilters: FieldFilter[] ids: string[] recommendedBy?: string + noFilters: NoFilter[] } export enum LabelFilterType { @@ -83,6 +84,10 @@ export interface FieldFilter { value: string } +export interface NoFilter { + field: string +} + const parseRecommendedBy = (str?: string): string | undefined => { if (str === undefined) { return undefined @@ -263,6 +268,22 @@ const parseIds = (field: string, str?: string): string[] | undefined => { return str.split(',') } +const parseNoFilter = (str?: string): NoFilter | undefined => { + if (str === undefined) { + return undefined + } + + const strLower = str.toLowerCase() + const accepted = ['highlight', 'label'] + if (accepted.includes(strLower)) { + return { + field: `${strLower}s`, + } + } + + return undefined +} + export const parseSearchQuery = (query: string | undefined): SearchFilter => { const searchQuery = query ? query.replace(/\W\s":/g, '') : undefined const result: SearchFilter = { @@ -275,6 +296,7 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => { termFilters: [], matchFilters: [], ids: [], + noFilters: [], } if (!searchQuery) { @@ -288,6 +310,7 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => { termFilters: [], matchFilters: [], ids: [], + noFilters: [], } } @@ -310,6 +333,7 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => { 'updated', 'includes', 'recommendedBy', + 'no', ], tokenize: true, }) @@ -395,6 +419,11 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => { result.recommendedBy = parseRecommendedBy(keyword.value) break } + case 'no': { + const noFilter = parseNoFilter(keyword.value) + noFilter && result.noFilters.push(noFilter) + break + } } } }