allow filter by wordsCount

This commit is contained in:
Hongbo Wu
2023-10-07 21:30:23 +08:00
parent fe3093834c
commit 835b61064f
4 changed files with 92 additions and 7 deletions

View File

@ -649,6 +649,7 @@ export const searchResolver = authorized<
size: first + 1, // fetch one more item to get next cursor
sort: searchQuery.sort,
includePending: true,
includeContent: params.includeContent || false,
...searchQuery,
},
uid

View File

@ -6,7 +6,7 @@ import { LibraryItem } from '../../entity/library_item'
import { env } from '../../env'
import { wait } from '../../utils/helpers'
import { logger } from '../../utils/logger'
import { getHighlightUrl } from '../highlights'
import { findHighlightsByLibraryItemId, getHighlightUrl } from '../highlights'
import { IntegrationService } from './integration'
interface ReadwiseHighlight {
@ -64,10 +64,12 @@ export class ReadwiseIntegration extends IntegrationService {
): Promise<boolean> => {
let result = true
const highlights = items.flatMap(this.libraryItemToReadwiseHighlight)
const highlights = await Promise.all(
items.map((item) => this.libraryItemToReadwiseHighlight(item))
)
// If there are no highlights, we will skip the sync
if (highlights.length > 0) {
result = await this.syncWithReadwise(integration.token, highlights)
result = await this.syncWithReadwise(integration.token, highlights.flat())
}
// update integration syncedAt if successful
@ -84,10 +86,16 @@ export class ReadwiseIntegration extends IntegrationService {
return result
}
libraryItemToReadwiseHighlight = (item: LibraryItem): ReadwiseHighlight[] => {
if (!item.highlights) return []
libraryItemToReadwiseHighlight = async (
item: LibraryItem
): Promise<ReadwiseHighlight[]> => {
let highlights = item.highlights
if (!highlights) {
highlights = await findHighlightsByLibraryItemId(item.id, item.user.id)
}
const category = item.siteName === 'Twitter' ? 'tweets' : 'articles'
return item.highlights
return highlights
.map((highlight) => {
// filter out highlights that are not of type highlight or have no quote
if (

View File

@ -6,7 +6,7 @@ import { Label } from '../entity/label'
import { LibraryItem, LibraryItemState } from '../entity/library_item'
import { BulkActionType } from '../generated/graphql'
import { createPubSubClient, EntityType } from '../pubsub'
import { authTrx } from '../repository'
import { authTrx, getColumns } from '../repository'
import { libraryItemRepository } from '../repository/library_item'
import { wordsCount } from '../utils/helpers'
import {
@ -17,6 +17,7 @@ import {
LabelFilter,
LabelFilterType,
NoFilter,
RangeFilter,
ReadFilter,
Sort,
SortBy,
@ -42,6 +43,7 @@ export interface SearchArgs {
recommendedBy?: string
includeContent?: boolean
noFilters?: NoFilter[]
rangeFilters?: RangeFilter[]
}
export interface SearchResultItem {
@ -258,6 +260,22 @@ const buildWhereClause = (
)
}
}
if (args.includeContent) {
queryBuilder.addSelect('library_item.readableContent')
}
if (args.rangeFilters && args.rangeFilters.length > 0) {
args.rangeFilters.forEach((filter, i) => {
const param = `range_${filter.field}_${i}`
queryBuilder.andWhere(
`library_item.${filter.field} ${filter.operator} ${param}`,
{
[param]: filter.value,
}
)
})
}
}
export const searchLibraryItems = async (
@ -271,11 +289,20 @@ export const searchLibraryItems = async (
// default sort by saved_at
const sortField = sort?.by || SortBy.SAVED
const selectColumns = getColumns(libraryItemRepository)
.map((column) => `library_item.${column}`)
.filter(
(column) =>
column !== 'library_item.readableContent' &&
column !== 'library_item.originalContent'
)
// add pagination and sorting
return authTrx(
async (tx) => {
const queryBuilder = tx
.createQueryBuilder(LibraryItem, 'library_item')
.select(selectColumns)
.where('library_item.user_id = :userId', { userId })
// build the where clause

View File

@ -40,6 +40,7 @@ export interface SearchFilter {
ids: string[]
recommendedBy?: string
noFilters: NoFilter[]
rangeFilters: RangeFilter[]
}
export enum LabelFilterType {
@ -63,6 +64,12 @@ export interface DateFilter {
endDate?: Date
}
export interface RangeFilter {
field: string
operator: string
value: number
}
export enum SortBy {
SAVED = 'savedAt',
UPDATED = 'updatedAt',
@ -255,6 +262,40 @@ const parseDateFilter = (
}
}
const parseRangeFilter = (
field: string,
str?: string
): RangeFilter | undefined => {
if (str === undefined) {
return undefined
}
switch (field.toUpperCase()) {
case 'WORDSCOUNT':
field = 'wordCount'
break
default:
return undefined
}
const operatorRegex = /([<>]=?)/
const operator = str.match(operatorRegex)?.[0]
if (!operator) {
return undefined
}
const value = str.replace(operatorRegex, '')
if (!value) {
return undefined
}
return {
field,
operator,
value: Number(value),
}
}
const parseFieldFilter = (
field: string,
str?: string
@ -323,6 +364,7 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => {
matchFilters: [],
ids: [],
noFilters: [],
rangeFilters: [],
}
if (!searchQuery) {
@ -337,6 +379,7 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => {
matchFilters: [],
ids: [],
noFilters: [],
rangeFilters: [],
}
}
@ -364,6 +407,7 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => {
'site',
'note',
'rss',
'wordCount',
],
tokenize: true,
})
@ -460,6 +504,11 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => {
case 'mode':
// mode is ignored and used only by the frontend
break
case 'wordCount': {
const rangeFilter = parseRangeFilter(keyword.keyword, keyword.value)
rangeFilter && result.rangeFilters.push(rangeFilter)
break
}
}
}
}