บทช่วยสอน Elasticsearch สำหรับ .NET Developers
เผยแพร่แล้ว: 2022-03-11นักพัฒนา .NET ควรใช้ Elasticsearch ในโครงการของตนหรือไม่ แม้ว่า Elasticsearch จะสร้างขึ้นบน Java แต่ฉันเชื่อว่ามีเหตุผลหลายประการที่ทำให้ Elasticsearch คุ้มค่าสำหรับการค้นหาข้อความแบบเต็มสำหรับโครงการใดๆ
Elasticsearch ในฐานะเทคโนโลยีได้พัฒนามาไกลมากในช่วงไม่กี่ปีที่ผ่านมา ไม่เพียงแต่ทำให้การค้นหาข้อความแบบเต็มรู้สึกเหมือนมายากล แต่ยังมีคุณสมบัติที่ซับซ้อนอื่นๆ เช่น การเติมข้อความอัตโนมัติ ไปป์ไลน์การรวม และอื่นๆ
หากความคิดที่จะแนะนำบริการที่ใช้ Java ให้กับระบบนิเวศ .NET ที่เรียบร้อยของคุณทำให้คุณรู้สึกไม่สบายใจ ก็ไม่ต้องกังวล เพราะเมื่อคุณติดตั้งและกำหนดค่า Elasticsearch แล้ว คุณจะใช้เวลาส่วนใหญ่กับหนึ่งในแพ็คเกจ .NET ที่ยอดเยี่ยมที่สุด ที่นั่น: เนสท์
ในบทความนี้ คุณจะได้เรียนรู้วิธีใช้โซลูชันเสิร์ชเอ็นจิ้นที่น่าตื่นตาตื่นใจอย่าง Elasticsearch ในโครงการ .NET ของคุณ
การติดตั้งและการกำหนดค่า
การติดตั้ง Elasticsearch ลงในสภาพแวดล้อมการพัฒนาของคุณนั้นขึ้นอยู่กับการดาวน์โหลด Elasticsearch และ Kibana หรือไม่ก็ได้
เมื่อคลายซิป ไฟล์ bat แบบนี้จะสะดวก:
cd "D:\elastic\elasticsearch-5.2.2\bin" start elasticsearch.bat cd "D:\elastic\kibana-5.0.0-windows-x86\bin" start kibana.bat exit
หลังจากเริ่มบริการทั้งสองแล้ว คุณสามารถตรวจสอบเซิร์ฟเวอร์ Kibana ในพื้นที่ได้เสมอ (โดยปกติจะมีอยู่ที่ http://localhost:5601) ลองใช้ดัชนีและประเภท และค้นหาโดยใช้ JSON แท้ ดังที่อธิบายไว้อย่างละเอียดที่นี่
ขั้นตอนแรก
การเป็นนักพัฒนาที่ละเอียดและดี ด้วยการสนับสนุนและความเข้าใจอย่างสมบูรณ์จากฝ่ายบริหาร คุณเริ่มต้นด้วยการเพิ่มโครงการทดสอบหน่วยและเขียน SearchService ที่มีโค้ดครอบคลุมอย่างน้อย 90%
ขั้นตอนแรกคือการกำหนดค่าไฟล์ app.config
อย่างชัดเจนเพื่อจัดเตรียมสตริงการเชื่อมต่อสำหรับเซิร์ฟเวอร์ Elasticsearch
สิ่งที่ยอดเยี่ยมเกี่ยวกับ Elasticsearch คือมันฟรีทั้งหมด แต่ฉันยังคงแนะนำให้ใช้บริการ Elastic Cloud ของ Elastic.co บริการโฮสต์ทำให้การบำรุงรักษาและการกำหนดค่าทั้งหมดค่อนข้างง่าย ยิ่งไปกว่านั้น คุณมีเวลาทดลองใช้งานฟรีสองสัปดาห์ ซึ่งน่าจะมากเกินพอที่จะลองใช้ตัวอย่างทั้งหมดได้ที่นี่!
เนื่องจากเราใช้งานในพื้นที่นี้ คีย์การกำหนดค่าเช่นนี้ควรทำดังนี้:
<add key="Search-Uri" value="http://localhost:9200" />
การติดตั้ง Elasticsearch ทำงานบนพอร์ต 9200 โดยค่าเริ่มต้น แต่คุณสามารถเปลี่ยนได้หากต้องการ
ElasticClient และแพ็คเกจ NEST
ElasticClient เป็นเพื่อนตัวน้อยที่ดีที่จะทำงานให้กับเราเป็นส่วนใหญ่ และมาพร้อมกับแพ็คเกจ NEST
ให้เราติดตั้งแพ็คเกจก่อน
ในการกำหนดค่าไคลเอนต์ คุณสามารถใช้สิ่งนี้:
var node = new Uri(ConfigurationManager.AppSettings["Search-Uri"]); var settings = new ConnectionSettings(node); settings.ThrowExceptions(alwaysThrow: true); // I like exceptions settings.PrettyJson(); // Good for DEBUG var client = new ElasticClient(settings);
การจัดทำดัชนีและการทำแผนที่
เพื่อให้สามารถค้นหาบางสิ่งได้ เราต้องจัดเก็บข้อมูลบางอย่างไว้ใน ES คำที่ใช้คือ "การจัดทำดัชนี"
คำว่า "การทำแผนที่" ใช้สำหรับการทำแผนที่ข้อมูลของเราในฐานข้อมูลกับอ็อบเจ็กต์ซึ่งจะถูกทำให้เป็นอนุกรมและจัดเก็บไว้ใน Elasticsearch เราจะใช้ Entity Framework (EF) ในบทช่วยสอนนี้
โดยทั่วไป เมื่อใช้ Elasticsearch คุณอาจกำลังมองหาโซลูชันเครื่องมือค้นหาทั่วทั้งไซต์ คุณจะใช้ฟีดหรือไดเจสต์บางประเภท หรือการค้นหาแบบ Google ซึ่งส่งคืนผลลัพธ์ทั้งหมดจากเอนทิตีต่างๆ เช่น ผู้ใช้ รายการบล็อก ผลิตภัณฑ์ หมวดหมู่ เหตุการณ์ ฯลฯ
สิ่งเหล่านี้อาจไม่ใช่แค่ตารางหรือเอนทิตีเดียวในฐานข้อมูลของคุณ แต่คุณจะต้องการรวบรวมข้อมูลที่หลากหลาย และอาจดึงหรือได้มาซึ่งคุณสมบัติทั่วไปบางอย่าง เช่น ชื่อ คำอธิบาย วันที่ ผู้เขียน/เจ้าของ รูปภาพ และอื่นๆ อีกอย่างคือ คุณจะไม่ทำในแบบสอบถามเดียว แต่ถ้าคุณใช้ ORM คุณจะต้องเขียนข้อความค้นหาแยกต่างหากสำหรับแต่ละรายการในบล็อก ผู้ใช้ ผลิตภัณฑ์ หมวดหมู่ เหตุการณ์ หรืออย่างอื่น
ฉันจัดโครงสร้างโครงการของฉันโดยสร้างดัชนีสำหรับประเภท "ใหญ่" แต่ละประเภท เช่น บล็อกโพสต์หรือผลิตภัณฑ์ คุณสามารถเพิ่ม Elasticsearch บางประเภทสำหรับประเภทที่เจาะจงมากขึ้นซึ่งจะอยู่ภายใต้ดัชนีเดียวกัน ตัวอย่างเช่น หากบทความสามารถเป็นเรื่องราว บทความวิดีโอ หรือพอดแคสต์ บทความนั้นจะยังคงอยู่ในดัชนี "บทความ" แต่เราจะมีสี่ประเภทดังกล่าวภายในดัชนีนั้น อย่างไรก็ตาม ก็ยังมีแนวโน้มที่จะเป็นแบบสอบถามเดียวกันในฐานข้อมูล
โปรดทราบว่าคุณต้องการอย่างน้อยหนึ่งประเภทสำหรับแต่ละดัชนี—อาจเป็นประเภทที่มีชื่อเดียวกันกับดัชนี
ในการแมปเอนทิตีของคุณ คุณจะต้องสร้างคลาสเพิ่มเติม ฉันมักจะใช้คลาส DocumentSearchItemBase
ซึ่งแต่ละคลาสเฉพาะจะสืบทอด BlogPostSearchItem
, ProductSearchItem
เป็นต้น
ฉันชอบที่จะมีนิพจน์ mapper ภายในคลาสเหล่านั้น ฉันปรับเปลี่ยนนิพจน์ได้เสมอหากต้องการ
ในโปรเจ็กต์แรกสุดของฉันกับ Elasticsearch ฉันเขียนคลาส SearchService ที่ค่อนข้างใหญ่พร้อมการแมปและการจัดทำดัชนีด้วยคำสั่ง switch-case ที่ดีและยาว: สำหรับเอนทิตีแต่ละประเภทที่ฉันต้องการใส่ลงใน Elasticsearch มีสวิตช์และคิวรีที่มีการแมปซึ่ง ทำอย่างนั้น
อย่างไรก็ตาม ตลอดกระบวนการนี้ ฉันได้เรียนรู้ว่านี่ไม่ใช่วิธีที่ดีที่สุด อย่างน้อยก็ไม่ใช่สำหรับฉัน
โซลูชันที่หรูหรากว่าคือการมีคลาส IndexDefinition
ชาญฉลาดและคลาสนิยามดัชนีเฉพาะสำหรับแต่ละดัชนี ด้วยวิธีนี้ คลาส IndexDefinition
พื้นฐานของฉันสามารถจัดเก็บรายการของดัชนีที่มีอยู่ทั้งหมดและวิธีการช่วยเหลือบางอย่าง เช่น ตัววิเคราะห์ที่จำเป็นและรายงานสถานะ ในขณะที่คลาสเฉพาะดัชนีที่ได้รับจะจัดการกับการสืบค้นฐานข้อมูลและการแมปข้อมูลสำหรับแต่ละดัชนีโดยเฉพาะ สิ่งนี้มีประโยชน์โดยเฉพาะอย่างยิ่งเมื่อคุณต้องเพิ่มเอนทิตีเพิ่มเติมใน ES ในภายหลัง การเพิ่มคลาส SomeIndexDefinition
อีกคลาสหนึ่งซึ่งสืบทอดมาจาก IndexDefinition
และต้องการให้คุณใช้วิธีการสองสามวิธีที่จะสืบค้นข้อมูลที่คุณต้องการในดัชนีของคุณ
The Elasticsearch พูด
แก่นแท้ของทุกสิ่งที่คุณสามารถทำได้ด้วย Elasticsearch คือภาษาที่ใช้ค้นหา ตามหลักการแล้ว สิ่งที่คุณต้องมีเพื่อสื่อสารกับ Elasticsearch ก็คือรู้วิธีสร้างวัตถุคิวรี
เบื้องหลัง Elasticsearch เปิดเผยฟังก์ชันการทำงานเป็น API แบบ JSON ผ่าน HTTP
แม้ว่าตัว API เองและโครงสร้างของออบเจกต์การสืบค้นจะค่อนข้างใช้งานง่าย แต่การจัดการกับสถานการณ์ในชีวิตจริงหลายๆ สถานการณ์ก็ยังคงเป็นเรื่องที่ยุ่งยากอยู่
โดยทั่วไป คำขอค้นหาไปยัง Elasticsearch ต้องการข้อมูลต่อไปนี้:
ดัชนีใดและประเภทใดที่จะค้นหา
ข้อมูลการแบ่งหน้า (ต้องข้ามกี่รายการ และส่งคืนกี่รายการ)
การเลือกประเภทที่เป็นรูปธรรม (เมื่อทำการรวมเช่นที่เรากำลังจะทำที่นี่)
แบบสอบถามเอง
คำจำกัดความของไฮไลต์ (Elasticsearch สามารถเน้น Hit โดยอัตโนมัติหากต้องการ)
ตัวอย่างเช่น คุณอาจต้องการใช้คุณลักษณะการค้นหาที่มีเฉพาะผู้ใช้บางรายเท่านั้นที่สามารถเห็นเนื้อหาพรีเมียมบนไซต์ของคุณ หรือคุณอาจต้องการให้เฉพาะ "เพื่อน" ของผู้เขียนมองเห็นเนื้อหาบางอย่างเท่านั้น เป็นต้น
ความสามารถในการสร้างออบเจ็กต์คิวรีเป็นหัวใจสำคัญของการแก้ปัญหาเหล่านี้ และอาจเป็นปัญหาได้เมื่อพยายามครอบคลุมสถานการณ์ต่างๆ มากมาย
จากทั้งหมดข้างต้น สิ่งสำคัญและยากที่สุดในการตั้งค่าคือกลุ่มการสืบค้นข้อมูล และที่นี่ เราจะเน้นที่ส่วนนั้นเป็นหลัก
เคียวรีเป็นโครงสร้างแบบเรียกซ้ำที่รวม BoolQuery
และเคียวรีอื่นๆ เช่น MatchPhraseQuery
, TermsQuery
, DateRangeQuery
และ ExistsQuery
สิ่งเหล่านี้เพียงพอที่จะตอบสนองความต้องการขั้นพื้นฐาน และน่าจะดีสำหรับการเริ่มต้น
แบบสอบถาม MultiMatch
ค่อนข้างสำคัญ เนื่องจากช่วยให้เราสามารถระบุฟิลด์ที่เราต้องการทำการค้นหาและปรับแต่งผลลัพธ์อีกเล็กน้อย ซึ่งเราจะกลับมาดูในภายหลัง
MatchPhraseQuery
สามารถกรองผลลัพธ์ตามสิ่งที่จะเป็นคีย์นอกในฐานข้อมูล SQL แบบธรรมดาหรือค่าคงที่ เช่น enums—เช่น เมื่อจับคู่ผลลัพธ์โดยผู้เขียนเฉพาะ ( AuthorId
) หรือจับคู่บทความสาธารณะทั้งหมด ( ContentPrivacy=Public
)
TermsQuery
จะถูกแปลเป็น "ใน" เป็นภาษา SQL ทั่วไป ตัวอย่างเช่น สามารถส่งคืนบทความทั้งหมดที่เขียนโดยเพื่อนของผู้ใช้รายใดรายหนึ่งหรือรับผลิตภัณฑ์จากกลุ่มผู้ค้าคงที่เท่านั้น เช่นเดียวกับ SQL เราไม่ควรใช้สิ่งนี้มากเกินไปและทำให้สมาชิก 10,000 รายในอาร์เรย์นี้เนื่องจากจะมีผลกระทบต่อประสิทธิภาพ แต่โดยทั่วไปจะจัดการกับปริมาณที่เหมาะสมได้ค่อนข้างดี
DateRangeQuery
จัดทำเอกสารด้วยตนเอง
ExistsQuery
เป็นสิ่งที่น่าสนใจ: ช่วยให้คุณสามารถละเว้นหรือส่งคืนเอกสารที่ไม่มีฟิลด์เฉพาะ
เมื่อรวมกับ BoolQuery
เหล่านี้จะช่วยให้คุณสามารถกำหนดตรรกะการกรองที่ซับซ้อนได้
ลองนึกถึงไซต์บล็อก เช่น ที่โพสต์ในบล็อกสามารถมีฟิลด์ AvailableFrom
ซึ่งระบุว่าเมื่อใดควรปรากฏให้เห็น
หากเราใช้ตัวกรองอย่าง AvailableFrom <= Now
เราจะไม่ได้รับเอกสารที่ไม่มีฟิลด์นั้นเลย (เรารวบรวมข้อมูล และเอกสารบางฉบับอาจไม่ได้กำหนดฟิลด์นั้นไว้) ในการแก้ปัญหา คุณจะต้องรวม ExistsQuery
กับ DateRangeQuery
และรวมไว้ใน BoolQuery
โดยมีเงื่อนไขว่าองค์ประกอบอย่างน้อยหนึ่งองค์ประกอบใน BoolQuery
ได้รับการเติมเต็ม บางอย่างเช่นนี้:
BoolQuery Should (at least one of the following conditions should be fulfilled) DateRangeQuery with AvailableFrom condition Negated ExistsQuery for field AvailableFrom
การปฏิเสธการสืบค้นไม่ใช่งานที่ทำได้ทันที แต่ด้วยความช่วยเหลือของ BoolQuery
ก็ยังเป็นไปได้:

BoolQuery MustNot ExistsQuery
ระบบอัตโนมัติและการทดสอบ
เพื่อให้ง่ายขึ้น วิธีที่แนะนำคือการเขียนแบบทดสอบในขณะที่คุณทำ
ด้วยวิธีนี้ คุณจะสามารถทดสอบได้อย่างมีประสิทธิภาพมากขึ้น และที่สำคัญยิ่งกว่านั้น คุณจะต้องแน่ใจว่าการเปลี่ยนแปลงใหม่ใดๆ ที่คุณแนะนำ (เช่น ตัวกรองที่ซับซ้อนมากขึ้น) จะไม่ทำลายฟังก์ชันที่มีอยู่ ฉันไม่ต้องการพูดว่า "การทดสอบหน่วย" อย่างชัดเจน เนื่องจากฉันไม่ชอบล้อเลียนบางอย่าง เช่น เอ็นจิ้นของ Elasticsearch การเยาะเย้ยแทบจะไม่เคยเป็นการประมาณที่เหมือนจริงว่า ES มีพฤติกรรมอย่างไร ดังนั้น นี่อาจเป็นการทดสอบการรวม ถ้า คุณเป็นแฟนคำศัพท์
ตัวอย่างในโลกแห่งความเป็นจริง
หลังจากที่งานพื้นฐานทั้งหมดเสร็จสิ้นด้วยการทำดัชนี การทำแผนที่ และการกรอง ตอนนี้เราพร้อมแล้วสำหรับส่วนที่น่าสนใจที่สุด: การปรับแต่งพารามิเตอร์การค้นหาเพื่อให้ได้ผลลัพธ์ที่ดีขึ้น
ในโครงการที่แล้วของฉัน ฉันใช้ Elasticsearch เพื่อจัดเตรียมฟีดผู้ใช้: เนื้อหาทั้งหมดรวมอยู่ในที่เดียวโดยเรียงลำดับตามวันที่สร้างและค้นหาข้อความแบบเต็มพร้อมตัวเลือกบางอย่าง ตัวฟีดนั้นค่อนข้างตรงไปตรงมา เพียงตรวจสอบให้แน่ใจว่ามีฟิลด์วันที่อยู่ในข้อมูลของคุณและเรียงลำดับตามฟิลด์นั้น
ในทางกลับกัน การค้นหาจะไม่ทำงานอย่างน่าอัศจรรย์เมื่อแกะกล่อง นั่นก็เพราะว่าโดยธรรมชาติแล้ว Elasticsearch ไม่สามารถรู้ได้ว่าข้อมูลของคุณมีความสำคัญอะไร สมมติว่าเรามีข้อมูลบางส่วนซึ่ง (ในฟิลด์อื่นๆ) มีฟิลด์ Title
Tags
(array) และ Body
ฟิลด์ body สามารถเป็นเนื้อหา HTML ได้ (เพื่อทำให้สิ่งต่าง ๆ สมจริงยิ่งขึ้น)
สะกดผิด
ข้อกำหนด: การค้นหาของเราควรส่งคืนผลลัพธ์แม้ว่าจะเกิดข้อผิดพลาดในการสะกดคำหรือคำลงท้ายต่างกัน ตัวอย่างเช่น หากมีบทความชื่อ "Magnificent Things You Can Do with a Wooden Spoon" เมื่อฉันค้นหาคำว่า "thing" หรือ "wood" ฉันก็ยังต้องการจับคู่
เพื่อจัดการกับสิ่งนี้ เราจะต้องทำความคุ้นเคยกับตัววิเคราะห์ ตัวสร้างโทเค็น ตัวกรองถ่าน และตัวกรองโทเค็น สิ่งเหล่านี้คือการแปลงที่ใช้ในขณะที่สร้างดัชนี
ต้องมีการกำหนดตัววิเคราะห์ สามารถกำหนดได้ต่อดัชนี
ตัววิเคราะห์สามารถนำไปใช้กับบางฟิลด์ในเอกสารของเรา สามารถทำได้โดยใช้แอตทริบิวต์หรือ API ที่คล่องแคล่ว ในตัวอย่างของเรา เรากำลังใช้แอตทริบิวต์
ตัววิเคราะห์คือการรวมกันของตัวกรอง ตัวกรองอักขระ และตัวสร้างโทเค็น
เพื่อให้เป็นไปตามข้อกำหนด (การจับคู่คำเพียงบางส่วน) เราจะสร้างตัววิเคราะห์ "การเติมข้อความอัตโนมัติ" ซึ่งประกอบด้วย:
ตัวกรองคำหยุดภาษาอังกฤษ: ตัวกรองที่ลบคำทั่วไปทั้งหมดในภาษาอังกฤษ เช่น "และ" หรือ "the"
ตัวกรองการตัดแต่ง: ลบพื้นที่สีขาวรอบๆ แต่ละโทเค็น
ตัวกรองตัวพิมพ์เล็ก: แปลงอักขระทั้งหมดเป็นตัวพิมพ์เล็ก นี่ไม่ได้หมายความว่าเมื่อเราดึงข้อมูล ข้อมูลจะถูกแปลงเป็นตัวพิมพ์เล็ก แต่เปิดใช้งานการค้นหาแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่แทน
ตัวสร้างโทเค็น Edge-n-gram: ตัวสร้างโทเค็นนี้ช่วยให้เราสามารถจับคู่บางส่วนได้ ตัวอย่างเช่น ถ้าเรามีประโยค “ย่าของฉันมีเก้าอี้ไม้” เมื่อมองหาคำว่า “ไม้” เราก็ยังคงอยากโดนประโยคนั้นอยู่ดี สิ่งที่ edge-n-gram ทำคือเก็บ "woo" "wood" "woode" และ "wooden" เพื่อให้พบคำบางส่วนที่ตรงกับตัวอักษรอย่างน้อยสามตัว พารามิเตอร์ MinGram และ MaxGram กำหนดจำนวนอักขระต่ำสุดและสูงสุดที่จะจัดเก็บ ในกรณีของเรา เราจะมีตัวอักษรอย่างน้อยสามตัวและสูงสุด 15 ตัว
ในส่วนต่อไปนี้ สิ่งเหล่านั้นทั้งหมดจะถูกผูกไว้ด้วยกัน:
analysis.Analyzers(a => a .Custom("autocomplete", cc => cc .Filters("eng_stopwords", "trim", "lowercase") .Tokenizer("autocomplete") ) .Tokenizers(tdesc => tdesc .EdgeNGram("autocomplete", e => e .MinGram(3) .MaxGram(15) .TokenChars(TokenChar.Letter, TokenChar.Digit) ) ) .TokenFilters(f => f .Stop("eng_stopwords", lang => lang .StopWords("_english_") ) );
และเมื่อเราต้องการใช้ตัววิเคราะห์นี้ เราควรใส่คำอธิบายประกอบลงในฟิลด์ที่เราต้องการดังนี้:
public class SearchItemDocumentBase { ... [Text(Analyzer = "autocomplete", Name = nameof(Title))] public string Title { get; set; } ... }
ตอนนี้ มาดูตัวอย่างบางส่วนที่แสดงความต้องการทั่วไปในเกือบทุกแอปพลิเคชันที่มีเนื้อหามากมาย
ทำความสะอาด HTML
ข้อกำหนด: ฟิลด์บางฟิลด์ของเราอาจมีข้อความ HTML อยู่ภายใน
โดยปกติ คุณคงไม่อยากค้นหา "section" เพื่อแสดงบางอย่างเช่น "<section>…</section>" หรือ "body" ส่งคืนองค์ประกอบ HTML "<body>" เพื่อหลีกเลี่ยงปัญหานั้น ในระหว่างการสร้างดัชนี เราจะตัด HTML ออกและเหลือแต่เนื้อหาไว้ข้างใน
โชคดีที่คุณไม่ใช่คนแรกที่มีปัญหานั้น Elasticsearch มาพร้อมกับตัวกรองถ่านที่มีประโยชน์สำหรับสิ่งนั้น:
analysis.Analyzers(a => a .Custom("html_stripper", cc => cc .Filters("eng_stopwords", "trim", "lowercase") .CharFilters("html_strip") .Tokenizer("autocomplete") )
และนำไปใช้:
[Text(Analyzer = "html_stripper", Name = nameof(HtmlText))] public string HtmlText { get; set; }
สาขาสำคัญ
ข้อกำหนด: การ จับคู่ในชื่อควรมีความสำคัญมากกว่าการจับคู่ภายในเนื้อหา
โชคดีที่ Elasticsearch เสนอกลยุทธ์ในการเพิ่มผลลัพธ์หากการแข่งขันเกิดขึ้นในฟิลด์ใดฟิลด์หนึ่ง สิ่งนี้ทำได้ภายในการสร้างคำค้นหาโดยใช้ตัวเลือกการ boost
:
const int titleBoost = 15; .Query(qx => qx.MultiMatch(m => m .Query(searchRequest.Query.ToLower()) .Fields(ff => ff .Field(f => f.Title, boost: titleBoost) .Field(f => f.Summary) ... ) .Type(TextQueryType.BestFields) ) && filteringQuery)
อย่างที่คุณเห็น แบบสอบถาม MultiMatch
มีประโยชน์มากในสถานการณ์เช่นนี้ และสถานการณ์เช่นนี้ไม่ได้หายากเลย! บ่อยครั้งที่บางสาขามีความสำคัญมากกว่าและบางสาขาไม่มีความสำคัญ—กลไกนี้ช่วยให้เราพิจารณาสิ่งนั้นได้
มันไม่ง่ายเสมอไปที่จะตั้งค่าบูสต์ในทันที คุณจะต้องเล่นกับสิ่งนี้เล็กน้อยเพื่อให้ได้ผลลัพธ์ที่ต้องการ
จัดลำดับความสำคัญบทความ
ข้อกำหนด: บางบทความมีความสำคัญมากกว่าบทความอื่นๆ ไม่ว่าผู้เขียนจะมีความสำคัญมากกว่า หรือตัวบทความเองก็มีไลค์/แชร์/อัพโหวต/อื่นๆ มากกว่า บทความที่สำคัญกว่าควรอยู่ในอันดับที่สูงกว่า
Elasticsearch ช่วยให้เราใช้ฟังก์ชันการให้คะแนนของเรา และเราลดความซับซ้อนในลักษณะที่เรากำหนดฟิลด์ "ความสำคัญ" ซึ่งเป็นค่าสองเท่า ในกรณีของเรา มากกว่า 1 คุณสามารถกำหนดฟังก์ชัน/ปัจจัยความสำคัญของคุณเอง และนำไปใช้ ในทำนองเดียวกัน คุณสามารถกำหนดโหมดเพิ่มและให้คะแนนได้หลายแบบ—แล้วแต่แบบใดที่เหมาะกับคุณที่สุด สิ่งนี้ใช้ได้ผลดีสำหรับเรา:
.Query(q => q .FunctionScore(fsc => fsc .BoostMode(FunctionBoostMode.Multiply) .ScoreMode(FunctionScoreMode.Sum) .Functions(f => f .FieldValueFactor(b => b .Field(nameof(SearchItemDocumentBase.Rating)) .Missing(0.7) .Modifier(FieldValueFactorModifier.None) ) ) .Query(qx => qx.MultiMatch(m => m .Query(searchRequest.Query.ToLower()) .Fields(ff => ff ... ) .Type(TextQueryType.BestFields) ) && filteringQuery) ) )
ภาพยนตร์แต่ละเรื่องมีการจัดเรต และเราสรุปการให้คะแนนนักแสดงด้วยคะแนนเฉลี่ยสำหรับภาพยนตร์ที่พวกเขาคัดเลือก (ไม่ใช่วิธีการทางวิทยาศาสตร์มากนัก) เราปรับขนาดการให้คะแนนนั้นเป็นค่าสองเท่าในช่วง [0,1]
การจับคู่คำแบบเต็ม
ข้อกำหนด: การจับคู่ทั้งคำควรอยู่ในอันดับที่สูงกว่า
ถึงตอนนี้ เราได้รับผลลัพธ์ที่ค่อนข้างดีสำหรับการค้นหาของเรา แต่คุณอาจสังเกตเห็นว่าผลลัพธ์บางรายการที่มีการจับคู่บางส่วนอาจอยู่ในอันดับที่สูงกว่าการจับคู่แบบตรงทั้งหมด เพื่อจัดการกับเรื่องนั้น เราได้เพิ่มฟิลด์เพิ่มเติมในเอกสารของเราที่ชื่อว่า "คำหลัก" ซึ่งไม่ได้ใช้ตัววิเคราะห์การเติมข้อความอัตโนมัติ แต่ใช้ตัวสร้างโทเค็นของคำหลักแทน และให้ปัจจัยกระตุ้นเพื่อผลักดันผลลัพธ์การจับคู่แบบตรงทั้งหมดให้สูงขึ้น
ช่องนี้จะตรงกันก็ต่อเมื่อตรงกับคำที่ตรงกันเท่านั้น จะไม่ตรงกับ "ไม้" สำหรับ "ไม้" เหมือนกับที่เครื่องวิเคราะห์การเติมข้อความอัตโนมัติทำ
สรุป
บทความนี้ควรให้ภาพรวมเกี่ยวกับวิธีตั้งค่า Elasticsearch ในโปรเจ็กต์ .NET ของคุณ และให้ฟังก์ชันการค้นหาที่ดีในทุกที่ด้วยความพยายามเพียงเล็กน้อย
เส้นโค้งการเรียนรู้อาจสูงชันเล็กน้อย แต่ก็คุ้มค่า โดยเฉพาะอย่างยิ่งเมื่อคุณปรับแต่งให้ถูกต้องและเริ่มได้ผลการค้นหาที่ยอดเยี่ยม
อย่าลืมเพิ่มกรณีทดสอบอย่างละเอียดพร้อมผลลัพธ์ที่คาดหวัง เพื่อให้แน่ใจว่าคุณจะไม่ทำให้พารามิเตอร์สับสนมากเกินไปเมื่อทำการเปลี่ยนแปลงและใช้งาน
รหัสเต็มสำหรับบทความนี้มีอยู่ใน GitHub และใช้ข้อมูลที่ดึงมาจากฐานข้อมูล TMDB เพื่อแสดงว่าผลการค้นหามีการปรับปรุงในแต่ละขั้นตอนอย่างไร