mirror of
https://github.com/docmost/docmost.git
synced 2026-08-25 15:57:11 +08:00
feat: browse creator-filtered results without a query
Selecting a creator with an empty query now lists their pages (or attachments in attachment mode) newest-first, with the same space and page permission filtering as typed search.
This commit is contained in:
@@ -106,7 +106,8 @@ export function SearchSpotlight({ spaceId }: SearchSpotlightProps) {
|
||||
}
|
||||
}, [aiSearchError, t]);
|
||||
|
||||
const isLabelBrowse = (filters.labelIds?.length ?? 0) > 0;
|
||||
const isFilterBrowse =
|
||||
(filters.labelIds?.length ?? 0) > 0 || !!filters.creatorId;
|
||||
// while the debounce is pending the empty list is not a settled "no results"
|
||||
const isQuerySettled = query === debouncedSearchQuery;
|
||||
|
||||
@@ -231,11 +232,11 @@ export function SearchSpotlight({ spaceId }: SearchSpotlightProps) {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{query.length === 0 && !isLabelBrowse && resultItems.length === 0 && (
|
||||
{query.length === 0 && !isFilterBrowse && resultItems.length === 0 && (
|
||||
<Spotlight.Empty>{t("Start typing to search...")}</Spotlight.Empty>
|
||||
)}
|
||||
|
||||
{(query.length > 0 || isLabelBrowse) &&
|
||||
{(query.length > 0 || isFilterBrowse) &&
|
||||
!isFetching &&
|
||||
isQuerySettled &&
|
||||
resultItems.length === 0 && (
|
||||
@@ -244,7 +245,7 @@ export function SearchSpotlight({ spaceId }: SearchSpotlightProps) {
|
||||
|
||||
{resultItems.length > 0 && <>{resultItems}</>}
|
||||
|
||||
{(query.length > 0 || isLabelBrowse) && isFetching && (
|
||||
{(query.length > 0 || isFilterBrowse) && isFetching && (
|
||||
<Spotlight.Empty>
|
||||
<Text size="sm" style={{ marginTop: 10 }}>
|
||||
{t("Searching...")}
|
||||
|
||||
@@ -39,11 +39,16 @@ export function useUnifiedSearch(
|
||||
return await searchPage(backendParams);
|
||||
}
|
||||
},
|
||||
enabled: (!!params.query || (params.labelIds?.length ?? 0) > 0) && enabled,
|
||||
enabled:
|
||||
(!!params.query ||
|
||||
(params.labelIds?.length ?? 0) > 0 ||
|
||||
!!params.creatorId) &&
|
||||
enabled,
|
||||
// keep previous results only within the same search type; page results
|
||||
// rendered as attachments (or vice versa) crash on missing fields
|
||||
placeholderData: (previousData, previousQuery) => {
|
||||
if (!params.query && !params.labelIds?.length) return undefined;
|
||||
if (!params.query && !params.labelIds?.length && !params.creatorId)
|
||||
return undefined;
|
||||
if (previousQuery && previousQuery.queryKey[1] !== searchType) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -31,17 +31,19 @@ export class SearchService {
|
||||
): Promise<{ items: SearchResponseDto[] }> {
|
||||
const query = searchParams.query?.trim() ?? '';
|
||||
const labelIds = [...new Set(searchParams.labelIds ?? [])];
|
||||
// selected labels are browsable without a query
|
||||
const browseByLabels = query.length < 1 && labelIds.length > 0;
|
||||
// selected filters (labels, creator) are browsable without a query
|
||||
const browseByFilters =
|
||||
query.length < 1 &&
|
||||
(labelIds.length > 0 || Boolean(searchParams.creatorId));
|
||||
|
||||
if (query.length < 1 && !browseByLabels) {
|
||||
if (query.length < 1 && !browseByFilters) {
|
||||
return { items: [] };
|
||||
}
|
||||
const searchQuery = tsquery(query + '*');
|
||||
const titleOnly = searchParams.titleOnly === true;
|
||||
const titleQuery = query;
|
||||
|
||||
const rankColumn = browseByLabels
|
||||
const rankColumn = browseByFilters
|
||||
? sql<number>`0`.as('rank')
|
||||
: titleOnly
|
||||
? sql<number>`word_similarity(lower(f_unaccent(${titleQuery})), lower(f_unaccent(pages.title)))`.as(
|
||||
@@ -50,7 +52,7 @@ export class SearchService {
|
||||
: sql<number>`ts_rank(tsv, to_tsquery('english', f_unaccent(${searchQuery})))`.as(
|
||||
'rank',
|
||||
);
|
||||
const highlightColumn = browseByLabels || titleOnly
|
||||
const highlightColumn = browseByFilters || titleOnly
|
||||
? sql<string>`''`.as('highlight')
|
||||
: sql<string>`ts_headline('english', text_content, to_tsquery('english', f_unaccent(${searchQuery})),'MinWords=9, MaxWords=10, MaxFragments=3')`.as(
|
||||
'highlight',
|
||||
@@ -70,14 +72,14 @@ export class SearchService {
|
||||
rankColumn,
|
||||
highlightColumn,
|
||||
])
|
||||
.$if(!browseByLabels && !titleOnly, (qb) =>
|
||||
.$if(!browseByFilters && !titleOnly, (qb) =>
|
||||
qb.where(
|
||||
'tsv',
|
||||
'@@',
|
||||
sql<string>`to_tsquery('english', f_unaccent(${searchQuery}))`,
|
||||
),
|
||||
)
|
||||
.$if(!browseByLabels && titleOnly, (qb) =>
|
||||
.$if(!browseByFilters && titleOnly, (qb) =>
|
||||
qb.where((eb) =>
|
||||
eb(
|
||||
sql`lower(f_unaccent(pages.title))`,
|
||||
@@ -100,8 +102,8 @@ export class SearchService {
|
||||
),
|
||||
)
|
||||
.where('deletedAt', 'is', null)
|
||||
.$if(browseByLabels, (qb) => qb.orderBy('updatedAt', 'desc'))
|
||||
.$if(!browseByLabels, (qb) => qb.orderBy('rank', 'desc'))
|
||||
.$if(browseByFilters, (qb) => qb.orderBy('updatedAt', 'desc'))
|
||||
.$if(!browseByFilters, (qb) => qb.orderBy('rank', 'desc'))
|
||||
.limit(searchParams.limit || 25)
|
||||
.offset(searchParams.offset || 0);
|
||||
|
||||
|
||||
+1
-1
Submodule apps/server/src/ee updated: add0054e96...0be4a76175
Reference in New Issue
Block a user