MongoDB
 sql >> डेटाबेस >  >> NoSQL >> MongoDB

नेवले के साथ अनेक से अनेक

आपके पास वर्तमान में समस्या यह है कि आपने संदर्भ को एक मॉडल में सहेजा है लेकिन आपने इसे दूसरे में सहेजा नहीं है। MongoDB में कोई "स्वचालित संदर्भात्मक अखंडता" नहीं है, और "संबंधों" की ऐसी अवधारणा वास्तव में एक "मैनुअल" मामला है, और वास्तव में .populate() के मामले में है। संदर्भित जानकारी को पुनः प्राप्त करने के लिए वास्तव में अतिरिक्त प्रश्नों का एक पूरा समूह है। यहाँ कोई "जादू" नहीं है।

"कई से अनेक" का सही संचालन तीन विकल्पों में आता है:

सूची 1 - दोनों दस्तावेज़ों पर सरणियाँ रखें

आपके वर्तमान डिज़ाइन के बाद, आप जो भाग खो रहे हैं, वह संबंधित वस्तुओं को "दोनों" पर संदर्भित कर रहा है। प्रदर्शित करने के लिए एक सूची के लिए:

const { Schema } = mongoose = require('mongoose');

mongoose.Promise = global.Promise;
mongoose.set('debug',true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);

const uri = 'mongodb://localhost:27017/manydemo',
      options = { useNewUrlParser: true };

const itemSchema = new Schema({
  name: String,
  stores: [{ type: Schema.Types.ObjectId, ref: 'Store' }]
});

const storeSchema = new Schema({
  name: String,
  items: [{ type: Schema.Types.ObjectId, ref: 'Item' }]
});

const Item = mongoose.model('Item', itemSchema);
const Store = mongoose.model('Store', storeSchema);


const log = data => console.log(JSON.stringify(data,undefined,2))

(async function() {

  try {

    const conn = await mongoose.connect(uri,options);

    // Clean data
    await Promise.all(
      Object.entries(conn.models).map(([k,m]) => m.deleteMany() )
    );


    // Create some instances
    let [toothpaste,brush] = ['toothpaste','brush'].map(
      name => new Item({ name })
    );

    let [billsStore,tedsStore] = ['Bills','Teds'].map(
      name => new Store({ name })
    );

    // Add items to stores
    [billsStore,tedsStore].forEach( store => {
      store.items.push(toothpaste);   // add toothpaste to store
      toothpaste.stores.push(store);  // add store to toothpaste
    });

    // Brush is only in billsStore
    billsStore.items.push(brush);
    brush.stores.push(billsStore);

    // Save everything
    await Promise.all(
      [toothpaste,brush,billsStore,tedsStore].map( m => m.save() )
    );

    // Show stores
    let stores = await Store.find().populate('items','-stores');
    log(stores);

    // Show items
    let items = await Item.find().populate('stores','-items');
    log(items);

  } catch(e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }

})();

यह "आइटम" संग्रह बनाता है:

{
    "_id" : ObjectId("59ab96d9c079220dd8eec428"),
    "name" : "toothpaste",
    "stores" : [
            ObjectId("59ab96d9c079220dd8eec42a"),
            ObjectId("59ab96d9c079220dd8eec42b")
    ],
    "__v" : 0
}
{
    "_id" : ObjectId("59ab96d9c079220dd8eec429"),
    "name" : "brush",
    "stores" : [
            ObjectId("59ab96d9c079220dd8eec42a")
    ],
    "__v" : 0
}

और "स्टोर" संग्रह:

{
    "_id" : ObjectId("59ab96d9c079220dd8eec42a"),
    "name" : "Bills",
    "items" : [
            ObjectId("59ab96d9c079220dd8eec428"),
            ObjectId("59ab96d9c079220dd8eec429")
    ],
    "__v" : 0
}
{
    "_id" : ObjectId("59ab96d9c079220dd8eec42b"),
    "name" : "Teds",
    "items" : [
            ObjectId("59ab96d9c079220dd8eec428")
    ],
    "__v" : 0
}

