Skip to content

Commit 4b3dc78

Browse files
authored
Merge branch 'main' into improve-errors
2 parents d3f490f + 01f51b4 commit 4b3dc78

File tree

11 files changed

+388
-10
lines changed

11 files changed

+388
-10
lines changed

.code-samples.meilisearch.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,3 +754,22 @@ negative_search_1: |-
754754
client.index('movies').search('-escape')
755755
negative_search_2: |-
756756
client.index('movies').search('-"escape"')
757+
search_parameter_reference_ranking_score_threshold_1: |-
758+
client.index('INDEX_NAME').search('badman', { rankingScoreThreshold: 0.2 })
759+
search_parameter_reference_retrieve_vectors_1: |-
760+
client.index('INDEX_NAME').search('kitchen utensils', {
761+
retrieveVectors: true,
762+
hybrid: { embedder: 'default'}
763+
})
764+
get_similar_post_1: |-
765+
client.index('INDEX_NAME').searchSimilarDocuments({ id: 'TARGET_DOCUMENT_ID'})
766+
search_parameter_guide_matching_strategy_3: |-
767+
client.index('movies').search('white shirt', {
768+
matchingStrategy: 'frequency'
769+
})
770+
search_parameter_reference_distinct_1: |-
771+
client.index('INDEX_NAME').search('QUERY TERMS', { distinct: 'ATTRIBUTE_A' })
772+
distinct_attribute_guide_filterable_1: |-
773+
client.index('products').updateFilterableAttributes(['product_id', 'sku', 'url'])
774+
distinct_attribute_guide_distinct_parameter_1: |-
775+
client.index('products').search('white shirt', { distinct: 'sku' })

.github/scripts/check-release.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ if [ "$current_tag" != "$package_json_version" ]; then
1010
exit 1
1111
fi
1212

13-
package_version_ts=$(grep "PACKAGE_VERSION =" src/package-version.ts | cut -d "=" -f 2- | tr -d " " | tr -d "'")
13+
package_version_ts=$(grep "PACKAGE_VERSION =" src/package-version.ts | cut -d "=" -f 2- | tr -d ';' | tr -d " " | tr -d "'")
1414
if [ "$current_tag" != "$package_version_ts" ]; then
1515
echo "Error: the current tag does not match the version in src/package-version.ts."
1616
echo "$current_tag vs $package_version_ts"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "meilisearch",
3-
"version": "0.40.0",
3+
"version": "0.41.0",
44
"description": "The Meilisearch JS client for Node.js and the browser.",
55
"keywords": [
66
"meilisearch",

playgrounds/javascript/yarn.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5311,16 +5311,16 @@ wrappy@1:
53115311
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
53125312

53135313
ws@^5.1.1:
5314-
version "5.2.3"
5315-
resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d"
5316-
integrity sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==
5314+
version "5.2.4"
5315+
resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.4.tgz#c7bea9f1cfb5f410de50e70e82662e562113f9a7"
5316+
integrity sha512-fFCejsuC8f9kOSu9FYaOw8CdO68O3h5v0lg4p74o8JqWpwTf9tniOD+nOB78aWoVSS6WptVUmDrp/KPsMVBWFQ==
53175317
dependencies:
53185318
async-limiter "~1.0.0"
53195319

53205320
ws@^6.1.2:
5321-
version "6.2.1"
5322-
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
5323-
integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
5321+
version "6.2.3"
5322+
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.3.tgz#ccc96e4add5fd6fedbc491903075c85c5a11d9ee"
5323+
integrity sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==
53245324
dependencies:
53255325
async-limiter "~1.0.0"
53265326

src/indexes.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import {
5353
ProximityPrecision,
5454
Embedders,
5555
SearchCutoffMs,
56+
SearchSimilarDocumentsParams,
5657
} from './types';
5758
import { removeUndefinedFromObject } from './utils';
5859
import { HttpRequests } from './http-requests';
@@ -177,6 +178,25 @@ class Index<T extends Record<string, any> = Record<string, any>> {
177178
);
178179
}
179180

181+
/**
182+
* Search for similar documents
183+
*
184+
* @param params - Parameters used to search for similar documents
185+
* @returns Promise containing the search response
186+
*/
187+
async searchSimilarDocuments<
188+
D extends Record<string, any> = T,
189+
S extends SearchParams = SearchParams,
190+
>(params: SearchSimilarDocumentsParams): Promise<SearchResponse<D, S>> {
191+
const url = `indexes/${this.uid}/similar`;
192+
193+
return await this.httpRequest.post(
194+
url,
195+
removeUndefinedFromObject(params),
196+
undefined,
197+
);
198+
}
199+
180200
///
181201
/// INDEX
182202
///

