오히려 좋아..

상황이 나쁘게만 흘러가는 것 같을 때 외쳐보자.. .

궁금한 마음으로 포트폴리오 보기

Web Programming/Django

[Django] Django MySQL Fulltext Index 설정

junha6316 2021. 11. 7. 16:20

오늘은 Django-mysql로 구성되어 있는 어플리케이션에서 Full text index를 설정하는 방법에 대해 알아보겠다.

 

먼저 현재 장고에서는 Fulltext 인덱스를 ORM으로 지원하지 않기 때문에 실제 쿼리를 데이터 베이스에 날려 동작해줘야한다.

아래 명령어를 통해 FullText를 설정하는 쿼리를 마이그레이션 파일로 만들어주자. app_name에 마이그레이션 파일을 생성하고자하는 app 이름을 작성해주면 된다.

python manage.py makemigrations app_name --empty --settings=app.settings.local

생성된 마이그레이션 파일은 아래와 같다. 이제 operations안에 Fulltext를 생성하는 쿼리를 작성해주자.

from django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        ('app_name', '0001_initail'),
    ]

    operations = [
    ]

첫번째 줄은 실제로 migrate를 실행하는 쿼리이고 두번째 줄은 마이그레이션을 되돌렸을 때 동작할 쿼리이다. table_name에 Fulltext Index를 생성할 테이블 이름, column_name에 인덱스를 먹일 column을 작성해주면 된다.


operations = [
	migrations.RunSQL(
            ('CREATE FULLTEXT INDEX column_fulltext_index ON table_name(column_name)',),
            ('DROP INDEX column_fulltext_index on sns_comment',),
        ),
]

이로서 우리가 원하는 column에 Fulltext가 만들어졌다. 실제 장고에서는 Fulltext 인덱스를 사용하기 위해선 search lookup을 사용해야하는데 아래 처럼 사용한다. 하지만 실제로 이런식으로 작성하면 에러가 발생한다. 

# description은 models.CharField 
Company.objects.filter(description__search='abc')
FieldError: Unsupported lookup 'search' for CharField or join on the field not permitted.

찾아보니 일단 장고에서 Native하게 search lookup을 제공하는건 postgresql 뿐이고 나머지 데이터베이스에서는 지원하지 않는다. 따라서 우리는 Search lookup을 만들어 줘야한다. Search Lookup은 Match 쿼리를 날리는데 나중에 이 부분에 대해서 다루도록 하겠다.

# core/lookups.py
from django.db import models

class Search(models.Lookup):
    
    lookup_name = 'search'

    def as_mysql(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.process_rhs(compiler, connection)
        params = lhs_params + rhs_params
        return 'MATCH (%s) AGAINST (%s IN BOOLEAN MODE)' % (lhs, rhs), params

# 생성한 lookup을 원하는 필드에 등록
models.CharField.register_lookup(Search)
models.TextField.register_lookup(Search)

파일만 생성하면 안되고 실제로 프로젝트가 로딩될 때 이 파일을 포함 시켜줘야한다.

# config/settings.py
from core.lookups import *

이제 정상적으로 동작하는 것을 볼 수 있다. 하지만 문제가 하나 있는데 이스케이프 문자의 경우 아래와 같은 에러가 발생한다.

(1064, "syntax error, unexpected '@', expecting $end")

이제 이 문제는 회사 바이 회사로 처리할 수 있다. 예를 들어 해당 이스케이프 문자를 제거해 쿼리를 날릴 수도 있고 아니면 해당문자를 쌍따옴표(double-quoted)로 감싸면 단어를 분리해서 검색하는게 아닌 문자 전체가 일치하는 행들을 찾는다. 아래처럼 구현할 수 있을 것 같다.

def _filter_escape_str(self, param):
    if str.isalnum(param):
         return param
    return f'"{param}"'

 

이번 글에서는 장고와 mysql 로 구성된 어플리케이션에서 Fulltext index를 설정하는 방법에 대해 알아보았다. 유용하게 쓰길 바랍니다