Jump to content

Fuzzy search helper for Script Filters


Recommended Posts

Add fuzzy search to your Script Filters


This is a simple script you can add to your Script Filters to replace "Alfred filters results" with a fuzzy search algorithm.

 

https://github.com/deanishe/alfred-fuzzy

 

demo.gif 

 

How it works


Instead of calling your script directly, you call it via fuzzy.py, which caches your script's output for the duration of the user session (as long as the user is using your workflow), and filters the titles of the items emitted by your script against the user's query using a fuzzy algorithm.


Example usage


fuzzy.py only works in Script Filters, and you should run it as a bash/zsh script (i.e. with Language = /bin/bash or Language = /bin/zsh).
Instead of running your own script directly, place ./fuzzy.py in front of it.
For example, if your Script Filter script looks like this:

/usr/bin/python myscript.py

You would replace it with:

# export user query to `query` environment variable, so `fuzzy.py` can read it
export query="$1"
# or if you're using "with input as {query}"
# export query="{query}"
# call your original script via `fuzzy.py`
./fuzzy.py /usr/bin/python myscript.py


Note: Don't forget to turn off "Alfred filters results"!

 

Caveats


As the script is written in Python and uses a more complex matching algorithm, it can only handle a few thousands items at most before it becomes irritatingly sluggish (Alfred can handle many tens of thousands).


If there's interest in the script, I will rewrite it in a compiled language. My Go library uses the same algorithm, and it can comfortably handle 20K+ items.
 


You can grab a demo workflow from GitHub to see it in action.


See the GitHub repo for more information.
 

Link to comment
  • 4 months later...

I've run into a hitch.  I want to use this to search a list of categories, then again within the same workflow to search elements within a category.  But the second run is always displaying the cached results of the first.  I tried duplicating the script and running each separately but it seems to still use the same session_id and hence display the cached results instead of the results of the second run.

 

Any quick & easy ways to reset the session ID?  I suppose I could duplicate it & change the os.getpid() to something else. 

 

UPDATE:

 

I ungraciously and crudely solved the problem by

 

1. duplicating the file,

2. editing the duplicate copy

a. changing line 45 to 

SID = 'fuzzy_session_id_2'

b. changing the end of line 311 to 

str(os.getpid())+"A"

I call the original in the first run and the modified duplicate in the second.  I'm sure it could be done more gracefully/elegantly/pythonically but that approach works for me.

Edited by dfay
Link to comment
8 minutes ago, dfay said:

Any quick & easy ways to reset the session ID?

 

8 minutes ago, dfay said:

I suppose I could duplicate it & change the os.getpid() to something else

 

Wouldn't work. That's only used to set the session ID when none exists. The problem is that both Script Filters are reading the session ID from the same variable (and therefore using the same cache).

 

I've added a new session_var option. Put export session_var=filter2 (or some such) in your downstream Script Filter, so it stores its session ID in a different variable to the first one (and therefore uses a different cache).

 

1 hour ago, dfay said:

In case others need this info -- you can keep the fuzzy searching without sorting the results by deleting (or commenting out) the line 


items.sort(reverse=True)

 

 

Hmm. If you only want match/no match, without the ranking, you can do that 10x more quickly with re:

pat =  '.*' + '.*'.join(query) + '.*'
match = re.compile(pat, re.I).match

 

Link to comment
  • 3 years later...

Is this still current? I tried it on a simple script returning a JSON list of names and got the following error:

  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.py", line 382, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

my input is 'as {query}' and I have tried both 

export query="$1"
./fuzzy.py python myScript.py

and 

export query="{query}"
./fuzzy.py /usr/bin/python myScript.py

🙏 thanks!

Link to comment

very simple, it's one of yours modified :)

some of the items have emojis, perhaps that's the problem? 

It works well with the 'Alfred filters results' option, but I wanted to add fuzzy filtering. 

 

#!/usr/bin/env python
# encoding: utf-8
#
# Copyright (c) 2014 deanishe@deanishe.net
#
# MIT Licence. See http://opensource.org/licenses/MIT
#
# Created on 2014-07-03
# Modified from books, to show folders for filtering

"""Workflow Script Filter to show search results in Alfred."""

from __future__ import print_function, unicode_literals

import sys
import csv
import os
import struct
from time import time

from workflow import Workflow, ICON_INFO, ICON_WARNING
from workflow.background import run_in_background, is_running

from config import INDEX_DB

log = None



def main(wf):

    mylabels=[]
    with open("FOLDERS_ALL.tsv", "r") as fp:
        reader = csv.reader(fp, delimiter=b'\t')
        for row in reader:
            mylabel, myCount = [v.decode('utf-8') for v in row]
            toShow = mylabel
            #log.info (mylabel)
            wf.add_item(toShow, myCount + " papers", valid=True, arg=mylabel, icon='icon_folder.png')
        wf.send_feedback()


if __name__ == '__main__':
    wf = Workflow()
    log = wf.logger
    sys.exit(wf.run(main))

 

Link to comment
  • 10 months later...

Hey @deanishe,

 

When I carbon-copy your Fuzzy demo, it won't work some reason, outputting
 

Quote

Code 1: [fuzzy] .

[fuzzy] cmd=['cat', 'books.json'], query='', session_id=None

Traceback (most recent call last):

  File "./fuzzy.py", line 377, in <module>

    main()

  File "./fuzzy.py", line 363, in main

    cache = Cache(cmd)

  File "./fuzzy.py", line 279, in __init__

    self.cache_dir = os.path.join(CACHEDIR, '_fuzzy')

  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.py", line 70, in join

    elif path == '' or path.endswith('/'):

AttributeError: 'NoneType' object has no attribute 'endswith'

 

Am I missing something here? Do I need to install a local package or something?

 

Thanks for your help,

 

Marc

Link to comment
  • 1 month later...

@deanishe The fuzzy script does not seem to be working after upgrading to 12.3. This is a really important script since it powers multiple workflows for me. Anything I can do to make it work? I tried changing /usr/bin/python to /usr/bin/python3 but that didn't help. Other errors came up which I could not fix:

 

error:

raceback (most recent call last):
  File "./fuzzy.py", line 377, in <module>
    main()
  File "./fuzzy.py", line 364, in main
    fb = cache.load()
  File "./fuzzy.py", line 312, in load
    json.dump(fb, fp)
  File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/json/__init__.py", line 180, in dump
    fp.write(chunk)
TypeError: a bytes-like object is required, not 'str'
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...