src/package-version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const PACKAGE_VERSION = '0.40.0';
1+
export const PACKAGE_VERSION = '0.41.0';

src/types/types.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export type IndexesResults<T> = ResourceResults<T> & {};
6363
export const MatchingStrategies = {
6464
ALL: 'all',
6565
LAST: 'last',
66+
FREQUENCY: 'frequency',
6667
} as const;
6768

6869
export type MatchingStrategies =
@@ -124,8 +125,11 @@ export type SearchParams = Query &
124125
vector?: number[] | null;
125126
showRankingScore?: boolean;
126127
showRankingScoreDetails?: boolean;
128+
rankingScoreThreshold?: number;
127129
attributesToSearchOn?: string[] | null;
128130
hybrid?: HybridSearch;
131+
distinct?: string;
132+
retrieveVectors?: boolean;
129133
};
130134

131135
// Search parameters for searches made with the GET method
@@ -145,6 +149,9 @@ export type SearchRequestGET = Pagination &
145149
attributesToSearchOn?: string | null;
146150
hybridEmbedder?: string;
147151
hybridSemanticRatio?: number;
152+
rankingScoreThreshold?: number;
153+
distinct?: string;
154+
retrieveVectors?: boolean;
148155
};
149156

150157
export type MultiSearchQuery = SearchParams & { indexUid: string };
@@ -262,6 +269,18 @@ export type FieldDistribution = {
262269
[field: string]: number;
263270
};
264271

272+
export type SearchSimilarDocumentsParams = {
273+
id: string | number;
274+
offset?: number;
275+
limit?: number;
276+
filter?: Filter;
277+
embedder?: string;
278+
attributesToRetrieve?: string[];
279+
showRankingScore?: boolean;
280+
showRankingScoreDetails?: boolean;
281+
rankingScoreThreshold?: number;
282+
};
283+
265284
/*
266285
** Documents
267286
*/
@@ -294,6 +313,7 @@ export type DocumentsQuery<T = Record<string, any>> = ResourceQuery & {
294313
filter?: Filter;
295314
limit?: number;
296315
offset?: number;
316+
retrieveVectors?: boolean;
297317
};
298318

299319
export type DocumentQuery<T = Record<string, any>> = {
@@ -1013,6 +1033,14 @@ export const ErrorStatusCode = {
10131033

10141034
/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_facet_search_facet_query */
10151035
INVALID_FACET_SEARCH_FACET_QUERY: 'invalid_facet_search_facet_query',
1036+
1037+
/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_ranking_score_threshold */
1038+
INVALID_SEARCH_RANKING_SCORE_THRESHOLD:
1039+
'invalid_search_ranking_score_threshold',
1040+
1041+
/** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_similar_ranking_score_threshold */
1042+
INVALID_SIMILAR_RANKING_SCORE_THRESHOLD:
1043+
'invalid_similar_ranking_score_threshold',
10161044
};
10171045

10181046
export type ErrorStatusCode =

tests/documents.test.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,92 @@ Hint: It might not be working because maybe you're not up to date with the Meili
218218
expect(documents.results.length).toEqual(dataset.length);
219219
});
220220

