Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

Upstash Redis 검색 소개:Redis 데이터에 대한 강력하고 확장 가능한 검색

앞으로 1~2주 안에 Upstash Redis Search를 출시할 예정이지만, 우리가 구축하고 있는 기능과 이에 대해 기대되는 이유에 대한 초기 생각을 공유하고 싶었습니다.

이것을 구축하는 이유

우리는 Upstash Vector를 시작으로 2024년부터 검색 공간에 있었습니다. 벡터를 통해 사람들은 의미론적 검색을 구현할 수 있었고 나중에 Upstash Search를 통해 이 제품 공간으로 두 배로 확장했습니다.

예를 들어, 이것은 벡터 기반 의미론적 검색 솔루션인 Upstash Search를 발표하는 2025년 출시 트윗입니다 👇

Upstash Redis 검색 소개:Redis 데이터에 대한 강력하고 확장 가능한 검색

그리고 검색을 통해 얻은 교훈을 바탕으로 더 나은 결과를 얻을 수 있다고 생각합니다.

우리는 대부분의 기존 검색 공급자에 대해 매우 만족하지 않습니다. 개인적으로 그들 중 어느 것도 서버리스 공간에 잘 들어맞지 않습니다. 너무 많아서 2023년에는 AWS Cloudsearch를 기반으로 자체 검색 앱을 구축하기도 했습니다 💀

우리가 원했던 것은 다음과 같습니다:

  • Redis가 빠르기 때문에 Redis에 상주
  • Upstash Redis SDK와 작동
  • 100% 유형 안전
  • 입력과 동시에 실시간 검색이 가능할 정도로 빠릅니다.

그래서 우리는 그것을 만들고 있습니다.

Redis API를 넘어서는 첫 번째 확장

이것은 우리에게 큰 문제입니다. 지금까지 @upstash/redis Redis 명령은 거의 1:1로 매핑되었습니다. 검색은 그 이상의 첫 번째 확장 기능입니다.

우리는 Apache Lucene에서 영감을 받아 Rust로 작성된 전체 텍스트 검색 엔진인 Tantivy를 내부적으로 사용하고 있습니다. 빠르다. 정말 빠릅니다. 그리고 토큰화, 형태소 분석, 유사 일치, 구문 쿼리, BM25 채점 등 우리에게 필요한 모든 기본 기능을 제공합니다.

목표는 이것이 SDK 및 Upstash Redis 자체에 고유한 느낌을 갖도록 하는 것입니다. @upstash/redis를 사용하는 경우 오늘날 검색 추가는 별도의 제품이 아닌 자연스러운 확장처럼 느껴져야 합니다.

유형 안전 스키마 빌더

제가 정말 만족하는 점 중 하나는 새로운 스키마 빌더입니다. 우리는 zod와 유사한 API를 사용하여 검색 가능한 필드를 정의합니다:

import { Redis, s } from "@upstash/redis";
 
const redis = Redis.fromEnv();
 
const schema = s.object({
 name: s.string(),
 description: s.string(),
 sku: s.string().noTokenize(),
 brand: s.string().noStem(),
 price: s.number(),
 inStock: s.boolean(),
});
 
const products = await redis.search.createIndex({
 name: "products",
 dataType: "json",
 prefix: "product:",
 schema,
});

.noTokenize() 그리고 .noStem() 메소드를 사용하면 텍스트 처리 방법을 제어할 수 있습니다.

  • 토큰화 텍스트를 검색 가능한 단어로 분할합니다. 자연어에 적합하지만 SKU(SKU-12345-BLK)와 같은 문제가 발생합니다. ["SKU", "12345", "BLK"]이 됩니다. ). 코드, 이메일, UUID에 대해서는 비활성화하세요.
  • 어간 추출 "running"이 "run"과 일치하도록 단어를 어근 형태로 줄입니다. 정확한 일치를 원하는 브랜드 이름과 고유 명사에 대해서는 이 기능을 비활성화하세요.

