Skip to content

feat: add autocomplete for search engine #586

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

Merged
merged 4 commits into from
Nov 18, 2024

Conversation

python357-1
Copy link
Collaborator

currently, the autocomplete will automatically show up when the user types something that can be autocompleted (right now, "filetype:", "mediatype:", "tag:", "tag_id:", and "path:").

filetype and mediatype: autocompletes with all filetypes/mediatypes currently registered in tagstudio, regardless of whether the current library contains any files of a type.

tag: autocompletes with all tag names, case-insensitive

tag_id: autocompletes with ids of all tags

path: autocompletes with all paths currently stored in the library

@CyanVoxel CyanVoxel added Type: Enhancement New feature or request Type: QoL A quality of life (QoL) enhancement or suggestion TagStudio: Search The TagStudio search engine Priority: Medium An issue that shouldn't be be saved for last Status: Review Needed A review of this is needed labels Nov 16, 2024
@CyanVoxel CyanVoxel added this to the Alpha v9.5 (Post-SQL) milestone Nov 16, 2024
Copy link
Member

@CyanVoxel CyanVoxel left a comment

Choose a reason for hiding this comment

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

Fantastic work! Seems to work great in my testing, I've just got a few changes/suggestions in the review.

One thing I didn't mention is the suggestion to use tag aliases to match with the main tag name, or even subtags that match with parent tags in the autocompletion, due to the current lack of these implementations on main. I would imagine this working on a priority system where actual tag names are prioritized first in the completion list, then the list of alias matches, then the list of subtag matches. I believe this is how the search results are ordered in any tag search panel, but could be mistaken. This is something we can explore later once those aspects of tags are implemented, though.

@@ -92,6 +93,8 @@
from src.qt.widgets.progress import ProgressWidget
from src.qt.widgets.thumb_renderer import ThumbRenderer

from ..core.media_types import MediaCategories
Copy link
Member

Choose a reason for hiding this comment

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

Suggestion to make this an absolute import to match the other imports:
from src.core.media_types import MediaCategories

Comment on lines 969 to 970
elif query_type == "tag_id":
completion_list = list(map(lambda x: "tag_id:" + str(x.id), self.lib.tags))
Copy link
Member

Choose a reason for hiding this comment

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

Tag IDs are intended for internal/backend usage only, so there's no need to include them in the user-facing autocomplete list

Comment on lines 973 to 976
elif query_type == "mediatype":
completion_list = list(
map(lambda x: 'mediatype:"' + x.name + '"', MediaCategories.ALL_CATEGORIES)
)
Copy link
Member

Choose a reason for hiding this comment

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

Suggestion to allow for mediatype names typed without quotes to still be recognized by the autocomplete list. Currently, typing mediatype:im will not suggest mediatype:"image" due to the lack of quotations. This would save the user the step of needing to type in a quote character before every mediatype.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I have added this, but the reason I didn't add it originally is because when you type mediatype: and scroll through the completions, you will get the section that isn't quoted and then the section that is quoted, just due to how the QCompleter works. Niche and, now that I think about it, probably not an issue, but a little quirky.

Actually now that I'm trying it, it is even less of an issue because you first have to type mediatype: (with a space) for it to fill in the completion list with all of the mediatypes, and then remove the space and scroll through.

Copy link
Member

Choose a reason for hiding this comment

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

So is there no (straightforward) way to just have the suggestions with quotations show up using QCompleter?

Copy link
Collaborator Author

@python357-1 python357-1 Nov 17, 2024

Choose a reason for hiding this comment

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

As far as I understand it, no. If you look at QCompleter.setFilterMode(), it uses Qt::MatchFlags to define how it decides what items to show from its completion list, but the description of setFilterMode() tells you that only MatchStartsWith, MatchContains, and MatchEndsWith are implemented. Using anything else just throws an error.

Because of that, the unquoted and quoted mediatypes will never show up at the same time, because the quoted ones technically start with mediatype:" and the unquoted ones start with mediatype:. My hack around that was to just always have them quoted, but that is annoying and, I'm now realizing, will not work for single quotes unless we explicitly add it.

Just as a note: we can't use MatchContains, because, for the same reason as above, the completion list needs to have strings that start with the type of query (i.e. we can't just have a list that contains photoshop, plaintext, etc. They need to be mediatype:photoshop, mediatype:plaintext, or mediatype:"photoshop", mediatype:"plaintext").

Copy link
Member

Choose a reason for hiding this comment

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

Dang, sounds like it would involve quite a bit of custom work in order to do this. Thanks for looking into this, perhaps it's something we can tackle in the future, but for now I'm content settling for Qt's limitations here if you are.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, as it stands it is pretty far from what I was envisioning, but still useful.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds like we're on the same page, works enough for this PR then 👍

Comment on lines +967 to +968
if query_type == "tag":
completion_list = list(map(lambda x: "tag:" + x.name, self.lib.tags))
Copy link
Member

Choose a reason for hiding this comment

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

I think using the tag's "display name" here might be better, as that would reduce confusion between identical tag names. It would also be nice if the tag name text was colored in the dropdown, but speaking from personal experience I don't know of a way to do that without creating a completely custom widget from scratch, so I don't expect that from you here.

For the future, I'm picturing the search bar as something a bit more abstracted from the internal search query. For tags, this could mean that the user would see a tag label widget (alongside other query text) in the search bar while internally the query can refer to it by the tag's ID, eliminating any confusion between tags with the same name.
Example:
jQuery-Tags-Input-Plugin-with-Autocomplete-Support-Mab-Tag-Input
This is a bigger undertaking than the scope of this PR though, and having the functionality here is already a great help over having no auto-completion at all.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

potentially dumb question: what is a tag's display name? its' shorthand?