और समग्र उत्पादन करता है जैसे:

Mongoose: items.deleteMany({}, {})
Mongoose: stores.deleteMany({}, {})
Mongoose: items.insertOne({ name: 'toothpaste', _id: ObjectId("59ab96d9c079220dd8eec428"), stores: [ ObjectId("59ab96d9c079220dd8eec42a"), ObjectId("59ab96d9c079220dd8eec42b") ], __v: 0 })
Mongoose: items.insertOne({ name: 'brush', _id: ObjectId("59ab96d9c079220dd8eec429"), stores: [ ObjectId("59ab96d9c079220dd8eec42a") ], __v: 0 })
Mongoose: stores.insertOne({ name: 'Bills', _id: ObjectId("59ab96d9c079220dd8eec42a"), items: [ ObjectId("59ab96d9c079220dd8eec428"), ObjectId("59ab96d9c079220dd8eec429") ], __v: 0 })
Mongoose: stores.insertOne({ name: 'Teds', _id: ObjectId("59ab96d9c079220dd8eec42b"), items: [ ObjectId("59ab96d9c079220dd8eec428") ], __v: 0 })
Mongoose: stores.find({}, { fields: {} })
Mongoose: items.find({ _id: { '$in': [ ObjectId("59ab96d9c079220dd8eec428"), ObjectId("59ab96d9c079220dd8eec429") ] } }, { fields: { stores: 0 } })
[
  {
    "_id": "59ab96d9c079220dd8eec42a",
    "name": "Bills",
    "__v": 0,
    "items": [
      {
        "_id": "59ab96d9c079220dd8eec428",
        "name": "toothpaste",
        "__v": 0
      },
      {
        "_id": "59ab96d9c079220dd8eec429",
        "name": "brush",
        "__v": 0
      }
    ]
  },
  {
    "_id": "59ab96d9c079220dd8eec42b",
    "name": "Teds",
    "__v": 0,
    "items": [
      {
        "_id": "59ab96d9c079220dd8eec428",
        "name": "toothpaste",
        "__v": 0
      }
    ]
  }
]
Mongoose: items.find({}, { fields: {} })
Mongoose: stores.find({ _id: { '$in': [ ObjectId("59ab96d9c079220dd8eec42a"), ObjectId("59ab96d9c079220dd8eec42b") ] } }, { fields: { items: 0 } })
[
  {
    "_id": "59ab96d9c079220dd8eec428",
    "name": "toothpaste",
    "__v": 0,
    "stores": [
      {
        "_id": "59ab96d9c079220dd8eec42a",
        "name": "Bills",
        "__v": 0
      },
      {
        "_id": "59ab96d9c079220dd8eec42b",
        "name": "Teds",
        "__v": 0
      }
    ]
  },
  {
    "_id": "59ab96d9c079220dd8eec429",
    "name": "brush",
    "__v": 0,
    "stores": [
      {
        "_id": "59ab96d9c079220dd8eec42a",
        "name": "Bills",
        "__v": 0
      }
    ]
  }
]

मुख्य बिंदु यह है कि आप वास्तव में प्रत्येक संग्रह में प्रत्येक दस्तावेज़ में संदर्भ डेटा जोड़ते हैं जहां एक संबंध मौजूद है। यहां मौजूद "सरणी" का उपयोग उन संदर्भों को संग्रहीत करने और संबंधित संग्रह से परिणामों को "लुकअप" करने के लिए किया जाता है और उन्हें वहां संग्रहीत ऑब्जेक्ट डेटा से बदल दिया जाता है।

जैसे भागों पर ध्यान दें:

// Add items to stores
[billsStore,tedsStore].forEach( store => {
  store.items.push(toothpaste);   // add toothpaste to store
  toothpaste.stores.push(store);  // add store to toothpaste
});