스키마는 쿼리에 대한 전체 유형 추론을 제공합니다. 존재하지 않는 필드를 쿼리하려고 하면 TypeScript가 이를 포착합니다. 우리는 스키마 구문을 zod 구문과 매우 유사하게 유지하여 사용하기 친숙하게 느껴집니다.

쿼리 프리미티브

우리는 대부분의 검색 사용 사례를 다룰 것으로 생각되는 5개의 주요 연산자를 출시할 예정입니다.

$smart 스마트 매칭을 위해

$smart 연산자를 사용하면 스마트 매칭을 자동으로 적용합니다. 이 연산자는 제대로 작동해야 하며 초보자가 시작하는 가장 좋은 방법입니다.

await products.query({
 filter: {
 name: { $smart: "wirless headphones" },
 },
});

내부적으로는 다음이 실행됩니다:

  1. 정확한 구문 일치 (가장 높은 부스트) - "무선 헤드폰"이 인접하고 순서대로 있는 문서
  2. 슬롭이 포함된 문구 (중간 부스트) - 단어가 순서대로 표시되지만 인접하지 않은 문서(예:무선 Bose 헤드폰)
  3. 용어 일치 (중간 부스트) - 모든 용어, 순서를 포함하는 문서
  4. 퍼지 매칭 (부스트 없음) - "무선 헤드폰"과 같은 오타가 있는 문서
  5. 마지막 단어의 접두어가 흐릿함 (부스트 없음) - 입력에 따른 검색 시나리오용

점수는 가장 관련성이 높은 결과를 얻기 위해 결합됩니다. 대부분의 검색창에는 말 그대로 이것이 필요한 전부입니다. 물론 이 연산자는 아래의 다른 기본 연산자를 기반으로 구축되었으므로 직접 구현하고 설정을 조작할 수 있습니다.

$eq 정확한 평등을 위해

정확한 일치를 원하는 필드의 경우:

await products.query({
 filter: {
 name: { $eq: "wireless headphones" },
 price: { $eq: 200 },
 },
});

$phrase 구문 일치를 위해

단어가 인접하고 순서대로 표시되어야 하는 경우:

await products.query({
 filter: { description: { $phrase: "noise cancelling" } },
});

slop을 추가할 수도 있습니다. 사이에 단어를 허용하려면:

await products.query({
 filter: {
 description: {
 $phrase: { value: "wireless headphones", slop: 2 },
 },
 },
});

$fuzzy 오타 허용

오타 허용 범위를 구성할 수 있는 퍼지 매칭의 경우(예:오타 2개):

await products.query({
 filter: { name: { $fuzzy: "headphonse", distance: 2 } },
});

$regex 패턴 일치를 위해

정규식 패턴이 필요한 경우:

await products.query({
 filter: { sku: { $regex: "SKU-[0-9]{5}-.*" } },
});

한 가지 참고할 점:정규식은 .noStem()가 있는 필드에서 가장 잘 작동합니다. 파생된 텍스트는 예상 패턴과 일치하지 않기 때문입니다.

특정 필드 강화

특정 경기에 더 높은 가중치를 부여하기 위해 부스트를 적용할 수 있습니다:

await products.query({
 filter: {
 $and: [
 { name: { $smart: "wireless", $boost: 2 } },
 { description: { $smart: "wireless" } },
 ],
 },
});

이렇게 하면 이름 일치가 설명 일치보다 두 배 더 가치가 있게 됩니다. 이는 제목 일치 항목이 본문 일치 항목보다 높은 순위를 차지하도록 할 때 유용합니다.

다음 단계

이 기사에 포함된 모든 내용은 여전히 변경될 수 있습니다. 우리는 여전히 가장자리를 다듬고 문서를 작성하는 중입니다. 정식 출시는 1~2주 후입니다.

그런데 같이 먼저 살펴 볼 수 있어서 정말 좋은 것 같아요 GW

출시 후 살펴볼 몇 가지 사항:

  • 벡터 검색 통합(의미 + 키워드 하이브리드 검색)
  • 자동완성 및 제안사항

조기 액세스를 원하거나 질문이 있는 경우 @joshtriedcoding에 문의하세요.

읽어주셔서 감사합니다 🙌