MongoDB / NoSQL: दस्तावेज़ परिवर्तन इतिहास को बनाए रखना


134

डेटाबेस अनुप्रयोगों में एक काफी सामान्य आवश्यकता डेटाबेस में एक या एक से अधिक विशिष्ट संस्थाओं के परिवर्तनों को ट्रैक करना है। मैंने इसे रॉ वर्जनिंग कहा है, एक लॉग टेबल या एक हिस्ट्री टेबल (मुझे यकीन है कि इसके अन्य नाम हैं)। RDBMS में इसे एप्रोच करने के कई तरीके हैं - आप सभी सोर्स टेबल से सभी बदलावों को एक टेबल (एक लॉग का अधिक) लिख सकते हैं या प्रत्येक सोर्स टेबल के लिए एक अलग हिस्ट्री टेबल रख सकते हैं। आपके पास आवेदन कोड में या डेटाबेस ट्रिगर्स के माध्यम से लॉगिंग को प्रबंधित करने का विकल्प भी है।

मैं यह सोचने की कोशिश कर रहा हूं कि एक ही समस्या का समाधान NoSQL / दस्तावेज़ डेटाबेस (विशेष रूप से MongoDB) में कैसा दिखेगा, और यह एक समान तरीके से कैसे हल किया जाएगा। क्या यह दस्तावेज़ों के लिए संस्करण संख्याएँ बनाने के समान सरल होगा, और उन्हें कभी भी ओवरराइट नहीं किया जाएगा? "वास्तविक" बनाम "लॉग इन" दस्तावेजों के लिए अलग-अलग संग्रह बनाना? यह क्वेरी और प्रदर्शन को कैसे प्रभावित करेगा?

वैसे भी, यह NoSQL डेटाबेस के साथ एक सामान्य परिदृश्य है, और यदि हां, तो क्या एक सामान्य समाधान है?


आप किस भाषा चालक का उपयोग कर रहे हैं?
जोशुआ अंशयोगी

