본문 바로가기

Dev/web

python, django,haystack, elasticsearch 장고 엘라스틱서치 연동 - 3(haystack 검색)

자주쓰는 커맨드 모음


인덱스 목록 보기

curl -XGET '127.0.0.1:9200/_cat/indices?v&pretty'


인덱스 삭제

curl -XDELETE '127.0.0.1:9200/test?pretty'


인덱스 설정+내용 조회

curl -XGET '127.0.0.1:9200/test/?pretty'


인덱스 다큐먼트 조회

curl -XGET '127.0.0.1:9200/test/_search?pretty'



curl -XGET '127.0.0.1:9200/korean/_analyze?analyzer=ngram_analyzer&text=understand&pretty'


내 인덱스에 애널라이저 제대로 적용됐는지 확인하기

 curl -XGET '127.0.0.1:9200/korean/modelresult/blog.post.3/_termvector?fields=text&pretty'


메모: python3 manage.py rebuild_index

        python3 manage.py update_index


python, django,haystack, elasticsearch 장고 엘라스틱서치 연동 - 3(haystack 커스텀 작업)


헤이스택은 진짜 좋다

Haystack의 엘라스틱 백엔드지원이 오직 default setting configuration 만 지원한다는 것을 알기전엔 말이다.(헤이스택소스)

소스를 보면 디폴드 세팅이 보인다.ㅠㅠ 이거를 한글 형태소 분석기로 재설정을 해야되는데 그건 오버헤드가 넘 많음 .. 

나중에 이거 보고 해볼꺼임 (그리고 그이후에는 자동 최신화 ) 해봐야지


일단 검색 창이나 만들어 보도록 하자


.html

<form action="" method="get">
<input type="text" name="q" value="{{ q }}" />
<input type="submit" value="검색" class = "btn btn-primary"/>
</form>

뷰에서는

def post_list(request):
q = request.GET.get('q', '')
results = SearchQuerySet().models(Post).filter(content=q)
for t in results:
print(t.text)

검색한 내용 전체내용 콘솔에 찍히게 만들었는데


문제가 있네.

내 세팅은 이렇게 분석기랑 잘 세팅되어있는데 결과를 보면 ..

 "settings" : {
      "index" : {
        "number_of_shards" : "5",
        "provided_name" : "korean",
        "creation_date" : "1509414260664",
        "analysis" : {
          "filter" : {
            "haystack_ngram" : {
              "type" : "nGram",
              "min_gram" : "3",
              "max_gram" : "15"
            },
            "haystack_edgengram" : {
              "type" : "edgeNGram",
              "min_gram" : "2",
              "max_gram" : "15"
            }
          },
          "analyzer" : {
            "edgengram_analyzer" : {
              "filter" : [
                "haystack_edgengram",
                "lowercase"
              ],
              "type" : "custom",
              "tokenizer" : "standard"
            },
            "ngram_analyzer" : {
              "filter" : [
                "haystack_ngram",
                "lowercase"
              ],
              "type" : "custom",
              "tokenizer" : "standard"
            }
          },
          "tokenizer" : {
            "haystack_edgengram_tokenizer" : {
              "min_gram" : "2",
              "side" : "front",
              "type" : "edgeNGram",
              "max_gram" : "15"
            },
            "haystack_ngram_tokenizer" : {
              "type" : "nGram",
              "min_gram" : "3",
              "max_gram" : "15"
            }
          }
        },


내 세팅은 이렇게 분석기랑 잘 세팅되어있는데 결과를 보면 .. 토큰분석이 안되어있음
curl -XGET '127.0.0.1:9200/korean/modelresult/blog.post.8/_termvector?fields=text&pretty'
{
  "_index" : "korean",
  "_type" : "modelresult",
  "_id" : "blog.post.8",
  "_version" : 1,
  "found" : true,
  "took" : 0,
  "term_vectors" : {
    "text" : {
      "field_statistics" : {
        "sum_doc_freq" : 13,
        "doc_count" : 2,
        "sum_ttf" : 13
      },
      "terms" : {
        "**github**" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 2,
              "start_offset" : 9,
              "end_offset" : 15
            }
          ]
        },
        "**haystack**" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 4,
              "start_offset" : 20,
              "end_offset" : 28
            }
          ]
        },
        "**hello**" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 1,
              "start_offset" : 3,
              "end_offset" : 8
            }
          ]
        },
   **"m"** : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 1
            }
          ]
        }
      }
    }
  }
}

hello haystack and elastic search를 하면

[he,hell,hello,hay,hays,haystack,elas,elastic,se,sear,search] 이런식으로 되야되는데

걍[hello, haystack, and, elastic,search] 만 됨 ㅋㅋ 이러면 왜 분석기써가면서 전문검색을 하는지 의미가 없는거 아닌가?


커스텀 분석기를 등록해야지 작동하는함

하 .. 진짜 손많이가네  (나중에추가된글: 알고보니 맵핑구조를 안했었음;; 하여튼 덕분에 한글분석기도 적용함)


