검색은 과거 애플리케이션에서 가장 간과되었던 기능 중 하나였을 것입니다. 하지만 최근 몇 년 동안 검색이 매출을 늘리거나 사용자의 행동과 참여를 유도할 수 있다는 사실이 밝혀지면서 검색이 다시 주목받기 시작했습니다.
이 새로운 블로그 시리즈에서는 초보자에서 전문가로 나아가고자 합니다. 따라서 마지막에는 다음과 같은 검색을 구현할 수 있을 것입니다.프로처럼".
'좋아요 %'로는 충분하지 않은 이유는 무엇인가요?
잘 구현된 검색은 기본적으로 빠르고 관련성이 높아야 합니다."%처럼'는 둘 다 아닙니다. 그 이유를 알아봅시다:
속도의 문제
제목에 따라 영화 검색을 구현하고 싶다고 가정해 보겠습니다. 모든 "스타트렉" 영화를 찾는 순진한 SQL 쿼리는 다음과 같습니다:
1 |
선택 * 에서 영화 어디 title 같은 "스타 Trek%" |
기본적으로 이 용어와 모든 행을 일치시키려고 전체 테이블 스캔을 실행합니다. 하지만 인덱스로 최적화할 수 있으며, 인덱스는 대부분 B-Tree 구조를 생성합니다. 따라서 다음과 같은 검색도 여전히 인덱스를 부분적으로 활용하게 됩니다:
1 2 |
선택 * 에서 영화 어디 title 같은 "Star%Trek" 선택 * 에서 영화 어디 title 같은 "St%Trek" |
하지만 사용자가 배트맨의 열렬한 팬이고 "다크 나이트"라는 용어를 검색하기로 결정했다면 어떻게 될까요? 앞의 예에 따르면 쿼리는 다음과 같이 표시됩니다:
1 |
선택 * 에서 영화 어디 title 같은 "Dark Knight%" |
위의 검색은 영화의 실제 이름이 ""이므로 결과가 표시되지 않습니다.그리고 다크 나이트 라이즈". 이 문제를 해결하려면 검색어 시작 부분에 와일드카드를 추가해 보겠습니다:
1 |
선택 * 에서 영화 어디 title 같은 “%Dark Knight%" |
수정되었습니다! 맞죠? 안타깝게도 그렇지 않습니다. 위의 쿼리는 다음과 같은 결과를 반환합니다.그리고 다크나이트 라이즈'가 있지만, 더 이상 지수를 활용하지 않으며 규모 면에서 좋은 성과를 거두지 못할 것입니다. %처럼 는 해당 분야의 콘텐츠를 '이해'하지 못합니다. 오히려 텍스트를 하나의 사물로 취급하기 때문에 일종의 기본 구조를 가진 모든 것을 처리하는 데 가장 적합한 선택이 아닙니다.
가능한 해결책은 MATCH를 사용하는 것입니다. 느린 것으로 보고됨 일부 데이터베이스에서
관련성 문제
영화의 개요도 검색하고 싶다면 어떻게 해야 할까요? 순진한 해결책은 쿼리에 새 필드를 추가하는 것입니다:
1 |
선택 * 에서 영화 어디 title 같은 “%Dark Knight%" 또는 개요 같은 “%Dark Knight%" |
이 접근 방식은 개요에 '다크 나이트'가 언급된 모든 영화는 제목에 '다크 나이트'가 포함된 영화와 동일한 관련성을 가지며 결과의 순서가 완전히 불확실하다는 새로운 문제를 야기합니다.
일반적인 실수는 UNION DISTINCT가 이 문제를 해결할 수 있다고 생각하는 것입니다. 그러나 오늘날 대부분의 쿼리 플래너는 유니온의 각 블록을 병렬로 실행하기 때문에 순서/관련성이 다시 엉망이 됩니다.
일반 SQL을 사용하여 일종의 관련성을 구현하려는 경우 두 개의 개별 쿼리를 실행하고 한 쿼리의 결과를 다른 쿼리에 추가하는 것이 간단한 해결책입니다. 물론 이 전략은 최적과는 거리가 멀고 여전히 수동으로 중복을 처리해야 합니다.
위의 예는 검색에 대한 무언의 진실 중 하나를 보여줍니다: 검색의 진정한 과제는 일치하는 항목을 찾는 것이 아니라 정렬하는 방법입니다.. 결국 텍스트를 일치시키는 것은 매우 간단한 작업이지만 올바른 점수를 부여하는 것은 신중하게 만들어야 하는 작업입니다.
전체 텍스트 검색이 구출합니다!
용어를 검색하는 방법에는 여러 가지가 있으며, 각 방법은 특정 전략에 초점을 맞추고 있습니다. 다음 글에서 대부분을 다룰 예정이지만 지금은 FTS가 "%처럼":
속도를 위한 솔루션
FTS를 사용하여 데이터베이스를 쿼리하려면 먼저 역 인덱스를 만들어야 합니다. 이 인덱스는 대략적으로 말하면 단어와 그 단어의 발생 빈도 지도입니다:
위의 색인을 사용하면 원하는 용어가 포함된 문서만 쉽게 찾을 수 있으므로 단어 검색이 간단해집니다.
반전 인덱스를 생성하는 동안 구문을 단어 배열로 변환했기 때문에 와일드카드는 필요하지 않습니다. 대상 용어를 반전 인덱스와 직접 일치시킬 수 있기 때문입니다. 하지만 여기서 와일드카드(예: *별표*)를 사용하더라도 데이터베이스의 모든 문서를 스캔하는 대신 인덱스를 반복하여 일치하는 모든 문서를 찾기만 하면 되므로 대규모에서는 여전히 더 빠르게 실행됩니다.
아직 사전, 어간, 분석기, 토큰화기, 동의어에 대해서는 언급하지 않았습니다. 이 시리즈의 2부에서는 이에 대해 다룰 예정입니다.
관련성 솔루션
관련성 높은 검색 결과를 구현하는 전략은 작업 중인 도메인에 따라 다를 수 있습니다. 일반적으로 연관성은 여러 가지 방법으로 조작할 수 있는데, 가장 간단한 방법은 부스팅입니다. 간단히 말해서 부스팅은 필드 일치 항목에 가중치를 할당하는 간단한 방법입니다:
쿼리:
제목:"스타트렉"^2 | 개요:"스타트렉"
위의 예에서 '스타트렉'에 대한 경기 점수는 title 에서의 경기보다 두 배의 가치가 있습니다. 개요 하나. 기본적으로 검색 결과 정렬 방식을 제안할 수 있는 방법입니다.
그렇다면 왜 모두가 전체 텍스트 검색을 사용하지 않을까요?
FTS는 일반적으로 완전히 새로운 인프라를 설정하고, 새로운 종속성을 추가하고, 여러 인덱스를 만든 다음 모든 문서 변경 사항을 다음과 같은 외부 시스템으로 푸시하도록 요구합니다. Elastic 검색 또는 Solr과 같이 아주 간단한 검색만 구현하려는 경우에도 마찬가지입니다. 따라서 개발자는 꼭 필요한 경우가 아니면 이 엄청난 양의 작업을 피하는 경향이 있습니다.
이것이 바로 우리가 FTS 엔진을 통합하기로 결정한 이유 중 하나입니다. Bleve 를 Couchbase에 추가한 후 웹 콘솔에서 새 인덱스를 생성하기만 하면 됩니다:
또한 기본 Couchbase SDK를 사용하여 자동으로 전체 텍스트 검색을 수행할 수 있습니다:
1 2 3 4 |
문자열 인덱스 이름 = "movies_index"; 문구 쿼리 쿼리 = 검색 쿼리.문구("스타트렉"); 검색 쿼리 결과 결과 = 영화 저장소.getCouchbaseOperations().카우치베이스 버킷 가져오기().쿼리( new 검색 쿼리(인덱스 이름, 쿼리).하이라이트().limit(20)); |
대부분의 관계형 데이터베이스는 이미 전체 텍스트 검색을 지원하고 있는데, 그냥 사용하지 않으시겠습니까?
사실 소규모 사용 사례에는 좋은 선택이 될 수 있으며, 실제로 개발자들로부터 긍정적인 피드백을 몇 차례 들었습니다. 개인적으로는 상당한 양의 데이터가 있거나 대규모로 확장해야 하는 경우에는 Bleve와 같은 적절한 도구를 고려하는 것이 좋다고 생각합니다.
적절한 FTS 엔진을 사용하면 또 다른 큰 장점이 있습니다: 쿼리 언어입니다. 대부분의 관계형 데이터베이스는 SQL을 재사용하려고 하는데, 고급 검색을 작성하려고 하면 정말 지저분해질 수 있습니다. 결국 SQL은 연결/분리 쿼리, 패싯, 복잡한 순위 요소 등을 처리하도록 설계되지 않았기 때문입니다.
전체 텍스트 검색 시리즈
- 분석기 및 토큰화 도구 이해하기 - 파트 2
- 퍼지 매칭 - 파트 3