1. 기본 세팅

엘라스틱 서치 설치 : https://www.elastic.co/kr/downloads/elasticsearch

한글 형태소 분석기 노리 설치: https://www.elastic.co/kr/blog/nori-the-official-elasticsearch-plugin-for-korean-language-analysis

파이썬3 엘라스틱서치 패키지 설치: https://elasticsearch-py.readthedocs.io/en/master/

2. 파이썬 Elastic Search 세팅

아래는 위에서 설치 받은 한글 형태소 분석기인 nori를 특정 필드에 적용도록 하는 세팅 방법 입니다.

from elasticsearch import Elasticsearch

# es 실행 기본포트: 9200, 기본 ip: 루프백 아이피 - 127.0.0.1 (localhost)
es = Elasticsearch()

# test-index 인덱스가 이미 사용하고 있을 시, 삭제
es.indices.delete(index='test-index', ignore=[400, 404])
body = {
    "settings": {
        "index": {
            "analysis": {
				
                "tokenizer": {
                    "nori_tokenizer": {
                        "type": "nori_tokenizer",
                    },
                },
                "analyzer": {
					# nori 분석기 설정
                    "nori_korean": {
                        "type": "custom",
                        "tokenizer": "nori_tokenizer"
                    },
                }
            }
        }
    },
    "mappings": {
        "goods": {
            "properties": {
                "name": {
                    "type": "text",
					# name에 nori 형태소 분석기 설정
                    "analyzer": "nori_korean",
                },
                "description": {
                    "type": "text",
                },
                "name_eng": {
                    "type": "text"
                },
                "pid": {
                    "type": "integer"
                },
            }
        }
    }
}
es.indices.create(index='test-index', body=body)
# 테스트 데이터
goods = [
    {
        "name": '주름원피스',
        "name_eng": "Pleated dress",
        "pid": 0,
        "description": '주름진 원피스'
    }
]
for i, data in enumerate(goods):
    res = es.index(index="test-index", doc_type='goods', body=data, id=i)
    print(res['result'])


3. 검색하기

query = {
    "query": {
        "bool": {
            "should": [
                {
                    "match": {
                        "name": {
                            "query": "주름", "boost": 3
                        }
                    }
                }
            ]
        }
    }
}

res = es.search(index="test-index", body=query)


무심코 사용하고 있는 용어들이 너무 햇갈린다...

라이브러리(Library)와 모듈(Module)

모듈의 의미는 구성 단위, 구성부분 이고 라이브러리는 도서관이라는 뜻을 가지고 있습니다. 개발에서는 라이브러리와 모듈을 동일한 의미라고 생각하면 됩니다. 자주 사용하게 되는 코드를 하나의 함수나 클래스라는 단위로 묶어서 코드를 재사용하게 됩니다. 즉, 이러한 함수 또는 클래스들을 모아서 라이브러리(library) 또는 모듈(module)이라 부릅니다. 라이브러리 또는 모듈은 개발자가 직접 만들 수도 있고 다른 사람이 만든 것을 설치받아 사용할 수도 있습니다. 다시 말해, 라이브러리와 모듈은 동일한 개념으로 재사용이 가능한 코드의 집합으로 볼 수 있습니다.

프레임워크(Framework)

라이브러리와 모듈을 공통적으로 사용하기 위한 부품이라 하면 프레임워크는 기본 뼈대라고 생각하면 됩니다. 개발자가 처음부터 모든 것을 개발을 할 수 있지만, 프레임워크를 사용하면 원하는 기능에만 집중하여 구현할 수 있습니다. 프레임워크 안에는 기본적으로 필요한 기능을 갖추고 있으므로 라이브러리(혹은 모듈)이 포함되어 있습니다.

crontab의 최소 실행시간은 분입니다. 하지만 편법으로 초 단위로 실행할 수 있습니다. 

아래는 30초마다 스크립트를 실행하는 예제입니다.

* * * * * /home/test.sh & sleep 30;/home/test.sh;


sleep을 사용하여 test.sh 스크립트가 1번 실행된 후, 30초 멈춘 다음 다시 세미콜론으로 연결된 test.sh 스크립트를 실행하는 방법입니다. 

