การดูแลระบบ › ค้นหา
เริ่มต้น
เนื้อหา
Plugins
การดูแลระบบ
การ Deploy
สูตรสำเร็จ
ค้นหา
หน้านี้สอนอะไร
ค้นเนื้อหาที่ publish แล้วแบบเรียงตามความเกี่ยวข้อง รวมถึงภาษาไทย ด้วย search adapter: index อะไรบ้าง index ตามทันการแก้ไขอย่างไร และสิทธิ์ของผู้อ่านยังถูกใช้อย่างไร
ควรอ่านก่อน: การตั้งค่า, ฉบับร่าง
ตัวกรอง like หาเอกสารที่ field มีข้อความที่ต้องการ บน Postgres
index: 'trigram' ทำให้เร็วแม้ collection ใหญ่
ทั้งภาษาไทยและภาษาอื่น ซึ่งพอสำหรับรายการใน admin หรือการกรอง field เดียว
แต่ช่องค้นหาบนเว็บต้องการมากกว่านั้น: ค้นครั้งเดียวได้หลาย field และหลาย collection ผลที่ตรงที่สุดขึ้นก่อน และเจอคำที่อยู่ใน rich text หรือ block งานนี้คือหน้าที่ของ search adapter
import { defineConfig, memorySearch } from '@easy-cms/core'
export default defineConfig({
search: memorySearch(),
collections: [
{
slug: 'posts',
drafts: true,
search: { fields: ['title', 'excerpt', 'body', 'seo.description'] },
fields: [/* … */],
},
],
})const { hits, total } = await cms.search('น้ำท่วม', { limit: 10 })
// hits: [{ collection: 'posts', doc: { id, title, … } }, …] เรียงจากตรงที่สุดindex อะไรบ้าง
- เฉพาะ collection ที่มี
searchโดยfieldsคือ field ที่จะ index เรียงจากสำคัญที่สุด: เจอในชื่อเรื่องจะอยู่เหนือเจอในเนื้อหา - field แบบ text, textarea, email, slug และ select, rich text ใช้ข้อความล้วน ส่วน array,
group และ block ใช้ข้อความทั้งหมดข้างใน path ผ่าน group ได้ เช่น
seo.description - เฉพาะเอกสารที่ publish แล้ว ถ้ามี ฉบับร่าง ฉบับร่างจะไม่ถูก index และฉบับร่างที่บันทึกบนเอกสารที่ publish อยู่ จะไม่แทนฉบับ publish ใน index จนกว่าจะ publish การยกเลิก publish หรือลบเอกสารจะเอาออกจาก index
- ถ้าเปิด หลายภาษา แต่ละภาษาถูก index แยกกัน และค้นทีละภาษา
(ภาษาเริ่มต้นถ้าไม่ได้ส่ง
locale)
config ถูกตรวจตอนเริ่ม: field ที่ไม่มีอยู่หรือไม่ใช่ข้อความ หรือ collection ที่มี search
แต่ไม่ได้ตั้ง adapter จะหยุดพร้อมบอกเหตุผล
ผลลัพธ์และสิทธิ์
index รู้แค่ id: cms.search() ถาม index ว่าเอกสารไหนตรงที่สุด แล้วอ่านเอกสารเหล่านั้นแบบ
find ตามตัวเลือกสิทธิ์ที่ส่งมา
// แบบที่ผู้เข้าชมเห็น: ใช้สิทธิ์อ่านของเขา
await cms.search(q, { overrideAccess: false, user: null })- เอกสารที่ผู้อ่านไม่มีสิทธิ์จะไม่อยู่ในหน้าผลลัพธ์ หน้าหนึ่งจึงอาจได้น้อยกว่า
limitส่วนtotalนับทุกเอกสารที่ตรงก่อนกรองสิทธิ์ - ตัวเลือก:
collections(ค่าเริ่มต้นคือทุก collection ที่มีsearch),locale,limit(ค่าเริ่มต้น 10 สูงสุด 100),page,depth
ผ่าน REST ใช้ GET <api>/search รับ q, collections (คั่นด้วยจุลภาค), locale, limit,
page และ depth โดยใช้สิทธิ์ของผู้เรียก
GET /api/cms/search?q=กาแฟ&collections=posts,pages&limit=5command palette ของ admin (⌘K หรือ Ctrl K) แสดงผลจาก index ก่อน แล้วตามด้วยเอกสารที่ชื่อตรง รวมถึงฉบับร่าง
ทำให้ index ตามทัน
การบันทึก publish ยกเลิก publish และลบเอกสาร จะอัปเดต index ทันทีหลังบันทึก โดยอ่านเอกสารใหม่แทนการใช้ข้อมูลจากตอนบันทึก การอัปเดตที่มาช้าหรือซ้ำจึงไม่เสียหาย
- ถ้าไม่ได้เปิด outbox การ index ทำในเบื้องหลัง ถ้าล้มจะ log ไว้ และ index จะค้างอยู่จนเอกสารถูกบันทึกอีกครั้งหรือสร้าง index ใหม่
- ถ้าเปิด
outbox: trueการ index เป็นขั้นหนึ่งของ outbox: retry จนสำเร็จ และถ้ายอมแพ้จะแสดงใน Settings → Jobs → Outbox
easy-cms search:reindex [collection] (หรือ cms.reindexSearch()) สร้าง index ใหม่จากฐานข้อมูล
ใช้หลังเพิ่ม search ให้ collection เปลี่ยน field หรือเพื่อซ่อม index
memorySearch()
adapter ที่มากับ Easy CMS เก็บ index ไว้ในหน่วยความจำของเซิร์ฟเวอร์
- ภาษาไทยที่ไม่เว้นวรรคค้นได้ ตัดคำด้วย
Intl.Segmenterซึ่งใช้การตัดคำแบบพจนานุกรมของ ICU ที่มีใน Node อยู่แล้ว:ข่าวน้ำท่วมกรุงเทพมหานครค้นเจอด้วยน้ำท่วมหรือกรุงเทพ - ทุกคำที่ค้นต้องอยู่ในเอกสาร ที่ต้นคำหรือคร่อมหลายคำ เจอทั้งคำจะอยู่เหนือเจอแค่บางส่วน
- สร้างจากฐานข้อมูลตอนเซิร์ฟเวอร์เริ่ม (การค้นจะรอให้เสร็จ) และแต่ละ process มี index ของตัวเอง
เหมาะกับ dev, test และเว็บเล็กที่รัน process เดียว ถ้าตั้ง
scheduler: falseให้เรียกcms.reindexSearch()เอง - ใน production จะ log คำเตือน: เว็บใหญ่หรือรันหลาย instance ควรใช้ adapter ของ search engine
การ์ด System บน dashboard แสดง adapter ที่ใช้อยู่
เขียน adapter เอง
adapter ของ search engine (Meilisearch, Typesense, OpenSearch…) implement SearchAdapter
จาก @easy-cms/core โดย Easy CMS แปลงเอกสารเป็นข้อความล้วนให้ adapter จึงไม่ต้องรู้จัก field
import type { SearchAdapter } from '@easy-cms/core'
export function mySearch(options: { url: string; apiKey: string }): SearchAdapter {
return {
name: 'my-engine',
apiVersion: 1,
// ก่อน reindex: สร้าง index และตั้ง attribute ที่ค้นได้ตามลำดับนี้
async setup(collections) {},
// { collection, id, locale, text: { title: '…', body: '…' } }: เพิ่มหรือแทนที่
async index(entries) {},
// ทุกภาษาของเอกสารเหล่านี้
async remove(documents) {},
async clear(collection) {},
// { query, collections, locale, limit, offset } → { hits: [{ collection, id }], total }
async search(query) {
return { hits: [], total: 0 }
},
// แสดงให้ admin เห็น ห้ามคืน secret
describe: () => [{ key: 'url', value: options.url }],
}
}- ใช้ลำดับ key ของ
textเป็นน้ำหนักของแต่ละ field ซึ่งมาจากsearch.fields - ข้อความภาษาไทยต้องใช้ engine ที่ตัดคำไทยได้ ตรวจก่อนเลือก
ephemeral: trueบอกว่า index เริ่มว่างในแต่ละ process เหมือนของmemorySearch()
ดู ADR-0056 ว่าทำไม search เป็น adapter แทนที่จะเป็นเซิร์ฟเวอร์ที่ Easy CMS ต้องมี