क्योंकि इसका मतलब है कि हम न केवल toothpaste जोड़ रहे हैं "items" . के लिए प्रत्येक स्टोर में सरणी, लेकिन हम प्रत्येक "store" . भी जोड़ रहे हैं "stores" . के लिए toothpaste . की सरणी वस्तु। ऐसा इसलिए किया जाता है ताकि रिश्ते किसी भी दिशा से पूछे जाने पर काम कर सकें। यदि आप केवल "स्टोर से आइटम" चाहते थे और कभी नहीं "आइटम से स्टोर", तो आपको "आइटम" प्रविष्टियों पर संबंध डेटा को बिल्कुल भी स्टोर करने की आवश्यकता नहीं होगी।

लिस्टिंग 2 - वर्चुअल और एक मध्यस्थ संग्रह का उपयोग करें

यह अनिवार्य रूप से क्लासिक "कई से कई" संबंध है। जहां दो संग्रहों के बीच संबंधों को सीधे परिभाषित करने के बजाय, एक और संग्रह (तालिका) है जो विवरण संग्रहीत करता है कि कौन सा आइटम किस स्टोर से संबंधित है।

पूरी सूची के रूप में:

const { Schema } = mongoose = require('mongoose');

mongoose.Promise = global.Promise;
mongoose.set('debug',true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);

const uri = 'mongodb://localhost:27017/manydemo',
      options = { useNewUrlParser: true };

const itemSchema = new Schema({
  name: String,
},{
 toJSON: { virtuals: true }
});

itemSchema.virtual('stores', {
  ref: 'StoreItem',
  localField: '_id',
  foreignField: 'itemId'
});

const storeSchema = new Schema({
  name: String,
},{
 toJSON: { virtuals: true }
});

storeSchema.virtual('items', {
  ref: 'StoreItem',
  localField: '_id',
  foreignField: 'storeId'
});

const storeItemSchema = new Schema({
  storeId: { type: Schema.Types.ObjectId, ref: 'Store', required: true },
  itemId: { type: Schema.Types.ObjectId, ref: 'Item', required: true }
});

const Item = mongoose.model('Item', itemSchema);
const Store = mongoose.model('Store', storeSchema);
const StoreItem = mongoose.model('StoreItem', storeItemSchema);

const log = data => console.log(JSON.stringify(data,undefined,2));

(async function() {

  try {

    const conn = await mongoose.connect(uri,options);

    // Clean data
    await Promise.all(
      Object.entries(conn.models).map(([k,m]) => m.deleteMany() )
    );

    // Create some instances
    let [toothpaste,brush] = await Item.insertMany(
      ['toothpaste','brush'].map( name => ({ name }) )
    );
    let [billsStore,tedsStore] = await Store.insertMany(
      ['Bills','Teds'].map( name => ({ name }) )
    );

    // Add toothpaste to both stores
    for( let store of [billsStore,tedsStore] ) {
      await StoreItem.update(
        { storeId: store._id, itemId: toothpaste._id },
        { },
        { 'upsert': true }
      );
    }

    // Add brush to billsStore
    await StoreItem.update(
      { storeId: billsStore._id, itemId: brush._id },
      {},
      { 'upsert': true }
    );

    // Show stores
    let stores = await Store.find().populate({
      path: 'items',
      populate: { path: 'itemId' }
    });
    log(stores);

    // Show Items
    let items = await Item.find().populate({
      path: 'stores',
      populate: { path: 'storeId' }
    });
    log(items);


  } catch(e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }

})();

संबंध अब अपने संग्रह में हैं, इसलिए डेटा अब "आइटम" के लिए अलग तरह से दिखाई देता है:

{
    "_id" : ObjectId("59ab996166d5cc0e0d164d74"),
    "__v" : 0,
    "name" : "toothpaste"
}
{
    "_id" : ObjectId("59ab996166d5cc0e0d164d75"),
    "__v" : 0,
    "name" : "brush"
}

और "स्टोर":

{
    "_id" : ObjectId("59ab996166d5cc0e0d164d76"),
    "__v" : 0,
    "name" : "Bills"
}
{
    "_id" : ObjectId("59ab996166d5cc0e0d164d77"),
    "__v" : 0,
    "name" : "Teds"
}

और अब "storeitems" के लिए जो संबंधों को मैप करता है:

{
    "_id" : ObjectId("59ab996179e41cc54405b72b"),
    "itemId" : ObjectId("59ab996166d5cc0e0d164d74"),
    "storeId" : ObjectId("59ab996166d5cc0e0d164d76"),
    "__v" : 0
}
{
    "_id" : ObjectId("59ab996179e41cc54405b72d"),
    "itemId" : ObjectId("59ab996166d5cc0e0d164d74"),
    "storeId" : ObjectId("59ab996166d5cc0e0d164d77"),
    "__v" : 0
}
{
    "_id" : ObjectId("59ab996179e41cc54405b72f"),
    "itemId" : ObjectId("59ab996166d5cc0e0d164d75"),
    "storeId" : ObjectId("59ab996166d5cc0e0d164d76"),
    "__v" : 0
}

जैसे पूर्ण आउटपुट के साथ:

Mongoose: items.deleteMany({}, {})
Mongoose: stores.deleteMany({}, {})
Mongoose: storeitems.deleteMany({}, {})
Mongoose: items.insertMany([ { __v: 0, name: 'toothpaste', _id: 59ab996166d5cc0e0d164d74 }, { __v: 0, name: 'brush', _id: 59ab996166d5cc0e0d164d75 } ])
Mongoose: stores.insertMany([ { __v: 0, name: 'Bills', _id: 59ab996166d5cc0e0d164d76 }, { __v: 0, name: 'Teds', _id: 59ab996166d5cc0e0d164d77 } ])
Mongoose: storeitems.update({ itemId: ObjectId("59ab996166d5cc0e0d164d74"), storeId: ObjectId("59ab996166d5cc0e0d164d76") }, { '$setOnInsert': { __v: 0 } }, { upsert: true })
Mongoose: storeitems.update({ itemId: ObjectId("59ab996166d5cc0e0d164d74"), storeId: ObjectId("59ab996166d5cc0e0d164d77") }, { '$setOnInsert': { __v: 0 } }, { upsert: true })
Mongoose: storeitems.update({ itemId: ObjectId("59ab996166d5cc0e0d164d75"), storeId: ObjectId("59ab996166d5cc0e0d164d76") }, { '$setOnInsert': { __v: 0 } }, { upsert: true })
Mongoose: stores.find({}, { fields: {} })
Mongoose: storeitems.find({ storeId: { '$in': [ ObjectId("59ab996166d5cc0e0d164d76"), ObjectId("59ab996166d5cc0e0d164d77") ] } }, { fields: {} })
Mongoose: items.find({ _id: { '$in': [ ObjectId("59ab996166d5cc0e0d164d74"), ObjectId("59ab996166d5cc0e0d164d75") ] } }, { fields: {} })
[
  {
    "_id": "59ab996166d5cc0e0d164d76",
    "__v": 0,
    "name": "Bills",
    "items": [
      {
        "_id": "59ab996179e41cc54405b72b",
        "itemId": {
          "_id": "59ab996166d5cc0e0d164d74",
          "__v": 0,
          "name": "toothpaste",
          "stores": null,
          "id": "59ab996166d5cc0e0d164d74"
        },
        "storeId": "59ab996166d5cc0e0d164d76",
        "__v": 0
      },
      {
        "_id": "59ab996179e41cc54405b72f",
        "itemId": {
          "_id": "59ab996166d5cc0e0d164d75",
          "__v": 0,
          "name": "brush",
          "stores": null,
          "id": "59ab996166d5cc0e0d164d75"
        },
        "storeId": "59ab996166d5cc0e0d164d76",
        "__v": 0
      }
    ],
    "id": "59ab996166d5cc0e0d164d76"
  },
  {
    "_id": "59ab996166d5cc0e0d164d77",
    "__v": 0,
    "name": "Teds",
    "items": [
      {
        "_id": "59ab996179e41cc54405b72d",
        "itemId": {
          "_id": "59ab996166d5cc0e0d164d74",
          "__v": 0,
          "name": "toothpaste",
          "stores": null,
          "id": "59ab996166d5cc0e0d164d74"
        },
        "storeId": "59ab996166d5cc0e0d164d77",
        "__v": 0
      }
    ],
    "id": "59ab996166d5cc0e0d164d77"
  }
]
Mongoose: items.find({}, { fields: {} })
Mongoose: storeitems.find({ itemId: { '$in': [ ObjectId("59ab996166d5cc0e0d164d74"), ObjectId("59ab996166d5cc0e0d164d75") ] } }, { fields: {} })
Mongoose: stores.find({ _id: { '$in': [ ObjectId("59ab996166d5cc0e0d164d76"), ObjectId("59ab996166d5cc0e0d164d77") ] } }, { fields: {} })
[
  {
    "_id": "59ab996166d5cc0e0d164d74",
    "__v": 0,
    "name": "toothpaste",
    "stores": [
      {
        "_id": "59ab996179e41cc54405b72b",
        "itemId": "59ab996166d5cc0e0d164d74",
        "storeId": {
          "_id": "59ab996166d5cc0e0d164d76",
          "__v": 0,
          "name": "Bills",
          "items": null,
          "id": "59ab996166d5cc0e0d164d76"
        },
        "__v": 0
      },
      {
        "_id": "59ab996179e41cc54405b72d",
        "itemId": "59ab996166d5cc0e0d164d74",
        "storeId": {
          "_id": "59ab996166d5cc0e0d164d77",
          "__v": 0,
          "name": "Teds",
          "items": null,
          "id": "59ab996166d5cc0e0d164d77"
        },
        "__v": 0
      }
    ],
    "id": "59ab996166d5cc0e0d164d74"
  },
  {
    "_id": "59ab996166d5cc0e0d164d75",
    "__v": 0,
    "name": "brush",
    "stores": [
      {
        "_id": "59ab996179e41cc54405b72f",
        "itemId": "59ab996166d5cc0e0d164d75",
        "storeId": {
          "_id": "59ab996166d5cc0e0d164d76",
          "__v": 0,
          "name": "Bills",
          "items": null,
          "id": "59ab996166d5cc0e0d164d76"
        },
        "__v": 0
      }
    ],
    "id": "59ab996166d5cc0e0d164d75"
  }
]

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

