TarekRaafat / autoComplete.js

Simple autocomplete pure vanilla Javascript library.
https://tarekraafat.github.io/autoComplete.js
Apache License 2.0
3.93k stars 236 forks source link

Suppress searchEngine with async data function is exhaustive #284

Open njaard opened 2 years ago

njaard commented 2 years ago

Is your feature request related to a problem? Please describe. When I have a src: async ... that makes a request to my backend, my backend will provide search results that are exhaustive. By exhaustive, I mean that they by definition should show in the results list, even though they may not contain a substring of the query string. Sometimes, the search results look nothing like the query string.

Because those search results look nothing like the query string, the searchEngine will reject it as a candidate, which means it doesn't show up in the results.

Thoroughly Describe the solution you'd like

I think the most conclusive way to answer this is that the backend should be able to also give the highlighted parts to return, and then the searchEngine is suppressed.

For example, let's say I search for "Mesa, AZ". The backend returns "Mesa, Arizona" and it also indicates that the highlighted substring is "Mesa, " (it shouldn't be as integer offsets, because then there's an ambiguity if it's unicode codepoints, bytes, etc).

A clear and concise description of what you want to happen.

Please provide a few use cases for this feature

  1. Let the backend be canonical
  2. Search for things that don't actually match by string, for example, you search for "Elevator" and you get "Lift"

Please Describe alternatives you've considered Make searchEngine just return the shortest length string, but that would look kinda weird and I don't really understand the searchEngine API as documented well enough.

folknor commented 2 years ago

It's kind of hard to parse what you want to accomplish, but I'm 90% certain that it's entirely possible already.

.searchEngine can be set to your own function, the signature is (query: string, record: any), so for example:

new autoComplete({
    searchEngine: (q, r) => return r.propertythatcontainsmatchingtext,
})

Whatever you return from the search engine is what will be displayed in the dropdown by default. You can of course change this with the resultItem.element setting, etc.

searchEngine is invoked either (a) once per result returned from data.src if data.keys is not set, or (b) once per key per result if data.keys is set.

The record argument is either (a) each array index returned from data.src if data.keys is not set, or (b) data.src[...][key] for each data.keys entry.

Returning null or false or undefined from searchEngine makes autoComplete.js ignore that data entry.

maevag commented 2 years ago

Hi, I am facing the same issue right now. And I also would like to have the results highlighted. With the solution above, matching results are not highlighted at all.

maevag commented 2 years ago

I came up with this solution :

searchEngine: (query, record) =>  record.includes(query)
          ? autoComplete.search(query, record, { highlight: true})
          : record
StephanU commented 2 years ago

I have a similiar situation. In 'data.src' I am requesting an API which performs a full text search in documents, the result of a search is the list of document names containing the searched keyword. What works for me is overwriting the search engine and directly returning the found record(s):

searchEngine: (query, record) => {
  return record
}
jules-w2 commented 1 year ago

I propose this solution.

It works well for me with multiple keywords and the highlight feature. I wrap with outside my loop to avoid problems if I have a string with 'mark' in my keywords.

searchEngine: function (query, record){
 query.replace(/\s+/g,' ').trim().split(" ").forEach(function (substring){
    var regex1 = RegExp(substring, "i")
      if(regex1.test(record)){
        record = record.replace(regex1 ,'!*$&*!');
      }
    })
    return record.replaceAll("!*", "<mark>").replaceAll("*!", "</mark>")
}
echolet commented 1 year ago

To display all records returned by my API yet highlighting those that do contain the query, I used:

searchEngine: (query, record) => autoComplete.search(query, record, { highlight: true }) || record