Skip to content

feat(findAnswers): implement the new method #1219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Nov 16, 2020
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
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";
Copy link
Contributor

Choose a reason for hiding this comment

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

@Haroenv to be clear, are we waiting for the engine fix, so that we can get rid of this line before merging this PR?

Copy link
Member

Choose a reason for hiding this comment

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

Hey @eunjae-lee, the engine fix won't be happening soon, so I'd rather merge this and prepare a v1.1 PR for when the engine has catched up :)

Copy link
Contributor

Choose a reason for hiding this comment

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

got it. thanks for clarification!

Copy link
Member

Choose a reason for hiding this comment

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


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);
});
});
});