const itemSchema = new Schema({
  name: String,
},{
 toJSON: { virtuals: true }
});

itemSchema.virtual('stores', {
  ref: 'StoreItem',
  localField: '_id',
  foreignField: 'itemId'
});

आप वर्चुअल फ़ील्ड को इसके localField . के साथ असाइन करते हैं और foreignField मैपिंग ताकि बाद में .populate() कॉल जानता है कि क्या उपयोग करना है।

मध्यस्थ संग्रह की काफी मानक परिभाषा है:

const storeItemSchema = new Schema({
  storeId: { type: Schema.Types.ObjectId, ref: 'Store', required: true },
  itemId: { type: Schema.Types.ObjectId, ref: 'Item', required: true }
});

और नए आइटम को सरणियों पर "धकेलने" के बजाय, हम उन्हें इस नए संग्रह में जोड़ते हैं। इसके लिए एक उचित तरीका "अप्सर्ट्स" का उपयोग केवल एक नई प्रविष्टि बनाने के लिए करना है जब यह संयोजन मौजूद न हो:

// Add toothpaste to both stores
for( let store of [billsStore,tedsStore] ) {
  await StoreItem.update(
    { storeId: store._id, itemId: toothpaste._id },
    { },
    { 'upsert': true }
  );
}

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

वास्तव में इस "संबंधित" डेटा को क्वेरी करना फिर से थोड़ा अलग तरीके से काम करता है। क्योंकि इसमें एक और संग्रह शामिल है, हम .populate() . कहते हैं एक तरह से जो मानता है कि उसे अन्य पुनर्प्राप्त संपत्ति पर भी संबंध "लुकअप" करने की आवश्यकता है। तो आपके पास इस तरह के कॉल आते हैं:

 // Show stores
  let stores = await Store.find().populate({
    path: 'items',
    populate: { path: 'itemId' }
  });
  log(stores);

