improve search result

This commit is contained in:
Hongbo Wu
2023-09-06 17:20:16 +08:00
parent 1bade22076
commit e1a7a24323
4 changed files with 58 additions and 32 deletions

View File

@ -518,6 +518,7 @@ export const setBookmarkArticleResolver = authorized<
articleID,
{
state: LibraryItemState.Deleted,
deletedAt: new Date(),
},
uid,
pubsub
@ -537,12 +538,6 @@ export const setBookmarkArticleResolver = authorized<
readableContent: undefined,
originalContent: undefined,
}),
labels: {
source: 'resolver',
resolver: 'setBookmarkArticleResolver',
userId: uid,
articleID,
},
})
// Make sure article.id instead of userArticle.id has passed. We use it for cache updates
return {

View File

@ -46,6 +46,7 @@ export interface SearchArgs {
includeContent?: boolean
noFilters?: NoFilter[]
siteName?: string
subscription?: string
}
export interface SearchResultItem {
@ -88,10 +89,15 @@ const buildWhereClause = (
) => {
if (args.query) {
queryBuilder
.addSelect('ts_rank_cd(library_item.search_tsv, query)', 'rank')
.addFrom("websearch_to_tsquery('english', ':query')", 'query')
.andWhere('query @@ library_item.search_tsv')
.addSelect(
`ts_rank_cd(library_item.search_tsv, websearch_to_tsquery('english', :query))`,
'rank'
)
.andWhere(
`websearch_to_tsquery('english', :query) @@ library_item.search_tsv`
)
.setParameter('query', args.query)
.orderBy('rank', 'DESC')
}
if (args.typeFilter) {
@ -114,6 +120,16 @@ const buildWhereClause = (
"library_item.state = 'DELETED' AND library_item.deleted_at >= now() - interval '14 days'"
)
break
case InFilter.SUBSCRIPTION:
queryBuilder
.andWhere('library_item.subscription_id IS NOT NULL')
.andWhere('library_item.archived_at IS NULL')
break
case InFilter.LIBRARY:
queryBuilder
.andWhere('library_item.subscription_id IS NULL')
.andWhere('library_item.archived_at IS NULL')
break
}
}
@ -133,11 +149,11 @@ const buildWhereClause = (
switch (filter) {
case HasFilter.HIGHLIGHTS:
queryBuilder.andWhere(
'library_item.highlight_annotations IS NOT NULL'
'array_length(library_item.highlight_annotations, 1) > 0'
)
break
case HasFilter.LABELS:
queryBuilder.andWhere('library_item.label_names IS NOT NULL')
queryBuilder.andWhere('array_length(library_item.label_names, 1) > 0')
break
}
})
@ -154,7 +170,7 @@ const buildWhereClause = (
if (includeLabels && includeLabels.length > 0) {
includeLabels.forEach((includeLabel) => {
queryBuilder.andWhere(
'(lower(library_item.label_names::text)::text[] @> ARRAY[:...includeLabels]::text[] OR lower(library_item.highlight_labels::text)::text[] @> ARRAY[:...includeLabels]::text[])',
'lower(array_cat(library_item.label_names, library_item.highlight_labels)::text)::text[] @> ARRAY[:...includeLabels]::text[]',
{
includeLabels: includeLabel.labels,
}
@ -164,9 +180,9 @@ const buildWhereClause = (
if (excludeLabels && excludeLabels.length > 0) {
queryBuilder.andWhere(
'(NOT lower(library_item.label_names::text)::text[] && ARRAY[:...excludeLabels]::text[] AND NOT lower(library_item.highlight_labels::text)::text[] && ARRAY[:...excludeLabels]::text[])',
'NOT lower(array_cat(library_item.label_names, library_item.highlight_labels)::text)::text[] && ARRAY[:...excludeLabels]::text[]',
{
excludeLabels,
excludeLabels: excludeLabels.flatMap((filter) => filter.labels),
}
)
}
@ -186,8 +202,8 @@ const buildWhereClause = (
if (args.termFilters && args.termFilters.length > 0) {
args.termFilters.forEach((filter) => {
queryBuilder.andWhere(`library_item.${filter.field} = :value`, {
value: filter.value,
queryBuilder.andWhere(`lower(library_item.${filter.field}) = :value`, {
value: filter.value.toLowerCase(),
})
})
}
@ -195,9 +211,9 @@ const buildWhereClause = (
if (args.matchFilters && args.matchFilters.length > 0) {
args.matchFilters.forEach((filter) => {
queryBuilder.andWhere(
`websearch_to_tsquery('english', ':query') @@ library_item.${filter.field}_tsv`,
`websearch_to_tsquery('english', :matchQuery) @@ library_item.${filter.field}_tsv`,
{
query: filter.value,
matchQuery: filter.value,
}
)
})
@ -224,6 +240,22 @@ const buildWhereClause = (
queryBuilder.andWhere(`library_item.${filter.field} = '{}'`)
})
}
if (args.recommendedBy) {
queryBuilder.innerJoin('library_item.recommendations', 'recommendations')
}
if (args.subscription) {
queryBuilder
.innerJoin('library_item.subscription', 'subscription')
.andWhere((qb) => {
qb.where('subscription.name = :subscription', {
subscription: args.subscription,
}).orWhere('subscription.url = :subscription', {
subscription: args.subscription,
})
})
}
}
export const searchLibraryItems = async (
@ -251,7 +283,7 @@ export const searchLibraryItems = async (
buildWhereClause(queryBuilder, args)
const libraryItems = await queryBuilder
.orderBy(`library_item.${sortField}`, sortOrder)
.addOrderBy(`library_item.${sortField}`, sortOrder)
.offset(from)
.limit(size)
.getMany()

View File

@ -42,6 +42,7 @@ export interface SearchFilter {
recommendedBy?: string
noFilters: NoFilter[]
siteName?: string
subscription?: string
}
export enum LabelFilterType {
@ -94,7 +95,7 @@ export interface NoFilter {
field: string
}
const parseRecommendedBy = (str?: string): string | undefined => {
const parseStringValue = (str?: string): string | undefined => {
if (str === undefined) {
return undefined
}
@ -268,22 +269,18 @@ const parseFieldFilter = (
return undefined
}
let nested = false
// normalize the term to lower case
const value = str.toLowerCase()
switch (field.toUpperCase()) {
case 'RSS':
field = 'rssFeedUrl'
break
case 'NOTE':
field = 'highlights.annotation'
nested = true
break
case 'LANGUAGE':
return {
field: 'item_language',
value,
}
}
return {
nested,
field,
value,
}
@ -427,9 +424,11 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => {
dateFilter && result.dateFilters.push(dateFilter)
break
}
// term filters
case 'subscription':
case 'rss':
result.subscription = parseStringValue(keyword.value)
break
// term filters
case 'language': {
const fieldFilter = parseFieldFilter(keyword.keyword, keyword.value)
fieldFilter && result.termFilters.push(fieldFilter)
@ -451,7 +450,7 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => {
break
}
case 'recommendedBy': {
result.recommendedBy = parseRecommendedBy(keyword.value)
result.recommendedBy = parseStringValue(keyword.value)
break
}
case 'no': {

View File

@ -12,6 +12,6 @@ CREATE TABLE omnivore.recommendation (
created_at timestamptz NOT NULL DEFAULT current_timestamp
);
GRANT SELECT, INSERT ON omnivore.library_item TO omnivore_user;
GRANT SELECT, INSERT ON omnivore.recommendation TO omnivore_user;
COMMIT;