Skip to content

Commit

Permalink
[Node-AzureSearch] Update to botbuilder 3.5.1
Browse files Browse the repository at this point in the history
- update to botbuilder 3.5.1
- other dependencies updated as well
- updated library usage to match v3.5 style
  • Loading branch information
pcostantini committed Jan 10, 2017
1 parent c3f9f2e commit e27a151
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 97 deletions.
1 change: 1 addition & 0 deletions Node/demo-Search/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"NODE_ENV": "development",
"MICROSOFT_APP_ID": "",
"MICROSOFT_APP_PASSWORD": ""
"BOT": "RealEstateBot"
},
"externalConsole": false,
"sourceMaps": false,
Expand Down
40 changes: 19 additions & 21 deletions Node/demo-Search/JobListingBot/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,38 @@ var _ = require('lodash');
var builder = require('botbuilder');
var restify = require('restify');

/// <reference path="../SearchDialogLibrary/index.d.ts" />
var SearchLibrary = require('../SearchDialogLibrary');
var AzureSearch = require('../SearchProviders/azure-search');

// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});

// Create chat bot
// Create chat bot and listen for messages
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
var bot = new builder.UniversalBot(connector);
server.post('/api/messages', connector.listen());

// Root dialog, triggers search and process its results
bot.dialog('/', [
function(session) {
// Bot with main dialog that triggers search and display its results
var bot = new builder.UniversalBot(connector, [
function (session) {
// Trigger the refine dialog with 'business_title' facet.
// Then continue the dialog waterfall with the selected refiner/filter
session.beginDialog('jobs:/refine', {
refiner: 'business_title',
prompt: 'Hi! To get started, what kind of position are you looking for?'
});
SearchLibrary.refine(session,
{
refiner: 'business_title',
prompt: 'Hi! To get started, what kind of position are you looking for?'
});
},
function(session, args) {
function (session, args) {
// trigger Search dialog root, using the generated Query created by /refine
var query = args.query;
session.beginDialog('jobs:/', { query });
SearchLibrary.begin(session, { query });
},
function (session, args) {
// Process selected search results
Expand All @@ -41,21 +45,15 @@ bot.dialog('/', [
]);

// Azure Search provider
var AzureSearch = require('../SearchProviders/azure-search');
var azureSearchClient = AzureSearch.create('azs-playground', '512C4FBA9EED64A31A1052CFE3F7D3DB', 'nycjobs');
var jobsResultsMapper = SearchLibrary.defaultResultsMapper(jobToSearchHit);

/// <reference path="../SearchDialogLibrary/index.d.ts" />
var SearchDialogLibrary = require('../SearchDialogLibrary');

// Jobs Listing Search
var jobsResultsMapper = SearchDialogLibrary.defaultResultsMapper(jobToSearchHit);
var realstate = SearchDialogLibrary.create('jobs', {
// Register Search Dialogs Library with bot
bot.library(SearchLibrary.create({
multipleSelection: true,
search: (query) => azureSearchClient.search(query).then(jobsResultsMapper),
refiners: ['business_title', 'agency', 'work_location']
});

bot.library(realstate);
}));

// Maps the AzureSearch Job Document into a SearchHit that the Search Library can use
function jobToSearchHit(job) {
Expand Down
25 changes: 12 additions & 13 deletions Node/demo-Search/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Content is modeled as a catalog of items where each item has several attributes

The minimum prerequisites to run this sample are:
* Latest Node.js with NPM. Download it from [here](https://nodejs.org/en/download/).
* The Bot Framework Emulator. To install the Bot Framework Emulator, download it from [here](https://aka.ms/bf-bc-emulator). Please refer to [this documentation article](https://docs.botframework.com/en-us/csharp/builder/sdkreference/gettingstarted.html#emulator) to know more about the Bot Framework Emulator.
* The Bot Framework Emulator. To install the Bot Framework Emulator, download it from [here](https://emulator.botframework.com/). Please refer to [this documentation article](https://github.com/microsoft/botframework-emulator/wiki/Getting-Started) to know more about the Bot Framework Emulator.
* **[Recommended]** Visual Studio Code for IntelliSense and debugging, download it from [here](https://code.visualstudio.com/) for free.

### Azure Search
Expand All @@ -28,9 +28,9 @@ The samples use [Azure Search](https://azure.microsoft.com/en-us/services/search

The samples include a bot library that contains a few different dialogs that are ready to use and can be customized as needed. The library is defined as the [SearchDialogLibrary](SearchDialogLibrary/index.js) module and it includes two main dialogs that can be used directly:

* [Root dialog](SearchDialogLibrary/index.js#L39) offers a complete keyword search + refine experience over a search index, and uses the other search dialogs as building blocks. Users can explore the catalog by refining (using facets) and by using keyword search. They can also select items and review their selection. At the end of this dialog a list of one or more selected items is returned.
* [Root dialog](SearchDialogLibrary/index.js#L35) offers a complete keyword search + refine experience over a search index, and uses the other search dialogs as building blocks. Users can explore the catalog by refining (using facets) and by using keyword search. They can also select items and review their selection. At the end of this dialog a list of one or more selected items is returned.

* [Refine dialog](SearchDialogLibrary/index.js#L155) helps users pick a refiner (facet). It's a simple wrapper around a "choice" prompt dialog to ensure you don't prompt users for a field you already refined on.
* [Refine dialog](SearchDialogLibrary/index.js#L151) helps users pick a refiner (facet). It's a simple wrapper around a "choice" prompt dialog to ensure you don't prompt users for a field you already refined on.

### Samples

Expand Down Expand Up @@ -68,11 +68,11 @@ We included two samples here:
### Usage

In order to use the library you need to create an instance of it using the module's `create()` function and specify a few parameters that provides the search implementation and some level of customization for the search experience. Then, you register the returned library object with [`bot.use()`](https://docs.botframework.com/en-us/node/builder/chat-reference/classes/_botbuilder_d_.universalbot.html#use) and trigger the dialogs with `session.beginDialog()`.
In order to use the library you need to create an instance of it using the module's `create()` function and specify parameters that provides the search implementation and some level of customization for the search experience. Then, you register the returned library object with [`bot.use()`](https://docs.botframework.com/en-us/node/builder/chat-reference/classes/_botbuilder_d_.universalbot.html#use) and trigger the dialogs with `SearchModule.begin(session)` or `SearchModule.refine(session, arguments)`.

````JavaScript
var SearchDialogLibrary = require('./SearchDialogLibrary');
var mySearchLibrary = SearchDialogLibrary.create('my-search', {
var SearchLibrary = require('./SearchDialogLibrary');
var mySearchLibrary = SearchLibrary.create({
multipleSelection: true,
search: (query) => { return mySearchClient.search(query).then(mapResultSetFunction); },
refiners: ['type', 'region']
Expand All @@ -84,7 +84,7 @@ bot.use(mySearchLibrary);
bot.dialog('/', [
function (session) {
// Triger search dialog
session.beginDialog('my-search:/');
SearchLibrary.begin(session);
},
function (session, args) {
// Display selected results
Expand All @@ -97,11 +97,10 @@ bot.dialog('/', [

#### Parameters and Settings

> SearchDialogLibrary.create(libraryId:string, settings):Library
> SearchDialogLibrary.create(settings):Library
* `libraryId` is required, defines the library name and the prefix you'll need to use when calling `session.beginDialog('libraryId:/')`;
* `settings` is also required. The only detail really needed is the `search` function.
* `search` is a required function. Accepts a `query` object and must return a Promise with a [`SearchResults`](SearchDialogLibrary/index.d.ts#L28-L31) object (See [Implementing a new Search Provider](#implementing-a-new-search-provider) below).
* `settings` is required. The only detail really needed is the `search` function.
* `search` is a required function. Accepts a `query` object and must return a Promise with a [`ISearchResults`](SearchDialogLibrary/index.d.ts#L30-L33) object (See [Implementing a new Search Provider](#implementing-a-new-search-provider) below).
* `multipleSelection` (optional - default=true). Boolean value indicating if multiple selection should be allowed. When `false`, the dialog will end as soon as an item is selected.
* `pageSize` (optional - default=5). Page size parameter passed to the search function.
* `refiners`. (optional). Array of strings containing the facets/refiners allowed to use.
Expand All @@ -119,7 +118,7 @@ In order to integrate the Search Dialog library with a new search engine, you ne
function search: (query: Query) => PromiseLike<SearchResults>
````

Input [`query`](SearchDialogLibrary/index.d.ts#L20-L26) object with the following fields:
Input [`query`](SearchDialogLibrary/index.d.ts#L22-L28) object with the following fields:

* `searchText`: string

Expand All @@ -141,7 +140,7 @@ Input [`query`](SearchDialogLibrary/index.d.ts#L20-L26) object with the followin

An array containing the facets you want to obtain possible values for. The search function should take this into account and proceed to *search* or *retrieve sub-categories* for the current search string.

The `search` must return a `Promise`, that once fulfilled, should resolve to a [`SearchResults`](SearchDialogLibrary/index.d.ts#L28-L31) object. This object must have the following fields:
The `search` must return a `Promise`, that once fulfilled, should resolve to a [`ISearchResults`](SearchDialogLibrary/index.d.ts#L30-L33) object. This object must have the following fields:

* `facets`: Array of `Facet`. Each Facet object contains a `key` field with the name of the facet and an `options` field with an array of possible values. Each value is a tuple of `key` representing a possible facet value, and a `count` field with the total items for that facet.

Expand Down
29 changes: 13 additions & 16 deletions Node/demo-Search/RealEstateBot/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,28 @@ var _ = require('lodash');
var builder = require('botbuilder');
var restify = require('restify');

/// <reference path="../SearchDialogLibrary/index.d.ts" />
var SearchLibrary = require('../SearchDialogLibrary');
var AzureSearch = require('../SearchProviders/azure-search');

// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});

// Create chat bot
// Create chat bot and listen for messages
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
var bot = new builder.UniversalBot(connector);
server.post('/api/messages', connector.listen());

// Root dialog, triggers search and process its results
bot.dialog('/', [
// Bot with main dialog that triggers search and display its results
var bot = new builder.UniversalBot(connector, [
function (session) {
// Trigger Search
session.beginDialog('realstate:/');
SearchLibrary.begin(session);
},
function (session, args) {
// Process selected search results
Expand All @@ -31,26 +34,20 @@ bot.dialog('/', [
}
]);

// Azure Search provider
var AzureSearch = require('../SearchProviders/azure-search');
// Azure Search
var azureSearchClient = AzureSearch.create('realestate', '82BCF03D2FC9AC7F4E9D7DE1DF3618A5', 'listings');
var realStateResultsMapper = SearchLibrary.defaultResultsMapper(realstateToSearchHit);

/// <reference path="../SearchDialogLibrary/index.d.ts" />
var SearchDialogLibrary = require('../SearchDialogLibrary');

// RealState Search
var realStateResultsMapper = SearchDialogLibrary.defaultResultsMapper(realstateToSearchHit);
var realstate = SearchDialogLibrary.create('realstate', {
// Register Search Dialogs Library with bot
bot.library(SearchLibrary.create({
multipleSelection: true,
search: (query) => azureSearchClient.search(query).then(realStateResultsMapper),
refiners: ['region', 'city', 'type'],
refineFormatter: (refiners) =>
_.zipObject(
refiners.map(r => 'By ' + _.capitalize(r)),
refiners)
});

bot.library(realstate);
}));

// Maps the AzureSearch RealState Document into a SearchHit that the Search Library can use
function realstateToSearchHit(realstate) {
Expand Down
40 changes: 21 additions & 19 deletions Node/demo-Search/SearchDialogLibrary/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,55 @@
import { Library } from "botbuilder";
import { Library, Session } from "botbuilder";

// Exported functions
export function create(libraryId: string, settings: SearchDialogSettings): Library;
// exported functions
export function create(settings: ISearchDialogSettings): Library;
export function begin(session: Session, args?: any): void;
export function refine(session: Session, args?: any): void;
export function defaultResultsMapper(itemMap: ItemMapper): SearchResultsMapper;

declare type ItemMapper = (input: any) => SearchHit;
declare type SearchResultsMapper = (providerResults: SearchResults) => SearchResults;
declare type ItemMapper = (input: any) => ISearchHit;
declare type SearchResultsMapper = (providerResults: ISearchResults) => ISearchResults;
declare type RefineFormatter = (refiners: Array<string>) => any;

// Entities
export interface SearchDialogSettings {
search: (query: Query) => PromiseLike<SearchResults>;
// entities
export interface ISearchDialogSettings {
search: (query: IQuery) => PromiseLike<ISearchResults>;
pageSize?: number;
multipleSelection?: boolean;
refiners?: Array<string>;
refineFormatter?: RefineFormatter;
}

export interface Query {
export interface IQuery {
searchText: string;
pageSize: number;
pageNumber: number;
filters: Array<FacetFilter>;
filters: Array<IFacetFilter>;
facets: Array<string>;
}

export interface SearchResults {
facets: Array<Facet>;
results: Array<SearchHit>
export interface ISearchResults {
facets: Array<IFacet>;
results: Array<ISearchHit>;
}

export interface SearchHit {
export interface ISearchHit {
key: string;
title: string;
description: string;
imageUrl?: string
imageUrl?: string;
}

export interface FacetFilter {
export interface IFacetFilter {
key: string;
value: any;
}

export interface Facet {
export interface IFacet {
key: string;
options: Array<FacetValue>;
options: Array<IFacetValue>;
}

export interface FacetValue {
export interface IFacetValue {
value: string;
count: number;
}
Loading

0 comments on commit e27a151

Please sign in to comment.