@spinque/query-api
Advanced tools
Comparing version 0.6.0 to 0.6.2
@@ -23,3 +23,3 @@ import { Query } from './types'; | ||
private emptyParameterQuery?; | ||
private facets; | ||
private _facets; | ||
constructor(searchQuery: Query, emptyParameterQuery?: Query | undefined); | ||
@@ -29,3 +29,4 @@ /** | ||
*/ | ||
withFacet(endpoint: string, type?: 'single' | 'multiple', filterEndpointPrefix?: string, filterEndpointParameterName?: string): FacetedSearch; | ||
addFacet(endpoint: string, type?: 'single' | 'multiple', filterEndpointPrefix?: string, filterEndpointParameterName?: string): void; | ||
get facets(): Facet[]; | ||
/** | ||
@@ -40,3 +41,3 @@ * Get the Query to get the search results. | ||
*/ | ||
getResultsQueries(): Query[]; | ||
getResultsQuery(): Query[]; | ||
/** | ||
@@ -46,3 +47,3 @@ * Get the Query objects to retrieve the facet options. When using multiple facets, the facetEndpoint | ||
*/ | ||
getFacetOptions(facetEndpoint?: string): Query[]; | ||
getFacetQuery(facetEndpoint: string): Query[]; | ||
/** | ||
@@ -49,0 +50,0 @@ * Set a parameter value for the searchQuery |
@@ -41,3 +41,3 @@ "use strict"; | ||
// Internal list of Facet objects | ||
this.facets = []; | ||
this._facets = []; | ||
// Throw an error if the searchQuery does not have parameters | ||
@@ -51,9 +51,7 @@ if (!searchQuery.parameters || Object.keys(searchQuery.parameters).length === 0) { | ||
*/ | ||
FacetedSearch.prototype.withFacet = function ( | ||
// Name of the | ||
endpoint, type, filterEndpointPrefix, filterEndpointParameterName) { | ||
FacetedSearch.prototype.addFacet = function (endpoint, type, filterEndpointPrefix, filterEndpointParameterName) { | ||
if (type === void 0) { type = 'single'; } | ||
if (filterEndpointPrefix === void 0) { filterEndpointPrefix = ':FILTER'; } | ||
if (filterEndpointParameterName === void 0) { filterEndpointParameterName = 'value'; } | ||
this.facets.push({ | ||
this._facets.push({ | ||
optionsEndpoint: endpoint, | ||
@@ -65,4 +63,10 @@ filterEndpoint: "".concat(endpoint).concat(filterEndpointPrefix), | ||
}); | ||
return this; | ||
}; | ||
Object.defineProperty(FacetedSearch.prototype, "facets", { | ||
get: function () { | ||
return this._facets; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
/** | ||
@@ -87,6 +91,6 @@ * Get the Query to get the search results. | ||
*/ | ||
FacetedSearch.prototype.getResultsQueries = function () { | ||
FacetedSearch.prototype.getResultsQuery = function () { | ||
return __spreadArray([ | ||
this.getBaseQuery() | ||
], this.facets | ||
], this._facets | ||
.filter(function (f) { return f.filterParameterValue !== undefined && f.filterParameterValue !== ''; }) | ||
@@ -105,13 +109,4 @@ .map(function (f) { | ||
*/ | ||
FacetedSearch.prototype.getFacetOptions = function (facetEndpoint) { | ||
var facet; | ||
if (facetEndpoint) { | ||
facet = this.facets.find(function (f) { return f.optionsEndpoint === facetEndpoint; }); | ||
} | ||
else if (this.facets.length > 1) { | ||
throw new Error('The facet to get options for has to be specified whenever FactedSearch has more than one facet.'); | ||
} | ||
else { | ||
facet = this.facets[0]; | ||
} | ||
FacetedSearch.prototype.getFacetQuery = function (facetEndpoint) { | ||
var facet = this._facets.find(function (f) { return f.optionsEndpoint === facetEndpoint; }); | ||
if (!facet) { | ||
@@ -122,3 +117,3 @@ throw new Error('Facet not found in FacetedSearch'); | ||
this.getBaseQuery() | ||
], this.facets | ||
], this._facets | ||
.filter(function (f) { | ||
@@ -162,3 +157,3 @@ return f.filterParameterValue !== undefined && | ||
} | ||
var facet = this.facets.find(function (f) { return f.optionsEndpoint === facetEndpoint; }); | ||
var facet = this._facets.find(function (f) { return f.optionsEndpoint === facetEndpoint; }); | ||
if (!facet) { | ||
@@ -182,3 +177,3 @@ throw new Error("FacetedSearch does not contain facet ".concat(facetEndpoint)); | ||
if (facetEndpoint) { | ||
var facet = this.facets.find(function (f) { return f.optionsEndpoint === facetEndpoint; }); | ||
var facet = this._facets.find(function (f) { return f.optionsEndpoint === facetEndpoint; }); | ||
if (!facet) { | ||
@@ -190,3 +185,3 @@ throw new Error("FacetedSearch does not contain facet ".concat(facetEndpoint)); | ||
else { | ||
this.facets.forEach(function (f) { | ||
this._facets.forEach(function (f) { | ||
f.filterParameterValue = ''; | ||
@@ -193,0 +188,0 @@ }); |
@@ -10,2 +10,41 @@ "use strict"; | ||
}); | ||
it('should be able to set facets', function () { | ||
var sq = { endpoint: 'my-endpoint', parameters: { q: '' } }; | ||
var fs = new FacetedSearch_1.FacetedSearch(sq); | ||
fs.addFacet('genre'); | ||
expect(fs.facets).toBeDefined(); | ||
expect(fs.facets.length).toEqual(1); | ||
}); | ||
it('should create single-select facets by default', function () { | ||
var sq = { endpoint: 'my-endpoint', parameters: { q: '' } }; | ||
var fs = new FacetedSearch_1.FacetedSearch(sq); | ||
fs.addFacet('genre'); | ||
expect(fs.facets[0].type).toEqual('single'); | ||
}); | ||
it('should set the facet value using setFacetSelection for single-select', function () { | ||
var sq = { endpoint: 'my-endpoint', parameters: { q: '' } }; | ||
var fs = new FacetedSearch_1.FacetedSearch(sq); | ||
fs.addFacet('genre'); | ||
fs.setFacetSelection('genre', 'a'); | ||
expect(fs.facets[0].filterParameterValue).toEqual('a'); | ||
}); | ||
it('should set the facet value using setFacetSelection for multiple-select', function () { | ||
var sq = { endpoint: 'my-endpoint', parameters: { q: '' } }; | ||
var fs = new FacetedSearch_1.FacetedSearch(sq); | ||
fs.addFacet('genre', 'multiple'); | ||
fs.setFacetSelection('genre', ['a', 'b']); | ||
expect(fs.facets[0].filterParameterValue).toEqual('1(a)|1(b)'); | ||
}); | ||
it('should not allow multiple values to be selected for single-select facets', function () { | ||
var sq = { endpoint: 'my-endpoint', parameters: { q: '' } }; | ||
var fs = new FacetedSearch_1.FacetedSearch(sq); | ||
fs.addFacet('genre'); | ||
expect(function () { return fs.setFacetSelection('genre', ['a', 'b']); }).toThrow(); | ||
}); | ||
it('should create mulitple-select facets on request', function () { | ||
var sq = { endpoint: 'my-endpoint', parameters: { q: '' } }; | ||
var fs = new FacetedSearch_1.FacetedSearch(sq); | ||
fs.addFacet('genre', 'multiple'); | ||
expect(fs.facets[0].type).toEqual('multiple'); | ||
}); | ||
}); |
@@ -70,2 +70,104 @@ "use strict"; | ||
}); | ||
it('urlFromQueries should add count and offset parameters', function () { | ||
var apiConfig = { | ||
baseUrl: 'https://rest.spinque.com/', | ||
version: '4', | ||
workspace: 'my-workspace', | ||
api: 'my-api', | ||
config: 'my-config', | ||
}; | ||
var query = { endpoint: 'my-endpoint' }; | ||
expect((0, utils_1.urlFromQueries)(apiConfig, query, { count: 314, offset: 15 })).toEqual('https://rest.spinque.com/4/my-workspace/api/my-api/e/my-endpoint/results?config=my-config&count=314&offset=15'); | ||
}); | ||
it('urlFromQueries should fail without a baseUrl, version, workspace or API name', function () { | ||
var query = { endpoint: 'my-endpoint' }; | ||
var apiConfig1 = { | ||
version: '4', | ||
workspace: 'my-workspace', | ||
api: 'my-api', | ||
config: 'my-config', | ||
}; | ||
expect(function () { return (0, utils_1.urlFromQueries)(apiConfig1, query); }).toThrow('Base URL missing'); | ||
var apiConfig2 = { | ||
baseUrl: 'https://rest.spinque.com/', | ||
workspace: 'my-workspace', | ||
api: 'my-api', | ||
config: 'my-config', | ||
}; | ||
expect(function () { return (0, utils_1.urlFromQueries)(apiConfig2, query); }).toThrow('Version missing'); | ||
var apiConfig3 = { | ||
baseUrl: 'https://rest.spinque.com/', | ||
version: '4', | ||
api: 'my-api', | ||
config: 'my-config', | ||
}; | ||
expect(function () { return (0, utils_1.urlFromQueries)(apiConfig3, query); }).toThrow('Workspace missing'); | ||
var apiConfig4 = { | ||
baseUrl: 'https://rest.spinque.com/', | ||
version: '4', | ||
workspace: 'my-workspace', | ||
config: 'my-config', | ||
}; | ||
expect(function () { return (0, utils_1.urlFromQueries)(apiConfig4, query); }).toThrow('API name missing'); | ||
}); | ||
it('urlFromQueries should not fail without config', function () { | ||
var apiConfig = { | ||
baseUrl: 'https://rest.spinque.com/', | ||
version: '4', | ||
workspace: 'my-workspace', | ||
api: 'my-api', | ||
}; | ||
var query = { endpoint: 'my-endpoint' }; | ||
expect((0, utils_1.urlFromQueries)(apiConfig, query)).toEqual('https://rest.spinque.com/4/my-workspace/api/my-api/e/my-endpoint/results'); | ||
}); | ||
it('urlFromQueries should be able to request statistics', function () { | ||
var apiConfig = { | ||
baseUrl: 'https://rest.spinque.com/', | ||
version: '4', | ||
workspace: 'my-workspace', | ||
api: 'my-api', | ||
config: 'default', | ||
}; | ||
var query = { endpoint: 'my-endpoint' }; | ||
expect((0, utils_1.urlFromQueries)(apiConfig, query, {}, 'statistics')).toEqual('https://rest.spinque.com/4/my-workspace/api/my-api/e/my-endpoint/statistics?config=default'); | ||
}); | ||
it('tupleListToString should convert everything to strings', function () { | ||
expect(typeof (0, utils_1.tupleListToString)(0)).toEqual('string'); | ||
expect(typeof (0, utils_1.tupleListToString)('hello')).toEqual('string'); | ||
}); | ||
it('tupleListToString should convert single strings or numbers', function () { | ||
expect((0, utils_1.tupleListToString)('hello')).toEqual('1(hello)'); | ||
expect((0, utils_1.tupleListToString)(123)).toEqual('1(123)'); | ||
}); | ||
it('tupleListToString should convert an array of strings or numbers', function () { | ||
expect((0, utils_1.tupleListToString)(['hello'])).toEqual('1(hello)'); | ||
expect((0, utils_1.tupleListToString)(['hello', 'there'])).toEqual('1(hello)|1(there)'); | ||
expect((0, utils_1.tupleListToString)([123])).toEqual('1(123)'); | ||
expect((0, utils_1.tupleListToString)([123, 456])).toEqual('1(123)|1(456)'); | ||
expect((0, utils_1.tupleListToString)([123, 'marco'])).toEqual('1(123)|1(marco)'); | ||
expect((0, utils_1.tupleListToString)(['polo', '456'])).toEqual('1(polo)|1(456)'); | ||
}); | ||
it('tupleListToString should convert an array of arrays of strings or numbers', function () { | ||
expect((0, utils_1.tupleListToString)([ | ||
['hello', 'there'], | ||
['marco', 'polo'], | ||
[123, 456], | ||
])).toEqual('1(hello,there)|1(marco,polo)|1(123,456)'); | ||
}); | ||
it('tupleListToString should throw an error if arrays are not of equal length', function () { | ||
expect(function () { return (0, utils_1.tupleListToString)([['hello', 'there'], ['marco'], [123, 456]]); }).toThrow(); | ||
}); | ||
it('tupleListToString should use scores if passed', function () { | ||
expect((0, utils_1.tupleListToString)([ | ||
['hello', 'there'], | ||
['marco', 'polo'], | ||
[123, 456], | ||
], [0.1, 0.5, 3.1415])).toEqual('0.1(hello,there)|0.5(marco,polo)|3.1415(123,456)'); | ||
}); | ||
it('tupleListToString should throw an error if scores length does not match arrays length', function () { | ||
expect(function () { return (0, utils_1.tupleListToString)([['hello', 'there'], ['marco'], [123, 456]], []); }).toThrow(); | ||
expect(function () { return (0, utils_1.tupleListToString)([['hello', 'there'], ['marco'], [123, 456]], [1]); }).toThrow(); | ||
expect(function () { return (0, utils_1.tupleListToString)([['hello', 'there'], ['marco'], [123, 456]], [1, 2]); }).toThrow(); | ||
expect(function () { return (0, utils_1.tupleListToString)([['hello', 'there'], ['marco'], [123, 456]], [1, 2, 3, 4]); }).toThrow(); | ||
}); | ||
}); |
@@ -121,14 +121,15 @@ /** | ||
type: DataType[]; | ||
items: { | ||
rank: number; | ||
probability: number; | ||
tuple: (string | number | { | ||
id: string; | ||
type: string[]; | ||
attributes?: { | ||
[attributeName: string]: any; | ||
}; | ||
})[]; | ||
}[]; | ||
items: ResultItem[]; | ||
} | ||
export interface ResultItem { | ||
rank: number; | ||
probability: number; | ||
tuple: (string | number | { | ||
id: string; | ||
type: string[]; | ||
attributes?: { | ||
[attributeName: string]: any; | ||
}; | ||
})[]; | ||
} | ||
/** | ||
@@ -135,0 +136,0 @@ * Response to a Query that contains statistics |
@@ -15,2 +15,5 @@ import { ApiConfig, Query } from '.'; | ||
export declare const urlFromQueries: (config: ApiConfig, queries: Query | Query[], options?: RequestOptions | undefined, requestType?: RequestType) => string; | ||
/** | ||
* Given a tuple list (and optionally scores), return a string representation. | ||
*/ | ||
export declare const tupleListToString: (tuples: (string | number)[][] | (string | number)[] | string | number, scores?: number[] | undefined) => string; |
@@ -73,3 +73,9 @@ "use strict"; | ||
exports.urlFromQueries = urlFromQueries; | ||
var tupleListToString = function (tuples, scores) { | ||
/** | ||
* Given a tuple list (and optionally scores), return a string representation. | ||
*/ | ||
var tupleListToString = function ( | ||
// tuples can be either a string, a number, an array of strings or numbers, | ||
// or an array of arrays of strings or numbers | ||
tuples, scores) { | ||
var _tuples = ensureTupleList(tuples); | ||
@@ -76,0 +82,0 @@ if (scores && scores.length !== _tuples.length) { |
{ | ||
"name": "@spinque/query-api", | ||
"version": "0.6.0", | ||
"version": "0.6.2", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
112
README.md
@@ -11,2 +11,3 @@ # @spinque/query-api | ||
## Installing | ||
@@ -159,20 +160,26 @@ | ||
const fs = new FacetedSearch(query).withFacet('genre', 'multiple'); | ||
const fs = new FacetedSearch(query); | ||
fs.addFacet('genre', 'multiple'); | ||
fs.addFacet('director', 'single'); | ||
// Get results and facet options | ||
let results = await api.fetch(fs.getResultsQueries()); | ||
let options = await api.fetch(fs.getFacetOptions()); | ||
let results = await api.fetch(fs.getResultsQuery()); | ||
let genreOptions = await api.fetch(fs.getFacetQuery('genre')); | ||
let directorOptions = await api.fetch(fs.getFacetQuery('director')); | ||
// Set the search query parameter (e.g. after the user has typed something) | ||
fs.setParameter('query', 'the grand'); | ||
fs.setParameter('query', 'dia'); | ||
// Get updated results and options | ||
results = await api.fetch(fs.getResultsQueries()); | ||
options = await api.fetch(fs.getFacetOptions()); | ||
results = await api.fetch(fs.getResultsQuery()); | ||
genreOptions = await api.fetch(fs.getFacetQuery('genre')); | ||
directorOptions = await api.fetch(fs.getFacetQuery('director')); | ||
// Select a facet option | ||
fs.setFacetSelection('genre', 'https://imdb.com/schema/Drama'); | ||
fs.setFacetSelection('genre', ['https://imdb.com/data/Drama', 'https://imdb.com/data/Biography']); | ||
fs.setFacetSelection('director', 'https://imdb.com/data/PabloLarrain'); | ||
// Get results again | ||
results = await api.fetch(fs.getResultsQueries()); | ||
results = await api.fetch(fs.getResultsQuery()); | ||
``` | ||
@@ -190,2 +197,89 @@ | ||
Note that the exact same behavior can also be achieved *without* the FacetedSearch class (though it's more involved). The following two sections produce equal results: | ||
With FacetedSearch: | ||
```typescript | ||
const query: Query = { | ||
endpoint: 'movie_search', | ||
parameters: { query: 'call me' } | ||
}; | ||
const fs = new FacetedSearch(query); | ||
fs.addFacet('genre', 'multiple'); | ||
fs.addFacet('director', 'single'); | ||
let results = await api.fetch(fs.getResultsQuery()); | ||
let genreOptions = await api.fetch(fs.getFacetQuery('genre')); | ||
let directorOptions = await api.fetch(fs.getFacetQuery('director')); | ||
fs.setParameter('query', 'dia'); | ||
results = await api.fetch(fs.getResultsQuery()); | ||
genreOptions = await api.fetch(fs.getFacetQuery('genre')); | ||
directorOptions = await api.fetch(fs.getFacetQuery('director')); | ||
fs.setFacetSelection('genre', ['https://imdb.com/data/Drama', 'https://imdb.com/data/Biography']); | ||
results = await api.fetch(fs.getResultsQuery()); | ||
``` | ||
Without FacetedSearch: | ||
```typescript | ||
const query: Query = { | ||
endpoint: 'movie_search', | ||
parameters: { query: 'call me' } | ||
}; | ||
const genreOptionsQuery: Query = { endpoint: 'genre' }; | ||
const genreFilterQuery: Query = { | ||
endpoint: 'genre:FILTER', | ||
parameters: { value: undefined } | ||
}; | ||
const directorOptionsQuery: Query = { endpoint: 'director' }; | ||
const directorFilterQuery: Query = { | ||
endpoint: 'director:FILTER', | ||
parameters: { value: undefined } | ||
}; | ||
let resultsQuery = [query]; | ||
if (genreFilterQuery.parameters.value) { | ||
resultsQuery.push(genreFilterQuery); | ||
} | ||
if (directorFilterQuery.parameters.value) { | ||
resultsQuery.push(directorFilterQuery); | ||
} | ||
let results = await api.fetch(resultsQuery); | ||
let genreOptions = await api.fetch([...resultsQuery, genreOptionsQuery]); | ||
let directorOptions = await api.fetch([...resultsQuery, directorOptionsQuery]); | ||
query.parameters.query = 'dia'; | ||
let resultsQuery = [query]; | ||
if (genreFilterQuery.parameters.value) { | ||
resultsQuery.push(genreFilterQuery); | ||
} | ||
if (directorFilterQuery.parameters.value) { | ||
resultsQuery.push(directorFilterQuery); | ||
} | ||
results = await api.fetch(resultsQuery); | ||
let genreOptions = await api.fetch([...resultsQuery, genreOptionsQuery]); | ||
let directorOptions = await api.fetch([...resultsQuery, directorOptionsQuery]); | ||
genreFilterQuery.parameters.value = tupleListToString(['https://imdb.com/data/Drama', 'https://imdb.com/data/Biography']); | ||
let resultsQuery = [query]; | ||
if (genreFilterQuery.parameters.value) { | ||
resultsQuery.push(genreFilterQuery); | ||
} | ||
if (directorFilterQuery.parameters.value) { | ||
resultsQuery.push(directorFilterQuery); | ||
} | ||
results = await api.fetch(resultsQuery); | ||
``` | ||
### Vanilla JavaScript | ||
@@ -214,2 +308,2 @@ | ||
} | ||
``` | ||
``` |
90707
1880
306