아래는 10초 마다 스크립트를 실행 한 후, 성공과 실패 로그를 파일에 쓰는 예시입니다.

* * * * * /home/test.sh > /home/success.log 2>/home/err.log & sleep 10; /home/test.sh > /home/success.log 2>/home/err.log & sleep 10; /home/test.sh > /home/success.log 2>/home/err.log & sleep 10; /home/test.sh > /home/success.log 2>/home/err.log & sleep 10; /home/test.sh > /home/success.log 2>/home/err.log & sleep 10; /home/test.sh > /home/success.log 2>/home/err.log;


'서버' 카테고리의 다른 글

crontab 초 단위 실행하기  (0) 2019.02.02
프로비저닝(Provisioning)이란?  (1) 2019.02.02
엣지 컴퓨팅(Edge Computing) 이란?  (0) 2019.01.29
Jupyter Notebook이란?  (0) 2017.10.18
Ansible이란?  (1) 2017.10.18
WSGI, WAS, CGI 이해  (0) 2017.04.19

프로비저닝(Provisioning)이란 어떠한 지식이나 자원 등을 미리 준비해놓고 요청이 들어왔을 때, 해당 요청에 맞게 공급하는 것을 의미합니다. 즉 사용자 혹은 비지니스 요구사항에 맞게 할당, 배치, 배포하여 시스템을 사용가능하도록 준비하는 절차를 뜻합니다.

프로비저닝은 아래와 같이 분류할 수 있습니다.

서버 자원 프로비저닝

서버의 CPU, Memory 등의 자원을 할당 또는 적절하게 배치하여 운영이 가능하도록 준비

OS 프로비저닝

OS 를 서버에 설치하고, 구성 작업을 해서 OS가 동작 가능하도록 준비

소프트웨어 프로비저닝

소프트웨어(WAS, DBMS, 어플리케이션 등) 를 시스템에 설치 배포하고 필요한 구성을 세팅해서 실행 가능하도록 준비

스토리지 프로비저닝

낭비되거나 사용되지 않는 스토리지를 식별하고 공통 풀에서 옮긴 후 스토리지에 대한 요구가 접수 되면 관리자는 공통 풀에서 스토리지를 꺼내 사용 효율성을 높일 수 있는 인프라 구축 가능하도록 준비

'서버' 카테고리의 다른 글

crontab 초 단위 실행하기  (0) 2019.02.02
프로비저닝(Provisioning)이란?  (1) 2019.02.02
엣지 컴퓨팅(Edge Computing) 이란?  (0) 2019.01.29
Jupyter Notebook이란?  (0) 2017.10.18
Ansible이란?  (1) 2017.10.18
WSGI, WAS, CGI 이해  (0) 2017.04.19
  1. 이충엽 2019.03.20 16:21 신고

    너무 감사합니다. 불곰 없었으면 전 개발 못했을거예요.

Django 모델에서 Manager는 데이터베이스와 상호 작용하는 인터페이스입니다. 기본적으로 Manager는 Model.objects 속성을 통해 사용할 수 있습니다. Django 모델마다 기본적으로 사용되는 기본 관리자는 django.db.models.Manager입니다.

from django.db import models

class DocumentManager(models.Manager):
    def pdfs(self):
        return self.filter(file_type='pdf')

    def smaller_than(self, size):
        return self.filter(size__lt=size)

class Document(models.Model):
    name = models.CharField(max_length=30)
    size = models.PositiveIntegerField(default=0)
    file_type = models.CharField(max_length=10, blank=True)

    objects = DocumentManager()


위 예시에서 filter_type='pdf'를 선언하는 방법은 아래와 같습니다. 또한 호출 후, filter()와 order_by()같이 추가적으로 연결하여 사용할 수 있습니다.

Document.objects.pdfs()


Document.objects.pdfs().filter(name='test')
Document.objects.pdfs().order_by('name')


하지만 Manager에 선언한 다른 메소드를 호출하고자 하면 오류가 발생합니다.

Document.objects.pdfs().smaller_than(1000)


