반응형
반응형

[DataBase] Postgres를 검색엔진으로 활용하기  

 

https://anyblockers.com/posts/postgres-as-a-search-engine

 

Postgres as a search engine

Build a retrieval system with semantic, full-text, and fuzzy search in Postgres to be used as a backbone in RAG pipelines.

anyblockers.com

 

 

 


  • Postgres 내에서 시맨틱, 전문, 퍼지 검색을 모두 갖춘 하이브리드 검색 엔진을 구축할 수 있음
  • 검색은 많은 앱에서 중요한 부분이지만 제대로 구현하기 쉽지 않음. 특히 RAG 파이프라인에서는 검색 품질이 전체 프로세스의 성패를 좌우할 수 있음
  • 의미론적(Semantic) 검색이 트렌디하지만, 전통적인 어휘 기반 검색은 여전히 검색의 중추임
  • 의미론적 기술은 결과를 개선할 수 있지만, 견고한 텍스트 기반 검색의 기반 위에서 가장 잘 작동함

Postgres를 활용한 검색 엔진 구현하기

  • 세 가지 기술을 결합:
    • tsvector를 사용한 전체 텍스트 검색
    • pgvector를 사용한 의미론적 검색
    • pg_trgm을 사용한 퍼지 매칭
  • 이 접근 방식은 모든 상황에서 절대적으로 최고는 아닐 수 있지만, 별도의 검색 서비스를 구축하는 것에 대한 훌륭한 대안임
  • 기존 Postgres 데이터베이스 내에서 구현하고 확장할 수 있는 견고한 출발점
  • Postgres를 모든 것에 사용해야 하는 이유 : 그냥 Postgres를 모든 곳에 사용하세요, PostgreSQL로 충분하다, 그냥 Postgres 쓰세요

FTS와 의미론적 검색 구현

  • Supabase에 하이브리드 검색 구현에 대한 훌륭한 문서가 있으므로, 이를 시작점으로 삼을 것임
  • 가이드에 따라 GIN 인덱스를 사용하여 FTS를 구현하고, pgvector(bi-encoder dense retrieval이라고도 함)를 사용하여 의미론적 검색을 구현함
  • 개인적인 경험으로는 1536차원의 임베딩을 선택하는 것이 훨씬 더 나은 결과를 얻을 수 있음
  • Supabase 함수를 CTE와 쿼리로 대체하고, 매개변수 앞에 $를 붙임.
  • 여기서는 RRF(Reciprocal Ranked Fusion)를 사용하여 결과를 병합함
  • 이 방법은 여러 목록에서 높은 순위를 차지하는 항목이 최종 목록에서 높은 순위를 부여받도록 보장함
  • 또한 일부 목록에서는 높은 순위지만 다른 목록에서는 낮은 순위인 항목이 최종 목록에서 높은 순위를 부여받지 않도록 보장함
  • 순위를 분모에 넣어 점수를 계산하면 순위가 낮은 레코드에 불이익을 줄 수 있음
  • 주목할 만한 사항
    • $rrf_k: 첫 번째 순위 항목의 점수가 극단적으로 높아지는 것을 방지하기 위해(순위로 나누기 때문에), 분모에 k 상수를 추가하여 점수를 평활화하는 경우가 많음
    • $ _weight: 각 방법에 가중치를 할당할 수 있음. 이는 결과를 조정할 때 매우 유용함

퍼지 검색 구현하기

  • 이전까지의 방법으로도 많은 부분을 해결할 수 있지만, 명명된 엔티티에서 오타가 있을 경우 즉각적인 이슈가 발생할 수 있음
  • 의미론적 검색은 유사성을 포착하여 이러한 이슈 중 일부를 제거하지만, 이름, 약어 및 의미론적으로 유사하지 않은 기타 텍스트에 대해서는 어려움을 겪음
  • 이를 완화하기 위해 pg_trgm 확장을 도입하여 퍼지 검색을 허용
    • 트라이그램으로 작동함. 트라이그램은 단어를 3자 시퀀스로 분해하기 때문에 퍼지 검색에 유용
    • 이를 통해 오타나 약간의 변형이 포함되어 있더라도 유사한 단어를 매칭할 수 있음
    • 예를 들어 "hello"와 "helo"는 많은 트라이그램을 공유하므로 퍼지 검색에서 더 쉽게 매칭될 수 있음
  • 원하는 열에 대해 새 인덱스를 생성하고, 그 후 전체 검색 쿼리에 추가
  • pg_trgm 확장은 % 연산자를 노출하여 유사도가 pg_trgm.similarity_threshold(기본값은 0.3)보다 큰 텍스트를 필터링함
  • 유용한 다른 여러 연산자도 있음.

