README
search-helper-js
This module is the companion of the clinia/clinia-client-javascript. It helps you keep track of the search parameters and provides a higher level API.
Features
- Search parameters management
- Facets exclusions
- Pagination
- Disjunctive faceting (search on two or more values of the same facet)
Examples
Vanilla JavaScript
A small example that uses Browserify to manage modules.
var clinia = require('clinia');
var searchHelper = require('@clinia/search-helper');
var client = clinia('appId', 'apiKey');
var helper = searchHelper(client, 'indexName', {
facets: ['type', 'address.place'],
disjunctiveFacets: ['services']
});
helper.on('result', function(event){
console.log(event.results);
});
helper.addDisjunctiveFacetRefinement('services', 'Vaccination');
helper.addDisjunctiveFacetRefinement('services', 'Strep test');
helper.addFacetRefinement('type', 'Pharmacy');
// Search for any health facilities of type pharmacy and offering Vaccination or Strep test services.
helper.search();
See more examples in the examples folder
Helper cheatsheet
Add the helper in your project
<script>
tag
Regular Use our jsDelivr build:
TODO
With NPM
npm install @clinia/search-helper
Init the helper
var helper = searchHelper(client, 'indexName'/*, parameters*/);
Helper lifecycle
- modify the parameters of the search (usually through user interactions)
helper.setQuery('jean coutu').addFacetRefinement('services', 'Vaccination') ```
- trigger the search (after all the modification have been applied)
helper.search() ```
- read the results (with the event "result" handler) and update the UI with the results
helper.on('result', function(event) { updateUI(event.results); }); ```
- go back to 1
Objects
CliniaSearchHelper: the helper. Keeps the state of the search, makes the queries and calls the handlers when an event happen.
SearchParameters: the object representing the state of the search. The current state is stored in helperInstance.state
.
SearchResults: the object in which the Clinia answers are transformed into. This object is passed to the result event handler. An example of SearchResults in JSON is available at the end of this readme
Search
The search is triggered by the search()
method.
It takes all the previous modifications to the search and uses them to create the queries to Clinia. The search parameters are immutable.
Example:
var helper = searchHelper(client, indexName);
// Let's monitor the results with the console
helper.on('result', function(event) {
console.log(event.results);
});
// Let's make an empty search
// The results are all sorted using the dashboard configuration
helper.search();
// Let's search for "jean coutu"
helper.setQuery('jean coutu').search();
Events
The helper is a Node.js EventEmitter instance.
result
: get notified when new results are received. The handler function will receive
two objects (SearchResults
and SearchParameters
).
error
: get notified when errors are received from the API.
change
: get notified when a property has changed in the helper
search
: get notified when a request is sent to Clinia
result
event
Listen to the helper.on('result', updateTheResults);
result
event once
Listen to a helper.once('result', updateTheResults);
result
listener
Remove a helper.removeListener('result', updateTheResults);
result
listeners
Remove all helper.removeAllListeners('result');
All the methods from Node.js EventEmitter class are available.
Query
Do a search with the query "fruit"
helper.setQuery('fruit').search();
Filtering results
Facets are filters to retrieve a subset of an index having a specific value for a given property.
Regular (conjunctive) facets
Refinements are ANDed by default (Conjunctive selection).
Facet definition
var helper = searchHelper(client, indexName, {
facets: ['ANDFacet']
});
Add a facet filter
helper.addFacetRefinement('ANDFacet', 'valueOfANDFacet').search();
Remove a facet filter
helper.removeFacetRefinement('ANDFacet', 'valueOfANDFacet').search();
Disjunctive facets
Refinements are ORed by default (Disjunctive selection).
Facet definition
var helper = searchHelper(client, indexName, {
disjunctiveFacets: ['ORFacet']
});
Add a facet filter
helper.addDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();
Remove a facet filter
helper.removeDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();
Negative facets
Filter so that we do NOT get a given facet
Facet definition (same as "AND" facet)
var helper = searchHelper(client, indexName, {
facets: ['ANDFacet']
}).search();
Exclude a value for a facet
helper.addFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');
Remove an exclude from the list of excluded values
helper.removeFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');
Clearing filters
Clear all the refinements for all the refined attributes
helper.clearRefinements().search();
Clear all the refinements for a specific attribute
helper.clearRefinements('ANDFacet').search();
[ADVANCED] Clear only the exclusions on the "ANDFacet" attribute
helper.clearRefinements(function(value, attribute, type) {
return type === 'exclude' && attribute === 'ANDFacet';
}).search();
Facet utilities
Get the values of a facet with the default sort
helper.on('result', function(event) {
// Get the facet values for the attribute age
event.results.getFacetValues('age');
// It will be ordered :
// - refined facets first
// - then ordered by number of occurence (bigger count -> higher in the list)
// - then ordered by name (alphabetically)
});
Get the values of a facet with a custom sort
helper.on('result', function(event) {
// Get the facet values for the attribute age
event.results.getFacetValues('age', {sortBy: ['count:asc']});
// It will be ordered by number of occurence (lower number => higher position)
// Elements that can be sorted : count, name, isRefined
// Type of sort : 'asc' for ascending order, 'desc' for descending order
});
Pagination
Get the current page
helper.getPage();
Change page
helper.setPage(3).search();
Automatic reset to page 0
During a search, changing the parameters will update the result set, which can then change the number of pages in the result set. Therefore, the behavior has been standardized so that any operation that may change the number of page will reset the pagination to page 0.
This may lead to some unexpected behavior. For example:
helper.setPage(4);
helper.getPage(); // 4
helper.setQuery('foo');
helper.getPage(); // 0
Non exhaustive list of operations that trigger a reset:
- refinements (conjunctive, exclude, disjunctive)
- index (setIndex)
- setQuery
- setPerPage
Index
Index can be changed.
Change the current index
helper.setIndex('index_xyz').search();
Get the current index
var currentIndex = helper.getIndex();
One time query
Sometime it's convenient to reuse the current search parameters with small changes
without changing the state stored in the helper. That's why there is a function
called searchOnce
. This method does not trigger change
or error
events.
In the following, we are using searchOnce
to fetch only a single element using
all the other parameters already set in the search parameters.
Using searchOnce with a callback
var state = helper.searchOnce(
{perPage: 1},
function(error, content, state) {
// if an error occured it will be passed in error, otherwise its value is null
// content contains the results formatted as a SearchResults
// state is the instance of SearchParameters used for this search
});
Using searchOnce with a promise
var state1 = helper.searchOnce({perPage: 1})
.then(function(res) {
// res contains
// {
// content : SearchResults
// state : SearchParameters (the one used for this specific search)
// }
});
Query parameters
There are lots of other parameters you can set.
Set a parameter at the initialization of the helper
var helper = searchHelper(client, indexName, {
perPage: 50
});
Set a parameter later
helper.setQueryParameter('perPage', 20).search();
Results format
Here is an example of a result object you get with the result
event.
{
"perPage": 10,
"processingTimeMS": 2,
"facets": [
{
"name": "type",
"data": {
"Pharmacy": 6627,
"Clinic": 550,
"Clsc": 665,
"Hospital": 131,
"Community Resource": 456,
"Other": 1571
},
"exhaustive": false
}
],
"records": [
{
"type":"PHARMACY",
"address":{
"streetAddress":"947 Boulevard du Séminaire Nord",
"suiteNumber":null,
"postalCode":"J3A 1K1",
"neighborhood":null,
"locality":null,
"place":"Saint-Jean-sur-Richelieu",
"region":null,
"regionCode":"QC",
"country":null,
"countryCode":"CA"
},
"geoPoint":{
"lat":45.33416219999999,
"lng":-73.2671249
},
"onlineBookingUrl":null,
"distance":null,
"openingHours":{
"1":[{"start":"08:00","end":"21:00"}],
"2":[{"start":"08:00","end":"21:00"}],
"3":[{"start":"08:00","end":"21:00"}],
"4":[{"start":"08:00","end":"21:00"}],
"5":[{"start":"08:00","end":"21:00"}],
"6":[{"start":"09:00","end":"21:00"}],
"7":[{"start":"09:00","end":"21:00"}]
},
"socials":[
{
"url":"https://www.jeancoutu.com/","type":"WEBSITE"
}
],
"id":"4566e06c-d01b-443d-aadb-21942b781aa0",
"name":"Jean Coutu - Saint-Jean-sur-Richelieu",
"phones":[
{
"countryCode":"+1",
"number":"4503489251",
"extension":null,
"type":"MAIN"
},
{
"countryCode":"+1",
"number":"4503486812",
"extension":null,
"type":"FAX"
}
]
}
....
],
"total": 10000,
"disjunctiveFacets": [
{
"data": {
"Vaccination": 142,
"Strep test": 60,
...
},
"name": "services",
"exhaustive": false
}
],
"query": "",
"numPages": 100,
"page": 0,
"index": "bestbuy"
}
Browser support
This project works on any ES5 browser, basically >= IE9+.