Copy link
Member

Choose a reason for hiding this comment

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

I've just realized that display names didn't make the jump from JSON... If #534 doesn't implement them then they'll need to be implemented in another new PR.

Essentially, they're a version of a Tag's named combined with their first parent tag in parenthesis (with preference to that parent tag's shorthand) to aide in telling tags with the same name apart. In 9.4 they were generated and grabbed from a method rather than being stored directly, which likely contributed to them going unnoticed during the migration so far.

Copy link
Contributor

Choose a reason for hiding this comment

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

Tomorrow I'll look into getting the display names add in on #534, no point in having another PR just for that.

@CyanVoxel CyanVoxel added Status: Changes Requested Changes are quested to this and removed Status: Review Needed A review of this is needed labels Nov 16, 2024
@seakrueger
Copy link
Collaborator

seakrueger commented Nov 16, 2024

I was trying this out yesterday and this will be an great addition. A couple of thoughts/suggestions, don't feel obligated to implement or even listen to, just food for thought:

  1. An incredibly minor bug: autocomplete isn't cleared after clearing the searchbar. Searching "tag:xyz", deleting the whole query, then typing 't' brings the autocomplete box back up with "tag:abc", etc.
  2. When searching for filetype (and mediatype), only types that actually exist in the library should be shown. Right now searching "filetype:" suggests "tex, blend10, blend6, 3ds, cr3, blend1, exr" of which only one of those actually exist.
  3. Path was different behavior than what I was first expecting. The current path query is more of a filename search just with the full path shown (suggestions path:foo/bar.txt). I was expecting it to be used to find all files in a directory (suggests "path:foo/*"). Perhaps splitting and adding a filename query?
  4. I feel typing in an empty searchbar should immediately suggest the different searches filters, so that its more obvious

Anyways, great feature.

@python357-1
Copy link
Collaborator Author

I was originally going to implement it as you talk about in your second point, but I talked to Cyan and he thought it'd be better if it just showed all the options all the time. Maybe we could make that a setting later on.

Your third point is something I noticed too. I originally wanted the autocomplete to work so you could type something like "path:folder*" and it autocomplete everything in from that, but the way QCompleter works, it will not show anything if you have a "*" in the text box because it doesn't show up anywhere in the actual completions. I'm gonna have to mess with it a little more until I like it.

I will make it show the search types on an empty search bar though, because that also seems like a good solution to your first point.

@CyanVoxel
Copy link
Member

I was originally going to implement it as you talk about in your second point, but I talked to Cyan and he thought it'd be better if it just showed all the options all the time. Maybe we could make that a setting later on.

Can confirm, although I was really intending this for mediatypes and didn't notice that the behavior also applied to filetypes (my test library contains most of these filetypes). I can see arguments for both cases though - to de-clutter searches that don't need superfluous media type results, but also to keep all potential types in order to inform the user of what's possible. Ideally this could be behind a toggle, but that would rely on settings menu (which I think is open for development again now?)

@python357-1
Copy link
Collaborator Author

After I've given it another thought I will keep the path search as it is. It does, effectively, stand in as a filename search for the time being (it does let you search directories, but not well), but making it any more useful would require moving away from the current implementation and making a custom widget, which we will have to do in the future anyway for it to not be entirely text based.

@CyanVoxel
Copy link
Member

The behavior of the full array of media type suggestions only showing up after backspacing from the mediatype: feels a bit odd, but might work as a temporary measure before a settings screen is implemented to let the user choose between showing all possible mediatypes vs only whats in the library. For filetype:, I feel this could still just show you all types present in the library - especially since the additional extensions inside MediaTypes is far from comprehensive, nor can it be. Actually, could the initial behavior of mediatype: also show you only what's in the library, then expand to show all possible types after backspacing?

@python357-1
Copy link
Collaborator Author

unless you know of a better way to do it, it will be a little hacky - QCompleter doesn't give me access to the previous key the user pressed, so I will have to either:

  • check the previous query (something like if it starts with mediatype: and the length is greater than 10)
  • check if the current query is mediatype: and the current completion list is equal to only those media types currently used by the library

just to be clear, im not opposed to hacky solutions, it just wouldnt be elegant (and i can already think of some weird edge cases)

@CyanVoxel
Copy link
Member

unless you know of a better way to do it, it will be a little hacky - QCompleter doesn't give me access to the previous key the user pressed, so I will have to either:

* check the previous query (something like if it starts with `mediatype:` and the length is greater than 10)

* check if the current query is `mediatype:` and the current completion list is equal to only those media types currently used by the library

just to be clear, im not opposed to hacky solutions, it just wouldnt be elegant (and i can already think of some weird edge cases)

Ahh if it's QCompleter shenanigans holding things back then I'm good pulling this as it is. Definitely room in the future to create a more expansive and controllable system, but for this PR I think it's a great first step in that direction. Thanks again for your work on this!

@CyanVoxel CyanVoxel added Status: Mergeable The code is ready to be merged and removed Status: Changes Requested Changes are quested to this labels Nov 17, 2024
@CyanVoxel CyanVoxel merged commit bec513f into TagStudioDev:main Nov 18, 2024
5 checks passed
@CyanVoxel CyanVoxel removed the Status: Mergeable The code is ready to be merged label Nov 18, 2024
yedpodtrzitko pushed a commit to yedpodtrzitko/TagStudio that referenced this pull request Nov 19, 2024
yedpodtrzitko added a commit to yedpodtrzitko/TagStudio that referenced this pull request Nov 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Priority: Medium An issue that shouldn't be be saved for last TagStudio: Search The TagStudio search engine Type: Enhancement New feature or request Type: QoL A quality of life (QoL) enhancement or suggestion
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

4 participants