लिस्टिंग 3 - इसे सर्वर पर करने के लिए आधुनिक सुविधाओं का उपयोग करें

तो दस्तावेजों के भीतर "बढ़ती सरणी" के विकल्प के रूप में संबंध डेटा को स्टोर करने के लिए सरणी या मध्यस्थ संग्रह का उपयोग करने के लिए कौन सा दृष्टिकोण लिया गया है, इस पर निर्भर करता है कि स्पष्ट बात यह है कि आपको ध्यान देना चाहिए कि .populate() उपयोग की गई कॉल वास्तव में MongoDB को अतिरिक्त क्वेरी कर रही हैं और उन दस्तावेज़ों को अलग-अलग अनुरोधों में नेटवर्क पर खींच रही हैं।

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

यही कारण है कि आधुनिक MongoDB रिलीज़ में शामिल हैं $lookup जो वास्तव में सर्वर पर ही डेटा को "जॉइन" करता है। अब तक आप उन सभी आउटपुट को देख रहे होंगे जो एपीआई कॉल उत्पन्न करते हैं जैसा कि mongoose.set('debug',true) द्वारा दिखाया गया है। ।

इसलिए कई क्वेरी बनाने के बजाय, इस बार हम सर्वर पर "जॉइन" करने के लिए इसे एक एग्रीगेशन स्टेटमेंट बनाते हैं, और एक अनुरोध में परिणाम लौटाते हैं:

// Show Stores
let stores = await Store.aggregate([
  { '$lookup': {
    'from': StoreItem.collection.name,
    'let': { 'id': '$_id' },
    'pipeline': [
      { '$match': {
        '$expr': { '$eq': [ '$$id', '$storeId' ] }
      }},
      { '$lookup': {
        'from': Item.collection.name,
        'let': { 'itemId': '$itemId' },
        'pipeline': [
          { '$match': {
            '$expr': { '$eq': [ '$_id', '$$itemId' ] }
          }}
        ],
        'as': 'items'
      }},
      { '$unwind': '$items' },
      { '$replaceRoot': { 'newRoot': '$items' } }
    ],
    'as': 'items'
  }}
])
log(stores);

जो कोडिंग में लंबे समय तक, वास्तव में यहां बहुत छोटी सी कार्रवाई के लिए भी दक्षता में कहीं बेहतर है। यह निश्चित रूप से काफी बड़ा है।

पहले की तरह ही "मध्यस्थ" मॉडल का अनुसरण करना (और केवल उदाहरण के लिए, क्योंकि इसे किसी भी तरह से किया जा सकता है) हमारे पास एक पूरी सूची है:

const { Schema } = mongoose = require('mongoose');

const uri = 'mongodb://localhost:27017/manydemo',
      options = { useNewUrlParser: true };

mongoose.Promise = global.Promise;
mongoose.set('debug', true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);

const itemSchema = new Schema({
  name: String
}, {
  toJSON: { virtuals: true }
});

itemSchema.virtual('stores', {
  ref: 'StoreItem',
  localField: '_id',
  foreignField: 'itemId'
});

const storeSchema = new Schema({
  name: String
}, {
  toJSON: { virtuals: true }
});

storeSchema.virtual('items', {
  ref: 'StoreItem',
  localField: '_id',
  foreignField: 'storeId'
});

const storeItemSchema = new Schema({
  storeId: { type: Schema.Types.ObjectId, ref: 'Store', required: true },
  itemId: { type: Schema.Types.ObjectId, ref: 'Item', required: true }
});

const Item = mongoose.model('Item', itemSchema);
const Store = mongoose.model('Store', storeSchema);
const StoreItem = mongoose.model('StoreItem', storeItemSchema);

const log = data => console.log(JSON.stringify(data, undefined, 2));

