삼차원 그림으로 모호한 검색 결과에 대해 관련성 정렬을 진행하다

이름이나 이메일로 사용자를 검색하고 싶다고 가정하십시오.만약 내가 'ana' 를 입력한다면, 이름이나 이메일에 'ana' 문자를 포함하는 모든 사용자를 얻고 싶습니다.내가 '포함' 이라고 말하는 이유는 일부 전자메일이나 이름이 '안나' 와 완전히 일치하지 않을 수도 있지만, 예를 들어 '조안나', '다이애나', '[email protected]' 을 포함할 수 있기 때문이다. 나는 내 결과에서 그것들을 배제하고 싶지 않다.다른 '안나' 와 비슷한 이름이나 이메일은 검색 중인 사람들에게 가치가 있을 수 있다.
이런 유형의 검색을 모호 문자열 일치라고 하는데, 어댑터를 사용하는 SQL을 통해 실현할 수 있다.PostgreSQL을 사용하지만 유사한 SQL은 다른 데이터베이스에서 작성할 수 있습니다.
SELECT id, name, email
FROM users
AND users.name ILIKE '%ana%'
OR users.email ILIKE '%ana%'
LIMIT 20
이 검색은 이름이나 이메일에 "ana"를 포함하는 모든 사용자에게 되돌아갈 것입니다. 그러나 특정한 순서대로 가져오지는 않을 것입니다.나는 '이름' 과 '전자 우편' 의 자모순으로 배열할 수 있지만, 이것은 관련성을 보장할 수 없다.나는 결과를 20개로 제한했기 때문에, 심지어는 알파벳순으로 관련 결과를 배제할 수도 있다.
다행히도 Postgres에는 매우 재미있는 확장자 pg_trgm이 있다는 것을 알게 되었다.참조 postgresql 파일:

[pg_trgm] provides functions and operators for determining the similarity of alphanumeric text based on trigram matching, as well as index operator classes that support fast searching for similar strings.


언어학에서 삼각형은 알파벳, 음절 또는 단어와 같은 세 개의 연속적인 쓰기 단위로 구성된 그룹이다.박사 후에 해야 할 일은 단어를 세 갈래로 나누는 것이다.예를 들어 "ruby"삼각형은 {"r", "ru", "by",rub,uby}이다.일부 삼각형에는 공백이 있음을 주의하세요.이것은 문자열에 포함된 삼각형 그룹을 확정할 때, 단어마다 두 개의 공백 접두사와 한 개의 공백 접두사가 있다고 여겨지기 때문이다.우리가 진정으로 받아들여야 할 것은postgres는 단어를 세 개의 알파벳 숫자 문자의 작은 블록으로 나누어 다른 단어와 같은 크기의 블록의 유사성을 검사하는 것이다.
따라서 삼원도를 사용하여 두 문자열을 비교할 때 Postgres는 그들 사이의 유사성을 0점에서 1점까지 (그 중 1은 완벽하게 일치) 할 수 있다.pg_trgm 확장을 사용하여 어떻게 작동하는지 알아보겠습니다.
우선 확장을 추가하겠습니다.
CREATE EXTENSION pg_trgm;
지금 나는 두 단어를 비교할 수 있다.
SELECT SIMILARITY('Ana', 'Addriana'); 
-- returns 0.3

SELECT SIMILARITY('Ana', 'Alana'); 
-- returns 0.42857143
이것은 알파벳순으로 정렬한 결과를 설명한다.만약 내가 '안나' 를 검색한다면, 'Addriana' 는 'Alana' 이전의 결과에 나타날 것이다. 비록 유사성 점수가 비교적 낮지만.당신은 제품의 요구에 따라 알파벳순으로 주문할 수 있습니다.유사성을 사용하면 코드에 약간의 복잡성을 증가시킬 수 있지만, 이 예에서, 이것은 확실히 내가 개발하고 있는 검색 기능에 가치를 증가시켰다.
삼각형과 유사성 함수가 어떻게 작동하는지 알았으니 초기 검색에 추가합시다.유사성 순서에 따라 순서를 정하기 위해서 나는 먼저 SELECT의 점수를 계산하여 별명을 주고 이 변수를 ORDER BY 자구에 전달한다.
SELECT
id,
name,              
email,
SIMILARITY(name, 'ana') AS name_score,
SIMILARITY(email, 'ana') AS email_score
FROM users
WHERE users.name ILIKE '%ana%'
OR users.email ILIKE '%ana%'
ORDER BY name_score DESC NULLS LAST, email_score DESC NULLS LAST, name
LIMIT 20;
ORDER BY 조항을 자세히 살펴보면 제가 두 열을 검색했기 때문에 가장 관련된 것은 name_score이라고 생각합니다.그리고 email_score을 보겠습니다. 필요하면 알파벳순으로 배열하겠습니다.

3원 인덱스로 빠른 검색
현재 우리는 이미 우리의 조회를 받았고, 나는 이미 그것의 성능을 분석했는데, 그것은 상당히 빠른 것 같다. (100-130밀리초)단, 사용자가 입력할 때 검색 결과를 되돌려 주기를 원하기 때문에, 나는 검색 속도가 너무 빠르기를 원하지 않는다. 나는 검색 속도가 매우 빠르기를 바란다.나는 즉각 피드백을 필요로 한다.응답 시간을 높일 수 있는지 알아보기 위해 색인을 몇 개 추가합니다.
GIN(광의적 역방향 인덱스)과 GIST(광의적 검색 트리) 인덱스는 텍스트 검색 기능에서 여러 값(예를 들어 세 갈래 트리 목록)을 한 줄에 비추는 데 매우 유용하다.다른 한편, 한 줄에 키 값이 하나일 때 Postgres의 기본 B 트리 인덱스는 최적화됩니다.
금주인지 키스트주인지 선택할 때 나는 금주 지수를 선택하기로 했다.쓰기에는 좀 느릴 수 있지만, 읽기에는 기본 색인보다 빠르다.성능 문제가 발생하지 않도록 로컬에서 색인을 만드는 것을 테스트했습니다. 색인의 데이터량은 생산 데이터와 유사합니다.색인을 만들면 테이블의 쓰기를 잠그고 문제가 있으면 정지할 수 있습니다.이러한 상황을 피하기 위해서, Postgres에 이 색인을 동시에 만드는 것을 알려 드리겠습니다.이것들은 구축하는 데 더 많은 시간이 걸릴 수 있지만, 쓰기 자물쇠를 막을 필요는 없다.
따라서 pg_trgm의 도움말 아래 삼각도를 기반으로 하는 인덱스를 지원합니다. 제가 검색하고 있는 모든 열 (nameemail) 에 GIN 인덱스를 추가하고, Postgres가 삼각도를 사용하여 이 인덱스를 구축하도록 조작부호 gin_trgm_ops을 전달합니다.
CREATE INDEX CONCURRENTLY index_users_on_name_trigram
ON users 
USING gin (name gin_trgm_ops);
CREATE INDEX CONCURRENTLY index_users_on_email_trigram
ON users 
USING gin (email gin_trgm_ops);
이 두 개의 인덱스가 있어서, 나는 조회 운행 시간을 약 60-80ms로 줄이는 데 성공했다🚀.

기타 실적 비고: ILIKELIKE나는 Postgresql ILIKE 함수를 LIKE 함수가 아니라 사용하기로 결정한 데는 두 가지 이유가 있다.하나는 기능 자체와 관계가 있고, 다른 하나는 성능과 관계가 있다.
기능의 주요 차이점은 ILIKE이 대소문자를 구분하지 않는다는 점이다.그래서 만약에'아나'와'아나'가 같은지 알고 싶다면 ILIKE은 이 말이 사실이라는 것을 알려주고 LIKE은 사실이 아니라는 것을 알려준다.
SELECT 'Ana' ILIKE 'ana'; 
-- returns true

SELECT 'Ana' LIKE 'ana'; 
-- returns false
LIKE 함수의 도움말 아래 LOWER도 대소문자를 구분하지 않습니다.
SELECT 'Ana' ILIKE 'ana'; 
--returns true

SELECT LOWER('Ana') LIKE LOWER('ana'); 
-- returns true
그러나 이것은 더 상세하고 오류가 발생하기 쉬운 버전으로 이 기능에 아무런 가치도 부여하지 않는다.그 밖에 ILIKE을 사용할 때 성능이 약간 개선되었다는 것을 알아차렸다.
만약 이 두 방법 사이의 성능 차이를 알고 싶다면, 나는 당신이 this stackoverflow thread을 사용하는 것을 건의합니다.관련 내용을 읽고 몇 가지 기준 테스트를 한 후에 여기는 제 2센트입니다. 색인을 사용하지 않으면 다음 옵션(LIKELOWER)이 첫 번째 옵션(ILIKE)보다 훨씬 빠를 수 있습니다.좋은 색인을 사용할 때, 이러한 차이는 줄어들고 심지어 뒤바뀔 수도 있다.나의 예에서 name, email을 위해 서로 다른 GIN 인덱스를 만들었는데 그 결과 ILIKE 옵션의 성능이 가장 좋다는 것을 알 수 있다(인덱스에 있어서 이런 차이는 그다지 큰 관련성이 없지만).

결론
PostgresSQL pg_trgm을 사용하여 검색 결과의 상관성을 높일 뿐만 아니라 이 기능의 성능도 크게 향상시켰다(전전의 느린 사용자 체험에 비해).최종 조회와 인덱스 솔루션을 제시하기 위해 저는 pgAdmin 을 사용하여 서로 다른 가설을 테스트했습니다.Postgres EXPLAIN ANALYZE은 훌륭한 명령입니다. 저는 전체 개발 과정에서 그것을 대량으로 사용했습니다.모든 문제가 다르다는 것을 명심하십시오. 따라서 저는 Postgres가 제공하는 도구를 사용하여 서로 다른 잠재적 해결 방안을 분석하고 평가하는 것을 권장합니다.

좋은 웹페이지 즐겨찾기