एक सरणी में ObjectId की $ खोज


104

किसी ऐसे फ़ील्ड पर एक $ लुकअप करने के लिए वाक्यविन्यास क्या है जो कि ऑब्जेक्ट का केवल एक ObjectId के बजाय ObjectIds का एक सरणी है?

उदाहरण आदेश दस्तावेज़:

{
  _id: ObjectId("..."),
  products: [
    ObjectId("..<Car ObjectId>.."),
    ObjectId("..<Bike ObjectId>..")
  ]
}

काम नहीं कर रहा प्रश्न:

db.orders.aggregate([
    {
       $lookup:
         {
           from: "products",
           localField: "products",
           foreignField: "_id",
           as: "productObjects"
         }
    }
])

वांछित परिणाम

{
  _id: ObjectId("..."),
  products: [
    ObjectId("..<Car ObjectId>.."),
    ObjectId("..<Bike ObjectId>..")
  ],
  productObjects: [
    {<Car Object>},
    {<Bike Object>}
  ],
}

क्या ऑर्डर दस्तावेज के साथ मेरा उदाहरण पर्याप्त स्पष्ट नहीं है? क्या आप उत्पादों के लिए उदाहरण दस्तावेज चाहते हैं?
जेसन लिन

SERVER-22881 उम्मीद के मुताबिक सरणी बनाने के काम को ट्रैक करेगा (शाब्दिक मूल्य के रूप में नहीं)।
असी कामस्की

जवाबों:


142

2017 अपडेट

$ लुकअप अब स्थानीय क्षेत्र के रूप में सीधे एक सरणी का उपयोग कर सकता है$unwindअब जरूरत नहीं है।

पुराना उत्तर

$lookupएकत्रीकरण पाइपलाइन चरण एक सरणी के साथ सीधे काम नहीं करेगा। डिजाइन का मुख्य आशय संभव संबंधित डेटा पर "लेफ्ट जॉइन" के लिए "एक से कई" प्रकार के जॉइन (या वास्तव में "लुकअप") के रूप में है। लेकिन मूल्य एकवचन के लिए है न कि एक सरणी के लिए।

इसलिए आपको इस $lookupकार्य को करने के लिए ऑपरेशन करने से पहले सामग्री को पहले "डी-सामान्य" करना होगा । और इसका मतलब है कि उपयोग करना $unwind:

db.orders.aggregate([
    // Unwind the source
    { "$unwind": "$products" },
    // Do the lookup matching
    { "$lookup": {
       "from": "products",
       "localField": "products",
       "foreignField": "_id",
       "as": "productObjects"
    }},
    // Unwind the result arrays ( likely one or none )
    { "$unwind": "$productObjects" },
    // Group back to arrays
    { "$group": {
        "_id": "$_id",
        "products": { "$push": "$products" },
        "productObjects": { "$push": "$productObjects" }
    }}
])

बाद $lookupमैचों प्रत्येक सरणी सदस्य परिणाम तो तुम, एक सरणी में ही है $unwindऔर फिर से $groupकरने के लिए $pushअंतिम परिणाम के लिए नए सरणियों।

ध्यान दें कि कोई भी "लेफ्ट जॉइन" मैच जो नहीं पाए जाते हैं, दिए गए उत्पाद पर "productObjects" के लिए एक खाली सरणी बनाएंगे और इस प्रकार "उत्पाद" तत्व के लिए दस्तावेज़ को नकार दें जब दूसरा $unwindकहा जाता है।

हालांकि एक सरणी के लिए एक सीधा आवेदन अच्छा होगा, यह सिर्फ यह है कि वर्तमान में यह कैसे संभव हो सकता है एक विलक्षण मूल्य मिलान करके कई संभव है।

के रूप में $lookupमूल रूप से बहुत नया है, इसलिए वर्तमान में जिन लोगों के साथ परिचित हैं के लिए परिचित हो जाएगा के रूप में काम करता है नेवला के एक "गरीब मनुष्य संस्करण" के रूप में .populate()विधि वहाँ की पेशकश की। अंतर यह है कि $lookup"सर्वर साइड" की पेशकश "जुड़ने" के प्रसंस्करण को ग्राहक के विपरीत करती है और इसमें कुछ "परिपक्वता" $lookupवर्तमान में किस .populate()ऑफ़र (जैसे किसी सरणी पर सीधे लुकअप को रोकना) से अभाव है ।

यह वास्तव में SERVER-22881 में सुधार के लिए एक असाइन किया गया मुद्दा है , इसलिए कुछ भाग्य के साथ यह अगली रिलीज या एक के तुरंत बाद हिट होगा।

