flexsearch icon indicating copy to clipboard operation
flexsearch copied to clipboard

Result highlighting

Open peterbe opened this issue 5 years ago • 15 comments

Perhaps this is a question, if so I couldn't find anything in the documentation searching for "highlighting" or "highlight".

In the awesome auto-complete demo you can type in marblles and it finds ...All the Marbles Screen Shot 2019-06-27 at 12 17 27 AM

What I'd love to be able to do is display:

<div class="search-result">
  ...All the <mark>Marbles</mark>
</div>

If it wasn't for the smarts within FlexSearch I could achieve this with a simple regex. Or, if the result object from a search could give me the clues (i.e. that the word "Marbles" matched the input "marblles") then I could use that to do a simple regex.

peterbe avatar Jun 27 '19 04:06 peterbe

@ts-thomas I've now written a simple highlighter based on splitting the search string and the string matched. It looks like this: Screen Shot 2019-08-31 at 10 06 31 PM

But the results match Embed if I "typoed" embbed too. But then, my regex solution breaks: Screen Shot 2019-08-31 at 10 08 07 PM

If only Flexsearch could have returned, as part of the result, a clue that "embbed" maps to "embed". I.e. "These are the results and they assumed the following map {"embbed": "embed"}". Then I could use that mapping to do my highlighting based on the values of that map. Would that be possible?

peterbe avatar Sep 01 '19 02:09 peterbe

Peter can you please share your code? I'm also looking for an highlighter.

kabouter avatar Sep 03 '19 10:09 kabouter

You can easily use regex to wrap the matching keyword with an tag for example and highlight using background-color: yellow. For example:

  const keyword = 'audio';
  const content = '&lt;audio&gt;: The Embed Audio element'

  // Make sure there's no regex special chars
  const sanitizedKeyword = keyword.replace(/\W/g, '');

  // Build regex
  const regexForContent = new RegExp(sanitizedKeyword, 'gi');

  // Replace content where regex matches
  content = content.replace(regexForContent, '<em>$&</em>');

  // Content would be
  &lt;<em>audio</em>&gt;: The Embed <em>Audio</em> element

SystemR avatar Sep 11 '19 21:09 SystemR

I probably found a way to extend the new language processing pipeline to give support for custom "wrap". At least it would be the best place to apply these logic without storing additional data/marker within the index.

ts-thomas avatar Nov 18 '19 18:11 ts-thomas

Would be awesome to get matching indices or at least strings in response to build up a highlighter.

evenfrost avatar Dec 16 '19 10:12 evenfrost

Do we have an example of how to use such pipelines?

artch avatar Jan 09 '20 12:01 artch

The issue with @SystemR's solution is that you cannot use such a simple search & replace when you have html tags in your content. For example, when your content is What is <strong>going</strong> on here? an your keyword is on, you will end up with What is <str<em>on</em>g>going</str<em>on</em>g> <em>on</em> here?.

I would suggest to use something like mark.js. It's super easy, for example with the jQuery plugin:

$(".content").unmark({
  done: function() {
     $(".content").mark(keyword);
  }
});

chovyy avatar Dec 09 '20 10:12 chovyy

I probably found a way to extend the new language processing pipeline to give support for custom "wrap". At least it would be the best place to apply these logic without storing additional data/marker within the index.

@ts-thomas The way some other libraries (like lunr.js and fuse.js) achieve this is by returning the index of the beginning and then the end of the match (which you usually need to whitelist when building the index). This gives more flexibility with what you want to do with that information than the wrap.

This is currently the only thing that is preventing us from moving our projects' search to flexsearch, as our indexes are huge docstrings, which we need to trim. The lib is all we need when it comes to speed + memory usage (as well as being maintained :sweat_smile:)

If you could point me in the right direction, I could try and implement it with my limited JS knowledge :)

davfsa avatar Jun 06 '21 18:06 davfsa

I probably found a way to extend the new language processing pipeline to give support for custom "wrap". At least it would be the best place to apply these logic without storing additional data/marker within the index.

Hi @ts-thomas ! Thank you sooooo much for your work on 0.7.0. I'm eager to explore it more. But do you think there's a connection with solving this issue using 0.7.0?

peterbe avatar Jun 16 '21 13:06 peterbe

Just want to give my +1 here.

Using mark.js did work for me, but it feels like a workaround, because it can only mark already created DOM elements. It would be better to already know where and how to highlight, then you can create the DOM elements with tags already included.

If the search results already contain the matches (like in fuse.js) I imagine that the performance could be much better.

Fannon avatar Aug 05 '21 08:08 Fannon

Well, an integrated solution would definitely be the best.

chovyy avatar Aug 05 '21 12:08 chovyy

I agree this would be very useful

rathboma avatar Sep 04 '21 02:09 rathboma

FYI here's the workaround I'm using on Beekeeper Studio using regexes:

      const regex = new RegExp(this.searchTerm.split(/\s+/).filter((i) => i?.length).join("|"), 'gi')
      const result = text.replace(regex, (match) => `<strong>${match}</strong>`)

It works great!

quicksearch

rathboma avatar Sep 14 '21 16:09 rathboma

I probably found a way to extend the new language processing pipeline to give support for custom "wrap". At least it would be the best place to apply these logic without storing additional data/marker within the index.

Hi @ts-thomas Now that 0.7.x is out. Is there any news on this front?

You mentioned "custom wrap" but I see no mention of it in the README.

To step back a bit, in the original post of this issue, I mentioned it would be nice to get find out which words, after suggestion-correction, were matched. I.e. I searched for marblles and it matched on Marbles. What about the simpler case. If I searched for SEARCH and a document is matched because it contain this is a search solution. Here it would be nice know that it matched on line 1, column 1-16. Or, if I use the OR operator and someone searched for SEARCH BLABLA it would be nice that SEARCH matched something but BLABLA didn't.

peterbe avatar Oct 21 '21 17:10 peterbe

Any further news?

uxiew avatar Sep 27 '23 07:09 uxiew