# AttributeError: 'QuerySet' object has no attribute 'smaller_than'


이 문제를 해결하는 것은 Custom QuerySet을 선언하는 것입니다.

class DocumentQuerySet(models.QuerySet):
    def pdfs(self):
        return self.filter(file_type='pdf')

    def smaller_than(self, size):
        return self.filter(size__lt=size)

class DocumentManager(models.Manager):
    def get_queryset(self):
        return DocumentQuerySet(self.model, using=self._db)  # 중요

    def pdfs(self):
        return self.get_queryset().pdfs()

    def smaller_than(self, size):
        return self.get_queryset().smaller_than(size)

class Document(models.Model):
    name = models.CharField(max_length=30)
    size = models.PositiveIntegerField(default=0)
    file_type = models.CharField(max_length=10, blank=True)

    objects = DocumentManager()




Document.objects.pdfs().smaller_than(1000).exclude(name='Article').order_by('name')


만약 Custom QuerySet만 정의하고자 하면 아래와 같이 선언하면 됩니다.

class DocumentQuerySet(models.QuerySet):
    def pdfs(self):
        return self.filter(file_type='pdf')

    def smaller_than(self, size):
        return self.filter(size__lt=size)

class Document(models.Model):
    name = models.CharField(max_length=30)
    size = models.PositiveIntegerField(default=0)
    file_type = models.CharField(max_length=10, blank=True)

    objects = DocumentQuerySet.as_manager()


pdfs()만 호출할 수 있는 것은 물론, smaller_than() 또한 연결하여 호출할 수 있습니다.

Document.objects.pdfs().smaller_than(1000)


models.py 안에 조회 쿼리를 넣을 수 있지만 코드가 커진다면 Manager와 QuerySet을 managers.py라는 다른 모듈에 유지하는 것을 추천합니다. Custom Manager와 Custom QuerySet을 사용하는 이점은 공통적으로 사용되는 쿼리를 공통 함수로 정의할 수 있고 실제 동작을 숨길 수 있습니다.

ORM을 조인하고 싶을 때 N:1의 관계나 N:N의 관계일 경우, prefetch_related()를 사용하게 됩니다. 

모델 정의

예시를 들기 위해 아래와 같이 모델과 모델간 관계를 정의합니다.

from django.db import models

class Topping(models.Model):
    name = models.CharField(max_length=30)

class Pizza(models.Model):
    name = models.CharField(max_length=50)
    toppings = models.ManyToManyField(Topping)
	vegetarian = models.BooleanField()

    def __str__(self):
        return "%s (%s)" % (
            self.name,
            ", ".join(topping.name for topping in self.toppings.all()),
        )


class Restaurant(models.Model):
    pizzas = models.ManyToManyField(Pizza, related_name='restaurants')
    best_pizza = models.ForeignKey(Pizza, related_name='championed_by', on_delete=models.CASCADE)

Prefetch

만약 첫 번째 레스토랑에서 채식주의자가 먹을 수 있는 피자를 조회하는 쿼리는 아래와 같습니다.

queryset = Pizza.objects.filter(vegetarian=True)


restaurants = Restaurant.objects.prefetch_related(Prefetch('pizzas', queryset=queryset))
vegetarian_pizzas = restaurants[0].pizzas.all()


만약 위와 같은 queryset이 다른 조인 쿼리에도 사용된다면 해당 쿼리가 실행될 때마다 새로 조회를 하므로 중복조회가 발생됩니다. 이때, Prefetch()에서 제공하는 to_attr을 사용하여 쿼리를 메모리에 저장하여 효율적으로 사용할 수 있습니다.

queryset = Pizza.objects.filter(vegetarian=True)


restaurants = Restaurant.objects.prefetch_related(Prefetch('pizzas', queryset=queryset, to_attr='vegetarian_pizzas'))
vegetarian_pizzas = restaurants[0].vegetarian_pizzas


to_attr에 저장되는 Prefetch()의 데이터 크기가 너무 크지 않다면, 메모리에 올려 재사용성을 늘리는 것이 효율적입니다.

+ Random Posts