A UI state management library to build js apps against Azure Search. Built with redux and typescript. Provides simple APIs for searching, suggestions, and faceted navigation. Built in extensibility endpoints allow you to call your own controllers rather than the search service directly, allowing for custom authentication or server side processing of results.
AzSearch.js companion project that makes it easy to quickly prototype a search application.
All samples and documentation assume the real estate sample index available through the portal. A demo account is provided for the samples. To create your own service and load the real estate sample see this guide.
- Getting Started
- Basic Usage
- State Tree
- Configuration
- Search & Suggest
- Faceting & Filtering
- Extensibility
npm install azsearchstore
-
Clone the repo
-
Install dependencies
yarn install
-
Build the project:
yarn run tscompile
yarn run devpack
-
Run tests:
yarn test
-
Install demos
- react
- cd examples/react_ts
- yarn install
- yarn run webpack
- knockout
- no configuration required
- react
-
Launch http-server
yarn run start_server
-
Navigate to
127.0.0.1:8080/examples/react_ts/index.html
or127.0.0.1:8080/examples/knockout/index.html
Minimum configuration is needed to get started, just a service, index, and queryKey. By default AzSearchStore makes all requests directly to the service. This means you'll likely need to configure CORS for your index. If developing locally you can set CORS to '*' for your index through the portal.azure.com.
// create an instance
var store = new AzSearchStore();
// basic service configuration
store.setConfig(
{
index: "yourIndex",
// can be found in azure portal
queryKey: "xxxxxxxYOUR-QUERY-KEY-HERExxxxxx",
// yourServiceName.search.windows.net
service: "yourServiceName"
});
If you've used redux, some of the following may look familiar. AzSearchStore is built as a redux store. The raw redux store can be accessed via store.store. AzSearchStore can be thought of as two parts. First, as data representing the state of a search application including search results, suggestions, and facets. Second, as a set of APIs to manipulate search state, these correspond with common actions performed by search UI elements such as autocomplete, or faceted search.
// read the current state
var state = store.getState();
// create a callback function
var callback = function(){
// read the changed state
var state = store.getState();
// do something with updated state...
}
// subscribe the callback to changes on the store
// callback will get called after every change to the state tree
store.subscribe(callback)
AzSearchStore has high level APIs that abstract the guts of searching, faceting, and managing search application state. Something missing from your scenario (for example, date faceting support)? Please file a request to help us prioritize.
In the following example, we issue set some search parameters and issue a query:
// update a parameter used for searching, in this instance $count
store.updateSearchParameters({ count: true });
// set the search text as "*"
store.setInput("*");
// make an http request to fetch search results
store.search();
// these two actions together fetch the next $top results and appending them to the existing results set in memory
store.incrementSkip();
store.loadMore();
// fetches the third page of results
store.setPage(3);
store.search();
Basic facet usage:
// create an checkbox facet, updates internal application state
// checkbox facets are used for discrete value filtering
// a common scenario is ratings on ecommerce sites
// a website might display checkboxes to filter by 1* 2* 3* 4* 5* rated products
store.addCheckboxFacet("beds", "number");
// issue a search request that will populate facets for that field
// note search() returns a promise
store.search().then(...)
// simulates a user selecting a facet checkbox for the value "urban"
// will produce the filter $filter="beds eq 3"
store.toggleCheckboxFacet("beds", "3");
// make the request to retrieve results, applying the recently generated filter
store.searchFromFacetAction();
Suggestions:
// set input for suggestions
store.setInput("redmo");
// set the suggester we will make requests against
store.updateSuggestionsParameters({ suggesterName: "sg" });
// send http request to get suggestions
store.suggest();
- config
- index
- queryKey
- service
- suggestCallback
- searchCallback
- results
- results
- isFetching
- lastUpdated
- count
- resultsProcessor
- suggestions
- suggestions
- isFetching
- lastUpdated
- suggestionsProcessor
- facets
- facetMode
- globalFilters
- facets
- parameters
- input
- searchParameters
- suggestionsParameters
searchParameters control different aspects of search such as paging, field selection, and sorting. These map directly to the API: https://docs.microsoft.com/en-us/rest/api/searchservice/search-documents
count
: boolean. When set to true, will request count of total matches to be returned with search resultstop
: number. Determines number of results to load, default 50 max 1000.skip
: number. Used for paging results.orderby
: string. Used for sorting,searchMode
: string, either "any" or "all". See api docs for detailsscoringProfile
: string. Used to alter result scoring, see api reference.select
: string. Limits the fields retrieved with search requestsearchFields
: string. controls which fields to search on a given queryminimumCoverage
: number. Advanced, the percentage of the index that must be covered by a search queryapiVersion
: string. Either: "2016-09-01" or "2015-02-28-Preview"queryType
: string. Either "simple" or "full". Defaults to simple. Standard keyword search scenarios use simple.scoringParameters
: array of strings. Indicates the values for each parameter defined in a scoring function, ex: ["name-value1,value2,..."]highlight
: string. Comma separated list of fields to apply hightlights, ex: "highlight_field_1, highlight_field_2, ..."highlightPreTag
: string. Opening HTML tag that is applied to matched text ex:<b>
highlightPostTag
: string. Closing HTML tag that is applied to matched text ex:</b>
// set api version for search
setSearchApiVersion(apiVersion);
// overwrite all parameters
setSearchParameters(searchParameters);
// merge in a subset of parameters
updateSearchParameters({ searchMode: "all" });
// convenient apis for manipulating $skip in the context of paging
incrementSkip();
decrementSkip();
map directly to the api: https://docs.microsoft.com/en-us/rest/api/searchservice/suggestions
top
: number. Determines number of results to load, default 50 max 1000.filter
: string. Expression that limits documents considered for suggestionsorderby
: string. Used for sorting,fuzzy
: boolean. Defaults to false. Enables fuzzy matching for suggestions.highlightPreTag
: string. Opening HTML tag that is applied to matched text ex:highlightPostTag
: string. Closing HTML tag that is applied to matched text ex:select
: string. Limits the fields retrieved with search requestsearchFields
: string. controls which fields to search on a given queryminimumCoverage
: number. Advanced, the percentage of the index that must be covered by a search queryapiVersion
: string. Either: "2016-09-01" or "2015-02-28-Preview"suggesterName
: string. Name of suggester associated with index that will be called for suggest()
// set api version for suggest
setSuggestionsApiVersion(apiVersion);
// overwrite all parameters for suggest
setSuggestionsParameters(suggestionsParameters);
// update a subset of parameters for suggest
updateSuggestionsParameters({ suggesterName: "sg" });
// standard search action, replaces current results
search()
// usually used in combination with incrementPage(), appends search results
loadMore()
// usually used after a change to facet selections, replaces current results and merges in new facet values
searchFromFacetAction()
// makes http call to retrieve suggestions
suggest()
// clears all suggestions from store
clearSuggestions()
Facets are stored as key value pairs in the part of the state tree corresponding to /facets/facets.
facets
: hashmap where keys are field names and values are the corresponding range/checkboxFacetsglobalFilters
hashmap that stores custom filters to be AND'd together and applied to all search queries in addition to the filters generated by facets. This can be useful if you'd like to do something like scope all search results to a given language. Global filters do not get cleared with calls to ClearFacetSelections.Take a look at the following example:
// set static filter on language field
store.setGlobalFilter("language", "language eq 'en'");
store.setGlobalFilter("foo", "foo lt 42");
// would produce the following filter: "language eq 'en' and foo lt 42"
// this filter would be AND'd with any filters produced by your facets
// remove the language filter, queries will now return results from any language
store.setGlobalFilter("language", "");
CheckboxFacet is for discreet value filtering. Think of a typical ratings filter on an e-commerce website. The internal state is as follows:
type
: "CheckboxFacet"dataType
: "string" | "number" | "collection", supports faceting over numeric, string or string collection fields.key
: string. The name of the field the faceting/filtering is applied tovalues
: { [key: string]: CheckboxFacetItem }. Key value pairs containing individual options that map to checkboxes. CheckboxFacetItem has propertiesvalue
,count
, andselected
count
: number of values to retrieve. Defaults to 5, currently not configurable.sort
: determines sorting of the retrieved values, currently not configurable.facetClause
: string. read only. facet clause auto-generated for the field in questionfilterClause
: string. ready only. filter clause auto-generated based on currently selected values.
RangeFacet is for filtering based on a user defined range. Typically this is done through a slider control, or two input boxes. RangeFacet supports both numeric and date based fields.
type
: "RangeFacet"key
: string. The name of the field the faceting/filtering is applied todataType
: "number" | "date". The data type of the field to facet/filter on.min
: number | Date. minimum value for the fieldmax
: number | Date. maximum value for the fieldfilterLowerBound
: number | Date. Defaults to min. The lower range of the filter.filterUpperBound
: number | Date. Defaults to max. The upper range of the filter.lowerBucketCount
: number. Count of values falling below specified range.middleBucketCount
: number. Count of values falling within specified range.upperBucketCount
: number. Count of values above specified range.facetClause
: string. read only. facet clause auto-generated for the field in questionfilterClause
: string. ready only. filter clause auto-generated based on currently selected values.
// configure a numberic range facet for a field
addRangeFacet(fieldName, "number" min, max);
// configure a date range facet for a field
addRangeFacet(fieldName, "date" new Date(1990), new Date());
// configure a checkbox facet for a field
addCheckboxFacet(fieldName, dataType);
// set the filtering limits for a range facet
setFacetRange(fieldName, lowerBound, upperBound);
// set selection and filter by a given value for a checkbox facet
toggleCheckboxFacet(fieldName, value);
// reset all selected values/ranged for all facets
clearFacetsSelections();
// set a global filter that will be AND'd with the filters produced by your facets
// multiple global filters can be set by specifying different keys
// this will set a static filter that will always filter results to english
setGlobalFilter("language", "language eq 'english'");
AzSearchStore calls the search service directly by default. Some scenarios may not allow disclosing the query key to the client. You may want to roll additional authentication, or augment search results on the server before displaying them in the client. For any of these cases, you can specify both searchCallback
, and suggestCallback
functions. When specified, AzSearchStore will call these functions in place of making an HTTP call directly to the service.
// specified callback will be invoked with both the full state, and the postBody computed for search POST request
var searchCallback = function(state, postBody) {
// do something, maybe authentication?
// make an http call and return a promise
// promise must resolve with data in the same format returned by the search api
};
// same contract as searchCallback
var suggestCallback = ...;
setSearchCallback(searchCallback);
setSuggestCallback(suggestCallback);
Wanting to remap, or compute additional statistics about results or suggestions is common. When specified the suggestionsProcessor
, and resultsProcessor
functions will be called after every search/suggest request.
// processor called on every results set before they store in the state
// parameter 'results' is an array of objects containing result fields
var resultsProcessor = function(results){
return results.map(function(result){
return result;
})
};
setResultsProcessor(resultsProcessor);
setSuggestionsProcessor(suggestionsProcessor);