Skip to content
This repository has been archived by the owner on Jul 11, 2023. It is now read-only.

feat(answers): add findAnswers #804

Merged
merged 12 commits into from Dec 23, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions index.d.ts
Expand Up @@ -16,6 +16,7 @@ import algoliasearch, {
import {
SearchOptions as SearchOptionsV4,
SearchResponse as SearchResponseV4,
FindAnswersResponse
// @ts-ignore
} from '@algolia/client-search';
import { EventEmitter } from 'events';
Expand Down Expand Up @@ -171,6 +172,20 @@ declare namespace algoliasearchHelper {
) => void
): void;

/**
* Start the search for answers with the parameters set in the state.
* This method returns a promise.
* @param {Object} options - the options for answers API call
* @param {string[]} options.attributesForPrediction - Attributes to use for predictions. If empty, `searchableAttributes` is used instead.
* @param {string[]} options.queryLanguages - The languages in the query. Currently only supports ['en'].
* @param {number} options.nbHits - Maximum number of answers to retrieve from the Answers Engine. Cannot be greater than 1000.
*/
findAnswers<TObject>(options: {
attributesForPrediction: string[];
queryLanguages: string[];
nbHits: number;
}): Promise<FindAnswersResponse<TObject>>;

/**
* Search for facet values based on an query and the name of a faceted attribute. This
* triggers a search and will return a promise. On top of using the query, it also sends
Expand Down
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -39,9 +39,9 @@
"index.d.ts"
],
"devDependencies": {
"@types/algoliasearch": "3.30.12",
"@types/algoliasearch": "3.34.11",
"algolia-frontend-components": "0.0.35",
"algoliasearch": "4.0.0-beta.14",
"algoliasearch": "4.8.3",
"babel-core": "6.26.3",
"babel-loader": "6.4.1",
"babel-preset-es2015": "6.24.1",
Expand Down
45 changes: 45 additions & 0 deletions src/algoliasearch.helper.js
Expand Up @@ -8,6 +8,8 @@ var requestBuilder = require('./requestBuilder');
var events = require('events');
var inherits = require('./functions/inherits');
var objectHasKeys = require('./functions/objectHasKeys');
var omit = require('./functions/omit');
var merge = require('./functions/merge');

var version = require('./version');

Expand Down Expand Up @@ -248,6 +250,49 @@ AlgoliaSearchHelper.prototype.searchOnce = function(options, cb) {
});
};

/**
* Start the search for answers with the parameters set in the state.
* This method returns a promise.
* @param {Object} options - the options for answers API call
* @param {string[]} options.attributesForPrediction - Attributes to use for predictions. If empty, `searchableAttributes` is used instead.
* @param {string[]} options.queryLanguages - The languages in the query. Currently only supports ['en'].
* @param {number} options.nbHits - Maximum number of answers to retrieve from the Answers Engine. Cannot be greater than 1000.
*
* @return {promise} the answer results
*/
AlgoliaSearchHelper.prototype.findAnswers = function(options) {
var state = this.state;
var derivedHelper = this.derivedHelpers[0];
if (!derivedHelper) {
return Promise.resolve([]);
}
var derivedState = derivedHelper.getModifiedState(state);
var data = merge(
{
attributesForPrediction: options.attributesForPrediction,
nbHits: options.nbHits
},
{
params: omit(requestBuilder._getHitsSearchParams(derivedState), [
'attributesToSnippet',
'hitsPerPage',
'restrictSearchableAttributes',
'snippetEllipsisText' // FIXME remove this line once the engine is fixed.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unlike the doc, snippetEllipsisText is throwing an error. I've reported it to the team, but for now let's have it here.

])
}
);

var errorMessage = 'search for answers was called, but this client does not have a function client.initIndex(index).findAnswers';
if (typeof this.client.initIndex !== 'function') {
throw new Error(errorMessage);
}
var index = this.client.initIndex(derivedState.index);
if (typeof index.findAnswers !== 'function') {
throw new Error(errorMessage);
}
return index.findAnswers(derivedState.query, options.queryLanguages, data);
};

/**
* Structure of each result when using
* [`searchForFacetValues()`](reference.html#AlgoliaSearchHelper#searchForFacetValues)
Expand Down
103 changes: 103 additions & 0 deletions test/spec/algoliasearch.helper/findAnswers.js
@@ -0,0 +1,103 @@
'use strict';

var algoliasearchHelper = require('../../../index');

function makeFakeFindAnswersResponse() {
return {
exhaustiveFacetsCount: true,
facetHits: [],
processingTimeMS: 3
};
}

function setupTestEnvironment(helperOptions) {
var findAnswers = jest.fn(function() {
return Promise.resolve([makeFakeFindAnswersResponse()]);
});

var fakeClient = {
initIndex: function() {
return {
findAnswers: findAnswers
};
}
};

var helper = algoliasearchHelper(fakeClient, 'index', helperOptions);

return {
findAnswers: findAnswers,
helper: helper
};
}

test('returns an empty array with no derived helper', function() {
var env = setupTestEnvironment();
var helper = env.helper;
var findAnswers = env.findAnswers;

return helper
.findAnswers({
attributesForPrediction: ['description'],
queryLanguages: ['en'],
nbHits: 1
})
.then(function(result) {
expect(findAnswers).toHaveBeenCalledTimes(0);
expect(result).toEqual([]);
});
});

test('returns a correct result with one derivation', function() {
var env = setupTestEnvironment();
var helper = env.helper;
var findAnswers = env.findAnswers;

helper.derive(function(state) {
return state;
});

return helper
.findAnswers({
attributesForPrediction: ['description'],
queryLanguages: ['en'],
nbHits: 1
})
.then(function(result) {
expect(findAnswers).toHaveBeenCalledTimes(1);
expect(result).toEqual([makeFakeFindAnswersResponse()]);
});
});

test('runs findAnswers with facets', function() {
var env = setupTestEnvironment({facets: ['facet1']});
var helper = env.helper;
var findAnswers = env.findAnswers;
helper.addFacetRefinement('facet1', 'facetValue');

helper.derive(function(state) {
return state;
});

helper.setQuery('hello');

return helper
.findAnswers({
attributesForPrediction: ['description'],
queryLanguages: ['en'],
nbHits: 1
})
.then(function() {
expect(findAnswers).toHaveBeenCalledTimes(1);
expect(findAnswers).toHaveBeenCalledWith('hello', ['en'], {
attributesForPrediction: ['description'],
nbHits: 1,
params: {
facetFilters: ['facet1:facetValue'],
facets: ['facet1'],
query: 'hello',
tagFilters: ''
}
});
});
});