> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
यह विनिर्देश उन मुद्दों और कमियों को संबोधित करता है जिन्हें ट्रस्टग्राफ के संरचित डेटा एकीकरण के प्रारंभिक कार्यान्वयन के दौरान पहचाना गया था, जैसा कि `structured-data.md` में वर्णित है।
## समस्या विवरण
### 1. नामकरण में असंगति: "ऑब्जेक्ट" बनाम "पंक्ति"
वर्तमान कार्यान्वयन में पूरे समय "ऑब्जेक्ट" शब्दावली का उपयोग किया जाता है (उदाहरण के लिए, `ExtractedObject`, ऑब्जेक्ट निष्कर्षण, ऑब्जेक्ट एम्बेडिंग)। यह नामकरण बहुत सामान्य है और भ्रम पैदा करता है:
"ऑब्जेक्ट" सॉफ्टवेयर में एक अतिव्यापी शब्द है (पायथन ऑब्जेक्ट, JSON ऑब्जेक्ट, आदि)।
संसाधित डेटा अनिवार्य रूप से सारणीबद्ध है - परिभाषित स्कीमा वाली तालिकाओं में पंक्तियाँ।
"पंक्ति" डेटा मॉडल का अधिक सटीक वर्णन है और यह डेटाबेस शब्दावली के साथ मेल खाता है।
यह असंगति मॉड्यूल नामों, क्लास नामों, संदेश प्रकारों और दस्तावेज़ों में दिखाई देती है।
### 2. पंक्ति भंडारण क्वेरी सीमाएँ
वर्तमान पंक्ति भंडारण कार्यान्वयन में महत्वपूर्ण क्वेरी सीमाएँ हैं:
**प्राकृतिक भाषा बेमेल**: क्वेरी वास्तविक दुनिया के डेटा विविधताओं के साथ संघर्ष करती हैं। उदाहरण के लिए:
`"CHESTNUT ST"` युक्त एक सड़क डेटाबेस को `"Chestnut Street"` के बारे में पूछने पर खोजना मुश्किल है।
संक्षिप्त नाम, केस अंतर और स्वरूपण भिन्नताएं सटीक-मेल क्वेरी को तोड़ देती हैं।
उपयोगकर्ताओं को सिमेंटिक समझ की उम्मीद होती है, लेकिन स्टोर शाब्दिक मिलान प्रदान करता है।
**स्कीमा विकास मुद्दे**: स्कीमा में परिवर्तन समस्याएं पैदा करते हैं:
मौजूदा डेटा अद्यतन स्कीमा के अनुरूप नहीं हो सकता है।
तालिका संरचना में परिवर्तन क्वेरी और डेटा अखंडता को तोड़ सकते हैं।
स्कीमा अपडेट के लिए कोई स्पष्ट माइग्रेशन पथ नहीं है।
### 3. पंक्ति एम्बेडिंग की आवश्यकता
समस्या 2 से संबंधित, सिस्टम को पंक्ति डेटा के लिए वेक्टर एम्बेडिंग की आवश्यकता है ताकि:
संरचित डेटा में सिमेंटिक खोज (जब डेटा में "CHESTNUT ST" हो तो "Chestnut Street" खोजना)।
धुंधली क्वेरी के लिए समानता मिलान।
संरचित फ़िल्टर को सिमेंटिक समानता के साथ मिलाने वाली हाइब्रिड खोज।
बेहतर प्राकृतिक भाषा क्वेरी समर्थन।
एम्बेडिंग सेवा को निर्दिष्ट किया गया था लेकिन लागू नहीं किया गया था।
### 4. पंक्ति डेटा अंतर्ग्रहण अधूरा
संरचित डेटा अंतर्ग्रहण पाइपलाइन पूरी तरह से कार्यात्मक नहीं है:
इनपुट प्रारूपों (CSV, JSON, आदि) को वर्गीकृत करने के लिए नैदानिक संकेत मौजूद हैं।
इन संकेतों का उपयोग करने वाली अंतर्ग्रहण सेवा को सिस्टम में प्लंब नहीं किया गया है।
पंक्ति स्टोर में पूर्व-संरचित डेटा लोड करने के लिए कोई एंड-टू-एंड पथ नहीं है।
## लक्ष्य
**स्कीमा लचीलापन**: मौजूदा डेटा को तोड़ने या माइग्रेशन की आवश्यकता के बिना स्कीमा विकास को सक्षम करें।
**संगत नामकरण**: पूरे कोडबेस में "पंक्ति" शब्दावली का मानकीकरण करें।
**सिमेंटिक क्वेरी क्षमता**: पंक्ति एम्बेडिंग के माध्यम से धुंधली/सिमेंटिक मिलान का समर्थन करें।
**पूर्ण अंतर्ग्रहण पाइपलाइन**: संरचित डेटा लोड करने के लिए एंड-टू-एंड पथ प्रदान करें।
## तकनीकी डिजाइन
### एकीकृत पंक्ति भंडारण स्कीमा
पिछले कार्यान्वयन में प्रत्येक स्कीमा के लिए एक अलग कैसेंड्रा तालिका बनाई गई थी। इससे तब समस्याएं हुईं जब स्कीमा विकसित हुए, क्योंकि तालिका संरचना में परिवर्तन के लिए माइग्रेशन की आवश्यकता होती थी।
नए डिज़ाइन में सभी पंक्ति डेटा के लिए एक एकल, एकीकृत तालिका का उपयोग किया जाता है:
| `schema_name` | `text` | स्कीमा का नाम जिससे यह पंक्ति मेल खाती है |
| `index_name` | `text` | अनुक्रमित फ़ील्ड का नाम, संयुक्त अल्पविराम के साथ (मिश्रित के लिए) |
| `index_value` | `frozen<list<text>>` | अनुक्रमित मानों की सूची |
| `data` | `map<text, text>` | पंक्ति डेटा कुंजी-मान जोड़े के रूप में |
| `source` | `text` | वैकल्पिक यूआरआई जो नॉलेज ग्राफ में मूल जानकारी से लिंक करता है। खाली स्ट्रिंग या NULL इंगित करता है कि कोई स्रोत नहीं है। |
#### अनुक्रमण प्रबंधन
प्रत्येक पंक्ति कई बार संग्रहीत की जाती है - स्कीमा में परिभाषित प्रत्येक अनुक्रमित फ़ील्ड के लिए एक बार। प्राथमिक कुंजी फ़ील्ड को किसी विशेष मार्कर के बिना एक अनुक्रमणिका के रूप में माना जाता है, जो भविष्य में लचीलापन प्रदान करता है।
**सिंगल-फ़ील्ड अनुक्रमणिका का उदाहरण:**
स्कीमा `email` को अनुक्रमित के रूप में परिभाषित करता है
`index_name = "email"`
`index_value = ['foo@bar.com']`
**मिश्रित अनुक्रमणिका का उदाहरण:**
स्कीमा `region` और `status` पर मिश्रित अनुक्रमणिका को परिभाषित करता है
`index_name = "region,status"` (फ़ील्ड नाम क्रमबद्ध और अल्पविराम से जुड़े हुए)
`index_value = ['US', 'active']` (फ़ील्ड नामों के समान क्रम में मान)
**प्राथमिक कुंजी का उदाहरण:**
स्कीमा `customer_id` को प्राथमिक कुंजी के रूप में परिभाषित करता है
`index_name = "customer_id"`
`index_value = ['CUST001']`
#### क्वेरी पैटर्न
सभी क्वेरी एक ही पैटर्न का पालन करती हैं, चाहे कोई भी अनुक्रमणिका उपयोग की जाए:
```sql
SELECT * FROM rows
WHERE collection = 'import_2024'
AND schema_name = 'customers'
AND index_name = 'email'
AND index_value = ['foo@bar.com']
```
#### डिज़ाइन में संतुलन
**लाभ:**
स्कीमा में बदलावों के लिए टेबल संरचना में बदलाव की आवश्यकता नहीं होती है।
पंक्ति डेटा कैसेंड्रा के लिए अस्पष्ट है - फ़ील्ड जोड़ने/हटाने से कोई फर्क नहीं पड़ता।
सभी एक्सेस विधियों के लिए सुसंगत क्वेरी पैटर्न।
कोई कैसेंड्रा सेकेंडरी इंडेक्स नहीं (जो बड़े पैमाने पर धीमे हो सकते हैं)।
पूरी तरह से देशी कैसेंड्रा प्रकार (`map`, `frozen<list>`)।
**गड़बड़ियाँ:**
राइट एम्प्लीफिकेशन: प्रत्येक पंक्ति डालने = N डालने (प्रत्येक इंडेक्स्ड फ़ील्ड के लिए एक)।
डुप्लिकेट पंक्ति डेटा के कारण स्टोरेज ओवरहेड।
प्रकार की जानकारी स्कीमा कॉन्फ़िगरेशन में संग्रहीत है, एप्लिकेशन लेयर पर रूपांतरण।
#### संगति मॉडल
डिज़ाइन कुछ सरलीकरणों को स्वीकार करता है:
1.**कोई पंक्ति अपडेट नहीं**: सिस्टम केवल अपेंड-ओनली है। यह एक ही पंक्ति की कई प्रतियों को अपडेट करने के बारे में संगति संबंधी चिंताओं को समाप्त करता है।
2.**स्कीमा परिवर्तन सहिष्णुता**: जब स्कीमा बदलते हैं (उदाहरण के लिए, इंडेक्स जोड़े/हटाए जाते हैं), तो मौजूदा पंक्तियाँ अपनी मूल इंडेक्सिंग को बनाए रखती हैं। पुराने पंक्तियों को नए इंडेक्स के माध्यम से खोजा नहीं जा सकता है। यदि आवश्यक हो, तो उपयोगकर्ता संगति सुनिश्चित करने के लिए एक स्कीमा को हटा और पुनः बना सकते हैं।
### विभाजन ट्रैकिंग और हटाना
#### समस्या
विभाजन कुंजी `(collection, schema_name, index_name)` के साथ, कुशल हटाने के लिए सभी विभाजन कुंजियों को हटाने की आवश्यकता होती है। केवल `collection` या `collection + schema_name` द्वारा हटाना उन सभी `index_name` मानों को जानने की आवश्यकता होती है जिनमें डेटा है।
#### विभाजन ट्रैकिंग टेबल
एक सेकेंडरी लुकअप टेबल ट्रैक करती है कि कौन से विभाजन मौजूद हैं:
यह हटाने के कार्यों के लिए विभाजनों की कुशल खोज को सक्षम बनाता है।
#### पंक्ति लेखक व्यवहार
पंक्ति लेखक पंजीकृत `(collection, schema_name)` युग्मों का एक इन-मेमोरी कैश बनाए रखता है। पंक्ति को संसाधित करते समय:
1. जांचें कि `(collection, schema_name)` कैश में है या नहीं।
2. यदि कैश में नहीं है (इस युग्म के लिए पहली पंक्ति):
सभी इंडेक्स नामों को प्राप्त करने के लिए स्कीमा कॉन्फ़िगरेशन देखें।
प्रत्येक `(collection, schema_name, index_name)` के लिए `row_partitions` में प्रविष्टियाँ डालें।
युग्म को कैश में जोड़ें।
3. पंक्ति डेटा लिखने के साथ आगे बढ़ें।
पंक्ति लेखक स्कीमा कॉन्फ़िगरेशन परिवर्तन घटनाओं की भी निगरानी करता है। जब कोई स्कीमा बदलता है, तो प्रासंगिक कैश प्रविष्टियाँ साफ़ कर दी जाती हैं ताकि अगली पंक्ति अपडेट किए गए इंडेक्स नामों के साथ पुनः पंजीकरण को ट्रिगर करे।
इस दृष्टिकोण से यह सुनिश्चित होता है:
लुकअप तालिका लेखन प्रत्येक `(collection, schema_name)` युग्म के लिए एक बार होता है, प्रत्येक पंक्ति के लिए नहीं।
लुकअप तालिका उन इंडेक्स को दर्शाती है जो डेटा लिखे जाने पर सक्रिय थे।
आयात के दौरान होने वाले स्कीमा परिवर्तनों को सही ढंग से पहचाना जाता है।
#### हटाने के कार्य
**संग्रह हटाएं:**
```sql
-- 1. Discover all partitions
SELECT schema_name, index_name FROM row_partitions WHERE collection = 'X';
-- 2. Delete each partition from rows table
DELETE FROM rows WHERE collection = 'X' AND schema_name = '...' AND index_name = '...';
-- (repeat for each discovered partition)
-- 3. Clean up the lookup table
DELETE FROM row_partitions WHERE collection = 'X';
```
**संग्रह और स्कीमा हटाएं:**
```sql
-- 1. Discover partitions for this schema
SELECT index_name FROM row_partitions WHERE collection = 'X' AND schema_name = 'Y';
-- 2. Delete each partition from rows table
DELETE FROM rows WHERE collection = 'X' AND schema_name = 'Y' AND index_name = '...';
-- (repeat for each discovered partition)
-- 3. Clean up the lookup table entries
DELETE FROM row_partitions WHERE collection = 'X' AND schema_name = 'Y';
```
### पंक्ति एम्बेडिंग
पंक्ति एम्बेडिंग, अनुक्रमित मानों पर अर्थपूर्ण/अस्पष्ट मिलान को सक्षम करते हैं, जिससे प्राकृतिक भाषा में विसंगति की समस्या हल होती है (उदाहरण के लिए, "चेस्टनट स्ट्रीट" के लिए खोज करते समय "चेस्टनट एसटी" खोजना)।
#### डिज़ाइन अवलोकन
प्रत्येक अनुक्रमित मान को एम्बेड किया जाता है और एक वेक्टर स्टोर (क्यूड्रेंट) में संग्रहीत किया जाता है। क्वेरी के समय, क्वेरी को एम्बेड किया जाता है, समान वेक्टर पाए जाते हैं, और संबंधित मेटाडेटा का उपयोग कैसेंड्रा में वास्तविक पंक्तियों को देखने के लिए किया जाता है।
#### क्यूड्रेंट संग्रह संरचना
प्रत्येक `(user, collection, schema_name, dimension)` टपल के लिए एक क्यूड्रेंट संग्रह:
नामों को साफ़ किया जाता है (गैर-अक्षरांकीय वर्णों को `_` से बदला जाता है, लोअरकेस किया जाता है, संख्यात्मक उपसर्गों को `r_` उपसर्ग मिलता है)
**तर्क:** एक `(user, collection, schema_name)` उदाहरण को मिलान वाले क्यूड्रेंट संग्रहों को हटाकर साफ-सुथरे तरीके से हटाने की अनुमति देता है; आयाम प्रत्यय विभिन्न एम्बेडिंग मॉडल को एक साथ मौजूद रहने की अनुमति देता है।
#### क्या एम्बेड किया जाता है
अनुक्रमण मानों का पाठ प्रतिनिधित्व:
| अनुक्रमण प्रकार | उदाहरण `index_value` | एम्बेड करने के लिए पाठ |
| `index_name` | यह एम्बेडिंग किस अनुक्रमित फ़ील्ड(s) का प्रतिनिधित्व करता है |
| `index_value` | मूल्यों की मूल सूची (कैसेंड्रा लुकअप के लिए) |
| `text` | वह पाठ जिसे एम्बेड किया गया था (डीबगिंग/प्रदर्शन के लिए) |
ध्यान दें: `user`, `collection`, और `schema_name` Qdrant संग्रह नाम से निहित हैं।
#### क्वेरी प्रवाह
1. उपयोगकर्ता U, संग्रह X, स्कीमा Y के भीतर "चेस्टनट स्ट्रीट" के लिए क्वेरी करता है।
2. क्वेरी टेक्स्ट को एम्बेड करें।
3. उपसर्ग `rows_U_X_Y_` से मेल खाने वाले Qdrant संग्रह नाम(s) निर्धारित करें।
4. निकटतम वैक्टरों के लिए मिलान करने वाले Qdrant संग्रह(s) की खोज करें।
5.`index_name` और `index_value` युक्त पेलोड वाले मिलान करने वाले बिंदुओं को प्राप्त करें।
6. कैसेंड्रा को क्वेरी करें:
```sql
SELECT * FROM rows
WHERE collection = 'X'
AND schema_name = 'Y'
AND index_name = '<frompayload>'
AND index_value = <frompayload>
```
7. मिलान किए गए पंक्तियों को वापस करना
#### वैकल्पिक: इंडेक्स नाम द्वारा फ़िल्टरिंग
क्वेरी वैकल्पिक रूप से Qdrant में `index_name` द्वारा फ़िल्टर की जा सकती हैं ताकि केवल विशिष्ट फ़ील्ड की खोज की जा सके:
**"उन सभी फ़ील्ड को खोजें जो 'Chestnut' से मेल खाते हैं"** → संग्रह में सभी वैक्टर की खोज करें
**"उन सड़कों को खोजें जिनका नाम 'Chestnut' से मेल खाता है"** → उन पंक्तियों को फ़िल्टर करें जहाँ `payload.index_name = 'street_name'`
#### आर्किटेक्चर
पंक्ति एम्बेडिंग **दो-चरणीय पैटर्न** का पालन करते हैं जिसका उपयोग GraphRAG (ग्राफ-एम्बेडिंग, दस्तावेज़-एम्बेडिंग) द्वारा किया जाता है:
**चरण 1: एम्बेडिंग गणना** (`trustgraph-flow/trustgraph/embeddings/row_embeddings/`) - `ExtractedObject` का उपयोग करता है, एम्बेडिंग सेवा के माध्यम से एम्बेडिंग की गणना करता है, `RowEmbeddings` आउटपुट करता है
**चरण 2: एम्बेडिंग भंडारण** (`trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/`) - `RowEmbeddings` का उपयोग करता है, वैक्टर को Qdrant में लिखता है
कैसेंड्रा पंक्ति लेखक एक अलग समानांतर उपभोक्ता है:
**कैसेंड्रा पंक्ति लेखक** (`trustgraph-flow/trustgraph/storage/rows/cassandra`) - `ExtractedObject` का उपयोग करता है, पंक्तियों को कैसेंड्रा में लिखता है
तीनों सेवाएं एक ही प्रवाह से डेटा लेती हैं, जिससे वे अलग-अलग रहती हैं। यह अनुमति देता है:
कैसेंड्रा लेखन बनाम एम्बेडिंग पीढ़ी बनाम वेक्टर भंडारण का स्वतंत्र स्केलिंग
यदि आवश्यक न हो तो एम्बेडिंग सेवाओं को अक्षम किया जा सकता है
एक सेवा में विफलता अन्य सेवाओं को प्रभावित नहीं करती है
GraphRAG पाइपलाइनों के साथ सुसंगत आर्किटेक्चर
#### लेखन पथ
**चरण 1 (पंक्ति-एम्बेडिंग प्रोसेसर):** जब एक `ExtractedObject` प्राप्त होता है:
1. अनुक्रमित फ़ील्ड खोजने के लिए स्कीमा देखें
2. प्रत्येक अनुक्रमित फ़ील्ड के लिए:
अनुक्रमण मान का पाठ प्रतिनिधित्व बनाएं
एम्बेडिंग सेवा के माध्यम से एम्बेडिंग की गणना करें
3. सभी गणना किए गए वैक्टर युक्त एक `RowEmbeddings` संदेश आउटपुट करें
**चरण 2 (पंक्ति-एम्बेडिंग-लिखें-Qdrant):** जब एक `RowEmbeddings` प्राप्त होता है:
1. संदेश में प्रत्येक एम्बेडिंग के लिए:
`(user, collection, schema_name, dimension)` से Qdrant संग्रह निर्धारित करें
यदि आवश्यक हो तो संग्रह बनाएं (पहली बार लिखने पर आलसी निर्माण)
WHERE collection = 'X' AND schema_name = 'customers'
AND index_name = 'email' AND index_value = ['foo@bar.com']
```
### मुख्य परिवर्तन
1.**क्वेरी सिमेंटिक्स को सरल बनाया गया**: नया स्कीमा केवल `index_value` पर सटीक मिलान का समर्थन करता है। वर्तमान GraphQL फ़िल्टर (`gt`, `lt`, `contains`, आदि) या तो:
लौटाए गए डेटा पर पोस्ट-फ़िल्टरिंग बन जाते हैं (यदि अभी भी आवश्यक हैं)
अस्पष्ट मिलान के लिए एम्बेडिंग एपीआई का उपयोग करने के पक्ष में हटा दिए जाते हैं
2.**GraphQL कोड दृढ़ता से युग्मित है**: वर्तमान `service.py` स्ट्रॉबेरी टाइप जनरेशन, फ़िल्टर पार्सिंग और कैसेंड्रा-विशिष्ट प्रश्नों को बंडल करता है। एक और रो स्टोर बैकएंड जोड़ने से ~400 लाइनों का GraphQL कोड दोहराया जाएगा।
### प्रस्तावित रिफैक्टर
रिफैक्टर में दो भाग हैं:
#### 1. GraphQL कोड को अलग करें
पुन: प्रयोज्य GraphQL घटकों को एक साझा मॉड्यूल में निकालें: