AzSearch.js
Automagical UI and sample react controls for Azure Search using AzSearchStore. Written in TypeScript.
Live Demo
Get started developing an AzSearch.js app in Typescript
App Generator
The following link contains a tool to help generate starting sample app that you can further customeize:
CORS
Don't forget to enable CORS on your index. Make sure to always use a query key.
Quick note on data
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.
Contents
Installation
CDN
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/azsearch.js@0.0.21/dist/AzSearch.css">
<script type="text/javascript" src="https://cdn.jsdelivr.net/react/15.5.4/react.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/react/15.5.4/react-dom.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/redux/3.6.0/redux.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/azsearch.js@0.0.21/dist/AzSearch.bundle.js"></script>
NPM
npm install azsearch.js --save
Automagic
Automagic provides a set of simple APIs to generate a sample search UI. Automagic is a wrapper of AzSearchStore (for state management) and the sample react components in this repo.
Basic usage
var automagic = new AzSearch.Automagic({
index: "realestate-us-sample",
queryKey: "D1CD08C7AC6A1886024E0F23B1824417",
service: "azs-playground" });
automagic.addSearchBox("searchBox",
{
highlightPreTag: "<b>",
highlightPostTag: "</b>",
suggesterName: "sg",
select: "number,street,city,region,countryCode"
});
automagic.addResults("results");
automagic.addPager("pager");
automagic.addRangeFacet("sqftFacet", "sqft", "number", 0, 17000);
automagic.addCheckboxFacet("bedsFacet", "beds", "number");
automagic.addCheckboxFacet("bathsFacet", "baths", "number");
automagic.addCheckboxFacet("typeFacet", "type", "string");
automagic.addCheckboxFacet("tagsFacet", "tags", "collection");
constructor
Sets basic configuration to connect to service. Expects an object of type Config from AzSearchStore
var automagic = new AzSearch.Automagic({
index: "realestate-us-sample",
queryKey: "D1CD08C7AC6A1886024E0F23B1824417",
service: "azs-playground" });
type Config = {
index: string;
queryKey: string;
service: string;
suggestCallback?: (state: Store.SearchState, postBody: {
[key: string]: any;
}) => Promise<any>;
searchCallback?: (state: Store.SearchState, postBody: {
[key: string]: any;
}) => Promise<any>;
};
addSearchBox
addSearchBox(htmlId, suggestionsParametersUpdate?, suggestionValueKey?, suggestionTemplate?, css?)
Adds an input field capable of suggestions/autocomplete and executing search requests. Attaches on the specified htmlID. Optionally takes SuggestionUpdateParameters (from AzSearchStore), an optional key indicating which suggestion value should be set when a suggestion is clicked (defaults to "@search.text"), a mustache template to customize rendering, or css overrides. When template is not specified, a json representation of the suggestions is displayed. When specified, the mustache template is rendered against east suggestion. The content of each suggestion can be customized by adding fields via the select parameter as shown in the example below, or by setting a
suggestions processor on the store.
var css = {
searchBox__button: "searchBox__button btn btn-default",
};
var suggestionsTemplate = "{{displayText}} <br/> {{{searchText}}}";
automagic.addSearchBox("searchBox",
{
highlightPreTag: "<b>",
highlightPostTag: "</b>",
suggesterName: "sg",
select: "number,street,city,region,countryCode"
},
"displayText"
suggestionsTemplate,
css);
var suggestionsProcessor = function(results) {
return results.map(function(result){
result.displayText = result.number + " " +
result.street+ " " +result.city+ ", " +result.region+ " " +
result.countryCode;
result.searchText = result["@search.text"];
return result;
});
};
automagic.store.setSuggestionsProcessor(suggestionsProcessor);
type SuggestionsParametersUpdate = {
top?: number;
filter?: string;
orderby?: string;
fuzzy?: boolean;
highlightPreTag?: string;
highlightPostTag?: string;
select?: string;
searchFields?: string;
minimumCoverage?: string;
apiVersion?: SearchApiVersion;
suggesterName?: string;
};
type SearchApiVersion = "2016-09-01" | "2015-02-28-Preview";
addResults
addResults(htmlId, searchParametersUpdate?, resultsTemplate?, css?)
Adds a view the search results on the specifed htmlId. Optionally takes searchParametersUpdate (from AzSearchStore), a mustache template used to format individual results, or css class overrides. When specified, the mustace template will be rendered against each individual search result. Otherwise formatted JSON will be displayed. The content of each search result can be customized by setting a
results processor on the store.
var resultTemplate =
`<div class="col-xs-12 col-sm-5 col-md-3 result_img">
<img class="img-responsive result_img" src={{thumbnail}} alt="image not found" />
</div>
<div class="col-xs-12 col-sm-7 col-md-9">
<h4>{{displayText}}</h4>
<div class="resultDescription">
{{{summary}}}
</div>
<div>
sqft: <b>{{sqft}}</b>
</div>
<div>
beds: <b>{{beds}}</b>
</div>
<div>
baths: <b>{{baths}}</b>
</div>
</div>`;
automagic.addResults("results", { count: true }, resultTemplate);
var resultsProcessor = function(results) {
return results.map(function(result){
result.displayText = result.number + " " + result.street+ " " +result.city+ ", " +result.region+ " " +result.countryCode;
var summary = result.description;
result.summary = summary.length < 200 ? summary : summary.substring(0, 200) + "...";
return result;
});
};
automagic.store.setResultsProcessor(resultsProcessor);
type SearchParametersUpdate = {
count?: boolean;
top?: number;
skip?: number;
orderby?: string;
searchMode?: SearchMode;
scoringProfile?: string;
select?: string;
searchFields?: string;
minimumCoverage?: string;
apiVersion?: SearchApiVersion;
queryType?: QueryType;
};
type QueryType = "simple" | "full";
type SearchApiVersion = "2016-09-01" | "2015-02-28-Preview";
type SearchMode = "any" | "all";
addLoadingIndicator
addLoadingIndicator(htmlId)
Adds a component to show loading state there are inflight requests for searching/faceting on the specified id.
automagic.addLoadingIndicator("spinner");
Adds a pagination control <<< 1 2 3 ... >>>
on the specified id after all of the results
automagic.addPager("pager");
addRangeFacet
addRangeFacet(htmlId, fieldName, dataType, min, max, css?)
Adds a range facet control for on the specified htmlId for the given fieldName. dataType can be either "number" or "date". Range will go from min to max. Type of min and max should correspond to specified dataType.
automagic.addRangeFacet("sqftFacet", "sqft", "number", 0, 17000);
let startDate = new Date();
startDate.setFullYear(2007);
let endDate = new Date();
automagic.addRangeFacet("publishedFacet", "published", "date", startDate, endDate);
addCheckboxFacet
addCheckboxFacet(htmlId, fieldName, dataType, css?)
Adds a checkbox style faceting control to the specified htmlId over the specified field. Supported dataTypes are "number" | "string" | "collection"
. Also accepts optional css overrides
automagic.addCheckboxFacet("bedsFacet", "beds", "number");
automagic.addCheckboxFacet("bathsFacet", "baths", "number");
automagic.addCheckboxFacet("typeFacet", "type", "string");
automagic.addCheckboxFacet("tagsFacet", "tags", "collection");
addClearFiltersButton
addClearFiltersButton(htmlId, css?)
Adds a button (anchor) to the specified element (htmlId
) which when triggered clears all applied filters (facets) and updates the search results. Also accepts optional css overrides.
addSortBy
addSortBy(htmlId, fields, defaultSortFieldName?, css?)
Adds sorting control to the specified htmlId for the specified sortable fields. Accepts optional default sorting field name & css overrides.
var fields = [
{displayName: "Relevance", fieldName: ""},
{displayName: "Size", fieldName: "sqft"},
{displayName: "Beds", fieldName: "beds"},
{displayName: "Baths", fieldName: "baths"},
{displayName: "Distance", fieldName: "location", latitude: 47.673988099999995, longitude: -122.12151199999998}
];
automagic.addSortBy("sortBy", fields, "sqft");
addStaticFilter
addStaticFilter(htmlId, filterKey, filters, defaultFilter, title?, css?)
Adds a dropdown style filter control with static pre-defined filters. Can be useful for scenarios such as language or geo filtering. You may want to allow user to set a constant filter for language to be english, or for only results within 50 miles to be shown. filterKey
is a unique key that will be used to lookup and set the filter from state within the store state.facets.globalFilter[filterKey]
, as it is possible to have multiple global filters.
var filters = [
{ displayName: "Any", filter: "" },
{ displayName: "House", filter: "type eq 'House'"},
{ displayName: "Apartment", filter: "type eq 'Apartment'"}
];
var defaultFilter = "";
var title = "Home Type";
automagic.addStaticFilter("typeFilter", "type", filters, defaultFilter, title);
store
Instance of AzSearchStore. Methods can be called directly on the store, and actions can be dispatched to the store using APIs documented in the AzSearchStore repo.
automagic.store.setSearchApiVersion("2015-02-28-Preview");
automagic.store.setInput("bears beets battlestar galactica");
automagic.store.subscibe(function() {
var state = automagic.store.getState();
console.info(JSON.stringify(state, null, 4));
});
automagic.store.clearFacetsSelections();
Custom CSS
If you wish to use a custom theme. Please use the browser tools element inspector ctrl shift C
and compare against css constants used in the project. Css classes can be overridden in the following manner and passed in to a component:
var css = {
searchBox__buttonIcon: "searchBox__button-icon glyphicon glyphicon-search",
searchBox__button: "searchBox__button btn btn-default",
};
automagic.addSearchBox("searchBox",
{
highlightPreTag: "<b>",
highlightPostTag: "</b>",
suggesterName: "sg",
select: "number,street,city,region,countryCode"
},
"@search.text",
suggestionsTemplate,
css);
Components & Containers
AzSearch.js is build with react components and react-redux containers. Both of these are exposed and available for direct consumption/extension with your own instance of AzSearchStore. More docs on this coming soon.
Development
Set up should be as simple as running yarn install
. Run yarn run devbuild
for tslint, typescript compilation, and webpack dev bundling all in one step. Run yarn run prodpack
for a minified bundle. Testing: yarn test