전문 검색 튜닝하기

  • tsvector 가중치 조정하기 :실제 문서에는 제목뿐만 아니라 내용도 포함됨
  • 열이 여러 개 있어도 임베딩 열은 하나만 유지함
  • 개인적으로 여러 임베딩을 유지하는 것보다 title과 body를 같은 임베딩에 유지하는 것이 성능에 큰 차이가 없다는 것을 발견함
  • 결국 title은 본문의 간단한 표현이어야 함. 필요에 따라 이를 실험해 보는 것이 좋음
  • title은 짧고 키워드가 풍부할 것으로 예상되는 반면, body는 더 길고 더 많은 세부 정보를 포함할 것임
  • 따라서 전체 텍스트 검색 열이 서로 어떻게 가중치를 부여하는지 조정해야 함
  • 문서에서 단어가 있는 위치나 중요도에 따라 우선순위를 부여할 수 있음
    • A-weight: 가장 중요(예: 제목, 헤더). 기본값 1.0
    • B-weight: 중요(예: 문서 시작 부분, 요약). 기본값 0.4
    • C-weight: 표준 중요도(예: 본문 텍스트). 기본값 0.2
    • D-weight: 가장 덜 중요(예: 각주, 주석). 기본값 0.1
  • 문서 구조와 애플리케이션 요구사항에 따라 가중치를 조정하여 관련성을 미세 조정함
  • 제목에 더 많은 가중치를 부여하는 이유
    • 제목은 일반적으로 문서의 주요 주제를 간결하게 표현하기 때문
    • 사용자는 검색할 때 먼저 제목을 훑어보는 경향이 있으므로 제목의 키워드 일치는 일반적으로 본문 텍스트의 일치보다 사용자의 의도와 더 관련이 있음

길이에 따른 조정

  • ts_rank_cd 문서를 읽어보면 정규화 매개변수가 있다는 것을 알 수 있음.
    • 두 랭킹 함수 모두 문서의 길이가 순위에 어떤 영향을 미쳐야 하는지 여부를 지정하는 정수 normalization 옵션을 사용함. 정수 옵션은 여러 동작을 제어하므로 비트 마스크임: |를 사용하여 하나 이상의 동작을 지정할 수 있음(예: 2|4).
  • 이러한 다양한 옵션을 사용하여 다음을 수행 가능
    • 문서 길이 편향 조정
    • 다양한 문서 집합에서 관련성 균형 조정
    • 일관된 표현을 위해 랭킹 결과 조정
  • 제목에는 0(정규화 없음), 본문에는 1(로그 문서 길이)을 설정하면 좋은 결과를 얻을 수 있음
  • 다시 말하지만, 사용 사례에 가장 적합한 옵션을 찾기 위해 다양한 옵션을 실험해 보는 것이 좋음

크로스 인코더를 사용한 재랭킹

  • 많은 검색 시스템은 두 단계로 구성됨
  • 즉, 양방향 인코더를 사용하여 초기 N개의 결과를 검색한 다음, 크로스 인코더를 사용하여 이러한 결과를 검색 쿼리와 비교하여 순위를 매김
    • 양방향 인코더(bi-encoder) : 빠르기 때문에 많은 수의 문서를 검색하는 데 좋음
    • 크로스 인코더(cross-encoder)
      • 더 느리지만 성능이 더 좋아 검색된 결과의 순위를 다시 매기는 데 좋음
      • 쿼리와 문서를 함께 처리하여 둘 사이의 관계에 대한 더 미묘한 이해를 가능하게 함
      • 이는 계산 시간과 확장성을 희생하면서 더 나은 랭킹 정확도를 제공함
  • 이를 수행하기 위한 다양한 도구들이 있음
  • 가장 좋은 것 중 하나는 Cohere의 Rerank
  • 또 다른 방법은 OpenAI의 GPT를 사용하여 자체적으로 구축하는 것
  • 크로스 인코더는 쿼리와 문서 간의 관계를 더 잘 이해할 수 있도록 하여 검색 결과의 정확도를 높일 수 있음
  • 그러나 계산 비용이 크기 때문에 확장성 면에서는 제한이 있음
  • 따라서 초기 검색에는 양방향 인코더를 사용하고, 검색된 소수의 문서에 대해서만 크로스 인코더를 적용하는 두 단계 접근 방식이 효과적임

