이제 Elasticsearch 인덱스를 구축하고 일부 문서를 로드했으므로 이제 전체 텍스트 검색을 구현할 준비가 되었습니다.
검색 작동 방식
튜토리얼 애플리케이션에서 검색 솔루션이 어떻게 작동하는지 간단히 살펴보겠습니다. 플라스크 애플리케이션을 실행한 상태에서 http://localhost:5001 으로 이동하여 다음과 같은 메인 페이지에 액세스할 수 있습니다:

이 페이지를 렌더링하는 코드는 app.py 파일에 구현되어 있습니다:
이것은 HTML 템플릿을 렌더링하는 매우 간단한 엔드포인트입니다. 플라스크 애플리케이션에서 템플릿은 템플릿 하위 디렉토리에 있으므로 이 템플릿과 애플리케이션에 포함된 다른 템플릿을 찾을 수 있습니다.
파일 템플릿/index.html의 검색 필드 구현을 살펴보겠습니다. 이 템플릿의 관련 부분은 다음과 같습니다:
여기에서 query 이라는 이름의 단일 필드( text 유형)가 있는 HTML 양식임을 알 수 있습니다. 양식의 method 속성은 POST 으로 설정되어 브라우저에 이 양식을 POST 요청으로 제출하도록 지시합니다. action 속성은 Flask 애플리케이션의 handle_search 엔드포인트에 해당하는 URL로 설정됩니다. 양식이 제출되면 handle_search() 함수가 실행됩니다.
handle_search() 의 현재 구현은 아래와 같습니다:
이 함수는 사용자가 텍스트 필드에 입력한 텍스트를 Flask의 request.form 사전에서 가져와 query 로컬 변수에 저장합니다. 그런 다음 이 함수는 index.html 템플릿을 렌더링하지만 이 유형에서는 페이지에 검색 결과를 표시할 수 있도록 몇 가지 추가 인수를 전달합니다. 템플릿이 받는 인수는 네 가지입니다:
query사용자가 양식에 입력한 쿼리 텍스트입니다.results검색 결과 목록from_: 첫 번째 결과의 영점 기반 인덱스total총 결과 수
검색 기능이 구현되지 않았으므로 현재 render_template() 함수에 전달되는 인수는 결과를 찾을 수 없음을 나타냅니다.
이제 할 일은 전체 텍스트 쿼리를 구현하고 실제 결과를 전달하여 index.html 페이지에 표시할 수 있도록 하는 것입니다.
Elasticsearch 서비스는 JSON 형식을 기반으로 하는 쿼리 DSL (도메인 특정 언어)을 사용하여 쿼리를 정의합니다.
Python용 Elasticsearch 클라이언트에는 검색 쿼리를 제출하는 데 사용되는 search() 메서드가 있습니다. 이 메서드를 사용하는 search() 헬퍼 메서드를 search.py에 추가해 보겠습니다:
이 메서드는 인덱스 이름으로 Elasticsearch 클라이언트의 search() 메서드를 호출합니다. query_args 인수는 메서드에 제공된 모든 키워드 인수를 캡처한 다음 es.search() 메서드에 전달합니다. 이러한 인수는 호출자가 검색할 대상을 지정하는 방식이 됩니다.
경기 쿼리
Elasticsearch 쿼리 DSL은 인덱스를 쿼리하는 여러 가지 방법을 제공합니다. 문서의 하위 섹션을 살펴보면 가능한 다양한 유형의 쿼리 유형에 익숙해질 수 있습니다. 매우 일반적인 텍스트 검색 작업은 전체 텍스트 쿼리 섹션에서 다룹니다.
첫 번째 검색 구현에서는 Match 쿼리를 사용하겠습니다. 아래에서 이 쿼리를 사용하는 예시를 볼 수 있습니다:
위의 예는 원시 HTTP 요청과 유사한 형식으로 제공됩니다. 이 형식은 Elasticsearch 설명서 및 Elasticsearch API 콘솔에서 광범위하게 사용되므로 이 형식에 익숙해지는 것이 유용합니다. 다행히도 이 형식은 Python 클라이언트 라이브러리를 사용하여 호출로 매우 쉽게 변환할 수 있습니다. 아래에서 위 예제에 해당하는 Python 코드를 확인할 수 있습니다:
API 콘솔 예제를 Python으로 변환할 때 쿼리 본문의 최상위 키는 Python 호출에서 키워드 인수로 변환해야 한다는 점을 기억하세요. 또한 예제에서는 파이썬 호출 시 필요한 인덱스를 지정하지 않습니다.
쿼리 구조를 보면 어떤 종류의 검색을 요청하는지 유추할 수 있을 것입니다. 이 호출은 name 이라는 필드에 대해 match 쿼리를 요청하고 검색할 텍스트는 search text here 입니다.
이 쿼리 스타일은 튜토리얼 애플리케이션에 쉽게 통합할 수 있습니다. app.py를 열고 handle_search() 메서드를 찾습니다. 현재 버전을 이 새 버전으로 교체합니다:
이 새 버전의 엔드포인트의 두 번째 줄에 있는 es.search() 호출은 search.py에 위에 추가된 search() 메서드를 호출합니다, 를 호출하고, 이 메서드는 다시 Elasticsearch 클라이언트의 search() 메서드를 호출합니다.
쿼리가 무엇을 수행할지 파악할 수 있나요? 위의 예와 유사한 match 쿼리입니다. 검색할 필드는 name 이며, 이전 섹션에서 작성한 my_documents 색인에 있는 문서 제목이 포함되어 있습니다. 검색할 텍스트는 사용자가 웹 페이지의 검색 필드에 입력한 내용으로 query 로컬 변수에 저장됩니다.
검색 응답에서 결과가 포함된 부분은 response['hits'] 입니다. 이 객체에는 몇 가지 키가 있으며, 이 중 두 가지가 이 구현에서 중요합니다:
response['hits']['hits']검색 결과 목록입니다.response['hits']['total']사용 가능한 총 결과 수입니다. 결과 수는value하위 키에 제공되므로 실제로는 총 결과 수를 구하는 표현식은results['hits']['total']['value']입니다. 결과 수가 많을 경우 총 결과 수는 근사치일 수 있다는 점에 유의하세요. 자세한 내용은 응답 본문 문서를 참조하세요.
이 새 버전의 엔드포인트에서 render_template() 로 호출하면 results 템플릿 인수의 결과 목록과 total 의 총 결과 수가 전달됩니다. query 인수는 이전과 같이 쿼리 문자열을 수신하며 from_ 은 나중에 페이지 매김이 추가될 때 구현되므로 여전히 0으로 하드코딩되어 있습니다.
그리고 이를 통해 애플리케이션에 전체 텍스트 검색이 처음으로 구현되었습니다. 웹 브라우저로 돌아가 http://localhost:5001 으로 이동하여 애플리케이션을 엽니다. 어떤 이유로든 플라스크 애플리케이션을 실행하고 있지 않다면 이 작업을 수행하기 전에 애플리케이션을 다시 시작하세요. policy 또는 work from home 등의 검색어를 입력하면 관련 검색 결과가 표시됩니다. 아래에서 work from home 을 검색하면 결과를 확인할 수 있습니다:

스타터 애플리케이션과 함께 다운로드한 index.html 템플릿에는 검색 결과를 렌더링하는 모든 로직이 포함되어 있습니다. 이에 대해 궁금한 점이 있다면 이 템플릿의 결과 목록을 렌더링하는 섹션을 참조하세요:
이 코드에서 흥미로운 점은 반환된 결과와 관련된 데이터를 _source 키 아래에서 사용할 수 있다는 점입니다. 결과에 할당된 고유 식별자를 포함하는 _id 필드도 있습니다.
각 결과와 관련된 점수는 _score 에서 확인할 수 있습니다. 점수는 관련성의 척도를 제공하며, 점수가 높을수록 쿼리 텍스트와 일치하는 항목이 많음을 나타냅니다. 기본적으로 결과는 가장 높은 점수부터 가장 낮은 점수 순으로 반환됩니다. Elasticsearch의 점수는 Okapi BM25 알고리즘을 사용하여 계산됩니다.
이 섹션에서 다루는 주제를 더 자세히 살펴보고 싶다면 다음 링크를 참조하세요:
개별 결과 검색
index.html 템플릿이 각 검색 결과의 제목을 링크로 렌더링하는 것을 보셨을 것입니다. 링크는 스타터 플라스크 애플리케이션에 구현된 세 번째이자 마지막 엔드포인트인 get_document 로 연결됩니다. 제공된 구현은 "찾을 수 없는 문서" 하드코딩된 텍스트를 반환하므로 애플리케이션을 플레이하는 동안 결과를 클릭하면 이 결과를 볼 수 있습니다.
개별 문서를 올바르게 렌더링하려면 search.py에 retrieve_document() 헬퍼 메서드를 추가해 보겠습니다, Elasticsearch 클라이언트의 get() 메서드를 사용합니다:
여기에서 각 문서에 할당된 고유 식별자가 애플리케이션이 개별 문서를 참조하는 데 사용할 수 있으므로 이 식별자가 어떻게 유용한지 확인할 수 있습니다.
다음은 get_document() 엔드포인트의 현재 구현입니다:
이 엔드포인트와 연결된 URL에 id 문서가 포함되어 있고 각 검색 결과에 대해 렌더링되는 링크에도 각 URL에 통합된 ID가 있으므로 이 간단한 구현을 문서를 검색하고 렌더링하는 구현으로 대체하기만 하면 됩니다. 엔드포인트를 이 업데이트된 버전으로 교체하세요:
여기서는 search.py의 retrieve_document() 메서드를 사용하여 요청된 문서를 가져옵니다. 그러면 name 필드에서 가져온 제목과 content 에서 가져온 단락 목록이 포함된 document.html이 렌더링됩니다.
몇 가지 쿼리를 더 실행하고 결과를 클릭하면 이제 전체 콘텐츠를 볼 수 있을 것입니다.
여러 필드 검색
한동안 애플리케이션을 사용하다 보면 많은 쿼리에서 결과가 반환되지 않는 것을 발견했을 수 있습니다. 기억하시겠지만, 현재 검색은 문서 제목이 저장되는 각 문서의 name 필드에 구현되어 있습니다. 문서에는 summary 및 content 필드도 있는데, 이 필드에는 검색하기 쉬운 긴 텍스트도 있지만 지금은 무시됩니다.
이 섹션에서는 인덱스의 여러 필드에서 검색을 수행하도록 요청하는 또 다른 일반적인 전체 텍스트 검색 쿼리인 다중 일치에 대해 알아봅니다.
다음은 문서에 있는 멀티매치 쿼리 예시입니다:
이 예제를 기반으로 handle_search() 엔드포인트를 확장하여 name, summary 및 content 필드에 대한 다중 일치 쿼리를 함께 실행해 보겠습니다. 업데이트된 엔드포인트 코드는 다음과 같습니다:
이 변경 사항으로 인해 검색할 텍스트가 훨씬 많아져 일부 쿼리에는 기본적으로 반환되는 최대 10개 이상의 결과가 표시될 수 있습니다. 다음 장에서는 페이지 매김을 통해 긴 결과 목록을 처리하는 방법에 대해 알아봅니다.