implement search function

This commit is contained in:
Hongbo Wu
2023-08-24 21:47:13 +08:00
parent 0b309b7a45
commit 751df68175
2 changed files with 149 additions and 9 deletions

View File

@ -1,4 +1,4 @@
import { DeepPartial } from 'typeorm'
import { DeepPartial, SelectQueryBuilder } from 'typeorm'
import { Highlight } from '../entity/highlight'
import { Label } from '../entity/label'
import {
@ -15,8 +15,11 @@ import {
HasFilter,
InFilter,
LabelFilter,
LabelFilterType,
NoFilter,
ReadFilter,
SortBy,
SortOrder,
SortParams,
} from '../utils/search'
@ -102,3 +105,140 @@ export const createLibraryItem = async (
libraryItem.wordCount ?? wordsCount(libraryItem.readableContent ?? ''),
})
}
const buildWhereClause = (
queryBuilder: SelectQueryBuilder<LibraryItem>,
userId: string,
args: PageSearchArgs
) => {
if (args.query) {
queryBuilder.andWhere(`tsv @@ websearch_to_tsquery(:query)`, {
query: args.query,
})
}
if (args.typeFilter) {
queryBuilder.andWhere(`library_item.item_type = :typeFilter`, {
typeFilter: args.typeFilter,
})
}
if (args.inFilter !== InFilter.ALL) {
switch (args.inFilter) {
case InFilter.INBOX:
queryBuilder.andWhere(`library_item.archived_at IS NULL`)
break
case InFilter.ARCHIVE:
queryBuilder.andWhere(`library_item.archived_at IS NOT NULL`)
break
case InFilter.TRASH:
// return only deleted pages within 14 days
queryBuilder.andWhere(
`library_item.state = :state AND deleted_at >= now() - interval '14 days'`,
{
state: LibraryItemState.Deleted,
}
)
break
}
}
if (args.readFilter !== ReadFilter.ALL) {
switch (args.readFilter) {
case ReadFilter.READ:
queryBuilder.andWhere('library_item.reading_progress_top_percent >= 98')
break
case ReadFilter.UNREAD:
queryBuilder.andWhere('library_item.reading_progress_top_percent < 98')
break
}
}
if (args.hasFilters && args.hasFilters.length > 0) {
args.hasFilters.forEach((filter) => {
switch (filter) {
case HasFilter.HIGHLIGHTS:
queryBuilder.andWhere(`library_item.highlights IS NOT NULL`)
break
}
})
}
if (args.labelFilters && args.labelFilters.length > 0) {
const includeLabels = args.labelFilters?.filter(
(filter) => filter.type === LabelFilterType.INCLUDE
)
const excludeLabels = args.labelFilters?.filter(
(filter) => filter.type === LabelFilterType.EXCLUDE
)
if (includeLabels && includeLabels.length > 0) {
queryBuilder.andWhere(
`library_item.id IN (SELECT library_item_id FROM library_item_label WHERE name IN (:...includeLabels))`,
{
includeLabels,
}
)
}
if (excludeLabels && excludeLabels.length > 0) {
queryBuilder.andWhere(
`library_item.id NOT IN (SELECT library_item_id FROM library_item_label WHERE name IN (:...excludeLabels))`,
{
excludeLabels,
}
)
}
}
if (args.dateFilters && args.dateFilters.length > 0) {
args.dateFilters.forEach((filter) => {
queryBuilder.andWhere(
`library_item.${filter.field} between :startDate and :endDate`,
{
startDate: filter.startDate ?? new Date(0),
endDate: filter.endDate ?? new Date(),
}
)
})
}
if (args.termFilters && args.termFilters.length > 0) {
args.termFilters.forEach((filter) => {
queryBuilder.andWhere(`library_item.${filter.field} = :value`, {
value: filter.value,
})
})
}
}
export const searchLibraryItems = async (
args: PageSearchArgs,
userId: string
): Promise<[LibraryItem[], number] | null> => {
const { from = 0, size = 10, sort } = args
// default order is descending
const sortOrder = sort?.order || SortOrder.DESCENDING
// default sort by saved_at
const sortField = sort?.by || SortBy.SAVED
const queryBuilder = entityManager
.createQueryBuilder(LibraryItem, 'library_item')
.select('library_item.*')
.where('library_item.user_id = :userId', { userId })
// build the where clause
buildWhereClause(queryBuilder, userId, args)
// add pagination and sorting
const libraryItems = await queryBuilder
.orderBy(`omnivore.library_item.${sortField}`, sortOrder)
.offset(from)
.limit(size)
.getMany()
const count = await queryBuilder.getCount()
return [libraryItems, count]
}

View File

@ -65,18 +65,18 @@ export interface DateFilter {
}
export enum SortBy {
SAVED = 'savedAt',
UPDATED = 'updatedAt',
SAVED = 'saved_at',
UPDATED = 'updated_at',
SCORE = '_score',
PUBLISHED = 'publishedAt',
READ = 'readAt',
LISTENED = 'listenedAt',
WORDS_COUNT = 'wordsCount',
PUBLISHED = 'published_at',
READ = 'read_at',
LISTENED = 'listened_at',
WORDS_COUNT = 'word_count',
}
export enum SortOrder {
ASCENDING = 'asc',
DESCENDING = 'desc',
ASCENDING = 'ASC',
DESCENDING = 'DESC',
}
export interface SortParams {