언제 대안 솔루션을 찾아야 할까

  • PostgreSQL은 많은 검색 시나리오에 적합한 선택이지만 한계가 없는 것은 아님
  • BM25와 같은 고급 알고리듬의 부재는 다양한 문서 길이를 처리할 때 느껴질 수 있음
  • PostgreSQL의 전체 텍스트 검색은 TF-IDF에 의존하므로 매우 긴 문서와 대규모 컬렉션의 희귀 용어에 어려움을 겪을 수 있음
  • 대안 솔루션을 찾기 전에 반드시 측정해 보아야 함. 그럴 가치가 없을 수도 있음

결론

  • 이 글에서는 기본적인 전체 텍스트 검색부터 퍼지 매칭, 의미론적 검색, 결과 부스팅과 같은 고급 기술까지 많은 내용을 다루었음
  • Postgres의 강력한 기능을 활용하여 특정 요구사항에 맞춘 강력하고 유연한 검색 엔진을 만들 수 있음
  • Postgres는 검색을 위해 가장 먼저 떠오르는 도구는 아니지만 정말 멀리 갈 수 있게 해줌
  • 훌륭한 검색 경험을 위한 핵심
    • 지속적인 반복과 미세 조정
    • 논의한 디버깅 기법을 사용하여 검색 성능을 이해하고, 사용자 피드백과 행동을 기반으로 가중치와 매개변수를 조정하는 것을 두려워하지 말아야 함
  • PostgreSQL은 고급 검색 기능이 부족할 수 있지만, 대부분의 경우 충분히 강력한 검색 엔진을 구축할 수 있음
  • 대안 솔루션을 찾기 전에 먼저 Postgres의 기능을 최대한 활용하고 성능을 측정해 보는 것이 좋고, 그래도 부족하다면 그때 다른 솔루션을 고려해 볼 수 있음

https://news.hada.io/topic?id=16468&utm_source=weekly&utm_medium=email&utm_campaign=202436

 

Postgres를 검색엔진으로 활용하기 | GeekNews

Postgres 내에서 시맨틱, 전문, 퍼지 검색을 모두 갖춘 하이브리드 검색 엔진을 구축할 수 있음검색은 많은 앱에서 중요한 부분이지만 제대로 구현하기 쉽지 않음. 특히 RAG 파이프라인에서는 검색

news.hada.io

 

 

반응형
반응형

MSSQL 링크드서버,  linked server

 

MSSQL 은 연결된서버 기능을 제공하는데 이를 이용하면 다른 네트워크의 데이터베이스를 원격으로 접속하여

   사용할 수 있도록 해줍니다. 

-- MSSQL 연결된 서버 생성

EXEC sp_addlinkedserver
      @server = '[연결된 서버별칭]',
      @srvproduct = '',
      @provider = 'SQLOLEDB',
      @datasrc = '[서버 아이피]',
      @catalog = '[데이터 베이스명]'



-- MSSQL 연결계정 생성

EXEC sp_addlinkedsrvlogin
      @rmtsrvname= '[연결된 서버별칭]',
      @useself= 'false',
      @rmtuser = '[사용자 이름]',
      @rmtpassword = '[사용자 암호]'
      
      
-- MSSQL 연결된 서버 확인
   SELECT * FROM master.dbo.sysservers WHERE srvname = '[연결된 서버별칭]'
   

-- MSSQL 연결계정 확인
   SELECT * FROM master.sys.linked_logins WHERE remote_name = '[사용자 이름]'
   
   