일단 라이브러리 수정할려면 fork떠서 수정본으로 작업하지만 걍 수정해보겠음

파이참에 setting-interpreter에 들어가 django-haystack-elasticsearch5 위치를 확인

home/austinkoma/.local/library/python3.5/site-package 에 있는걸 확인


파일을 까보니깐 간단함 왜냐면 django-haystack을 따로 상속받기 때문

하면튼 나는 django-haystack-elasticsearch5를 수정하려고 들어왔음

맨밑에 아래 코드 추가함

/home/austinkoma/.local/lib/python3.5/site-packages/haystack_elasticsearch5


from haystack.fields import CharField as BaseCharField


class ConfigurableFieldMixin(object):
def __init__(self, **kwargs):
self.analyzer = kwargs.pop('analyzer', None)
super(ConfigurableFieldMixin, self).__init__(**kwargs)

class CharField(ConfigurableFieldMixin, BaseCharField):
pass

class ConfigurableElasticBackend(Elasticsearch5SearchBackend):
DEFAULT_ANALYZER = "snowball"

def __init__(self, connection_alias, **connection_options):
super(ConfigurableElasticBackend, self).__init__(
connection_alias, **connection_options)
user_settings = getattr(settings, 'ELASTICSEARCH_INDEX_SETTINGS')
if user_settings:
setattr(self, 'DEFAULT_SETTINGS', user_settings)

def build_schema(self, fields):
content_field_name, mapping = super(ConfigurableElasticBackend,
self).build_schema(fields)

for field_name, field_class in fields.items():
field_mapping = mapping[field_class.index_fieldname]

if field_mapping['type'] == 'string' and field_class.indexed:
if not hasattr(field_class, 'facet_for') and not \
field_class.field_type in ('ngram', 'edge_ngram'):
field_mapping['analyzer'] = getattr(field_class, 'analyzer',
self.DEFAULT_ANALYZER)
mapping.update({field_class.index_fieldname: field_mapping})
return (content_field_name, mapping)

그리고 마지막에

class Elasticsearch5SearchEngine(BaseEngine):
backend = ConfigurableElasticBackend
query = Elasticsearch5SearchQuery

이것도 해당 클래스 이름 맞춰주고

설정에는

HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack_elasticsearch5.Elasticsearch5SearchEngine',
'URL': 'http://127.0.0.1:9200/',
'INDEX_NAME': 'korean',
'TYPE_NAME' : 'model',
'INCLUDE_SPELLING': True,
}
}

ELASTICSEARCH_DEFAULT_ANALYZER = 'korean_index'


ELASTICSEARCH_INDEX_SETTINGS = {
'settings': {
"analysis": {
"analyzer": {
"korean_index": {
"type": "custom",
"tokenizer": "mecab_ko_standard_tokenizer"
},
"korean_query": {
"type": "custom",
"tokenizer": "korean_query_tokenizer"
},
"ngram_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": ["haystack_ngram", "lowercase"]
},
"edgengram_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": ["haystack_edgengram", "lowercase"]
}
},
"tokenizer": {
"korean_query_tokenizer": {
"type": "mecab_ko_standard_tokenizer",
                    "min_token_length" : 1,

"compound_noun_min_length": 100
},
"haystack_ngram_tokenizer": {
"type": "nGram",
"min_gram": 3,
"max_gram": 15,
},
"haystack_edgengram_tokenizer": {
"type": "edgeNGram",
"min_gram": 2,
"max_gram": 15,
"side": "front"
}
},
"filter": {
"haystack_ngram": {
"type": "nGram",
"min_gram": 3,
"max_gram": 15
},
"haystack_edgengram": {
"type": "edgeNGram",
"min_gram": 2,
"max_gram": 15
}
}
}
},
"mappings": {
"model": {
"properties": {
"text": {
"type": "string",
"analyzer": "korean_index",
"analyzer": "korean_query",
"analyzer": "edgengram_analyzer",
"analyzer": "ngram_analyzer",

}
}
}
}
}

이렇게 해줌  rm 그리고 python3 manage.py rebuild_index 해줘야함

그럼 검색도 됨

근데 새로운값 들어올때마다  python3 manage.py update_index 또는 python3 manage.py rebuild_index 하는거 오바임


HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

이거쓰면 해결.

http://django-haystack.readthedocs.io/en/master/signal_processors.html(공식문서) 참고해서~

헤이스틱을 사용할때 디비에 새로운값 들어오면 엘라스틱서치와 (자동)동기화하는 문제는 어려운 문제임.

빠르게 update_index 명령어를 입력해도 검색엔진과 저장소 간 지연은 있을수밖에 없음


방법은 장고기능인 models.db.signals.post_save&models.db.signals.post_delete 를 이용해서 동기화 하면 됨

헤이스택에서는 signalProcessor라는게 있음 이건 리스너같은건데 장고에서 변경된게 있으면 리스너처럼 캐치함.