(async function() {

  try {

    const conn = await mongoose.connect(uri, options);

    // Clean data
    await Promise.all(
      Object.entries(conn.models).map(([k,m]) => m.deleteMany())
    );

    // Create some instances
    let [toothpaste, brush] = await Item.insertMany(
      ['toothpaste', 'brush'].map(name => ({ name }) )
    );
    let [billsStore, tedsStore] = await Store.insertMany(
      ['Bills', 'Teds'].map( name => ({ name }) )
    );

    // Add toothpaste to both stores
    for ( let { _id: storeId }  of [billsStore, tedsStore] ) {
      await StoreItem.updateOne(
        { storeId, itemId: toothpaste._id },
        { },
        { 'upsert': true }
      );
    }

    // Add brush to billsStore
    await StoreItem.updateOne(
      { storeId: billsStore._id, itemId: brush._id },
      { },
      { 'upsert': true }
    );

    // Show Stores
    let stores = await Store.aggregate([
      { '$lookup': {
        'from': StoreItem.collection.name,
        'let': { 'id': '$_id' },
        'pipeline': [
          { '$match': {
            '$expr': { '$eq': [ '$$id', '$storeId' ] }
          }},
          { '$lookup': {
            'from': Item.collection.name,
            'let': { 'itemId': '$itemId' },
            'pipeline': [
              { '$match': {
                '$expr': { '$eq': [ '$_id', '$$itemId' ] }
              }}
            ],
            'as': 'items'
          }},
          { '$unwind': '$items' },
          { '$replaceRoot': { 'newRoot': '$items' } }
        ],
        'as': 'items'
      }}
    ])

    log(stores);

    // Show Items
    let items = await Item.aggregate([
      { '$lookup': {
        'from': StoreItem.collection.name,
        'let': { 'id': '$_id' },
        'pipeline': [
          { '$match': {
            '$expr': { '$eq': [ '$$id', '$itemId' ] }
          }},
          { '$lookup': {
            'from': Store.collection.name,
            'let': { 'storeId': '$storeId' },
            'pipeline': [
              { '$match': {
                '$expr': { '$eq': [ '$_id', '$$storeId' ] }
              }}
            ],
            'as': 'stores',
          }},
          { '$unwind': '$stores' },
          { '$replaceRoot': { 'newRoot': '$stores' } }
        ],
        'as': 'stores'
      }}
    ]);

    log(items);


  } catch(e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }

})()

और आउटपुट:

Mongoose: stores.aggregate([ { '$lookup': { from: 'storeitems', let: { id: '$_id' }, pipeline: [ { '$match': { '$expr': { '$eq': [ '$$id', '$storeId' ] } } }, { '$lookup': { from: 'items', let: { itemId: '$itemId' }, pipeline: [ { '$match': { '$expr': { '$eq': [ '$_id', '$$itemId' ] } } } ], as: 'items' } }, { '$unwind': '$items' }, { '$replaceRoot': { newRoot: '$items' } } ], as: 'items' } } ], {})
[
  {
    "_id": "5ca7210717dadc69652b37da",
    "name": "Bills",
    "__v": 0,
    "items": [
      {
        "_id": "5ca7210717dadc69652b37d8",
        "name": "toothpaste",
        "__v": 0
      },
      {
        "_id": "5ca7210717dadc69652b37d9",
        "name": "brush",
        "__v": 0
      }
    ]
  },
  {
    "_id": "5ca7210717dadc69652b37db",
    "name": "Teds",
    "__v": 0,
    "items": [
      {
        "_id": "5ca7210717dadc69652b37d8",
        "name": "toothpaste",
        "__v": 0
      }
    ]
  }
]
Mongoose: items.aggregate([ { '$lookup': { from: 'storeitems', let: { id: '$_id' }, pipeline: [ { '$match': { '$expr': { '$eq': [ '$$id', '$itemId' ] } } }, { '$lookup': { from: 'stores', let: { storeId: '$storeId' }, pipeline: [ { '$match': { '$expr': { '$eq': [ '$_id', '$$storeId' ] } } } ], as: 'stores' } }, { '$unwind': '$stores' }, { '$replaceRoot': { newRoot: '$stores' } } ], as: 'stores' } } ], {})
[
  {
    "_id": "5ca7210717dadc69652b37d8",
    "name": "toothpaste",
    "__v": 0,
    "stores": [
      {
        "_id": "5ca7210717dadc69652b37da",
        "name": "Bills",
        "__v": 0
      },
      {
        "_id": "5ca7210717dadc69652b37db",
        "name": "Teds",
        "__v": 0
      }
    ]
  },
  {
    "_id": "5ca7210717dadc69652b37d9",
    "name": "brush",
    "__v": 0,
    "stores": [
      {
        "_id": "5ca7210717dadc69652b37da",
        "name": "Bills",
        "__v": 0
      }
    ]
  }
]

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