एक डिजाइन सिद्धांत के रूप में, आपकी वर्तमान संरचना न तो अच्छी है और न ही खराब है, लेकिन किसी भी "जॉइन" को बनाते समय केवल ओवरहेड्स के अधीन है। इस तरह, मोंगोबीडी इन्टीटेशन का मूल सिद्धांत लागू होता है, जहाँ यदि आप "डेटा" के साथ एक ही संग्रह में "प्री-जॉइन" कर सकते हैं, तो ऐसा करना सबसे अच्छा है।

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

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


धन्यवाद यह काम करता है! क्या यह एक संकेतक है कि मेरा डेटा ठीक से संरचित / सामान्य नहीं है?
जेसन लिन

1
@JasonLin "अच्छे / बुरे" के रूप में स्पष्ट नहीं है, इसलिए उत्तर में थोड़ा और स्पष्टीकरण जोड़ा गया है। यह इस बात पर निर्भर करता है कि आप पर क्या सूट करता है।
ब्लेक सेवेन

2
वर्तमान कार्यान्वयन कुछ हद तक अनजाने में है। यह स्थानीय क्षेत्र की एक सरणी में सभी मूल्यों को देखने के लिए समझ में आता है, यह सरणी का उपयोग करने के लिए शाब्दिक अर्थ नहीं है, इसलिए SERVER-22881 फिक्सिंग को ट्रैक करेगा।
अस्या कामस्की

@AsyaKamsky जो समझ में आता है। मैं आम तौर पर $lookupअपनी प्रारंभिक अवस्था में सुविधाओं के रूप में पुनः और दस्तावेज़ सत्यापन का इलाज कर रहा हूं और इसमें सुधार की संभावना है। तो परिणाम को फ़िल्टर करने के लिए "क्वेरी" के रूप में एक सरणी पर प्रत्यक्ष विस्तार का स्वागत किया जाएगा। उन दोनों को बहुत अधिक संयोग .populate()प्रक्रिया के साथ जोड़ा जाएगा, जिनका उपयोग कई लोग करते हैं। सीधे उत्तर सामग्री में समस्या लिंक जोड़ना।
ब्लेक सेवन

2
ध्यान दें कि इस एक के नीचे दिए गए उत्तर के अनुसार, यह अब लागू हो गया है और $lookupअब एक सरणी पर सीधे काम करता है।
एडम रीस


15

आप pipelineउप-डॉक्यूमेंट सरणी पर चेक प्रदर्शन करने के लिए चरण का उपयोग भी कर सकते हैं

यहाँ उदाहरण का उपयोग pythonकर रहा हूँ (क्षमा करें, मैं सांप हूँ)।

