Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions src/clients/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
* Copyright: 2019, MeiliSearch
*/

'use strict';

import { Index } from '../indexes';
import {
KeyCreation,
Expand Down Expand Up @@ -34,6 +32,8 @@ import {
DeleteTasksQuery,
MultiSearchParams,
MultiSearchResponse,
SearchResponse,
FederatedMultiSearchParams,
} from '../types';
import { HttpRequests } from '../http-requests';
import { TaskClient, Task } from '../task';
Expand Down Expand Up @@ -216,10 +216,18 @@ class Client {
* @param config - Additional request configuration options
* @returns Promise containing the search responses
*/
async multiSearch<T extends Record<string, any> = Record<string, any>>(
queries?: MultiSearchParams,
multiSearch<T extends Record<string, unknown> = Record<string, any>>(
queries: MultiSearchParams,
config?: Partial<Request>,
): Promise<MultiSearchResponse<T>>;
multiSearch<T extends Record<string, unknown> = Record<string, any>>(
queries: FederatedMultiSearchParams,
config?: Partial<Request>,
): Promise<SearchResponse<T>>;
async multiSearch<T extends Record<string, unknown> = Record<string, any>>(
queries: MultiSearchParams | FederatedMultiSearchParams,
config?: Partial<Request>,
): Promise<MultiSearchResponse<T>> {
): Promise<MultiSearchResponse<T> | SearchResponse<T>> {
Comment on lines +219 to +230
Copy link
Member

@mdubus mdubus Aug 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems really overkill to me (and hardly maintainable). I think we can just keep it as it was, and find a way to type MultiSearchResponse differently in the types.ts file :) (see my other comment below)

Copy link
Collaborator Author

@flevi29 flevi29 Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look into http-request.ts we have a ton of overloads there too, I am planning on addressing these eventually.

I have tried typing queries with generics so that multiSearch would be smaller and simpler, but the problem with that is that we have an optional generic already T extends Record<string, unknown> = Record<string, any>, and when you have an optional generic all the following generics have to be optional as well, and optional generics don't have type inference, and if we make the queries generic the first one and required we cannot specify the type of hits without also explicitly specify the shape of the queries, thus typing it with generics doesn't really work as it is now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok thanks for the explanation 👍
Let's keep your suggestion then. That would be amazing if you could open an issue about addressing those overloads, though, to keep in mind that it's something we might want to change in the future 🙌

const url = `multi-search`;

return await this.httpRequest.post(url, queries, undefined, config);
Expand Down
31 changes: 24 additions & 7 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,21 @@ export type SearchRequestGET = Pagination &
locales?: Locale[];
};

export type FederationOptions = { weight: number };
export type MultiSearchFederation = { limit?: number; offset?: number };

export type MultiSearchQuery = SearchParams & { indexUid: string };
export type MultiSearchQueryWithFederation = MultiSearchQuery & {
federationOptions?: FederationOptions;
};

export type MultiSearchParams = {
queries: MultiSearchQuery[];
};
export type FederatedMultiSearchParams = {
federation: MultiSearchFederation;
queries: MultiSearchQueryWithFederation[];
};

export type CategoriesDistribution = {
[category: string]: number;
Expand All @@ -175,13 +185,6 @@ export type MatchesPosition<T> = Partial<
Record<keyof T, Array<{ start: number; length: number }>>
>;

export type Hit<T = Record<string, any>> = T & {
_formatted?: Partial<T>;
_matchesPosition?: MatchesPosition<T>;
_rankingScore?: number;
_rankingScoreDetails?: RankingScoreDetails;
};

export type RankingScoreDetails = {
words?: {
order: number;
Expand Down Expand Up @@ -213,6 +216,20 @@ export type RankingScoreDetails = {
[key: string]: Record<string, any> | undefined;
};

export type FederationDetails = {
indexUid: string;
queriesPosition: number;
weightedRankingScore: number;
};

export type Hit<T = Record<string, any>> = T & {
_formatted?: Partial<T>;
_matchesPosition?: MatchesPosition<T>;
_rankingScore?: number;
_rankingScoreDetails?: RankingScoreDetails;
_federation?: FederationDetails;
};

export type Hits<T = Record<string, any>> = Array<Hit<T>>;

export type FacetStat = { min: number; max: number };
Expand Down
42 changes: 42 additions & 0 deletions tests/search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,48 @@ describe.each([
expect(response.results[0].hits[0].title).toEqual('Le Petit Prince');
});

test(`${permission} key: Multi index search with federation`, async () => {
const client = await getClient(permission);

const response1 = await client.multiSearch<
Books | { id: number; asd: string }
>({
federation: {},
queries: [
{ indexUid: index.uid, q: '456', attributesToSearchOn: ['id'] },
{
indexUid: index.uid,
q: '1344',
federationOptions: { weight: 0.9 },
attributesToSearchOn: ['id'],
},
],
});

expect(response1).toHaveProperty('hits');
expect(Array.isArray(response1.hits)).toBe(true);
expect(response1.hits.length).toEqual(2);
expect(response1.hits[0].id).toEqual(456);

const response2 = await client.multiSearch({
federation: {},
queries: [
{
indexUid: index.uid,
q: '456',
federationOptions: { weight: 0.9 },
attributesToSearchOn: ['id'],
},
{ indexUid: index.uid, q: '1344', attributesToSearchOn: ['id'] },
],
});

expect(response2).toHaveProperty('hits');
expect(Array.isArray(response2.hits)).toBe(true);
expect(response2.hits.length).toEqual(2);
expect(response2.hits[0].id).toEqual(1344);
});

test(`${permission} key: Basic search`, async () => {
const client = await getClient(permission);
const response = await client.index(index.uid).search('prince', {});
Expand Down