Skip to content

Commit

Permalink
feat(findAnswers): implement the new method (experimental) (#1219)
Browse files Browse the repository at this point in the history
* feat(searchForAnswers): implement the new method

- adds searchForAnswers to node / browser (not lite for now)
- fix small typo in SearchOptions
- add an integration test

* make test pass

* chore: Rename to findAnswers

* fix(test): Remove superfluous newline in url

* chore: Order keys after renaming [ci skip]

* add newline

* restructure tests

* update bundlesize

* chore: don't omit any search parameters for now

Co-authored-by: Paul-Louis NECH <[email protected]>
Co-authored-by: Eunjae Lee <[email protected]>
  • Loading branch information
3 people authored Nov 16, 2020
1 parent d5cb2ad commit 8d962ea
Show file tree
Hide file tree
Showing 12 changed files with 186 additions and 15 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
"bundlesize": [
{
"path": "packages/algoliasearch/dist/algoliasearch.umd.js",
"maxSize": "7.57KB"
"maxSize": "7.65KB"
},
{
"path": "packages/algoliasearch/dist/algoliasearch-lite.umd.js",
Expand Down
9 changes: 9 additions & 0 deletions packages/algoliasearch/src/builds/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ import {
deleteSynonym,
DeleteSynonymOptions,
exists,
findAnswers,
FindAnswersOptions,
FindAnswersResponse,
findObject,
FindObjectOptions,
FindObjectResponse,
Expand Down Expand Up @@ -237,6 +240,7 @@ export default function algoliasearch(
methods: {
batch,
delete: deleteIndex,
findAnswers,
getObject,
getObjects,
saveObject,
Expand Down Expand Up @@ -345,6 +349,11 @@ export type SearchIndex = BaseSearchIndex & {
query: string,
requestOptions?: RequestOptions & SearchOptions
) => Readonly<Promise<SearchResponse<TObject>>>;
readonly findAnswers: (
query: string,
queryLanguages: readonly string[],
requestOptions?: RequestOptions & FindAnswersOptions
) => Readonly<Promise<FindAnswersResponse>>;
readonly searchForFacetValues: (
facetName: string,
facetQuery: string,
Expand Down
9 changes: 9 additions & 0 deletions packages/algoliasearch/src/builds/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ import {
deleteSynonym,
DeleteSynonymOptions,
exists,
findAnswers,
FindAnswersOptions,
FindAnswersResponse,
findObject,
FindObjectOptions,
FindObjectResponse,
Expand Down Expand Up @@ -240,6 +243,7 @@ export default function algoliasearch(
methods: {
batch,
delete: deleteIndex,
findAnswers,
getObject,
getObjects,
saveObject,
Expand Down Expand Up @@ -353,6 +357,11 @@ export type SearchIndex = BaseSearchIndex & {
facetQuery: string,
requestOptions?: RequestOptions & SearchOptions
) => Readonly<Promise<SearchForFacetValuesResponse>>;
readonly findAnswers: (
query: string,
queryLanguages: readonly string[],
requestOptions?: RequestOptions & FindAnswersOptions
) => Readonly<Promise<FindAnswersResponse>>;
readonly batch: (
requests: readonly BatchRequest[],
requestOptions?: RequestOptions
Expand Down
26 changes: 26 additions & 0 deletions packages/client-search/src/methods/index/findAnswers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { encode } from '@algolia/client-common';
import { MethodEnum } from '@algolia/requester-common';
import { RequestOptions } from '@algolia/transporter';

import { FindAnswersOptions, FindAnswersResponse, SearchIndex } from '../..';

export const findAnswers = (base: SearchIndex) => {
return <TObject>(
query: string,
queryLanguages: readonly string[],
requestOptions?: RequestOptions & FindAnswersOptions
): Readonly<Promise<FindAnswersResponse<TObject>>> => {
return base.transporter.read(
{
method: MethodEnum.Post,
path: encode('1/answers/%s/prediction', base.indexName),
data: {
query,
queryLanguages,
},
cacheable: true,
},
requestOptions
);
};
};
1 change: 1 addition & 0 deletions packages/client-search/src/methods/index/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from './deleteObjects';
export * from './deleteRule';
export * from './deleteSynonym';
export * from './exists';
export * from './findAnswers';
export * from './findObject';
export * from './getObject';
export * from './getObjectPosition';
Expand Down
8 changes: 8 additions & 0 deletions packages/client-search/src/types/FindAnswersOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { SearchOptions } from './SearchOptions';

export type FindAnswersOptions = {
readonly attributesForPrediction?: readonly string[];
readonly nbHits?: number;
readonly threshold?: number;
readonly params?: SearchOptions;
};
21 changes: 21 additions & 0 deletions packages/client-search/src/types/FindAnswersResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Hit } from './Hit';
import { SearchResponse } from './SearchResponse';

export type FindAnswersResponse<TObject = {}> = SearchResponse<TObject> & {
/**
* The hits returned by the search.
*
* Hits are ordered according to the ranking or sorting of the index being queried.
*/
hits: Array<
Hit<
TObject & {
_answer?: {
extract: string;
score: number;
extractAttribute: string;
};
}
>
>;
};
2 changes: 1 addition & 1 deletion packages/client-search/src/types/SearchOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ export type SearchOptions = {
/**
* List of supported languages with their associated language ISO code.
*
* Apply a set of natural language best practices such asignorePlurals,
* Apply a set of natural language best practices such as ignorePlurals,
* removeStopWords, removeWordsIfNoResults, analyticsTags and ruleContexts.
*/
readonly naturalLanguages?: readonly string[];
Expand Down
2 changes: 2 additions & 0 deletions packages/client-search/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export * from './DeleteByFiltersOptions';
export * from './DeleteResponse';
export * from './DeleteSynonymOptions';
export * from './FacetHit';
export * from './FindAnswersOptions';
export * from './FindAnswersResponse';
export * from './FindObjectOptions';
export * from './FindObjectResponse';
export * from './GetApiKeyResponse';
Expand Down
3 changes: 3 additions & 0 deletions specs/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"singleQuote": false
}
70 changes: 70 additions & 0 deletions specs/answers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import algolia from "../packages/algoliasearch/dist/algoliasearch";
declare const algoliasearch: typeof algolia;

declare const browser: {
executeAsync<TArg, TResult>(
cb: (arg: TArg, done: (res: TResult) => TResult) => void,
arg: TArg
): TResult;

url(to: string): void;
};

const credentials = {
appId: "CKOEQ4XGMU",
apiKey: "6560d3886292a5aec86d63b9a2cba447"
// TODO: change these credentials to the main ones once enabled on our app
// appId: `${process.env.ALGOLIA_APPLICATION_ID_1}`,
// apiKey: `${process.env.ALGOLIA_SEARCH_KEY_1}`
};

describe("answers features - algoliasearch.com", () => {
beforeEach(async () => browser.url("algoliasearch.com"));

it("searchIndex::findAnswers", async () => {
const results: any = await browser.executeAsync(function(
credentials,
done
) {
const client = algoliasearch(credentials.appId, credentials.apiKey);

// TODO: remove this customization once the engine accepts url encoded query params
client.transporter.userAgent.value = "answers-test";

const index = client.initIndex("ted");

Promise.all([
index.findAnswers("sir ken robinson", ["en"]),
index.findAnswers("what", ["en"]),
index.findAnswers("sarah jones", ["en"], {
nbHits: 2,
attributesForPrediction: ["main_speaker"],
params: {
highlightPreTag: "_pre_",
highlightPostTag: "_post_"
}
})
]).then(function(responses) {
done({
kenRobinson: responses[0],
what: responses[1],
sarah: responses[2]
});
});
},
credentials);

expect(results.kenRobinson.nbHits).toBe(10);

expect(results.what.nbHits).toBe(0);

expect(results.sarah.nbHits).toBe(1);
expect(results.sarah.hits[0]._highlightResult.main_speaker.value).toBe(
"_pre_Sarah_post_ _pre_Jones_post_"
);

expect(results.sarah.hits[0]._answer.extract).toBe(
"_pre_Sarah_post_ _pre_Jones_post_"
);
});
});
48 changes: 35 additions & 13 deletions specs/search.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
import algolia from "../packages/algoliasearch/dist/algoliasearch";
declare const algoliasearch: typeof algolia;

declare const browser: {
executeAsync<TArg, TResult>(
cb: (arg: TArg, done: (res: TResult) => TResult) => void,
arg: TArg
): TResult;
executeAsync<TResult>(cb: (done: (res: TResult) => void) => void): TResult;

url(to: string): void;
};

const objects = [
{
color: "red",
Expand Down Expand Up @@ -29,7 +42,6 @@ const objects = [
}
];

// @ts-ignore
const credentials = {
appId: `${process.env.ALGOLIA_APPLICATION_ID_1}`,
apiKey: `${process.env.ALGOLIA_SEARCH_KEY_1}`
Expand All @@ -38,7 +50,6 @@ const credentials = {
// @ts-ignore
const version = require("../lerna.json").version;


["algoliasearch-lite.com", "algoliasearch.com"].forEach(preset => {
describe(`search features - ${preset}`, () => {
beforeEach(async () => browser.url(preset));
Expand Down Expand Up @@ -66,7 +77,10 @@ const version = require("../lerna.json").version;
});

it("searchClient::searchForFacetValues and searchIndex::searchForFacetValues", async () => {
const results = await browser.executeAsync(function(credentials, done) {
const results: any = await browser.executeAsync(function(
credentials,
done
) {
const client = algoliasearch(credentials.appId, credentials.apiKey);

const index = client.initIndex("javascript-browser-testing-lite");
Expand All @@ -80,27 +94,32 @@ const version = require("../lerna.json").version;
green: responses[1]
});
});
}, credentials);
},
credentials);

expect(results.red.facetHits.pop().value).toEqual("red");
expect(results.green.facetHits.pop().value).toEqual("green");
});

it("cache requests", async () => {
const responses = await browser.executeAsync(function(credentials, done) {
const responses: any = await browser.executeAsync(function(
credentials,
done
) {
const client = algoliasearch(credentials.appId, credentials.apiKey);
const params = [
{
indexName: "javascript-browser-testing-lite",
params: { clickAnalytics: "true" }
params: { clickAnalytics: true }
}
];
const promise = client.search(params);
const promise2 = client.search(params);
const promise3 = client.search(params, { cacheable: false });

return Promise.all([promise, promise2, promise3]).then(done);
}, credentials);
},
credentials);

expect(responses.length).toBe(3);
const queryID = responses[0].results[0].queryID;
Expand All @@ -113,12 +132,15 @@ const version = require("../lerna.json").version;
});

it("cache responses", async () => {
const responses = await browser.executeAsync(function(credentials, done) {
const responses: any = await browser.executeAsync(function(
credentials,
done
) {
const client = algoliasearch(credentials.appId, credentials.apiKey);
const params = [
{
indexName: "javascript-browser-testing-lite",
params: { clickAnalytics: "true" }
params: { clickAnalytics: true }
}
];
return client
Expand All @@ -127,22 +149,22 @@ const version = require("../lerna.json").version;
return Promise.all([response, client.search(params)]);
})
.then(done);
}, credentials);
},
credentials);

expect(responses.length).toBe(2);
const queryID = responses[0].results[0].queryID;
const queryID2 = responses[1].results[0].queryID;
expect(queryID).toBe(queryID2);
});


it("contains version", async () => {
const browserVersion: string = await browser.executeAsync(function(done) {
done(algoliasearch.version);
});

expect(browserVersion).toBe(version);
expect(browserVersion.startsWith('4.')).toBe(true);
expect(browserVersion.startsWith("4.")).toBe(true);
});
});
});

0 comments on commit 8d962ea

Please sign in to comment.