- Property Graph ของ Postgres 19 คือความสามารถ SQL/PGQ ที่ประกาศตารางเดิมให้เป็นจุดยอดและเส้นเชื่อม แล้วค้นหารูปแบบความสัมพันธ์แบบคงที่ด้วย
MATCH โดยไม่ต้องคัดลอกข้อมูลหรือสร้างเอนจินประมวลผลกราฟแยกต่างหาก
- รูปแบบกราฟจะถูก คอมไพล์เป็น relational join จึงยังใช้ optimizer, index และสถิติเดิมได้ทั้งหมด ทำให้การวิเคราะห์และปรับแต่ง traversal ที่ช้าทำได้แบบเดียวกับ join ปกติ
- ตารางมิติสามารถแมปเป็นจุดยอดได้ตามธรรมชาติ และตาราง join ล้วน ๆ ก็แมปเป็นเส้นเชื่อมได้เช่นกัน แต่ fact table ที่มี foreign key หลายตัวและมีคุณสมบัติของตัวเอง เหมาะจะโมเดลเป็นจุดยอดศูนย์กลางที่แทนอีเวนต์มากกว่า
- ตารางเดียวกันสามารถประกาศเป็นทั้งจุดยอดและหลายเส้นเชื่อมพร้อมกันได้ด้วย alias คนละตัว จึง ไม่จำเป็นต้องสร้างหรือเก็บตารางเส้นเชื่อมแยก โดยใช้ foreign key เดิมของ
results ได้เลย
- เนื่องจาก Postgres 19 ยังไม่รองรับเส้นทางความยาวแปรผัน จึงไม่เหมาะกับ shortest path, การเข้าถึงภายใน N-hop หรือ PageRank แต่เหมาะกับการค้นหาแบบความยาวคงที่ที่รู้โครงสร้างความสัมพันธ์ล่วงหน้า
วิธีอ่าน relational schema ให้เป็นกราฟ
- ใน relational schema แบบ normalized ตารางมิติอย่าง
drivers, constructors, circuits จะเก็บเอนทิตี ส่วน fact table จะบันทึกอีเวนต์อย่างผลการแข่งขันหรือรอบคัดเลือก
- แต่ละแถวคือ จุดยอด ที่เป็นไปได้ และ foreign key คือ เส้นเชื่อม ที่เป็นไปได้ซึ่งชี้ไปยังแถวอื่น
- ER diagram เทียบได้กับกราฟในระดับสคีมา
- แถวจริงและความสัมพันธ์ผ่าน foreign key เทียบได้กับกราฟในระดับอินสแตนซ์
- คำขออย่าง “หา constructor ที่สอดคล้องกับแต่ละ result” ก็คือการ traversal กราฟที่ใน relational SQL เขียนเป็น
results JOIN constructors ON ...
- SQL/PGQ ไม่ได้เพิ่มกราฟชุดใหม่เข้าไป แต่ช่วยให้ เขียนคำถามในรูปแบบกราฟ แทนการไล่เขียน join ตรง ๆ
- แนวทาง RelBench จะดึงตารางออกมาเป็น pandas แล้วประกอบเป็นกราฟ PyTorch ในหน่วยความจำ แต่ property graph ของ Postgres ใช้ความสัมพันธ์ของตารางเดิมในฐานข้อมูลโดยตรง
- ตัวอย่างของ PyG ก็ส่วนใหญ่ใช้ไฟล์แบบแบนหรือกราฟในหน่วยความจำ
- โค้ดทดลองกับชุดข้อมูล Formula 1 ดูได้ที่ GitHub repository
องค์ประกอบของ property graph
CREATE PROPERTY GRAPH คือ อ็อบเจ็กต์ประกาศแบบมีชื่อ ที่สร้างขึ้นบนตารางเดิม
VERTEX TABLES ระบุตารางที่จะอ่านแต่ละแถวเป็นจุดยอด
EDGE TABLES ระบุตารางที่จะอ่านแต่ละแถวเป็นความสัมพันธ์เชื่อมต่อ
- สำหรับแต่ละตารางจุดยอด จะกำหนดองค์ประกอบต่อไปนี้
KEY: ตัวระบุของจุดยอด ซึ่งส่วนใหญ่มักใช้ primary key
LABEL: ชื่อประเภทจุดยอดที่จะใช้ใน MATCH
PROPERTIES: รายการคอลัมน์ที่เข้าถึงได้จากการ query แบบกราฟ
- สำหรับแต่ละตารางเส้นเชื่อม ต้องระบุ
SOURCE และ DESTINATION โดยทั้งสองค่านี้อ้างอิงคีย์ของจุดยอด
CREATE PROPERTY GRAPH ไม่ได้ย้ายหรือคัดลอกข้อมูล
- แถวยังคงอยู่ในตารางเดิม
- ตัวประกาศเพียงแค่นิยามวิธีอ่านโครงสร้าง foreign key เดิมให้เป็นกราฟ
- องค์ประกอบเชิงโครงสร้างของ property graph มีเพียง จุดยอดและเส้นเชื่อม สองชนิดเท่านั้น
- label และ property เป็นคุณลักษณะที่สังกัดจุดยอดหรือเส้นเชื่อม
- ใน
psql ค่า Element Kind ของแต่ละองค์ประกอบจะเป็น vertex หรือ edge เท่านั้น
query รูปแบบคงที่ด้วย MATCH
- การ query กราฟจะเขียนรูปแบบ
MATCH ภายใน GRAPH_TABLE(...)
- แพตเทิร์นต่อไปนี้แสดงความสัมพันธ์จาก driver ผ่าน result ไปยัง race
MATCH (d IS driver)<-[IS of_driver]-(res IS result)-[IS in_race]->(ra IS race)
- clause
COLUMNS ใช้ระบุคอลัมน์ที่ผลลัพธ์ของการ query กราฟจะส่งออกมา
- ใน
SELECT ชั้นนอก สามารถ query ผลลัพธ์จาก GRAPH_TABLE(...) ได้เหมือนตารางทั่วไป
- เพราะแพตเทิร์นมีทั้งทิศทางและ label จึงอ่านความสัมพันธ์ได้คล้ายประโยค ทำให้เข้าใจโครงสร้างได้ง่ายกว่า multi-join แบบเดียวกัน
โครงสร้างที่ถูกแปลงเป็น relational join ตอนรันจริง
MATCH ไม่ใช่เอนจินประมวลผลกราฟแยกต่างหากที่นำมาต่อเพิ่มใน Postgres แต่เป็น syntax ที่คอมไพล์เป็น relational join
- เมื่อลอง
EXPLAIN กับแพตเทิร์น driver–result–race จะเห็น hash join 4 ตัวที่ทำงานบนตารางต้นทาง
- เช่นเดียวกับ join ที่เขียนเอง มันยังใช้สิ่งต่อไปนี้
- optimizer เดิมของ Postgres
- index เดิม
- สถิติเดิม
- หาก traversal ของกราฟช้า สาเหตุและแนวทางปรับแต่งก็เหมือนกับกรณีที่ join ปกติช้า
- ใน
psql ก็สามารถสำรวจกราฟได้ด้วยคำสั่งลักษณะคล้ายการดูตาราง
\dG: แสดงรายการ property graph
\d f1: แสดงแต่ละจุดยอดและเส้นเชื่อม ตารางต้นทาง ชนิดขององค์ประกอบ และจุดยอดต้นทาง/ปลายทางของเส้นเชื่อม
\d+ f1: สร้างคำสั่ง CREATE PROPERTY GRAPH แบบเต็มขึ้นมาใหม่ รวมถึงคีย์ของเส้นเชื่อมที่อนุมานจาก primary key ของตารางเส้นเชื่อม
ความต่างระหว่าง key และ property
- คอลัมน์ที่กำหนดเป็น
KEY ของจุดยอด จะไม่กลายเป็น property โดยอัตโนมัติ
- ถ้ากำหนด
driver_id เป็นเพียงคีย์ของจุดยอด แล้วกรองด้วย d.driver_id = 1 จะล้มเหลว เพราะคอลัมน์นั้นไม่ได้ถูกเปิดให้ query เป็น property
- key ใช้ระบุจุดยอด แต่ไม่ได้เปิดเผยต่อ query
- หากต้องการกรองหรือคืนค่าคอลัมน์ ID ต้องเพิ่มลงในรายการ
PROPERTIES อย่างชัดเจน
ข้อจำกัดเรื่องเส้นทางความยาวแปรผันใน Postgres 19
- Postgres 19 ยังไม่รองรับ element pattern quantifier แบบตามเส้นเชื่อม 1~3 ครั้ง
ERROR: element pattern quantifier is not supported
- ถ้าต้องการเดินสองฮอป ต้องระบุแพตเทิร์นเส้นเชื่อมสองชุดใน
MATCH แบบชัดเจน
- ไม่สามารถแสดงเส้นทางความยาวตามอำเภอใจด้วย syntax ของ property graph ได้
- การเดินกราฟที่เปิดความลึกไว้ต้องใช้ recursive CTE บนตารางต้นทาง ซึ่งจะออกนอก syntax ของ property graph
การแมปตารางเดิมให้เป็นจุดยอดและเส้นเชื่อม
-
ตารางมิติเป็นจุดยอด
- ตารางเอนทิตีอย่าง
drivers, constructors, circuits ที่มี primary key คงที่และมีแอตทริบิวต์ของตัวเอง สามารถแมปเป็นจุดยอดได้ตรงไปตรงมา
- ใช้ primary key เป็นคีย์ของจุดยอด และเปิดเผยคอลัมน์ที่ต้องการเป็น property ก็เพียงพอ
-
ตาราง join ล้วน ๆ เป็นเส้นเชื่อม
- ตาราง bridge แบบ many-to-many อย่าง
student_courses(student_id, course_id) มีหน้าที่เดิมคือเชื่อมสองเอนทิตี จึง แมปเป็นเส้นเชื่อมได้อย่างเป็นธรรมชาติ
- ประกาศ foreign key ฝั่งหนึ่งเป็นจุดยอดต้นทาง และอีกฝั่งเป็นจุดยอดปลายทาง
- เพราะแต่ละแถวของตาราง join คือความสัมพันธ์หนึ่งรายการอยู่แล้ว จึงไม่ต้องแปลงข้อมูลแยกสำหรับกราฟ
- ในโครงสร้างนี้
students และ courses จะเป็นจุดยอด ส่วน student_courses จะเป็นเส้นเชื่อม enrolled_in
-
fact table เป็นจุดยอดของอีเวนต์
- แถวใน
results ชี้ไปยังสามเอนทิตีคือ driver, race และ constructor พร้อมทั้งมีข้อมูลของตัวเองอย่าง grid, position, points, status
- เนื่องจากเส้นเชื่อมใน SQL/PGQ เป็น ความสัมพันธ์แบบสองทางเลือก ที่มีต้นทางหนึ่งและปลายทางหนึ่ง จึงไม่สามารถนำทั้งแถวที่มี foreign key สามตัวมาเป็นเส้นเชื่อมเดี่ยวได้
- ถ้า fact row เองเป็นสิ่งที่ต้องการวิเคราะห์ การประกาศตารางนั้นเป็นจุดยอดจะเหมาะกว่า
- จุดยอด
results จะเก็บอีเวนต์และคุณสมบัติของมัน
- เส้นเชื่อมขนาดแคบอย่าง
results_driver, results_race, results_constructor จะใช้เชื่อมไปยังเอนทิตีภายนอก
- โมเดลนี้สร้าง โครงสร้างแบบ hub เช่น
driver <- result -> race และสามารถหยุดที่จุดยอด result เพื่อกรองหรืออ่าน property ได้
- หากสิ่งที่สนใจคือความสัมพันธ์เอง ให้โมเดลเป็นเส้นเชื่อม แต่ถ้าสิ่งที่สนใจคือแถวเฉพาะหรืออีเวนต์ที่มีคุณสมบัติเฉพาะ ให้โมเดลเป็นจุดยอด
- ตาราง join แทนความสัมพันธ์ ส่วน fact table แทนอีเวนต์
ใช้ตารางเดียวกันเป็นทั้งจุดยอดและเส้นเชื่อมพร้อมกัน
- การแบ่งว่าเป็น “จุดยอดหรือเส้นเชื่อม” ใช้กับ การประกาศองค์ประกอบ แต่ละตัวในกราฟ ไม่ได้ผูกติดกับตารางต้นทาง
- ตารางเดียวกันสามารถประกาศในทั้ง
VERTEX TABLES และ EDGE TABLES ได้พร้อมกันด้วย alias ที่ต่างกัน
- สามารถใช้
results เป็นจุดยอด result และนำตารางเดียวกันกลับมาใช้ซ้ำเป็น alias ของเส้นเชื่อมดังนี้
results AS res_driver: เชื่อมจาก result ไปยัง driver
results AS res_race: เชื่อมจาก result ไปยัง race
results AS res_constr: เชื่อมจาก result ไปยัง constructor
- alias ของเส้นเชื่อมแต่ละตัวใช้คอลัมน์ primary key และ foreign key ที่มีอยู่แล้วใน
results
- ไม่จำเป็นต้องสร้างตาราง
results_driver, results_race, results_constructor แยก และ alias ก็ไม่ได้เก็บข้อมูลใด ๆ
- fact table ที่มี foreign key สามตัวต้องประกาศเป็น alias ของเส้นเชื่อมสามตัว โดยแต่ละตัวมีต้นทางและปลายทางอย่างละหนึ่ง ไม่ใช่เส้นเชื่อมเดียวที่มีหลายปลายทาง
- วิธีนี้ใช้ alias เชิงประกาศสามตัวบนตารางต้นทางเดียว แทนการสร้างตารางจริงหรือวิวสามชุด
การชนกันของชื่อ property และชนิดข้อมูล
- ถ้าละ
PROPERTIES ไว้ SQL/PGQ จะ เปิดเผยทุกคอลัมน์ของตารางเป็น property
- หากทั้ง
results และ qualifying มีคอลัมน์ number เหมือนกัน แต่ชนิดข้อมูลเป็น double precision กับ bigint ตามลำดับ จะเกิดข้อผิดพลาดนี้
ERROR: property "number" data type mismatch: double precision vs. bigint
- เพราะภายในกราฟเดียวกัน property ที่มีชื่อเดียวกันต้องมีชนิดข้อมูลเดียวกัน คอลัมน์ชื่อซ้ำที่ชนิดต่างกันจึงชนกัน
- การระบุเฉพาะคอลัมน์ที่ต้องการใน
PROPERTIES จะช่วยตัดคอลัมน์ที่ชนกันออกจากกราฟได้
- ทั้งปัญหาที่ key ไม่ได้กลายเป็น property อัตโนมัติ และปัญหาชนกันของชนิดข้อมูล แก้ได้ด้วย allowlist ของ property แบบ explicit
query ที่เหมาะและไม่เหมาะ
-
รูปแบบความสัมพันธ์แบบคงที่
- property graph เหมาะกับคำถามประเภท “หาสิ่งที่เชื่อมกับ X ด้วยแพตเทิร์นเฉพาะนี้”
- ในกราฟ Formula 1 สามารถเขียน query อย่างเช่น
- หา driver คนหนึ่งว่าเคยแข่งให้ constructor ใดบ้าง
- หา rival ด้วยแพตเทิร์นจาก driver ผ่าน result และ race ไปยัง result อื่นและ driver อื่น
- กรองทั้งโครงสร้างและเงื่อนไขของ property เช่นผลการแข่งขันที่อันดับออกตัวเกิน 10 และ driver เป็นชาวอิตาลี
- เมื่อรู้รูปร่างของความสัมพันธ์ล่วงหน้า และโครงสร้างนั้น คงที่และมีขอบเขตจำกัด syntax สำหรับค้นหา กรอง และสรุปผลจะอ่านง่ายขึ้น
- แม้เป็น query ที่ต้อง self-join หลายครั้ง ก็สามารถเขียนให้อ่านง่ายด้วยแพตเทิร์น
MATCH เดียว
-
ปัญหาที่ไม่รู้เส้นทางล่วงหน้า
- ปัญหาต่อไปนี้ไม่เหมาะกับ property graph ของ Postgres 19
- shortest path ระหว่าง driver สองคน
- ทุกเป้าหมายที่เข้าถึงได้ภายใน N-hop
- การสำรวจความเชื่อมต่อที่ไม่รู้ความลึกล่วงหน้า
- query เหล่านี้ต้องใช้ traversal ความยาวแปรผัน จึงต้องใช้ recursive CTE บนตารางต้นทาง
- อัลกอริทึมกราฟอย่าง PageRank, การตรวจหาคอมมูนิตี้, การคำนวณ centrality ก็เป็นคนละปัญหากับ pattern matching จึงไม่อยู่ในขอบเขตของฟีเจอร์นี้
- property graph ช่วยหาสิ่งที่เชื่อมต่อกันเมื่อรู้โครงสร้างเส้นทางอยู่แล้ว แต่ไม่สามารถค้นพบเส้นทางเมื่อยังไม่รู้ว่า X กับ Y เชื่อมกันอย่างไร หรือคำนวณความสำคัญเชิงโครงสร้างของกราฟได้
สิ่งที่ควรพิจารณาก่อนนำไปใช้
- SQL/PGQ คือ declarative overlay ที่วางทับบนโครงสร้าง foreign key เดิม และจะไม่เก็บข้อมูลเพิ่ม เว้นแต่จะเลือกทำแยกเอง
- ประโยชน์หลักมีสองอย่าง
- เขียน traversal แบบคงรูปให้อ่านง่ายกว่า relational join
- มีอ็อบเจ็กต์แบบมีชื่อไว้ใช้บันทึกสคีมาในรูปกราฟ
- ข้อจำกัดใหญ่ที่สุดคือไม่มีเส้นทางความยาวแปรผัน ทำให้การสำรวจที่ลึกหรือเปิดปลายต้องใช้ recursive SQL
- ตัวตารางเองไม่ได้เป็นจุดยอดหรือเส้นเชื่อมโดยเนื้อแท้ แต่ในแต่ละกราฟสามารถตัดสินใจได้ว่าจะอ่านแถวเหล่านั้นเป็นจุดยอด เส้นเชื่อม หรือทั้งสองแบบ
- หากต้องการ query รูปแบบกราฟแบบคงที่บนสคีมาเดิมใน Postgres 19 อาจลองพิจารณา
CREATE PROPERTY GRAPH และ MATCH ก่อนย้ายข้อมูลไปฐานข้อมูลกราฟแยก
- หากต้องสำรวจความลึกที่ไม่ทราบล่วงหน้า ก็ยังต้องใช้ recursive CTE แต่ไม่ว่าจะใช้วิธีใด ข้อมูลก็ไม่จำเป็นต้องออกจาก Postgres
- ก่อนใช้งานจริงควรทดสอบด้วยตัวเองว่าสามารถให้ประสิทธิภาพตามต้องการได้หรือไม่
ยังไม่มีความคิดเห็น