अंतिम नोट

वे आम तौर पर "कई से कई" संबंधों से निपटने के लिए आपके दृष्टिकोण हैं, जो अनिवार्य रूप से या तो नीचे आते हैं:

  • संबंधित वस्तुओं के संदर्भों को पकड़े हुए प्रत्येक दस्तावेज़ में सरणियाँ रखना।

  • एक मध्यस्थ संग्रह संग्रहीत करना और अन्य डेटा प्राप्त करने के लिए एक लुकअप संदर्भ के रूप में उसका उपयोग करना।

सभी मामलों में यह आप पर निर्भर करता है वास्तव में उन संदर्भों को संग्रहीत करने के लिए यदि आप चीजों को "दोनों दिशाओं" पर काम करने की अपेक्षा करते हैं। बेशक $lookup और यहां तक ​​​​कि "वर्चुअल" जहां यह लागू होता है, इसका मतलब है कि आपको हमेशा हर स्रोत पर स्टोर करने की आवश्यकता नहीं है क्योंकि आप केवल एक ही स्थान पर "संदर्भ" कर सकते हैं और उन तरीकों को लागू करके उस जानकारी का उपयोग कर सकते हैं।

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

इसका मतलब सिर्फ ObjectId नहीं है मान जो अन्य मदों को इंगित करते हैं, लेकिन वास्तव में प्रत्येक दस्तावेज़ में सरणियों के भीतर पूर्ण डेटा संग्रहीत करते हैं। निश्चित रूप से "आकार" का एक मुद्दा है और निश्चित रूप से कई जगहों पर डेटा अपडेट करने के मुद्दे हैं। यह आमतौर पर एक एकल अनुरोध . होने के लिए ट्रेड ऑफ है और एक साधारण अनुरोध इसे अन्य संग्रहों में जाकर डेटा खोजने की आवश्यकता नहीं है क्योंकि यह "पहले से ही है"।

संदर्भ बनाम एम्बेडिंग के विषय पर बहुत सारी सामग्री है। एक बार ऐसा सारांश स्रोत मोंगोज़ पॉप्युलेट बनाम ऑब्जेक्ट नेस्टिंग है या यहां तक ​​कि सामान्य MongoDB संबंध:एम्बेड या संदर्भ? और कई अन्य।

आपको अवधारणाओं के बारे में सोचने में कुछ समय बिताना चाहिए और यह सामान्य रूप से आपके आवेदन पर कैसे लागू होता है। और ध्यान दें कि आप वास्तव में यहां RDBMS का उपयोग नहीं कर रहे हैं, इसलिए आप केवल एक कार्य को दूसरे की तरह करने के बजाय सही सुविधाओं का उपयोग कर सकते हैं जिनका आप फायदा उठाने के लिए हैं।



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. आधिकारिक C# ड्राइवर का उपयोग करके MongoDB Bson दस्तावेज़ को क्रमानुसार/डिसेरिएलाइज़ करें

  2. $lookup Mongodb . के लिए लोकलफ़ील्ड में स्ट्रिंग को ऑब्जेक्ट में कैसे बदलें?

  3. नेस्टेड फ़ील्ड से मान के साथ फ़ील्ड अपडेट करें

  4. Nodejs UUIDv4 नेवला में उपयोग किए जाने पर निरंतर आईडी मान उत्पन्न करता है

  5. PHP कोड का उपयोग करके csv को mongodb में आयात करना