-- MSSQL 연결된 서버 이용방법 
   /*연결된 서버를 등록한 후 사용하려면 [연결된 서버별칭].[데이터 베이스명].[데이터베이스 소유자명].[테이블명]
   형태로 호출하여 사용할 수 있습니다.
   SELECT 쿼리를 예로 들면 아래와 같습니다. */

 -- MSSQL 일반서버에 SELECT 쿼리시
   SELECT [컬럼명] FROM [테이블명] WHERE [조건절]

-- MSSQL 연결된 서버에 SELECT 쿼리시
   SELECT [컬럼명] FROM [연결된 서버별칭].[데이터 베이스명].[데이터베이스 소유자명].[테이블명] WHERE [조건절]

-- MSSQL 연결된 서버 삭제
  EXEC sp_dropserver
      @server = '[연결된 서버별칭]'

-- MSSQL 연결계정 삭제
   EXEC sp_droplinkedsrvlogin
      @rmtsrvname= '[연결된 서버별칭]',
      @locallogin = NULL
반응형
반응형

SQL Server 모든 테이블 크기를 조회하는 쿼리

테이블의 건수와, 테이블에 구성된 인덱스들의 합도 같이 확인할 수 있습니다.

SELECT
    OBJECT_SCHEMA_NAME(a2.object_id) AS SchemaName,
    a2.name AS TableName,
    a1.rows as [RowCount],
    CAST(ROUND(((a1.reserved + ISNULL(a4.reserved,0)) * 8) / 1024.00, 2) AS NUMERIC(36, 2)) AS ReservedSize_MB,
    CAST(ROUND(a1.data * 8 / 1024.00, 2) AS NUMERIC(36, 2)) AS DataSize_MB,
    CAST(ROUND((CASE WHEN (a1.used + ISNULL(a4.used,0)) > a1.data THEN (a1.used + ISNULL(a4.used,0)) - a1.data ELSE 0 END) * 8 / 1024.00, 2) AS NUMERIC(36, 2)) AS IndexSize_MB,
    CAST(ROUND((CASE WHEN (a1.reserved + ISNULL(a4.reserved,0)) > a1.used THEN (a1.reserved + ISNULL(a4.reserved,0)) - a1.used ELSE 0 END) * 8 / 1024.00, 2) AS NUMERIC(36, 2)) AS UnusedSize_MB
FROM
    (SELECT 
        ps.object_id,
        SUM (CASE WHEN (ps.index_id < 2) THEN row_count ELSE 0 END) AS [rows],
        SUM (ps.reserved_page_count) AS reserved,
        SUM (CASE
                WHEN (ps.index_id < 2) THEN (ps.in_row_data_page_count + ps.lob_used_page_count + ps.row_overflow_used_page_count)
                ELSE (ps.lob_used_page_count + ps.row_overflow_used_page_count)
            END
            ) AS data,
        SUM (ps.used_page_count) AS used
    FROM sys.dm_db_partition_stats ps
    GROUP BY ps.object_id) AS a1
LEFT OUTER JOIN 
    (SELECT 
        it.parent_id,
        SUM(ps.reserved_page_count) AS reserved,
        SUM(ps.used_page_count) AS used
     FROM sys.dm_db_partition_stats ps
     INNER JOIN sys.internal_tables it ON (it.object_id = ps.object_id)
     WHERE it.internal_type IN (202,204)
     GROUP BY it.parent_id) AS a4 ON (a4.parent_id = a1.object_id)
INNER JOIN sys.all_objects a2  ON ( a1.object_id = a2.object_id ) 
WHERE a2.type <> N'S' and a2.type <> N'IT'
ORDER BY ReservedSize_MB DESC
반응형
반응형

구문

JSON_MODIFY ( expression , path , newValue )  

기본 쿼리


DECLARE @info NVARCHAR(100)='{"name":"John","skills":["C#","SQL"]}'

PRINT @info

-- Update name  

SET @info=JSON_MODIFY(@info,'$.name','Mike')

PRINT @info

-- Insert surname  

SET @info=JSON_MODIFY(@info,'$.surname','Smith')

PRINT @info

-- Set name NULL 

SET @info=JSON_MODIFY(@info,'strict $.name',NULL)

PRINT @info

-- Delete name  

SET @info=JSON_MODIFY(@info,'$.name',NULL)

PRINT @info

-- Add skill  

SET @info=JSON_MODIFY(@info,'append $.skills','Azure')

PRINT @info

결과

