Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

ขอแนะนำ Upstash Redis Search:การค้นหาที่ทรงพลังและปรับขนาดได้สำหรับข้อมูล Redis ของคุณ

เรากำลังเปิดตัว Upstash Redis Search ในอีก 1-2 สัปดาห์ข้างหน้า แต่ฉันต้องการแบ่งปันความคิดเบื้องต้นเกี่ยวกับสิ่งที่เรากำลังสร้าง และเหตุผลที่ฉันตื่นเต้นกับสิ่งนี้

ทำไมเราถึงสร้างสิ่งนี้

เราอยู่ในแวดวงการค้นหามาตั้งแต่ปี 2024 โดยเริ่มจาก Upstash Vector Vector อนุญาตให้ผู้คนใช้การค้นหาเชิงความหมาย และต่อมาเราก็เพิ่มเข้าไปในกลุ่มผลิตภัณฑ์นี้เป็นสองเท่าด้วย Upstash Search

ตัวอย่างเช่น นี่คือทวีตเปิดตัวของเราในปี 2025 ที่ประกาศ Upstash Search ซึ่งเป็นโซลูชันการค้นหาความหมายแบบเวกเตอร์ของเรา 👇

ขอแนะนำ Upstash Redis Search:การค้นหาที่ทรงพลังและปรับขนาดได้สำหรับข้อมูล Redis ของคุณ

และฉันคิดว่าเราทำได้ดียิ่งขึ้นไปอีกและต่อยอดการเรียนรู้จาก Search

เราไม่พอใจกับผู้ให้บริการค้นหาที่มีอยู่ส่วนใหญ่ สำหรับฉันโดยส่วนตัวแล้ว ไม่มีโปรแกรมใดที่เหมาะกับพื้นที่ไร้เซิร์ฟเวอร์เลย มากเสียจนฉันสร้างแอปค้นหาของตัวเองโดยใช้ AWS Cloudsearch ย้อนกลับไปในปี 2023 ☺

สิ่งที่เราต้องการคือสิ่งที่:

  • อาศัยอยู่ใน Redis เพราะ Redis รวดเร็ว
  • ทำงานร่วมกับ Upstash Redis SDK
  • ปลอดภัยต่อการพิมพ์ 100%
  • รวดเร็วเพียงพอสำหรับการค้นหาแบบเรียลไทม์ขณะที่คุณพิมพ์

ดังนั้นเราจึงสร้างมันขึ้นมา

ส่วนขยายแรกของเรานอกเหนือจาก Redis API

นี่เป็นเรื่องใหญ่สำหรับเรา จนถึงขณะนี้ 01 ได้รับการแมปคำสั่ง Redis ที่ใกล้เคียง 1:1 การค้นหาคือส่วนขยายแรกของเรานอกเหนือจากนั้น

เรากำลังใช้ Tantivy ภายใต้ประทุน ซึ่งเป็นเครื่องมือค้นหาข้อความแบบเต็มที่เขียนด้วยภาษา Rust ซึ่งได้รับแรงบันดาลใจจาก Apache Lucene มันรวดเร็ว รวดเร็วจริงๆ และมันทำให้เรามีพื้นฐานทั้งหมดที่เราต้องการ เช่น โทเค็นไนเซชั่น การกั้น การจับคู่แบบคลุมเครือ ข้อความค้นหาวลี และการให้คะแนน BM25

เป้าหมายคือการทำให้สิ่งนี้ให้ความรู้สึกเป็นธรรมชาติกับ SDK และ Upstash Redis เอง หากคุณใช้ 18 ในปัจจุบัน การเพิ่มการค้นหาควรให้ความรู้สึกเหมือนเป็นส่วนขยายตามธรรมชาติ ไม่ใช่เป็นผลิตภัณฑ์แยกต่างหาก

ตัวสร้างสคีมาแบบปลอดภัย

สิ่งหนึ่งที่ฉันพอใจมากคือตัวสร้างสคีมาใหม่ เรากำหนดฟิลด์ที่สามารถค้นหาได้ด้วย 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,
});

25 และ 31 วิธีการให้เราควบคุมวิธีการประมวลผลข้อความ:

  • การแปลงโทเค็น แยกข้อความเป็นคำที่สามารถค้นหาได้ เหมาะสำหรับภาษาธรรมชาติ แต่ใช้ไม่ได้กับ SKU (45 กลายเป็น 57 ). ปิดการใช้งานสำหรับรหัส อีเมล และ UUID
  • การต่อกิ่ง ลดคำเป็นรูปแบบรากดังนั้น "การวิ่ง" จึงตรงกับ "การวิ่ง" ปิดใช้งานสำหรับชื่อแบรนด์และคำนามเฉพาะที่เราต้องการให้ตรงกันทุกประการ

สคีมาช่วยให้เราสามารถอนุมานข้อความค้นหาได้อย่างเต็มรูปแบบ หากเราพยายามค้นหาฟิลด์ที่ไม่มีอยู่ TypeScript จะตรวจจับได้ เรากำลังรักษาไวยากรณ์ของสคีมาให้ใกล้เคียงกับไวยากรณ์ของโซดมาก ดังนั้นจึงให้ความรู้สึกคุ้นเคยในการใช้งาน

