diff --git a/src/indexes.ts b/src/indexes.ts index c797f2ebe..5f9208ddd 100644 --- a/src/indexes.ts +++ b/src/indexes.ts @@ -139,6 +139,7 @@ class Index = Record> { attributesToRetrieve: options?.attributesToRetrieve?.join(','), attributesToCrop: options?.attributesToCrop?.join(','), attributesToHighlight: options?.attributesToHighlight?.join(','), + vector: options?.vector?.join(','), } return await this.httpRequest.get>( diff --git a/src/types/types.ts b/src/types/types.ts index c1fe80dbc..b4904ed23 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -78,13 +78,7 @@ export type Crop = { cropMarker?: string } -export const SortFacetValuesBy = { - COUNT: 'error', - ALPHA: 'alpha', -} - -export type SortFacetValuesBy = typeof SortFacetValuesBy[keyof typeof SortFacetValuesBy] - +// `facetName` becomes mandatory when using `searchForFacetValues` export type SearchForFacetValuesParams = Omit & { facetName: string } @@ -112,9 +106,9 @@ export type SearchParams = Query & matchingStrategy?: MatchingStrategies hitsPerPage?: number page?: number - sortFacetValuesBy?: SortFacetValuesBy facetName?: string facetQuery?: string + vector?: number[] | null } // Search parameters for searches made with the GET method @@ -130,6 +124,7 @@ export type SearchRequestGET = Pagination & attributesToHighlight?: string attributesToCrop?: string showMatchesPosition?: boolean + vector?: string | null } export type MultiSearchQuery = SearchParams & { indexUid: string } @@ -167,6 +162,7 @@ export type SearchResponse< facetDistribution?: FacetDistribution query: string facetStats?: FacetStats + vector: number[] } & (undefined extends S ? Partial : true extends IsFinitePagination> @@ -585,12 +581,15 @@ export const enum ErrorStatusCode { /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_offset */ INVALID_DOCUMENT_OFFSET = 'invalid_document_offset', - /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_offset */ + /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_filter */ INVALID_DOCUMENT_FILTER = 'invalid_document_filter', - /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_offset */ + /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_document_filter */ MISSING_DOCUMENT_FILTER = 'missing_document_filter', + /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_vectors_field */ + INVALID_DOCUMENT_VECTORS_FIELD = 'invalid_document_vectors_field', + /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#payload_too_large */ PAYLOAD_TOO_LARGE = 'payload_too_large', @@ -666,6 +665,9 @@ export const enum ErrorStatusCode { /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_matching_strategy */ INVALID_SEARCH_MATCHING_STRATEGY = 'invalid_search_matching_strategy', + /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_vector */ + INVALID_SEARCH_VECTOR = 'invalid_search_vector', + /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#bad_request */ BAD_REQUEST = 'bad_request', diff --git a/tests/__snapshots__/facet_search.test.ts.snap b/tests/__snapshots__/facet_search.test.ts.snap new file mode 100644 index 000000000..17c3d1bf0 --- /dev/null +++ b/tests/__snapshots__/facet_search.test.ts.snap @@ -0,0 +1,205 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Test on POST search Admin key: basic facet value search 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "action", + }, + Object { + "count": 2, + "value": "adventure", + }, + ], + "facetQuery": "a", + "processingTimeMs": 0, +} +`; + +exports[`Test on POST search Admin key: facet value search with filter 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "action", + }, + ], + "facetQuery": "a", + "processingTimeMs": 0, +} +`; + +exports[`Test on POST search Admin key: facet value search with no facet query 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "action", + }, + Object { + "count": 2, + "value": "adventure", + }, + Object { + "count": 1, + "value": "comedy", + }, + Object { + "count": 2, + "value": "romance", + }, + ], + "facetQuery": null, + "processingTimeMs": 0, +} +`; + +exports[`Test on POST search Admin key: facet value search with search query 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "adventure", + }, + ], + "facetQuery": "a", + "processingTimeMs": 0, +} +`; + +exports[`Test on POST search Master key: basic facet value search 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "action", + }, + Object { + "count": 2, + "value": "adventure", + }, + ], + "facetQuery": "a", + "processingTimeMs": 0, +} +`; + +exports[`Test on POST search Master key: facet value search with filter 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "action", + }, + ], + "facetQuery": "a", + "processingTimeMs": 0, +} +`; + +exports[`Test on POST search Master key: facet value search with no facet query 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "action", + }, + Object { + "count": 2, + "value": "adventure", + }, + Object { + "count": 1, + "value": "comedy", + }, + Object { + "count": 2, + "value": "romance", + }, + ], + "facetQuery": null, + "processingTimeMs": 0, +} +`; + +exports[`Test on POST search Master key: facet value search with search query 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "adventure", + }, + ], + "facetQuery": "a", + "processingTimeMs": 0, +} +`; + +exports[`Test on POST search Search key: basic facet value search 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "action", + }, + Object { + "count": 2, + "value": "adventure", + }, + ], + "facetQuery": "a", + "processingTimeMs": 0, +} +`; + +exports[`Test on POST search Search key: facet value search with filter 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "action", + }, + ], + "facetQuery": "a", + "processingTimeMs": 0, +} +`; + +exports[`Test on POST search Search key: facet value search with no facet query 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "action", + }, + Object { + "count": 2, + "value": "adventure", + }, + Object { + "count": 1, + "value": "comedy", + }, + Object { + "count": 2, + "value": "romance", + }, + ], + "facetQuery": null, + "processingTimeMs": 0, +} +`; + +exports[`Test on POST search Search key: facet value search with search query 1`] = ` +Object { + "facetHits": Array [ + Object { + "count": 1, + "value": "adventure", + }, + ], + "facetQuery": "a", + "processingTimeMs": 0, +} +`; diff --git a/tests/facet_search.test.ts b/tests/facet_search.test.ts index 394e692c3..e20866b1f 100644 --- a/tests/facet_search.test.ts +++ b/tests/facet_search.test.ts @@ -57,8 +57,7 @@ describe.each([ } const response = await client.index(index.uid).searchForFacetValues(params) - expect(response.facetHits.length).toEqual(2) - expect(response.facetQuery).toEqual('a') + expect(response).toMatchSnapshot() }) test(`${permission} key: facet value search with no facet query`, async () => { @@ -69,8 +68,7 @@ describe.each([ } const response = await client.index(index.uid).searchForFacetValues(params) - expect(response.facetHits.length).toEqual(4) - expect(response.facetQuery).toEqual(null) + expect(response).toMatchSnapshot() }) test(`${permission} key: facet value search with filter`, async () => { @@ -84,7 +82,7 @@ describe.each([ const response = await client.index(index.uid).searchForFacetValues(params) - expect(response.facetHits.length).toEqual(1) + expect(response).toMatchSnapshot() }) test(`${permission} key: facet value search with search query`, async () => { @@ -97,7 +95,7 @@ describe.each([ } const response = await client.index(index.uid).searchForFacetValues(params) - expect(response.facetHits.length).toEqual(1) + expect(response).toMatchSnapshot() }) }) diff --git a/tests/get_search.test.ts b/tests/get_search.test.ts index 33c19a79e..55c3c8d7e 100644 --- a/tests/get_search.test.ts +++ b/tests/get_search.test.ts @@ -6,6 +6,8 @@ import { BAD_HOST, MeiliSearch, getClient, + HOST, + getKey, } from './utils/meilisearch-test-utils' const index = { @@ -423,6 +425,25 @@ describe.each([ 'The filter query parameter should be in string format when using searchGet' ) }) + test(`${permission} key: search with vectors`, async () => { + const client = await getClient(permission) + const key = await getKey(permission) + + await fetch(`${HOST}/experimental-features`, { + body: JSON.stringify({ vectorStore: true }), + headers: { + Authorization: `Bearer ${key}`, + 'Content-Type': 'application/json', + }, + method: 'PATCH', + }) + + const response = await client + .index(emptyIndex.uid) + .searchGet('', { vector: [1] }) + + expect(response.vector).toEqual([1]) + }) test(`${permission} key: Try to search on deleted index and fail`, async () => { const client = await getClient(permission) diff --git a/tests/search.test.ts b/tests/search.test.ts index 7edd89360..164f5baf3 100644 --- a/tests/search.test.ts +++ b/tests/search.test.ts @@ -8,8 +8,14 @@ import { MeiliSearch, getClient, datasetWithNests, + HOST, + getKey, } from './utils/meilisearch-test-utils' +if (typeof fetch === 'undefined') { + require('cross-fetch/polyfill') +} + const index = { uid: 'movies_test', } @@ -767,6 +773,26 @@ describe.each([ expect(response.hits.length).toEqual(0) }) + test(`${permission} key: search with vectors`, async () => { + const client = await getClient(permission) + const key = await getKey(permission) + + await fetch(`${HOST}/experimental-features`, { + body: JSON.stringify({ vectorStore: true }), + headers: { + Authorization: `Bearer ${key}`, + 'Content-Type': 'application/json', + }, + method: 'PATCH', + }) + + const response = await client + .index(emptyIndex.uid) + .search('', { vector: [1] }) + + expect(response.vector).toEqual([1]) + }) + test(`${permission} key: Try to search on deleted index and fail`, async () => { const client = await getClient(permission) const masterClient = await getClient('Master')