{
    "name": "John",
    "skills": ["C#", "SQL"]
} {
    "name": "Mike",
    "skills": ["C#", "SQL"]
} {
    "name": "Mike",
    "skills": ["C#", "SQL"],
    "surname": "Smith"
} {
    "skills": ["C#", "SQL"],
    "surname": "Smith"
} {
    "skills": ["C#", "SQL", "Azure"],
    "surname": "Smith"
}

 

https://docs.microsoft.com/ko-kr/sql/t-sql/functions/json-modify-transact-sql?view=sql-server-ver15 

 

 

JSON_MODIFY(Transact-SQL) - SQL Server

JSON_MODIFY(Transact-SQL)

docs.microsoft.com

 

반응형
반응형

▶ 데이터 타입

SQL Server PostgreSQL
BIGINT 64-bit integer BIGINT
BINARY(n) Fixed-length byte string BYTEA
BIT 1, 0 or NULL BOOLEAN
CHAR(n), CHARACTER(n) Fixed-length character string, 1 ⇐ n ⇐ 8000 CHAR(n), CHARACTER(n)
DATE Date (year, month and day) DATE
DATETIME Date and time with fraction TIMESTAMP(3)
DATETIME2(p) Date and time with fraction TIMESTAMP(p)
DATETIMEOFFSET(p) Date and time with fraction and time zone TIMESTAMP(p) WITH TIME ZONE
DECIMAL(p,s), DEC(p,s) Fixed-point number DECIMAL(p,s), DEC(p,s)
DOUBLE PRECISION Double-precision floating-point number DOUBLE PRECISION
FLOAT(p) Floating-point number DOUBLE PRECISION
IMAGE Variable-length binary data, ⇐ 2G BYTEA
INT, INTEGER 32-bit integer INT, INTEGER
MONEY 64-bit currency amount MONEY
NCHAR(n) Fixed-length Unicode UCS-2 string CHAR(n)
NTEXT Variable-length Unicode UCS-2 data, ⇐ 2G TEXT
NUMERIC(p,s) Fixed-point number NUMERIC(p,s)
NVARCHAR(n) Variable-length Unicode UCS-2 string VARCHAR(n)
NVARCHAR(max) Variable-length Unicode UCS-2 data, ⇐ 2G TEXT
REAL Single-precision floating-point number REAL
ROWVERSION Automatically updated binary data BYTEA
SMALLDATETIME Date and time TIMESTAMP(0)
SMALLINT 16-bit integer SMALLINT
SMALLMONEY 32-bit currency amount MONEY
TEXT Variable-length character data, ⇐ 2G TEXT
TIME(p) Time (hour, minute, second and fraction) TIME(p)
TIMESTAMP Automatically updated binary data BYTEA
TINYINT 8-bit unsigned integer, 0 to 255 SMALLINT
UNIQUEIDENTIFIER 16-byte GUID (UUID) data CHAR(16)
VARBINARY(n) Variable-length byte string, 1 ⇐ n ⇐ 8000 BYTEA
VARBINARY(max) Variable-length binary data, ⇐ 2G BYTEA
VARCHAR(n) Variable-length character string, 1 ⇐ n ⇐ 8000 VARCHAR(n)
VARCHAR(max) Variable-length character data, ⇐ 2G TEXT
XML XML data XML

▶ 함수

SQL Server PostgreSQL
DATEADD Add an interval to datetime INTERVAL expression
ISNULL(exp, replacement) Replace NULL with the specified value COALESCE(exp, replacement)



출처: https://icodebroker.tistory.com/6429 [ICODEBROKER]

 

반응형
반응형

PostgreSQL의 프로세스 구조

 

클라이언트는 인터페이스 라이브러리(libpg, JDBC, ODBC 등의 다양한 인터페이스)를 통해 서버와의 연결을 요청(1)하면, Postmaster 프로세스가 서버와의 연결을 중계(2)한다. 이후 클라이언트는 할당된 서버와의 연결을 통해 질의를 수행(3)한다(그림 5).

 

서버 내부의 질의 수행 과정을 간단히 살펴보면 다음과 같다.

 

 

 

*** 한눈에 살펴보는 PostgreSQL : d2.naver.com/helloworld/227936

 

반응형

+ Recent posts