Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Address Bar Suggestions for Buff Items #8

Draft
wants to merge 31 commits into
base: master
Choose a base branch
from

Conversation

atalantus
Copy link
Contributor

@atalantus atalantus commented Mar 19, 2024

Uses the browser.omnibox API to show search suggestions for items to directly visit an item's buff page.

To use this feature, the user has to type the keyword (currently "buff") and press tab. Any text typed afterward is used to suggest CS items available on buff (courtesy of https://github.com/ModestSerhat/buff163-ids).

The best 10 suggestions are shown.

Item Suggestion Algorithm

Given a search term, return the N Buff Items that match the search term best.

The search term is divided into keywords by splitting it at every whitespace.

  • Only items that match ALL keywords are considered
  • The matching is done case-insensitive
  • The matching is done fuzzily via "Optimal string alignment distance"
  • For every keyword, only the single best match within each item name is taken into consideration
  • Every keyword match is scored based on its "quality". The quality is in the order of:
    keyword matches an entire word > keyword matches the start of a word > keyword matches > keyword matches fuzzily (based on edit distance) > keyword does not match fuzzily
  • The final score for a given item name is the sum of all best-match scores per keyword

TODOs

  • Check for performance improvements
  • Check for smarter suggestions
  • Allow explicitly searching for Vanilla knives
  • Use rich address bar suggestions
  • Add script to automatically update buff-items JSON file
  • Add Unit-Tests
  • Cleanup commit history before merge

Comment on lines 30 to 93
const keywords = text.toLowerCase().split(' ');
const suggestions = [];

for (const buffItem of Object.keys(buffItems)) {
if (keywords.every(k => buffItem.toLowerCase().includes(k))) {
suggestions.push({
deletable: false,
description: buffItem,
content: `https://buff.163.com/goods/${buffItems[buffItem]}`,
});

if (suggestions.length > 5) {
break;
}
}
}
Copy link
Contributor Author

@atalantus atalantus Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of potential performance improvements here. Both execution time and memory.

For me, this whole keyword filter took around 2-8 ms, depending on the number of keywords (whitespace in the text).

Copy link
Contributor Author

@atalantus atalantus Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So by grouping the buff-items-sorted.json under common prefixes (e.g. {"Sticker": {"Foo": 1, "Bar": 2}} instead of {"Sticker | Foo": 1, "Sticker | Bar": 2}) one can save quite a bit of memory (from 1.8 MB down to 600 KB). Will implement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, to be honest if it's 2 MB of memory or 600 KB is probably irrelevant in this context 😅. The filter speed would be more important.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, memory is not as important as speed.

Currently the result sorting may give a lot of unwanted results unfortunately though.

Take a look at these examples:
image
image

Copy link
Contributor Author

@atalantus atalantus Mar 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, yes. I've considered modifying the filter so that the keywords you type only match the start of a word. That would solve your first example. 99% of the time, when you search for something, you start typing the beginning of the word and not the middle of it anyway.

For your Graffiti Example, I think the best item order would be:
Skins < StatTrak Skins < Knives & Gloves < StatTrak Knives < Stickers < Other (e.g. Graffiti, Music Kits, Agents...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I forgot Souvenirs:

Skins < StatTrak Skins < Souvenir Skins < Knives & Gloves < StatTrak Knives < Stickers < Other (e.g. Graffiti, Music Kits, Agents...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, although this could then mitigate the results when looking for stickers, agents etc.

I personally would prefer something like a prioritization in the matching.

So for example the earlier a word is matched the higher the prio. So if the first word (e.g. Karambit as knife type) matches, it gets a higher prio then a matching later in the item name (e.g. Karambit as variant of a grafitti). Additionally we could reduce prios for partial word matches in difference to full-word matches (Exoskeleton vs. Skeleton Knife). Then just sort all found matches after the assigned prios.

Yes, that was one of the first approaches that came to my mind. However, I thought that always evaluating the full 22k different item strings would be too slow in general. That's why I implemented it with the rough "order of importance" being baked into the data itself and then just aborting after the first 6 matches.

Copy link
Contributor Author

@atalantus atalantus Mar 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I haven't tested and implemented this other approach yet, and you're right. Evaluating and scoring every item would allow for a much more intelligent search.

I will look into it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the quickest search (aborting pretty much at the beginning (for example, searching for "ak") is like 2ms.
The worst search (searching for "stattrak music kit" which are the very end of the buff-items-sorted.json) is around 22ms.

To be honest, 20-30ms still seems to be responsive enough, so maybe always evaluating all 22k items would be worthwhile.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything below 50ms is competely fine in the current web standard. Even up to 100ms can be totally justifyable.

Looking forward to seeing your results 😄

@GODrums
Copy link
Owner

GODrums commented Mar 20, 2024

A really nice feature and I'm really looking forward to using it myself and including it as one of BetterBuff's primary features!

There are a few smaller adaptations regarding the sorting of the results, but other than that it looks great.
Thank you for the great work!

@atalantus
Copy link
Contributor Author

Btw. just to explicitly mention it. I've created a "simple" node.js script under /scripts/generateBuffItemsSorted.js which fetches the latest buff-id stats from https://github.com/ModestSerhat/buff163-ids and then automatically updates the buff-items-sorted.json. So whenever new stuff is available on buff and Serhat has updated his repository, which he normally does relatively fast, just run the script and push the update to include the new items in the search suggestions.

You can run the script from the root directory via npm run update-buff-items.

@atalantus atalantus force-pushed the buff-address-bar-search branch from 034428f to 0e39fed Compare March 29, 2024 01:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants