Skip to content
This repository was archived by the owner on Dec 30, 2022. It is now read-only.

Commit b96809e

Browse files
authored
fix(ssr): perform initial multi-index search using a single request (#3385)
1 parent d718f7e commit b96809e

File tree

2 files changed

+53
-27
lines changed

2 files changed

+53
-27
lines changed

packages/react-instantsearch-dom/src/core/__tests__/createInstantSearchServer.js

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,38 @@ describe('findResultsState', () => {
11361136
});
11371137
});
11381138

1139+
it('searches once with multiple indices', async () => {
1140+
const Connected = createWidget();
1141+
const App = (props) => (
1142+
<InstantSearch {...props}>
1143+
<Connected />
1144+
<Index indexId="index1WithRefinement" indexName="index1">
1145+
<Connected />
1146+
</Index>
1147+
<Index indexId="index2WithRefinement" indexName="index2">
1148+
<Connected />
1149+
</Index>
1150+
</InstantSearch>
1151+
);
1152+
1153+
const props = {
1154+
searchClient: createSearchClient(),
1155+
indexName: 'abc',
1156+
searchState: {
1157+
query: 'iPhone',
1158+
},
1159+
};
1160+
1161+
const { results } = await findResultsState(App, props);
1162+
1163+
expect(props.searchClient.search).toHaveBeenCalledTimes(1);
1164+
expect(results.map(({ _internalIndexId }) => _internalIndexId)).toEqual([
1165+
'abc',
1166+
'index1WithRefinement',
1167+
'index2WithRefinement',
1168+
]);
1169+
});
1170+
11391171
it('searches twice (cached) with dynamic widgets', async () => {
11401172
const RefinementList = connectRefinementList(() => null);
11411173
const App = (props) => (
@@ -1156,15 +1188,12 @@ describe('findResultsState', () => {
11561188

11571189
await findResultsState(App, props);
11581190

1159-
// [search (index 1), search (index 2), dynamic (index 1), dynamic (index 2)]
1160-
expect(props.searchClient.search).toHaveBeenCalledTimes(4);
1191+
// [search, dynamic]
1192+
expect(props.searchClient.search).toHaveBeenCalledTimes(2);
11611193

11621194
// both calls are the same, so they're cached
11631195
expect(props.searchClient.search.mock.calls[0][0]).toEqual(
1164-
props.searchClient.search.mock.calls[2][0]
1165-
);
1166-
expect(props.searchClient.search.mock.calls[1][0]).toEqual(
1167-
props.searchClient.search.mock.calls[3][0]
1196+
props.searchClient.search.mock.calls[1][0]
11681197
);
11691198
});
11701199

@@ -1193,11 +1222,11 @@ describe('findResultsState', () => {
11931222

11941223
await findResultsState(App, props);
11951224

1196-
// [search (index 1), search (index 2), dynamic (index 1), dynamic (index 2)]
1197-
expect(props.searchClient.search).toHaveBeenCalledTimes(4);
1225+
// [search, dynamic]
1226+
expect(props.searchClient.search).toHaveBeenCalledTimes(2);
11981227

11991228
// first query doesn't have the fallback widget mounted yet
1200-
expect(props.searchClient.search.mock.calls[1][0][0]).toEqual({
1229+
expect(props.searchClient.search.mock.calls[0][0][1]).toEqual({
12011230
indexName: 'index1WithRefinement',
12021231
params: {
12031232
facets: ['*'],
@@ -1209,7 +1238,7 @@ describe('findResultsState', () => {
12091238
});
12101239

12111240
// second query does have the fallback widget mounted, and thus also the refinement
1212-
expect(props.searchClient.search.mock.calls[3][0][0]).toEqual({
1241+
expect(props.searchClient.search.mock.calls[1][0][1]).toEqual({
12131242
indexName: 'index1WithRefinement',
12141243
params: {
12151244
facetFilters: [['categories:refined!']],

packages/react-instantsearch-dom/src/core/createInstantSearchServer.js

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import { renderToString } from 'react-dom/server';
3-
import algoliasearchHelper from 'algoliasearch-helper';
3+
import algoliasearchHelper, { SearchParameters } from 'algoliasearch-helper';
44
import { version, HIGHLIGHT_TAGS } from 'react-instantsearch-core';
55

66
const hasMultipleIndices = (context) => context && context.multiIndexContext;
@@ -98,33 +98,31 @@ const singleIndexSearch = (helper, parameters) =>
9898
const multiIndexSearch = (
9999
indexName,
100100
client,
101-
helper,
102101
sharedParameters,
103102
{ [indexName]: mainParameters, ...derivedParameters }
104103
) => {
104+
const helper = algoliasearchHelper(client, indexName);
105105
const indexIds = Object.keys(derivedParameters);
106106

107107
const searches = [
108-
helper.searchOnce({
109-
...sharedParameters,
110-
...mainParameters,
111-
}),
112-
...indexIds.map((indexId) => {
113-
const parameters = derivedParameters[indexId];
114-
115-
return algoliasearchHelper(client, parameters.index).searchOnce(
116-
parameters
117-
);
118-
}),
119-
];
108+
new SearchParameters({ ...sharedParameters, ...mainParameters }),
109+
...indexIds.map((indexId) => derivedParameters[indexId]),
110+
].map(
111+
(params) =>
112+
new Promise((resolve) =>
113+
helper.derive(() => params).once('result', resolve)
114+
)
115+
);
116+
117+
helper.searchOnlyWithDerivedHelpers();
120118

121119
// We attach `indexId` on the results to be able to reconstruct the object
122120
// on the client side. We cannot rely on `state.index` anymore because we
123121
// may have multiple times the same index.
124122
return Promise.all(searches).then((results) =>
125123
[indexName, ...indexIds].map((indexId, i) => ({
126-
rawResults: cleanRawResults(results[i].content._rawResults),
127-
state: results[i].content._state,
124+
rawResults: cleanRawResults(results[i].results._rawResults),
125+
state: results[i].results._state,
128126
_internalIndexId: indexId,
129127
}))
130128
);
@@ -193,7 +191,6 @@ export const findResultsState = function (App, props) {
193191
return multiIndexSearch(
194192
props.indexName,
195193
props.searchClient,
196-
helper,
197194
sharedParameters,
198195
derivedParameters
199196
).then((results) => {

0 commit comments

Comments
 (0)