अभी तक तय नहीं किया गया है - अभी भी छेड़छाड़ और अभी तक अंतिम छोर की पसंद को अंतिम रूप नहीं दिया है (हालांकि मोंगोबीडी चरम संभावना दिखती है)। मैं NoRM (C #) के साथ छेड़छाड़ कर रहा हूं, और मुझे उस प्रोजेक्ट से जुड़े कुछ नाम पसंद हैं, इसलिए यह पसंद की संभावना है।
फिल सैंडलर

2
मुझे पता है कि यह एक पुराना प्रश्न है, लेकिन जो कोई भी MongoDB के साथ संस्करण की तलाश में है, यह SO प्रश्न संबंधित है और बेहतर उत्तरों के साथ मेरी राय में है।
एडोल्फ

जवाबों:


107

अच्छा सवाल है, मैं खुद को भी देख रहा था।

प्रत्येक परिवर्तन पर एक नया संस्करण बनाएँ

मैं रूबी के लिए मोंगॉयड ड्राइवर के संस्करण मोड में आया था । मैंने स्वयं इसका उपयोग नहीं किया है, लेकिन जो मुझे मिल सकता है , वह प्रत्येक दस्तावेज़ में एक संस्करण संख्या जोड़ता है। पुराने संस्करण दस्तावेज़ में ही एम्बेडेड हैं। मुख्य दोष यह है कि प्रत्येक परिवर्तन पर पूरे दस्तावेज़ को डुप्लिकेट किया जाता है , जिसके परिणामस्वरूप जब आप बड़े दस्तावेज़ों के साथ काम कर रहे हैं तो बहुत सारी डुप्लिकेट सामग्री संग्रहीत की जाएगी। यह तब ठीक है जब आप छोटे आकार के दस्तावेज़ों के साथ काम कर रहे हैं और / या बहुत बार दस्तावेजों को अपडेट नहीं करते हैं।

केवल एक नए संस्करण में परिवर्तन स्टोर करें

एक और दृष्टिकोण केवल नए संस्करण में केवल परिवर्तित फ़ील्ड्स को संग्रहीत करना होगा । फिर आप दस्तावेज़ के किसी भी संस्करण को फिर से बनाने के लिए अपने इतिहास को 'समतल' कर सकते हैं। यह हालांकि जटिल है, क्योंकि आपको अपने मॉडल में परिवर्तनों को ट्रैक करने और अद्यतनों को स्टोर करने और इस तरह से हटाने की आवश्यकता है कि आपका एप्लिकेशन अप-टू-डेट दस्तावेज़ को फिर से संगठित कर सके। यह मुश्किल हो सकता है, क्योंकि आप फ्लैट SQL टेबल के बजाय संरचित दस्तावेजों के साथ काम कर रहे हैं।

दस्तावेज़ के भीतर स्टोर परिवर्तन

प्रत्येक क्षेत्र का एक अलग इतिहास भी हो सकता है। किसी दिए गए संस्करण में दस्तावेजों का पुनर्निर्माण इस तरह से बहुत आसान है। आपके एप्लिकेशन में आपको परिवर्तनों को स्पष्ट रूप से ट्रैक करने की आवश्यकता नहीं है, लेकिन जब आप इसके मूल्य को बदलते हैं तो संपत्ति का एक नया संस्करण बनाएं। एक दस्तावेज़ कुछ इस तरह दिख सकता है:

{
  _id: "4c6b9456f61f000000007ba6"
  title: [
    { version: 1, value: "Hello world" },
    { version: 6, value: "Foo" }
  ],
  body: [
    { version: 1, value: "Is this thing on?" },
    { version: 2, value: "What should I write?" },
    { version: 6, value: "This is the new body" }
  ],
  tags: [
    { version: 1, value: [ "test", "trivial" ] },
    { version: 6, value: [ "foo", "test" ] }
  ],
  comments: [
    {
      author: "joe", // Unversioned field
      body: [
        { version: 3, value: "Something cool" }
      ]
    },
    {
      author: "xxx",
      body: [
        { version: 4, value: "Spam" },
        { version: 5, deleted: true }
      ]
    },
    {
      author: "jim",
      body: [
        { version: 7, value: "Not bad" },
        { version: 8, value: "Not bad at all" }
      ]
    }
  ]
}

किसी संस्करण में हटाए गए दस्तावेज़ का चिह्नित भाग अभी भी कुछ अजीब है, हालांकि। आप stateउन हिस्सों के लिए एक फ़ील्ड प्रस्तुत कर सकते हैं जिन्हें आपके आवेदन से हटाया / पुनर्स्थापित किया जा सकता है:

{
  author: "xxx",
  body: [
    { version: 4, value: "Spam" }
  ],
  state: [
    { version: 4, deleted: false },
    { version: 5, deleted: true }
  ]
}

इनमें से प्रत्येक दृष्टिकोण के साथ आप एक संग्रह में एक अप-टू-डेट और चपटा संस्करण स्टोर कर सकते हैं और एक अलग संग्रह में इतिहास डेटा। यदि आप केवल किसी दस्तावेज़ के नवीनतम संस्करण में रुचि रखते हैं तो यह क्वेरी समय में सुधार करना चाहिए। लेकिन जब आपको नवीनतम संस्करण और ऐतिहासिक डेटा दोनों की आवश्यकता होती है, तो आपको एक के बजाय दो प्रश्न करने होंगे। इसलिए एक ही संग्रह बनाम दो अलग-अलग संग्रह का उपयोग करने का विकल्प इस बात पर निर्भर होना चाहिए कि आपके आवेदन को ऐतिहासिक संस्करणों की कितनी बार आवश्यकता है

इस जवाब में से अधिकांश सिर्फ मेरे विचारों का एक मस्तिष्क डंप है, मैंने वास्तव में अभी तक इसमें से कोई भी कोशिश नहीं की है। उस पर पीछे मुड़ते हुए, पहला विकल्प संभवतः सबसे आसान और सबसे अच्छा समाधान है, जब तक कि आपके आवेदन के लिए डुप्लिकेट डेटा का ओवरहेड बहुत महत्वपूर्ण न हो। दूसरा विकल्प काफी जटिल है और शायद प्रयास के लायक नहीं है। तीसरा विकल्प मूल रूप से विकल्प दो का अनुकूलन है और इसे लागू करना आसान होना चाहिए, लेकिन संभवतः कार्यान्वयन प्रयास के लायक नहीं है जब तक कि आप वास्तव में विकल्प एक के साथ नहीं जा सकते।

इस पर प्रतिक्रिया के लिए आगे देख रहे हैं, और समस्या के अन्य लोगों के समाधान :)