db.products.aggregate([
  { '$lookup': {
      'from': 'products',
      'let': { 'pid': '$products' },
      'pipeline': [
        { '$match': { '$expr': { '$in': ['$_id', '$$pid'] } } }
        // Add additional stages here 
      ],
      'as':'productObjects'
  }
])

यहां पकड़ सभी वस्तुओं को ObjectId array(विदेशी _idजो कि localक्षेत्र / प्रोप में है products) से मेल खाती है ।

आप अतिरिक्त रिकॉर्ड के साथ विदेशी रिकॉर्ड को साफ या प्रोजेक्ट भी कर सकते हैं stage, जैसा कि ऊपर टिप्पणी द्वारा इंगित किया गया है।


4

$ खोलना का उपयोग करें आप वस्तुओं की सरणी के बजाय पहली वस्तु प्राप्त करेंगे

क्वेरी:

db.getCollection('vehicles').aggregate([
  {
    $match: {
      status: "AVAILABLE",
      vehicleTypeId: {
        $in: Array.from(newSet(d.vehicleTypeIds))
      }
    }
  },
  {
    $lookup: {
      from: "servicelocations",
      localField: "locationId",
      foreignField: "serviceLocationId",
      as: "locations"
    }
  },
  {
    $unwind: "$locations"
  }
]);

परिणाम:

{
    "_id" : ObjectId("59c3983a647101ec58ddcf90"),
    "vehicleId" : "45680",
    "regionId" : 1.0,
    "vehicleTypeId" : "10TONBOX",
    "locationId" : "100",
    "description" : "Isuzu/2003-10 Ton/Box",
    "deviceId" : "",
    "earliestStart" : 36000.0,
    "latestArrival" : 54000.0,
    "status" : "AVAILABLE",
    "accountId" : 1.0,
    "locations" : {
        "_id" : ObjectId("59c3afeab7799c90ebb3291f"),
        "serviceLocationId" : "100",
        "regionId" : 1.0,
        "zoneId" : "DXBZONE1",
        "description" : "Masafi Park Al Quoz",
        "locationPriority" : 1.0,
        "accountTypeId" : 0.0,
        "locationType" : "DEPOT",
        "location" : {
            "makani" : "",
            "lat" : 25.123091,
            "lng" : 55.21082
        },
        "deliveryDays" : "MTWRFSU",
        "timeWindow" : {
            "timeWindowTypeId" : "1"
        },
        "address1" : "",
        "address2" : "",
        "phone" : "",
        "city" : "",
        "county" : "",
        "state" : "",
        "country" : "",
        "zipcode" : "",
        "imageUrl" : "",
        "contact" : {
            "name" : "",
            "email" : ""
        },
        "status" : "",
        "createdBy" : "",
        "updatedBy" : "",
        "updateDate" : "",
        "accountId" : 1.0,
        "serviceTimeTypeId" : "1"
    }
}


{
    "_id" : ObjectId("59c3983a647101ec58ddcf91"),
    "vehicleId" : "81765",
    "regionId" : 1.0,
    "vehicleTypeId" : "10TONBOX",
    "locationId" : "100",
    "description" : "Hino/2004-10 Ton/Box",
    "deviceId" : "",
    "earliestStart" : 36000.0,
    "latestArrival" : 54000.0,
    "status" : "AVAILABLE",
    "accountId" : 1.0,
    "locations" : {
        "_id" : ObjectId("59c3afeab7799c90ebb3291f"),
        "serviceLocationId" : "100",
        "regionId" : 1.0,
        "zoneId" : "DXBZONE1",
        "description" : "Masafi Park Al Quoz",
        "locationPriority" : 1.0,
        "accountTypeId" : 0.0,
        "locationType" : "DEPOT",
        "location" : {
            "makani" : "",
            "lat" : 25.123091,
            "lng" : 55.21082
        },
        "deliveryDays" : "MTWRFSU",
        "timeWindow" : {
            "timeWindowTypeId" : "1"
        },
        "address1" : "",
        "address2" : "",
        "phone" : "",
        "city" : "",
        "county" : "",
        "state" : "",
        "country" : "",
        "zipcode" : "",
        "imageUrl" : "",
        "contact" : {
            "name" : "",
            "email" : ""
        },
        "status" : "",
        "createdBy" : "",
        "updatedBy" : "",
        "updateDate" : "",
        "accountId" : 1.0,
        "serviceTimeTypeId" : "1"
    }
}

0

के साथ $lookupऔर बाद $groupमें एकत्र करना बहुत बोझिल है, इसलिए यदि (और यह एक माध्यम है) तो आप नोड और Mongoose या स्कीमा में कुछ संकेत के साथ एक सहायक पुस्तकालय का उपयोग कर रहे हैं, आप .populate()उन दस्तावेजों को लाने के लिए उपयोग कर सकते हैं :

var mongoose = require("mongoose"),
    Schema = mongoose.Schema;

var productSchema = Schema({ ... });

var orderSchema = Schema({
  _id     : Number,
  products: [ { type: Schema.Types.ObjectId, ref: "Product" } ]
});

var Product = mongoose.model("Product", productSchema);
var Order   = mongoose.model("Order", orderSchema);

...

Order
    .find(...)
    .populate("products")
    ...

0

मुझे असहमत होना पड़ेगा, अगर हम इसे $ माच के चरण के साथ पेश करते हैं, तो हम आईडी व्यू के साथ $ लुकअप का काम कर सकते हैं।

// replace IDs array with lookup results
db.products.aggregate([
    { $match: { products : { $exists: true } } },
    {
        $lookup: {
            from: "products",
            localField: "products",
            foreignField: "_id",
            as: "productObjects"
        }
    }
])

यह और अधिक जटिल हो जाता है यदि हम लुकअप परिणाम को एक पाइपलाइन में पास करना चाहते हैं। लेकिन फिर ऐसा करने का एक तरीका है (पहले से ही @ user12164 द्वारा सुझाया गया है):

// replace IDs array with lookup results passed to pipeline
db.products.aggregate([
    { $match: { products : { $exists: true } } },
    {
        $lookup: {
            from: "products",
             let: { products: "$products"},
             pipeline: [
                 { $match: { $expr: {$in: ["$_id", "$$products"] } } },
                 { $project: {_id: 0} } // suppress _id
             ],
            as: "productObjects"
        }
    }
])

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