221+
test(`${permission} key: Get documents with retrieveVectors to true`, async () => {
222+
const client = await getClient(permission);
223+
const adminKey = await getKey('Admin');
224+
225+
await fetch(`${HOST}/experimental-features`, {
226+
body: JSON.stringify({ vectorStore: true }),
227+
headers: {
228+
Authorization: `Bearer ${adminKey}`,
229+
'Content-Type': 'application/json',
230+
},
231+
method: 'PATCH',
232+
});
233+
234+
const { taskUid } = await client
235+
.index(indexPk.uid)
236+
.addDocuments(dataset);
237+
await client.index(indexPk.uid).waitForTask(taskUid);
238+
239+
// Get documents with POST
240+
const documentsPost = await client
241+
.index(indexPk.uid)
242+
.getDocuments<Book>({ retrieveVectors: true });
243+
244+
expect(documentsPost.results.length).toEqual(dataset.length);
245+
expect(documentsPost.results[0]).toHaveProperty('_vectors');
246+
247+
// Get documents with GET
248+
const res = await fetch(
249+
`${HOST}/indexes/${indexPk.uid}/documents?retrieveVectors=true`,
250+
{
251+
headers: {
252+
Authorization: `Bearer ${adminKey}`,
253+
'Content-Type': 'application/json',
254+
},
255+
method: 'GET',
256+
},
257+
);
258+
const documentsGet = await res.json();
259+
260+
expect(documentsGet.results.length).toEqual(dataset.length);
261+
expect(documentsGet.results[0]).toHaveProperty('_vectors');
262+
});
263+
264+
test(`${permission} key: Get documents without retrieveVectors`, async () => {
265+
const client = await getClient(permission);
266+
const adminKey = await getKey('Admin');
267+
268+
await fetch(`${HOST}/experimental-features`, {
269+
body: JSON.stringify({ vectorStore: true }),
270+
headers: {
271+
Authorization: `Bearer ${adminKey}`,
272+
'Content-Type': 'application/json',
273+
},
274+
method: 'PATCH',
275+
});
276+
277+
const { taskUid } = await client
278+
.index(indexPk.uid)
279+
.addDocuments(dataset);
280+
await client.index(indexPk.uid).waitForTask(taskUid);
281+
282+
// Get documents with POST
283+
const documentsPost = await client
284+
.index(indexPk.uid)
285+
.getDocuments<Book>();
286+
287+
expect(documentsPost.results.length).toEqual(dataset.length);
288+
expect(documentsPost.results[0]).not.toHaveProperty('_vectors');
289+
290+
// Get documents with GET
291+
const res = await fetch(
292+
`${HOST}/indexes/${indexPk.uid}/documents?retrieveVectors=false`,
293+
{
294+
headers: {
295+
Authorization: `Bearer ${adminKey}`,
296+
'Content-Type': 'application/json',
297+
},
298+
method: 'GET',
299+
},
300+
);
301+
const documentsGet = await res.json();
302+
303+
expect(documentsGet.results.length).toEqual(dataset.length);
304+
expect(documentsGet.results[0]).not.toHaveProperty('_vectors');
305+
});
306+
221307
test(`${permission} key: Replace documents from index that has NO primary key`, async () => {
222308
const client = await getClient(permission);
223309
const { taskUid: addDocTask } = await client

tests/embedders.test.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,39 @@ const index = {
1414
uid: 'movies_test',
1515
};
1616

17+
const datasetSimilarSearch = [
18+
{
19+
title: 'Shazam!',
20+
release_year: 2019,
21+
id: '287947',
22+
_vectors: { manual: [0.8, 0.4, -0.5] },
23+
},
24+
{
25+
title: 'Captain Marvel',
26+
release_year: 2019,
27+
id: '299537',
28+
_vectors: { manual: [0.6, 0.8, -0.2] },
29+
},
30+
{
31+
title: 'Escape Room',
32+
release_year: 2019,
33+
id: '522681',
34+
_vectors: { manual: [0.1, 0.6, 0.8] },
35+
},
36+
{
37+
title: 'How to Train Your Dragon: The Hidden World',
38+
release_year: 2019,
39+
id: '166428',
40+
_vectors: { manual: [0.7, 0.7, -0.4] },
41+
},
42+
{
43+
title: 'All Quiet on the Western Front',
44+
release_year: 1930,
45+
id: '143',
46+
_vectors: { manual: [-0.5, 0.3, 0.85] },
47+
},
48+
];
49+
1750
jest.setTimeout(100 * 1000);
1851

1952
afterAll(() => {
@@ -223,6 +256,38 @@ describe.each([{ permission: 'Master' }, { permission: 'Admin' }])(
223256

224257
expect(response).toEqual(null);
225258
});
259+
260+
test(`${permission} key: search for similar documents`, async () => {
261+
const client = await getClient(permission);
262+
263+
const newEmbedder: Embedders = {
264+
manual: {
265+
source: 'userProvided',
266+
dimensions: 3,
267+
},
268+
};
269+
const { taskUid: updateEmbeddersTask }: EnqueuedTask = await client
270+
.index(index.uid)
271+
.updateEmbedders(newEmbedder);
272+
273+
await client.waitForTask(updateEmbeddersTask);
274+
275+
const { taskUid: documentAdditionTask } = await client
276+
.index(index.uid)
277+
.addDocuments(datasetSimilarSearch);
278+
279+
await client.waitForTask(documentAdditionTask);
280+
281+
const response = await client.index(index.uid).searchSimilarDocuments({
282+
id: '143',
283+
});
284+
285+
expect(response).toHaveProperty('hits');
286+
expect(response.hits.length).toEqual(4);
287+
expect(response).toHaveProperty('offset', 0);
288+
expect(response).toHaveProperty('limit', 20);
289+
expect(response).toHaveProperty('estimatedTotalHits', 4);
290+
});
226291
},
227292
);
228293

0 commit comments

Comments
 (0)