डेल्टास के भंडारण के बारे में क्या है, ताकि आपको एक ऐतिहासिक दस्तावेज प्राप्त करने के लिए समतल करना पड़े और हमेशा वर्तमान उपलब्ध हो?
jpmc26

@ jpmc26 यह दूसरे दृष्टिकोण के समान है, लेकिन नवीनतम संस्करणों को पाने के लिए डेल्टास को बचाने के बजाय, आप ऐतिहासिक संस्करणों को प्राप्त करने के लिए डेल्टास को बचा रहे हैं। उपयोग करने के लिए कौन सा दृष्टिकोण इस बात पर निर्भर करता है कि आपको ऐतिहासिक संस्करणों की कितनी बार आवश्यकता होगी।
नील्स वैन डेर रेस्ट

आप दस्तावेज़ को वर्तमान स्थिति में एक दृश्य के रूप में उपयोग करने के बारे में एक पैराग्राफ जोड़ सकते हैं और एक चेंजगॉग के रूप में एक दूसरा दस्तावेज़ हो सकता है जो टाइमस्टैम्प सहित प्रत्येक परिवर्तन को ट्रैक करेगा (प्रारंभिक मान इस लॉग में दिखाई देने की आवश्यकता है) - आप तब 'पुनरावृत्ति कर सकते हैं 'किसी भी समय बिंदु पर और उदाहरण के लिए जब आपका एल्गोरिथ्म उसे छूता है या उपयोगकर्ता द्वारा उस पर क्लिक करने पर कोई आइटम कैसे प्रदर्शित होता है, यह देखने के लिए क्या चल रहा था, को सहसंबंधित करें।
मैनुएल ने Schwidt

यदि यह अनुक्रमित फ़ील्ड को सरणियों के रूप में प्रदर्शित किया जाता है तो क्या यह प्रदर्शन को प्रभावित करेगा?
दिमित्री

@ सभी - क्या आप इसे प्राप्त करने के लिए कुछ कोड साझा कर सकते हैं?
प्र। ए।

8

हमने इसे अपनी साइट पर आंशिक रूप से लागू कर दिया है और हम 'स्टोर रिव्यूज इन ए अलग डॉक्यूमेंट' (और अलग डेटाबेस) का उपयोग करते हैं। हमने डिफरेंशियल को वापस करने के लिए एक कस्टम फंक्शन लिखा है और हम इसे स्टोर करते हैं। इतना कठिन नहीं है और स्वचालित रिकवरी की अनुमति दे सकता है।


2
क्या आप कृपया कुछ कोड उसी के आसपास साझा कर सकते हैं? यह दृष्टिकोण आशाजनक लगता है
Pra_A

1
@smilyface - स्प्रिंग बूट जेवर्स एकीकरण इसे प्राप्त करने के लिए सबसे अच्छा है
प्राहा

@PAA - मैंने एक प्रश्न (लगभग एक ही अवधारणा) पूछा है। stackoverflow.com/questions/56683389/… क्या आपके पास इसके लिए कोई इनपुट है?
स्माइलफेस

6

दस्तावेज़ में स्टोर परिवर्तन पर भिन्नता क्यों नहीं ?

