When there are thousands of choices in a menu (like assigning a task to someone, when there's thousands of people in your org), you need to have a searchable select menu that dynamically loads data as the user searches (since including 1,000 or more choices statically in the card/data isn't a good option).
Status: Proposal Approved

isMultiSelect support. graph.microsoft.com/users dataset). If the host hasn't registered a matching dataset, the request will be sent to the backend."isMultiSelect": true and "style": "compact" where possible| Property | Type | Required | Description |
| -------------- | ------------ | -------- | ---------------------------------------------------------------- |
| choices.data | Data.Query | No | Enables dynamic autocomplete as the user types, by fetching a remote set of choices from a backend |
Data.Query includes the metadata necessary to fetch remote data.
| Property | Type | Required | Description |
| --------- | -------- | -------- | --------------------------------------------------- |
| dataset | string | Yes | The type of data that should be fetched dynamically |
{
"type": "Input.ChoiceSet",
"id": "selectedUser",
"choices": [
{ "title": "Static 1", "value": "Static 1" }
],
"choices.data": {
"type": "Data.Query",
"dataset": "graph.microsoft.com/users"
}
}
As the user types, the renderer will create a JSON object that includes all the properties from the Data.Query, along with what the user has typed, plus any additional options such as the current skip/count and max results to be returned.
{
"type": "Data.Query",
"dataset": "graph.microsoft.com/users",
"value": "<VALUE-AS-TYPED-BY-USER>",
"count": 25,
"skip: 0
}
Universal Action Model support:
Out of the box we will include support for the
The format will be as follows:
{
"type": "invoke",
"name": "adaptivecard/action",
"value": {
"action": {
"type": "Data.Query",
"dataset": "graph.microsoft.com/users",
"value": "<VALUE-AS-TYPED-BY-USER>"
}
}
choicesThe backend will inspect the Data.Query to determine what type of data should be fetched, and what the user has currently typed.
E.g., if the user had typed "Ma" it would return something like:
[
{ "title": "Matt", "value": "1" },
{ "title": "Mark", "value": "2" }
{ "title": "Mack", "value": "3" }
{ "title": "May", "value": "4" }
]
The Input choices property is set to the array returned by the backend and renders as specified by the isMultiSelect and style properties.
ProductBoard auto-generated feature
@matthidinger my suggestions to this spec are parked here https://github.com/siduppal/general/blob/master/AutoComplete-Proposal.md
@siduppal here is the latest, taking into consideration the group chat we had separately.
We would extend the existing Input.ChoiceSet as follows:
{
"type": "Input.ChoiceSet",
"id": "selectedUser",
"choices": [
{ "title": "Matt", "value": "1" },
{ "title": "David", "value": "2" }
],
"value": "2",
"choicesSource": {
"type": "Data.Query",
"dataSet": "<name of data set to fetch from>",
"searchField": "<name of field in the dataset to match what the user types to>
}
}
The contract here is:
choices property holds the default, built-in choices that can be shown immediately to the end userchoicesSource is specified, it MUST be an object of type "Data.Query" (see notes below) and its dataSet property must be specifieddataSet is set to "Directory"), it can do thatQuestions:
searchField property really necessary? Are there scenarios where a Bot might search a single dataset against different fields?The response to a "Data.Query" MUST be an object that includes a data property of type array. Each item in the array MUST have at least a title and a value property. Example:
{
"data": [
{
"title": "David Claux",
"value": "[email protected]",
... optional fields
},
{
"title": "Sid Uppal",
"value": "[email protected]",
... optional fields
}
]
}
As the client receives this response, it dynamically populates the list of choices with the data from the response.
"Data.Query" can be mapped to the existing composeExtension/query activity. Example:
{
"type": "Input.ChoiceSet",
"id": "selectedUser",
"choices": [
{ "title": "Matt", "value": "1" },
{ "title": "David", "value": "2" }
],
"value": "2",
"choicesSource": {
"type": "Data.Query",
"dataSet": "Directory",
"searchField": "userName"
}
}
The above "Data.Query" translates into this composeExtension/query activity:
{
"type": "invoke",
"name": "composeExtension/query",
"value": {
"commandId": "Directory",
"parameters": [
{
"name": "userName",
"value": "<VALUE-AS-TYPED-BY-USER>"
}
],
"queryOptions": {
"skip": 0,
"count": 25
}
}
}
Pros:
composeExtension/query already exists and is already documented. We'd be reusing a model Bot developers are already familiar with.Not pros but also not cons:
commandId property is used to specify the "dataSet" - probably not a big deal?queryOptions.skip would always be 0. That wouldn't pose any problem.Cons:
We could define a model that fits better with UB/ACv2:
{
"type": "invoke",
"name": "adatptiveCard/action",
"value": {
"action": {
"type": "Data.Query",
"dataSet": "Directory",
"searchField": "userName"
}
"options": {
"skip": 0,
"count": 25,
"value": "<VALUE-AS-TYPED-BY-USER>"
}
}
}
The response format would be the same, but encapsulated into a standard ACv2 response shape.
Pros:
Not pros but also not cons:
Cons:
@dclaux Couple notes on your proposal:
choices and dynamicChoices, instead of choicesSource maybe?Data.Query is a completely new type, right? If so, would it make sense to keep it consistent with the other actions and name it Action.Query? This way we can also define a consistent way to do query in other locations.searchField property might be a little too verbose, since the dataset is a custom variable anyways the bot developer/integration could support Directory/username,email to specify searchfields, or just define Directory to search in the default way.@JamyDev thanks for your comments. Let me try to address:
dynamicChoices is not out of the question. The reason for choicesSource though is that it is consistent with the way XAML does thingssearchField is that it maps to the way compose extensions are done already. Also, I have a question in the proposal asking whether or not we believe there is a scenario where two queries might target the same data set but search against different fields - I think answering that could allow us to simply not support the search field concept at least to start with, as you suggest in your last sentence. One thing however that I happen to have a strong opinion on (might be just me) is I would really prefer to avoid conflating the dataSet property with other things. I'd really rather keep things separate and not introduce a convention for parsing the dataSet property into its various componentsThanks @dclaux. Overall, the proposal looks good to me. Few queries I have on the proposal -
searchField - In case of directory search, as I understand, the search matching is fuzzy and not really a single field that's searched. Is there a strong need for searchField, since it's being specified by the bot anyway in the first place? Instead can we have some parameters (like we have verb and data in ACv2) that could be passed for bot to be able to respond?Data.Query is a new model (as is implied with Data prefix) with completely new response type and response types for ACv2 such as Adaptive Card and message also do not apply here. Should we have a different invoke name like adaptiveCard/data? isMultiSelect and other options available in Input.ChoiceSet for autocomplete scenarios as well.dataSet property - some of these dataSet choices could have a different UX - such as showing the profile picture, email/title in case of "Directory" etc. See screenshot below. How do we recommend hosts to handle such cases?
dataSet without Data.Query fallback? In case a client does not support a specific dataset natively, it could only use the static choices specified in the card and wouldn't query the bot@sowrabh thanks for your feedback. Let me try to address:
I asked this very question in my write up: is searchField really necessary? Personally I do not think so. I am fine excluding it for now.
The ACv2 model we adopted for activities is the activity name is always adaptiveCard/action and the activity's value property must be an object with an 'action` property set to a copy of what was found in the card:
{
"type": "invoke",
"name": "adatptiveCard/action",
"value": {
"action": {
"type": "Data.Query",
"dataSet": "Directory",
"searchField": "userName"
}
"options": {
"skip": 0,
"count": 25,
"value": "<VALUE-AS-TYPED-BY-USER>"
}
}
}
So a developer would know what type of action to execute by looking at activity.value.action and not at activity.name. What would be the value add of introducing a new adaptiveCard/data activity name?
Supporting isMultiselect considerably complicates the feature in terms on the interaction model. Unless there is a strong need for it, I would rather table it for now.
I believe we've discussed this to some extent aside from this issue, and the conclusion was that it was fine not to support visual customization for now. Hosts that really need/want specific visuals in certain cases can piggyback on the value of the dataSet property and display a custom control.
Thanks @dclaux.
For scenarios like Approvals, the average number of approvers is 2 as per their data, so multiselect becomes an important part of the scenario that we need to support for People Pickers and its a strong P1 @dclaux.
composeExtension/querycomposeExtension/query was a bit too strange for this scenario, so we should get broader feedback and explore either reusing the ACv2 invoke activity as-is, or create a new similar one that specifically matches this scenario.choices.data made the most sense and allows us to apply a similar pattern to other properties that may get dynamic-querying support in the future. isMultiSelect supportSome brainstorming ideas for the property name:
choicesSourcedynamicChoiceschoices.data {
"type": "Input.ChoiceSet",
"id": "selectedUser",
"choices": [
{ "title": "Static 1", "value": "Static 1" }
],
"choices.data": {
...
}
}
Previously in this issue we've discussed two distinct requirement for fetching data
Other schema techniques to support this would be to use the Data.<QUERY-TYPE> naming convention. Or a similar model to Adaptive Components (#4761) where the name property could be namespaced (e.g., graph.microsoft.com/users.
To fetch from a Bot, we could use something like:
{
"type": "Input.ChoiceSet",
"id": "selectedUser",
"choices": [
{ "title": "Static 1", "value": "Static 1" }
],
"choices.data": {
"type": "Data.Execute",
"verb": "findCities"
}
}
To fetch from a well-known source, that Hosts would be able to provide, we could do:
{
"type": "Input.ChoiceSet",
"id": "selectedUser",
"choices": [
{ "title": "Static 1", "value": "Static 1" }
],
"choices.data": {
"type": "Data.MicrosoftGraph.Users"
}
}
OR
{
"type": "Data",
"name": "graph.microsoft.com/users"
}
Reuse same thing from Action.Execute
{
"type": "invoke",
"name": "adaptivecard/action",
"value": {
"action": {
"type": "Data.Query",
"verb": "findUser"
},
"options": {
"skip": 0,
"count": 25,
"value": "<VALUE-AS-TYPED-BY-USER>"
}
}
}
Slightly tweak the activity to match the scenario exactly.
{
"type": "invoke",
"name": "adaptivecard",
"value": {
"query": {
"type": "Data.Query",
"verb": "findUsers",
"keyword": "<WHAT-USER-TYPED>"
}
}
}
{
"type": "Input.ChoiceSet",
"id": "selectedUser",
"choices": [
{ "title": "Static 1", "value": "Static 1" }
],
"isMultiSelect": true,
"choices.data": {
...
}
}
I wasn't able to attend the meeting (last week?) so I would like to share my opinion now that's I've been able to review the above.
verb property, then we need to stick to Action.Execute.adaptiveCard/action. If we do introduce a prefix other than "Action." then I do think that using simply "adaptivecard" as the activity name makes sense, but then we should also consider using that in the context of Action.Executedata property. In this case I think options should follow the same model. That would allow us to define this model as part of the AC spec, and every implementation would follow that same model (Matt this is something you said you'd like)So all in all I think this would make more sense:
{
"type": "Input.ChoiceSet",
"id": "selectedUser",
"choices": [
{ "title": "Static 1", "value": "Static 1" }
],
"choices.data": {
"type": "Action.DataQuery",
"dataset": "cities"
}
}
And the activity protocol - note options is part of the action object:
{
"type": "invoke",
"name": "adaptiveCard/action",
"value": {
"action": {
"type": "Action.DataQuery",
"dataset": "cities",
"options": {
"skip": 0,
"count": 25,
"value": "<VALUE-AS-TYPED-BY-USER>"
}
}
}
Meeting 10/28
Directory dataset is used.isMultiSelect will be supported for "people picker" out of the box, and we will work hard to ensure it works with other types of choices, pending investigation on the cost to implemented on each platform. Without this it will cause confusion/fragmentation if the property only works properly with the PeoplePicker data set vs a custom data set, so hopefully we'll be able to figure something out hereUpdated the root issue with the final proposal and set to approved.
@matthidinger under the Data.Query definition in the root issue, it looks like the dataset is defined as all lowercase, but in the examples dataSet is written in camelCase. Am I right to assume the camelCase version is the correct one?
@JamyDev I did some quick internet searching and it appears that Dataset is all one word lowercase. So I will update the spec accordingly. @dclaux sound good?
@matthidinger yes, one word all lowercase.
Another question came up:
Should the static choices be ignored if we receive a set back from the server, or should they be merged. Based on the line The Input choices property is set to the array returned by the backend I am guessing replace, and only render the static choices if the server fails to reply?
@JamyDev yes the static choices should only be used if the backend doesn't respond. @matthidinger you OK with that?