Rails विशिष्ट स्थितियों के लिए बहुत सारे उपयोगी बिल्ट-इन टूल्स के साथ एक बड़ा फ्रेमवर्क है। इस श्रृंखला में, हम Rails के बड़े कोडबेस में छिपे कुछ कम-ज्ञात टूल्स पर एक नज़र डालेंगे।
इस लेख में, हम ActiveRecord के store और store_accessor मेथड्स पर ध्यान केंद्रित करेंगे। ये दोनों मेथड्स डेटाबेस कॉलम में संरचित डेटा स्टोर करने के उपयोग के मामले के लिए लक्षित हैं, जैसे JSON या YAML। जबकि store_accessor हमें गेटर मेथड्स के साथ मॉडल को भरे बिना इन डेटा से मान प्राप्त करने का एक आसान तरीका देता है, store एक कदम आगे जाता है और हमारे चुने हुए फॉर्मेट में डेटा को पारदर्शी रूप से सीरियलाइज़/डीसीरियलाइज़ करता है। यह समझने के लिए कि यह कहाँ उपयोगी हो सकता है, हम रिलेशनल डेटाबेस में JSON स्टोर करने के विकल्पों और कुछ कारणों पर भी नज़र डालेंगे कि आप ऐसा क्यों करना चाहेंगे।
डेटाबेस में JSON
मुझे स्पष्ट करना चाहिए कि जब मैं इस लेख में 'डेटाबेस' कहता हूँ, तो मेरा मतलब रिलेशनल डेटाबेस से है, विशेष रूप से PostgreSQL और MySQL, क्योंकि वे Rails समुदाय में सबसे अधिक उपयोग किए जाते हैं।
कोई पूछ सकता है कि आप रिलेशनल डेटाबेस में JSON क्यों स्टोर करना चाहेंगे। वास्तव में, रिलेशनल डेटाबेस के लाभों का लाभ उठाने का तरीका डेटा को तोड़ना है ताकि उनके बीच संबंध डेटाबेस द्वारा लागू किए जा सकें (जैसे, फॉरेन की), और डेटा को क्वेरी प्रदर्शन में सुधार के लिए इंडेक्स किया जा सके।
रिलेशनल डेटाबेस मॉडल की एक कमी यह है कि डेटा संरचना को न केवल समय से पहले जाना जाना चाहिए बल्कि एक टेबल में प्रत्येक पंक्ति के लिए समान भी होना चाहिए। यदि आपका एप्लिकेशन ऐसे डेटा के इर्द-गिर्द बनाया गया है जो इन आवश्यकताओं को पूरा नहीं करता है, तो आप NoSQL डेटाबेस की जांच करना चाह सकते हैं। हालांकि, अधिकांश वेब ऐप्स के लिए, हम अधिकांश डेटा के लिए ज्ञात रिलेशनल डेटाबेस के साथ रहना चाहते हैं और बस इन डायनामिक डेटा संरचनाओं को विवेकपूर्ण तरीके से "छिड़कना" चाहते हैं। इन मामलों में, JSON कॉलम जैसा कुछ बहुत मायने रख सकता है।
JSON बनाम JSONB
PostgreSQL में दो प्रकार के JSON कॉलम होते हैं: json और jsonb। मुख्य अंतर यह है कि jsonb राइट-टाइम पर पार्स किया जाता है, जिसका अर्थ है कि डेटा एक ऐसे फॉर्मेट में स्टोर होता है जिसे डेटाबेस तेजी से क्वेरी कर सकता है। चेतावनी यह है कि क्योंकि JSON पहले से पार्स्ड है, जब टेक्स्ट के रूप में आउटपुट किया जाता है, तो यह अब बिल्कुल वैसा नहीं हो सकता जैसा उपयोगकर्ता ने दर्ज किया था। उदाहरण के लिए, डुप्लिकेट कुंजियाँ हटाई जा सकती हैं, या कुंजी का क्रम मूल से मेल नहीं खा सकता है।
PostgreSQL दस्तावेज़ीकरण में कहा गया है कि ज्यादातर मामलों में, jsonb वही है जो आप चाहते हैं जब तक कि आपके पास अन्यथा कोई विशिष्ट कारण न हो।
MySQL का json कॉलम PostgreSQL में jsonb के समान व्यवहार करता है। 'सिर्फ वही जो उपयोगकर्ता ने दर्ज किया' आउटपुट का समर्थन करने के लिए, आपको शायद varchar कॉलम या कुछ समान का उपयोग करना होगा।
JSON बनाम टेक्स्ट
डेटा को प्री-पार्स्ड होने की अनुमति देने के अलावा, उसी डेटा को टेक्स्ट फील्ड में स्टोर करने के बजाय JSON कॉलम का उपयोग करने से क्वेरी की अनुमति मिलती है जो डेटा का उपयोग करती हैं। उदाहरण के लिए, आप उन सभी रिकॉर्ड्स को क्वेरी कर सकते हैं जहाँ कॉलम में एक विशेष कुंजी-मान जोड़ी मौजूद है। ध्यान दें कि Rails स्वयं कई (यदि कोई हो) JSON-विशिष्ट क्वेरी का समर्थन नहीं करता, क्योंकि वे डेटाबेस-विशिष्ट हैं। इस प्रकार, यदि आप इन सुविधाओं का लाभ उठाना चाहते हैं, तो आपको ऐसा करने के लिए SQL क्वेरी का उपयोग करना होगा।
Rails में JSON कॉलम
Rails में माइग्रेशन में json (और PostgreSQL पर jsonb) कॉलम बनाने के लिए समर्थन है:
class CreateItems < ActiveRecord::Migration[7.0]
def change
create_table :items do |t|
t.jsonb :user_attributes
...
end
end
end
इस कॉलम को पढ़ने पर, वापस आया परिणाम एक Hash होता है:
> Item.first.user_attributes
Item Load (0.6ms) SELECT "items".* FROM "items" ORDER BY "items"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> {"color"=>"text-red-400"}
> Item.first.update!(user_attributes: {color: "text-blue-400"})
> Item.first.user_attributes.dig(:color)
=> "text-blue-400"
अब जब हमारे पास एक Hash एट्रिब्यूट है, तो आप मॉडल में मान पढ़ने/लिखने के लिए कुछ हेल्पर मेथड्स जोड़ने के लिए ललचा सकते हैं:
class Item < ApplicationRecord
def color=(value)
self.user_attributes["color"] = value
end
def color
user_attributes.dig("color")
end
end
इस तरह के मेथड्स पूरी तरह से ठीक काम करते हैं लेकिन यदि आपके पास डील करने के लिए बहुत सारी JSON कुंजियाँ हैं तो जल्दी से अनियंत्रित हो सकते हैं; सौभाग्य से, Rails ने हमें कवर किया है।
ActiveRecord का store और store_accessor
डेटाबेस में JSON स्टोर करने के दो पहलू हैं: सीरियलाइज़ेशन और एक्सेस। यदि आप अपने डेटाबेस में json-टाइप कॉलम का उपयोग कर रहे हैं, तो आपको सीरियलाइज़ेशन पहलू के बारे में चिंता करने की ज़रूरत नहीं है। Rails और डेटाबेस एडाप्टर इसे आपके लिए संभाल लेंगे (आप सीधे store_accessor पर जा सकते हैं)। यदि आप डेटा को टेक्स्ट कॉलम में स्टोर कर रहे हैं, तो ActiveRecord का store मेथड आपके लिए है, जो यह सुनिश्चित करता है कि आप जो डेटा कॉलम में लिखते हैं वह आपके चुने हुए फॉर्मेट में सीरियलाइज़ हो।
ActiveRecord का store
ActiveRecord में हमारे कॉलम से पढ़े या लिखे गए डेटा को स्वचालित रूप से सीरियलाइज़ करने के लिए एक store मेथड है:
class Item < ApplicationRecord
store :user_attributes, accessors: [:color], coder: JSON
end
यहाँ, :user_attributes वह कॉलम है जिसका हम उपयोग करना चाहते हैं, जबकि accessors उन कुंजियों की सूची है जिन्हें हम एक्सेस करना चाहते हैं (हमारे मामले में सिर्फ color), और अंत में, हम निर्दिष्ट करते हैं कि हम डेटा को कैसे एन्कोड करना चाहते हैं। हम JSON का उपयोग कर रहे हैं, लेकिन आप यहाँ कुछ भी उपयोग कर सकते हैं, जिसमें YAML या कस्टम एन्कोडिंग जैसी चीज़ें शामिल हैं। यह मेथड केवल आपके चुने हुए कोडर के साथ सीरियलाइज़ेशन को संभालता है और हुड के नीचे store_accessor को कॉल करता है।
ActiveRecord का store_accessor
हम store_accessor का उपयोग करके अपने मॉडल में गेट/सेट मेथड्स बनाते हैं:
class Item < ApplicationRecord
store_accessor :user_attributes, :color
store_accessor :user_attributes, :name, prefix: true
store_accessor :user_attributes, :location, prefix: 'primary'
end
यहाँ फिर से, user_attributes वह डेटाबेस कॉलम है जिसका हम उपयोग करना चाहते हैं, उसके बाद JSON डेटा में हम जिस कुंजी का उपयोग करना चाहते हैं, और अंत में, हमारे पास प्रीफिक्स (या सफ़िक्स) का उपयोग करने का विकल्प है। ध्यान दें कि store_accessor नेस्टेड डेटा का समर्थन नहीं करता, केवल टॉप-लेवल कुंजी-मान जोड़े। prefix और suffix विकल्प या तो एक बूलियन, स्ट्रिंग, या सिंबल लेते हैं। यदि बूलियन true पास किया जाता है, तो कॉलम का नाम प्रीफिक्स/सफ़िक्स के रूप में उपयोग किया जाता है।
=> item = Item.create!(color: 'red', user_attributes_name: 'Jonathan', primary_location: 'New Zealand')
#<Item:0x000055d63f4f0360 id: 4, user_attributes: {"color"=>"red", "name"=>"Jonathan", "location"=>"New Zealand"}>
=> item.color
=> "red"
=> item.user_attributes_name
=> "Jonathan"
=> item.name
=> NoMethodError: undefined method `name'...
=> item.primary_location
=> "New Zealand"
वास्तविक-विश्व उपयोग
मैंने केवल कभी-कभी सामान्य ज्ञात-पूर्व-रिलेशनल डेटाबेस स्कीमा से भटकने की आवश्यकता महसूस की है। जिन कुछ बार मैंने ऐसा किया है, इसने डेटाबेस संरचना को दोनों क्लीनर और सरल बना दिया है जितना कि इन विकल्पों के बिना हो सकता है।
एक उदाहरण जो मुझे मिला है वह कई APIs का समर्थन कर रहा है जहाँ उपयोगकर्ता अपने स्वयं के खाते कनेक्ट करते हैं। यह तब मुश्किल हो जाता है जब APIs समान प्रमाणीकरण योजनाओं का उपयोग नहीं करते हैं। कुछ उपयोगकर्ता नाम+पासवर्ड का उपयोग कर सकते हैं, जबकि अन्य API कुंजी का उपयोग करते हैं, और फिर भी अन्य में एक API कुंजी, एक सीक्रेट, और एक मर्चेंट ID होती है। एक दृष्टिकोण केवल टेबल में कॉलम जोड़ते रहना है, जहाँ उनमें से अधिकांश अधिकांश प्रोवाइडर्स के लिए null होंगे। हालांकि, json का उपयोग करके, हम केवल वही मान स्टोर कर सकते हैं जिनकी एक विशेष API को आवश्यकता है।
एक साइड प्रोजेक्ट जिस पर मैं काम कर रहा हूँ, वह उपयोगकर्ताओं को आइटम्स पर मनमाने एट्रिब्यूट सेट करने की अनुमति देने के लिए JSON स्टोरेज का भी उपयोग करता है, जिसमें उपयोगकर्ता-परिभाषित एट्रिब्यूट शामिल हैं। इस डेटा की तरल और अप्रत्याशित प्रकृति को देखते हुए, JSON स्टोरेज (ज्ञात एट्रिब्यूट्स के लिए store_accessor के साथ) एक स्वाभाविक फिट है।
सारांश
JSON डेटा (और इसके चारों ओर ActiveRecord के हेल्पर्स) बहुत उपयोगी हो सकते हैं जब डेटा और डेटा संरचना परिवर्तनशील या अज्ञात हो। बेशक, इस तरह का डेटा स्टोरेज, अधिकांश चीजों की तरह, एक ट्रेड-ऑफ है। जबकि आपको विशेष रिकॉर्ड्स के लिए डेटा संरचना में बहुत लचीलापन मिलता है, आप कुछ डेटा अखंडता छोड़ देते हैं जो डेटाबेस कंस्ट्रेंट्स आपको दे सकते हैं। आप सामान्य ActiveRecord क्वेरीज़, जॉइन आदि के साथ रिकॉर्ड्स में क्वेरी करने की अपनी क्षमता भी कम कर देते हैं।
यहाँ कुछ नियम हैं, यदि आप:
- जानते हैं कि JSON कुंजियाँ सभी पंक्तियों के लिए समान होंगी, या
- किसी अन्य डेटाबेस टेबल की ID (प्राइमरी की) स्टोर कर रहे हैं, या
- JSON में किसी टेबल से रिकॉर्ड देखने के लिए उपयोग किए जाने वाले मान को स्टोर कर रहे हैं
तो, आप एक नई टेबल बनाने से बेहतर हो सकते हैं जो आपके लिए डेटा अखंडता लागू करने के लिए डेटाबेस का लाभ उठा सके। यदि, हालांकि, आप पंक्ति-विशिष्ट डेटा स्टोर कर रहे हैं जो सीधे अन्य टेबल से संबंधित नहीं हैं, तो JSON आपकी डेटाबेस संरचना को सरल बनाने में मदद कर सकता है।