mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Allow scoping searches to site:
This commit is contained in:
parent
30fb1af9fa
commit
e5f1f25a4d
4 changed files with 82 additions and 46 deletions
|
|
@ -1,13 +1,6 @@
|
|||
import {
|
||||
ArticleSavingRequestStatus,
|
||||
Page,
|
||||
PageContext,
|
||||
PageSearchArgs,
|
||||
PageType,
|
||||
ParamSet,
|
||||
SearchBody,
|
||||
SearchResponse,
|
||||
} from './types'
|
||||
import { ResponseError } from '@elastic/elasticsearch/lib/errors'
|
||||
import { EntityType } from '../datalayer/pubsub'
|
||||
import { BulkActionType } from '../generated/graphql'
|
||||
import {
|
||||
DateFilter,
|
||||
FieldFilter,
|
||||
|
|
@ -21,9 +14,16 @@ import {
|
|||
SortOrder,
|
||||
} from '../utils/search'
|
||||
import { client, INDEX_ALIAS } from './index'
|
||||
import { EntityType } from '../datalayer/pubsub'
|
||||
import { ResponseError } from '@elastic/elasticsearch/lib/errors'
|
||||
import { BulkActionType } from '../generated/graphql'
|
||||
import {
|
||||
ArticleSavingRequestStatus,
|
||||
Page,
|
||||
PageContext,
|
||||
PageSearchArgs,
|
||||
PageType,
|
||||
ParamSet,
|
||||
SearchBody,
|
||||
SearchResponse,
|
||||
} from './types'
|
||||
|
||||
const appendQuery = (body: SearchBody, query: string): void => {
|
||||
body.query.bool.should.push({
|
||||
|
|
@ -224,6 +224,17 @@ const appendNoFilters = (body: SearchBody, noFilters: NoFilter[]): void => {
|
|||
})
|
||||
}
|
||||
|
||||
const appendSiteNameFilter = (body: SearchBody, siteName: string): void => {
|
||||
body.query.bool.should.push({
|
||||
multi_match: {
|
||||
query: siteName,
|
||||
fields: ['siteName', 'url'],
|
||||
analyzer: 'simple',
|
||||
},
|
||||
})
|
||||
body.query.bool.minimum_should_match = 1
|
||||
}
|
||||
|
||||
export const createPage = async (
|
||||
page: Page,
|
||||
ctx: PageContext
|
||||
|
|
@ -404,6 +415,7 @@ export const searchPages = async (
|
|||
ids,
|
||||
includeContent,
|
||||
noFilters,
|
||||
siteName,
|
||||
} = args
|
||||
// default order is descending
|
||||
const sortOrder = sort?.order || SortOrder.DESCENDING
|
||||
|
|
@ -499,11 +511,10 @@ export const searchPages = async (
|
|||
})
|
||||
}
|
||||
|
||||
if (noFilters) {
|
||||
appendNoFilters(body, noFilters)
|
||||
}
|
||||
noFilters && appendNoFilters(body, noFilters)
|
||||
siteName && appendSiteNameFilter(body, siteName)
|
||||
|
||||
console.log('searching pages in elastic', JSON.stringify(body))
|
||||
console.debug('searching pages in elastic', JSON.stringify(body))
|
||||
|
||||
const response = await client.search<SearchResponse<Page>, SearchBody>({
|
||||
index: INDEX_ALIAS,
|
||||
|
|
@ -523,6 +534,10 @@ export const searchPages = async (
|
|||
response.body.hits.total.value,
|
||||
]
|
||||
} catch (e) {
|
||||
if (e instanceof ResponseError) {
|
||||
console.error('failed to search pages in elastic', e.meta.body.error)
|
||||
return undefined
|
||||
}
|
||||
console.error('failed to search pages in elastic', e)
|
||||
return undefined
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Define the type of the body for the Search request
|
||||
import { PickTuple } from '../util'
|
||||
import { PubsubClient } from '../datalayer/pubsub'
|
||||
import { PickTuple } from '../util'
|
||||
import {
|
||||
DateFilter,
|
||||
FieldFilter,
|
||||
|
|
@ -80,13 +80,15 @@ export interface SearchBody {
|
|||
multi_match: {
|
||||
query: string
|
||||
fields: string[]
|
||||
operator: 'and' | 'or'
|
||||
type:
|
||||
operator?: 'and' | 'or'
|
||||
type?:
|
||||
| 'best_fields'
|
||||
| 'most_fields'
|
||||
| 'cross_fields'
|
||||
| 'phrase'
|
||||
| 'phrase_prefix'
|
||||
| 'bool_prefix'
|
||||
analyzer?: string
|
||||
}
|
||||
}[]
|
||||
minimum_should_match?: number
|
||||
|
|
@ -331,4 +333,5 @@ export interface PageSearchArgs {
|
|||
recommendedBy?: string
|
||||
includeContent?: boolean
|
||||
noFilters?: NoFilter[]
|
||||
siteName?: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export interface SearchFilter {
|
|||
ids: string[]
|
||||
recommendedBy?: string
|
||||
noFilters: NoFilter[]
|
||||
siteName?: string
|
||||
}
|
||||
|
||||
export enum LabelFilterType {
|
||||
|
|
@ -335,6 +336,7 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => {
|
|||
'recommendedBy',
|
||||
'no',
|
||||
'mode',
|
||||
'site',
|
||||
],
|
||||
tokenize: true,
|
||||
})
|
||||
|
|
@ -428,6 +430,9 @@ export const parseSearchQuery = (query: string | undefined): SearchFilter => {
|
|||
case 'mode':
|
||||
// mode is ignored and used only by the frontend
|
||||
break
|
||||
case 'site':
|
||||
result.siteName = keyword.value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,10 @@
|
|||
import { createTestUser, deleteTestUser } from '../db'
|
||||
import {
|
||||
createTestElasticPage,
|
||||
generateFakeUuid,
|
||||
graphqlRequest,
|
||||
request,
|
||||
} from '../util'
|
||||
import * as chai from 'chai'
|
||||
import { expect } from 'chai'
|
||||
import 'mocha'
|
||||
import { User } from '../../src/entity/user'
|
||||
import chaiString from 'chai-string'
|
||||
import {
|
||||
BulkActionType,
|
||||
SyncUpdatedItemEdge,
|
||||
UpdateReason,
|
||||
UploadFileStatus,
|
||||
} from '../../src/generated/graphql'
|
||||
import {
|
||||
ArticleSavingRequestStatus,
|
||||
Highlight,
|
||||
Page,
|
||||
PageContext,
|
||||
PageType,
|
||||
} from '../../src/elastic/types'
|
||||
import { UploadFile } from '../../src/entity/upload_file'
|
||||
import 'mocha'
|
||||
import { createPubSubClient } from '../../src/datalayer/pubsub'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { refreshIndex } from '../../src/elastic'
|
||||
import { addHighlightToPage } from '../../src/elastic/highlights'
|
||||
import {
|
||||
createPage,
|
||||
deletePage,
|
||||
|
|
@ -33,9 +12,30 @@ import {
|
|||
getPageById,
|
||||
updatePage,
|
||||
} from '../../src/elastic/pages'
|
||||
import { addHighlightToPage } from '../../src/elastic/highlights'
|
||||
import { refreshIndex } from '../../src/elastic'
|
||||
import {
|
||||
ArticleSavingRequestStatus,
|
||||
Highlight,
|
||||
Page,
|
||||
PageContext,
|
||||
PageType,
|
||||
} from '../../src/elastic/types'
|
||||
import { SearchHistory } from '../../src/entity/search_history'
|
||||
import { UploadFile } from '../../src/entity/upload_file'
|
||||
import { User } from '../../src/entity/user'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import {
|
||||
BulkActionType,
|
||||
SyncUpdatedItemEdge,
|
||||
UpdateReason,
|
||||
UploadFileStatus,
|
||||
} from '../../src/generated/graphql'
|
||||
import { createTestUser, deleteTestUser } from '../db'
|
||||
import {
|
||||
createTestElasticPage,
|
||||
generateFakeUuid,
|
||||
graphqlRequest,
|
||||
request,
|
||||
} from '../util'
|
||||
|
||||
chai.use(chaiString)
|
||||
|
||||
|
|
@ -847,6 +847,7 @@ describe('Article API', () => {
|
|||
url: url,
|
||||
savedAt: new Date(),
|
||||
state: ArticleSavingRequestStatus.Succeeded,
|
||||
siteName: 'Example',
|
||||
}
|
||||
page.id = (await createPage(page, ctx))!
|
||||
pages.push(page)
|
||||
|
|
@ -984,6 +985,18 @@ describe('Article API', () => {
|
|||
expect(res.body.data.search.pageInfo.totalCount).to.eq(0)
|
||||
})
|
||||
})
|
||||
|
||||
context('when site:${site_name} is in the query', () => {
|
||||
before(async () => {
|
||||
keyword = "'search api' site:example"
|
||||
})
|
||||
|
||||
it('returns items from the site', async () => {
|
||||
const res = await graphqlRequest(query, authToken).expect(200)
|
||||
|
||||
expect(res.body.data.search.pageInfo.totalCount).to.eq(5)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('TypeaheadSearch API', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue