많은 애플리케이션은 검색 쿼리만으로 할 수 있는 기능을 보완하는 방식으로 사용자에게 쿼리를 사용자 지정할 수 있는 기능을 제공해야 합니다. 이 장에서는 주어진 조건을 만족하는 인덱스에 포함된 문서의 하위 집합에서만 검색 쿼리가 실행되도록 지정할 수 있는 기술인 필터링에 대해 알아봅니다.
부울 쿼리 소개
필터를 구현하려면 먼저 Elasticsearch에서 복합 쿼리가 어떻게 구현되는지 이해해야 합니다.
복합 쿼리를 사용하면 애플리케이션에서 두 개 이상의 개별 쿼리를 결합하여 함께 실행하고 적절한 경우 결합된 결과 집합을 반환할 수 있습니다. Elasticsearch에서 복합 쿼리를 생성하는 표준 방법은 부울 쿼리를 사용하는 것입니다.
부울 쿼리는 두 개 이상의 개별 쿼리 또는 절에 대한 래퍼 역할을 합니다. 쿼리를 결합하는 방법에는 네 가지가 있습니다:
bool.must절이 일치해야 합니다. 여러 절이 주어지면 모두 일치해야 합니다(AND 논리 연산과 유사).bool.should없이 사용할 경우must, 하나 이상의 절이 일치해야 합니다(OR 논리 연산과 유사).must와 결합하면 일치하는 각 절이 문서의 관련성 점수를 높입니다.bool.filter해당 절과 일치하는 문서만 검색 결과 후보로 간주합니다.bool.must_not해당 절과 일치하지 않는 문서만 검색 결과 후보로 간주합니다.
위에서 짐작할 수 있듯이 부울 쿼리는 상당히 복잡하며 다양한 방식으로 사용될 수 있습니다. 이 장에서는 이전 장에서 구현한 다중 일치 전체 텍스트 검색 절을 하나의 문서 카테고리로 결과를 제한하는 필터와 결합하는 방법에 대해 알아봅니다. 이 튜토리얼에 사용된 데이터 세트에는 sharepoint, teams 또는 github 로 설정할 수 있는 category 필드가 포함되어 있음을 기억하십시오.
쿼리에 필터 추가하기
현재 튜토리얼 애플리케이션에서 구현된 멀티매치 쿼리는 다음과 같은 구조를 사용합니다:
이 검색을 특정 카테고리로 제한하는 필터를 추가하려면 다음과 같이 쿼리를 확장해야 합니다:
이 쿼리의 새로운 구성 요소를 자세히 살펴보겠습니다.
우선 multi_match 쿼리가 bool.must 절 내부로 이동했습니다. bool.must 절은 일반적으로 기본 쿼리가 정의되는 곳입니다. must 에서는 검색할 쿼리 목록을 허용하므로 원하는 경우 여러 개의 기본 수준 쿼리를 결합할 수 있습니다.
필터링은 새로운 쿼리 유형인 term 쿼리를 사용하여 bool.filter 섹션에서 구현됩니다. 필터에 match 또는 multi_match 쿼리를 사용하는 것은 전체 텍스트 검색 쿼리이므로 좋지 않습니다. 필터링을 위해 쿼리는 일치 쿼리처럼 관련성 점수가 아닌 각 문서에 대해 절대적인 참 또는 거짓 답변을 반환해야 합니다.
쿼리라는 용어는 주어진 필드에서 a 값을 정확하게 검색합니다. 이 유형의 쿼리는 식별자, 레이블, 태그 또는 이 경우와 같이 카테고리를 검색하는 데 유용합니다.
이 쿼리는 전체 텍스트 검색을 위해 색인된 필드에서는 제대로 작동하지 않습니다. 문자열 필드에는 기본 텍스트 유형이 할당되며, 색인화되기 전에 내용을 분석하여 개별 단어로 분리합니다. Elasticsearch는 문자열 필드에 보조 유형의 키워드를 할당하여 필드 내용을 전체적으로 색인하므로 term 쿼리로 필터링하는 데 더 적합합니다. 쿼리의 필터 부분에 category.keyword 필드 이름을 사용하면 기본값인 text 대신 keyword 입력한 변형 필드가 사용됩니다.
필터 지정하기
필터링된 쿼리를 구현하려면 먼저 최종 사용자가 원하는 필터를 입력할 수 있는 방법을 추가해야 합니다. 이 튜토리얼에서 구현된 솔루션은 검색 쿼리 텍스트에서 category:<category-name> 패턴을 찾습니다. app.py에 extract_filters() 라는 함수를 추가하여 필터 표현식을 찾아보겠습니다:
이 함수는 사용자가 입력한 쿼리를 받아 쿼리에서 발견된 필터와 필터가 제거된 후 수정된 쿼리가 포함된 튜플을 반환합니다. 필터 패턴을 찾기 위해 정규식을 사용합니다. 이 기능은 추가 필터를 통해 확장할 수 있도록 설계되었습니다.
필터가 발견되면 filters 목록이 해당 필터 표현식으로 확장되며, 이 경우 위에서 설명한 대로 term 쿼리를 기반으로 합니다.
이 함수의 작동 방식을 더 잘 이해하려면 먼저 가상 환경이 활성화되어 있는지 확인한 후 Python 세션을 시작하고 다음 코드를 실행하세요:
함수에서 반환되는 튜플은 다음과 같아야 합니다:
필터링된 검색 구현하기
남은 작업은 handle_search() 함수를 변경하여 사용자가 필터를 지정한 경우 전체 텍스트 검색 표현식과 필터를 결합한 업데이트된 쿼리를 보내도록 하는 것입니다. 아래는 이 기능의 새 버전입니다:
이제 쿼리는 bool 표현식을 보내도록 변경되었으며 검색 표현식은 그 아래 must 섹션으로 이동했습니다. extract_filters() 함수는 쿼리의 필터 부분을 Elasticsearch에 전송해야 하는 형식으로 반환하므로 최상위 수준 bool 키 아래에도 쿼리 사전에 삽입됩니다.
work from home category:sharepoint 같은 검색어를 입력해 보면 지정된 카테고리의 문서만 반환되는 방식을 확인할 수 있습니다.
범위 필터
Elasticsearch는 term 필터 외에도 다양한 필터를 지원합니다. 일반적으로 사용되는 또 다른 필터는 range 필터로, 숫자와 날짜에 사용할 수 있습니다. 마지막으로 업데이트된 연도를 기준으로 결과를 제한하는 데 사용할 수 있는 year 필터를 updated_at 필드에 추가해 보겠습니다.
다음은 category:<category> 및 year:<yyyy> 를 모두 필터로 찾는 extract_filters() 함수의 업데이트된 버전입니다:
이 버전에서는 두 번째 정규식을 추가하여 쿼리 문자열에서 year:yyyy 을 찾습니다. updated_at 필드에 대해 range 필터를 만들고 범위의 하한과 상한을 콜론 뒤에 지정된 연도로 설정하며, 정규식 일치에서 m.group(1) 로 캡처됩니다.
updated_at 필드에는 전체 날짜가 포함되어 있고 이 필터에서는 연도만 확인하면 되기 때문에 약간의 복잡성이 있습니다. 다행히도 범위 필터를 날짜 필드와 함께 사용하면 날짜 수학을 통해 범위의 경계를 강화할 수 있습니다. 범위의 gte (하한) 및 lte (상한) 매개변수에 추가되는 ||/y 접미사는 주어진 값이 필드와 비교할 수 있는 전체 날짜를 형성하기 위해 완료되어야 하는 연도임을 나타냅니다.
이 변경 사항에서는 year:2020 work from home 같은 쿼리를 포함하여 요청된 연도의 결과만 볼 수 있습니다. 쿼리에는 두 필터도 포함할 수 있습니다(예: year:2020 category:teams work from home).
모든 쿼리 일치
새 주제로 넘어가기 전에 검색어 텍스트 필드에 필터만 입력해 보세요(예: category:github). 안타깝게도 이 방법은 결과를 반환하지 않지만 이 경우 예상되는 동작은 요청된 카테고리와 일치하는 모든 결과를 수신하는 것입니다.
extract_filters() 함수는 첫 번째 요소에 필터가 있고 두 번째 요소에 빈 쿼리 문자열이 있는 튜플을 반환합니다. multi_match 쿼리는 빈 문자열을 수신하고 빈 문자열과 일치하는 항목이 없으므로 빈 결과 목록을 반환합니다.
이 특수한 경우를 해결하기 위해 검색 텍스트가 비어 있는 경우 multi_match 쿼리를 match_all 으로 대체할 수 있습니다. 아래 handle_search() 함수의 버전은 이를 수행하는 로직을 추가합니다. app.py에서 함수를 업데이트합니다.
이 버전에서는 카테고리와 일치하는 모든 문서를 요청할 수 있습니다. 점수를 계산할 검색어가 없기 때문에 반환되는 모든 결과가 동일한 점수인 1.0으로 돌아오는 점에 유의하세요.