प्रत्येक कुंजी जोड़ी के खिलाफ संस्करणों को संग्रहीत करने के बजाय, दस्तावेज़ में वर्तमान प्रमुख जोड़े हमेशा सबसे हाल की स्थिति का प्रतिनिधित्व करते हैं और परिवर्तनों का एक 'लॉग' एक इतिहास सरणी में संग्रहीत होता है। केवल वे कुंजी जो निर्माण के बाद से बदल गई हैं, लॉग में एक प्रविष्टि होगी।

{
  _id: "4c6b9456f61f000000007ba6"
  title: "Bar",
  body: "Is this thing on?",
  tags: [ "test", "trivial" ],
  comments: [
    { key: 1, author: "joe", body: "Something cool" },
    { key: 2, author: "xxx", body: "Spam", deleted: true },
    { key: 3, author: "jim", body: "Not bad at all" }
  ],
  history: [
    { 
      who: "joe",
      when: 20160101,
      what: { title: "Foo", body: "What should I write?" }
    },
    { 
      who: "jim",
      when: 20160105,
      what: { tags: ["test", "test2"], comments: { key: 3, body: "Not baaad at all" }
    }
  ]
}

2

एक वर्तमान NoSQL डेटाबेस और एक ऐतिहासिक NoSQL डेटाबेस हो सकता है। हर रोज रात में चलने वाला ईटीएल होगा। यह ईटीएल एक टाइमस्टैम्प के साथ हर मूल्य को रिकॉर्ड करेगा, इसलिए मूल्यों के बजाय यह हमेशा ट्यूपल (संस्करण वाले क्षेत्र) होगा। यह केवल एक नया मान रिकॉर्ड करेगा यदि प्रक्रिया में अंतरिक्ष की बचत, वर्तमान मूल्य पर कोई बदलाव किया गया था। उदाहरण के लिए, यह ऐतिहासिक NoSQL डेटाबेस json फ़ाइल इस तरह दिख सकती है:

{
  _id: "4c6b9456f61f000000007ba6"
  title: [
    { date: 20160101, value: "Hello world" },
    { date: 20160202, value: "Foo" }
  ],
  body: [
    { date: 20160101, value: "Is this thing on?" },
    { date: 20160102, value: "What should I write?" },
    { date: 20160202, value: "This is the new body" }
  ],
  tags: [
    { date: 20160101, value: [ "test", "trivial" ] },
    { date: 20160102, value: [ "foo", "test" ] }
  ],
  comments: [
    {
      author: "joe", // Unversioned field
      body: [
        { date: 20160301, value: "Something cool" }
      ]
    },
    {
      author: "xxx",
      body: [
        { date: 20160101, value: "Spam" },
        { date: 20160102, deleted: true }
      ]
    },
    {
      author: "jim",
      body: [
        { date: 20160101, value: "Not bad" },
        { date: 20160102, value: "Not bad at all" }
      ]
    }
  ]
}

0

पायथन के उपयोगकर्ताओं (अजगर 3+, और निश्चित रूप से) के लिए, हिस्टोरिकलकोलिलेशन है जो कि पाइमोन्गो के संग्रह ऑब्जेक्ट का विस्तार है।

डॉक्स से उदाहरण:

from historical_collection.historical import HistoricalCollection
from pymongo import MongoClient
class Users(HistoricalCollection):
    PK_FIELDS = ['username', ]  # <<= This is the only requirement

# ...

users = Users(database=db)

users.patch_one({"username": "darth_later", "email": "darthlater@example.com"})
users.patch_one({"username": "darth_later", "email": "darthlater@example.com", "laser_sword_color": "red"})

list(users.revisions({"username": "darth_later"}))

# [{'_id': ObjectId('5d98c3385d8edadaf0bb845b'),
#   'username': 'darth_later',
#   'email': 'darthlater@example.com',
#   '_revision_metadata': None},
#  {'_id': ObjectId('5d98c3385d8edadaf0bb845b'),
#   'username': 'darth_later',
#   'email': 'darthlater@example.com',
#   '_revision_metadata': None,
#   'laser_sword_color': 'red'}]

पूर्ण प्रकटीकरण, मैं पैकेज लेखक हूं। :)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.