Select to view content in your preferred language

How to filter Search Widget Results to certain categories

1351
6
Jump to solution
08-31-2022 03:15 PM
RyanSutcliffe
Occasional Contributor II

I've got a very customized Search Widget in an ArcGIS JavaScript API app. I would like to have it configured so that "keyword" search results are not suggested in autosuggest. For example, if I type "Piz", I don't want to see "Pizza" as an option.

I'm okay with most other categories of results, although I might wish to filter results further.

I've currently got my searchWidget configured like so:

const searchWidget = new Search({
        includeDefaultSources: false,
        maxSuggestions: 5,
        minSuggestCharacters: 2,
        popupEnabled: false,
        sources: [
          {
            apiKey: ourAPIKey,
            filter: { geometry: bufferExtent},
            locator: new LocatorSearchSource({
              categories: ["Point Address"], //or whatever I want
              url: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer", // default
            }),
            name: "ArcGIS World Geocoding Service",
          },
          ...layerSources.filter((l) => l !== null), // other custom layer sources
        ],
        view: this.props.view,
      });
//... some stuff
const suggestions = await searchWidget.suggest("piz");

But this seems to have no effect. I've had this problem at 4.20, tried at 4.23 no difference.

 

I was assuming that supplying a string list of categories from here to the LocatorSearchSource would do the trick but it seems not. 

Anyone know what needs to be done?SearchProblem.gif

0 Kudos
1 Solution

Accepted Solutions
JoelBennett
MVP Regular Contributor

If there's no means of telling the server not to return keyword results, then the alternative would be to strip them out from the response before that response is received by the Search widget.  Have you considered using a RequestInterceptor?  These can be added via the esriConfig.request property.  Using this, you can examine the suggestions in the response, and strip out any that have isCollection set to true before the widget receives those results.  Since some results are therefore likely to be stripped out, you might also consider increasing your maxSuggestions setting.

View solution in original post

6 Replies
RyanSutcliffe
Occasional Contributor II

In older versions of ESRI ArcGIS JavaScript API, I have had success with making a fetch request to findAddressCandidates for each suggestion result using the `key` as the `magickey`. With the response for each suggestion, if the response had more than one candidate there, I would ignore it. That worked until now, where I am seeing some CORS issues with my tool refactored for API version 4.23+. 
But in any case, it wasn't a great process, because I'd have to make a large amount of requests to the geocoding service. So I'm hoping there is a better way.

 

0 Kudos
RyanSutcliffe
Occasional Contributor II

I took a second look through the API docs this morning. I was mistaking the structure of the object being passed to the Search instance. The correct usage is as per below:

 

 

 const searchWidget = new Search({
        includeDefaultSources: false,
        maxSuggestions: 5,
        minSuggestCharacters: 2,
        popupEnabled: false,
        sources: [
          new LocatorSearchSource({
            apiKey: mytoken,
            categories: [
              "Address",
              "Postal",
              "Populated Place",
              "Land Features",
              "Water Features",
            ],
            filter: { geometry: bufferExtent },
            name: "ArcGIS World Geocoding Service",
            url: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer/",
          }),

          ...layerSources.filter((l) => l !== null),
        ],
        view: this.props.view,
      });

 

 

Note that the LocatorSearchSource is up a level and itself includes the url and other properties.

 

This works in filtering values to certain categories. It seems, by excluding "POI" values I don't get keyword search results like: "Pizza", "Italian", "Groceries" etc. Its not perfect, for example I would like to still have things like business name searches return specific addresses. So I want specific POI related addresses, just not keyword results.

If anyone knows a trick where the search term "Pizza" will return specific Pizza-named business addresses but not a keyword with many results I'll mark that as the answer.

As @Noah-Sager pointed out, 'locator' should now be 'url' and is needed at 4.24+. I've updated that in this revised working sample.

0 Kudos
JoelBennett
MVP Regular Contributor

If there's no means of telling the server not to return keyword results, then the alternative would be to strip them out from the response before that response is received by the Search widget.  Have you considered using a RequestInterceptor?  These can be added via the esriConfig.request property.  Using this, you can examine the suggestions in the response, and strip out any that have isCollection set to true before the widget receives those results.  Since some results are therefore likely to be stripped out, you might also consider increasing your maxSuggestions setting.

RyanSutcliffe
Occasional Contributor II

That's a good point @JoelBennett
I just looked into it now-- it turns out that when you use the `.suggest` method on a Search class instance, the result data structure you get back is different than if you made a raw fetch to the API url. So `.suggest` returns an object with a results property that is a SuggestResponse object. You can see that in there, there is no isCollection property returned!
If I look at what actual request is sent over the network I can see that it is still a suggest request under the hood. eg:
https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest
Which has the `isCollection` property that I need. It just doesn't get exposed in the API.

That's too bad because that would be a lot easier. I guess that is why you are proposing using the RequestInteceptor approach, where you can modify any esriRequest right at the network level. I guess I could add logic there, although I am a bit wary of inserting logic that fires after every esri-based network request.
Not sure if I'll give this a whirl myself or just drop support for POI results completely for my own use case.

The other solution that this brings up for me (or others who really just want the business logic) is that I might consider dropping the Search class entirely. I've got a completely customized UX and a component with its own methods and could probably just wire in raw esriRequest calls which would get me the data structure with isCollection that I need. Not sure, but might not really need the full class for my use case.

I'm going to accept this as the best solution unless someone pipes in with a better approach.

0 Kudos
JoelBennett
MVP Regular Contributor

After looking into this a little further, I've found the Search widget calls the locator.suggestLocations method, which returns an array of SuggestionResult objects that differ slightly than the SuggestResult objects you mentioned above.  These SuggestionResult objects do contain the isCollection property, but that information gets lost in esri/widgets/Search/support/locatorUtils when they get transformed into SuggestResult objects.

@Noah-Sager any chance that isCollection values can get carried over into SuggestResult?

0 Kudos
Noah-Sager
Esri Regular Contributor

Hi @RyanSutcliffe, have you tried with "Address" instead of "Point Address"? I'm wondering if you need to use a Category Level 1 property instead of a Category Level 2: category: https://developers.arcgis.com/rest/geocode/api-reference/geocoding-category-filtering.htm

Also, note that Locator is no longer supported for the Search widget sources, you can use the `url` property instead: https://developers.arcgis.com/javascript/latest/breaking-changes/#424