`TrigramSimiliarity` on `ArrayField`

Hi there Guys,

I have a small question regarding django’s full text search

Lets take a look at the following example

# models.py
class AnimeModel(models.Model):
   name_synonyms = ArrayField(
        # https://stackoverflow.com/questions/61206968/setting-arrayfield-to-null-or
        default=list,
        blank=True,
        null=False,
        base_field=models.CharField(max_length=1024),
    )
# views.py
name = "Naruto"
query = (
    AnimeModel.objects
        .annotate(
            similiarity=TrigramSimilarity(
                "name_synonyms", name
            )
        )
        .filter(
            similiarity__gte=0.1,
        )
        .order_by("-similiarity")
    )

Now i am getting this error

django.db.utils.ProgrammingError: function similarity(character varying[], unknown) does not exist
LINE 1: ...l". SIMILARITY...
                                                             ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

Does anyone know how to do fuzzy index lookup on ArrayField?


Django version : 4.2a1
Psycopg Version : 2.9.5

Thanks :smiley:
Heres my postgres output

Server [localhost]:
Database [postgres]: django
Port [5432]:
Username [postgres]:
Password for user postgres:
psql (15.1)
WARNING: Console code page (437) differs from Windows code page (1252)
         8-bit characters might not work correctly. See psql reference
         page "Notes for Windows users" for details.
Type "help" for help.

django=#
django=# \dx
                                     List of installed extensions
   Name    | Version |   Schema   |                            Description
-----------+---------+------------+-------------------------------------------------------------------
 btree_gin | 1.3     | public     | support for indexing common datatypes in GIN
 hstore    | 1.8     | public     | data type for storing sets of (key, value) pairs
 pg_trgm   | 1.6     | public     | text similarity measurement and index searching based on trigrams
 plpgsql   | 1.0     | pg_catalog | PL/pgSQL procedural language
 unaccent  | 1.1     | public     | text search dictionary that removes accents
(5 rows)


django=# CREATE EXTENSION pg_trgm;
ERROR:  extension "pg_trgm" already exists

Is this feature not available on postgres or is it something more django specific

Stacktrace :

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\ninja\operation.py", line 99, in run
    result = self.view_func(request, **values)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\ninja\pagination.py", line 143, in view_with_pagination 
    items = func(*args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\Desktop\CoreProject\backend\django_core\apps\api\views\anime\__init__.py", line 91, in get_anime_info
    if special_query:
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\models\query.py", line 412, in __bool__       
    self._fetch_all()
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\models\query.py", line 1881, in _fetch_all    
    self._result_cache = list(self._iterable_class(self))
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\models\query.py", line 91, in __iter__        
    results = compiler.execute_sql(
              ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\models\sql\compiler.py", line 1527, in execute_sql
    cursor.execute(sql, params)
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\debug_toolbar\panels\sql\tracking.py", line 230, in execute
    return self._record(self.cursor.execute, sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\debug_toolbar\panels\sql\tracking.py", line 154, in _record
    return method(sql, params)
           ^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\backends\utils.py", line 103, in execute      
    return super().execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\backends\utils.py", line 67, in execute       
    return self._execute_with_wrappers(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\backends\utils.py", line 80, in _execute_with_wrappers
    return executor(sql, params, many, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\backends\utils.py", line 84, in _execute      
    with self.db.wrap_database_errors:
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\utils.py", line 91, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\backends\utils.py", line 89, in _execute      
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
django.db.utils.ProgrammingError: function similarity(character varying[], unknown) does not exist
LINE 1: ...l"."theme_endings", "anime_animemodel"."updated", SIMILARITY...
                                                             ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

What is the “name” you are referencing here? Where does that come from?

1 Like

Hi there, thanks for responding.

So this full model is available at CoreProject/__init__.py at d60de2e79000fef1d119fb997eb5984faa3f11c3 · baseplate-admin/CoreProject · GitHub

I just gave a POC that can reproduce the error i am facing

Ok, so name is a field in the model.

When you are referencing a field within an expression in a query, you need to reference it using the F function.

Okay so if i modify my code like this

query = (
            AnimeModel.objects
            .annotate(
                similiarity=TrigramSimilarity(
                    F("name_synonyms"),
                    name,
                )
            )
            .filter(
                similiarity__gte=0.1,
            )
            .order_by("-similiarity")
        )

It still gives me the same error …

Traceback (most recent call last):
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\backends\utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.UndefinedFunction: function similarity(character varying[], unknown) does not exist
LINE 1: ...l"."theme_endings", "anime_animemodel"."updated", SIMILARITY...
                                                             ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\ninja\operation.py", line 99, in run
    result = self.view_func(request, **values)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\ninja\pagination.py", line 143, in view_with_pagination
    items = func(*args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\Desktop\CoreProject\backend\django_core\apps\api\views\anime\__init__.py", line 95, in get_anime_info
    if special_query:
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\models\query.py", line 412, in __bool__
    self._fetch_all()
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\models\query.py", line 1881, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\models\query.py", line 91, in __iter__
    results = compiler.execute_sql(
              ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\models\sql\compiler.py", line 1527, in execute_sql
    cursor.execute(sql, params)
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\debug_toolbar\panels\sql\tracking.py", line 230, in execute
    return self._record(self.cursor.execute, sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\debug_toolbar\panels\sql\tracking.py", line 154, in _record
    return method(sql, params)
           ^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\backends\utils.py", line 103, in execute
    return super().execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\backends\utils.py", line 67, in execute
    return self._execute_with_wrappers(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\backends\utils.py", line 80, in _execute_with_wrappers
    return executor(sql, params, many, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    with self.db.wrap_database_errors:
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\utils.py", line 91, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "C:\Users\Asus\AppData\Local\pypoetry\Cache\virtualenvs\coreproject-jIQ747rN-py3.11\Lib\site-packages\django\db\backends\utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
django.db.utils.ProgrammingError: function similarity(character varying[], unknown) does not exist
LINE 1: ...l"."theme_endings", "anime_animemodel"."updated", SIMILARITY...
                                                             ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

Which identifier did I say you needed to change? (You changed the wrong reference - or, you need to change both - I’m not sure.)

I am truly sorry that i didn’t understand your previous response… I thought you meant name as in model. I used name as another variable in my views.py code…

        name = 'naruto'
        query = (
            AnimeModel.objects
            .annotate(
                similiarity=TrigramSimilarity(
                    "name_synonyms",
                    name,
                )
            )
            .filter(
                similiarity__gte=0.1,
            )
            .order_by("-similiarity")
        )

I have refactored my original response to clarify my question

Ok, which is why I asked where name came from in that expression, and then you directed me to your model definition showing name as a field.

Anyway, from what I can see, the first parameter to TrigramSimilarity must be an “atomic” field and not an array. You’re right that the field name looks like it should be a string and not a field reference, but I don’t see anything in the docs that allow it to be used with an array field.

See PostgreSQL: Documentation: 15: F.35. pg_trgm

1 Like

You are right on this part. “Array” is not “atomic”

From the docs

Arrays are not sets; searching for specific array elements can be a sign of database misdesign. Consider using a separate table with a row for each item that would be an array element. This will be easier to search, and is likely to scale better for a large number of elements.

Tho i wish it was possible


Never the less thank you for your time and have a good day

I thought I could bypass this by constructing a subquery that is first a combined string aggregate of the ArrayField, is this not a valid strategy either? Have tried a few Raw SQL and more pythonic ORM attempts at this and returned similar errors where either SIMILARITY or the STRING_AGG itself do not exist. Thoughts appreciated!

I think this is probably a limitation of postgres itself.