แบบสอบถามเบื้องต้น

เรากำลังเปิดตัวพร้อมกับโอเปอเรเตอร์หลัก 5 ตัวที่เราคิดว่าครอบคลุมกรณีการใช้งานการค้นหาส่วนใหญ่:

60 สำหรับการจับคู่อัจฉริยะ

ด้วยตัวดำเนินการ $smart เราจะใช้การจับคู่อัจฉริยะโดยอัตโนมัติ โอเปอเรเตอร์นี้ควรใช้งานได้™ และเป็นวิธีที่ดีที่สุดสำหรับผู้เริ่มต้นในการเริ่มต้น

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

ภายใต้ประทุน สิ่งนี้จะทำงาน:

  1. การทำงานแบบวลีแบบตรงทั้งหมด (บูสต์สูงสุด) - เอกสารที่มี "หูฟังไร้สาย" ติดกันและเรียงตามลำดับ
  2. วลีที่มีคำหยาบคาย (เน้นเสียงปานกลาง) - เอกสารที่มีคำปรากฏตามลำดับแต่ไม่อยู่ติดกัน (เช่น หูฟัง bose ไร้สาย)
  3. การจับคู่ข้อกำหนด (เพิ่มระดับปานกลาง) - เอกสารที่มีเงื่อนไขทั้งหมด คำสั่งใดๆ
  4. การจับคู่ที่ไม่ชัดเจน (ไม่มีการเร่ง) - เอกสารที่มีการพิมพ์ผิด เช่น "หูฟังไร้สาย"
  5. คำนำหน้าคลุมเครือในคำสุดท้าย (ไม่มีการเพิ่มประสิทธิภาพ) - สำหรับสถานการณ์การค้นหาขณะพิมพ์

คะแนนจะรวมกันเพื่อให้ได้ผลลัพธ์ที่เกี่ยวข้องมากที่สุด สำหรับช่องค้นหาส่วนใหญ่ นี่คือสิ่งที่คุณต้องการอย่างแท้จริง แน่นอนคุณสามารถใช้โอเปอเรเตอร์นี้ด้วยตนเองและลองใช้การตั้งค่าได้ เพราะมันสร้างขึ้นจากโอเปอเรเตอร์พื้นฐานอื่นๆ ด้านล่าง

77 เพื่อความเท่าเทียมกัน

สำหรับช่องที่เราต้องการให้ตรงกันทุกประการ:

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

80 สำหรับการจับคู่วลี

เมื่อเราต้องการให้คำปรากฏติดกันและตามลำดับ:

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

นอกจากนี้เรายังสามารถเพิ่ม 97 ได้อีกด้วย เพื่ออนุญาตคำที่อยู่ระหว่าง:

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

108 สำหรับการทนต่อการพิมพ์ผิด

สำหรับการจับคู่แบบคลุมเครือที่มีความทนทานต่อการพิมพ์ผิดที่กำหนดค่าได้ (เช่น พิมพ์ผิด 2 ครั้ง):

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

113 สำหรับการจับคู่รูปแบบ

เมื่อเราต้องการรูปแบบนิพจน์ทั่วไป:

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

สิ่งหนึ่งที่ควรทราบ:regex ทำงานได้ดีที่สุดในฟิลด์ที่มี 121 เนื่องจากข้อความที่มีต้นกำเนิดไม่ตรงกับรูปแบบที่คาดหวัง

การเพิ่มประสิทธิภาพฟิลด์เฉพาะ

เราสามารถใช้บูสต์เพื่อเพิ่มน้ำหนักให้กับแมตช์บางแมตช์ให้สูงขึ้นได้:

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

ซึ่งจะทำให้การจับคู่ชื่อมีมูลค่ามากกว่าการจับคู่คำอธิบายถึง 2 เท่า สิ่งนี้มีประโยชน์เมื่อเราต้องการให้การจับคู่ชื่อมีอันดับเหนือการจับคู่เนื้อหา

อะไรต่อไป

ทุกสิ่งที่ฉันใส่ลงในบทความนี้ยังคงเปิดกว้างสำหรับการเปลี่ยนแปลง เรายังคงขัดขอบและเขียนเอกสาร การเปิดตัวอย่างเป็นทางการคือในอีก 1-2 สัปดาห์

แต่ฉันคิดว่ามันเจ๋งจริงๆ ที่เราจะได้เห็นด้วยกันก่อน 👀

บางสิ่งที่เราอาจสำรวจหลังการเปิดตัว:

  • การรวมการค้นหาเวกเตอร์ (การค้นหาแบบผสมความหมาย + คำหลัก)
  • เติมข้อความอัตโนมัติและคำแนะนำ

หากคุณต้องการทดลองใช้ก่อนใครหรือมีคำถาม โปรดติดต่อฉันที่ @joshtriedcoding

ขอบคุณที่อ่าน🙌