MongoDB संग्रह में ऑब्जेक्ट सरणी में केवल queried तत्व प्राप्त करें


377

मान लीजिए मेरे संग्रह में आपके पास निम्नलिखित दस्तावेज हैं:

{  
   "_id":ObjectId("562e7c594c12942f08fe4192"),
   "shapes":[  
      {  
         "shape":"square",
         "color":"blue"
      },
      {  
         "shape":"circle",
         "color":"red"
      }
   ]
},
{  
   "_id":ObjectId("562e7c594c12942f08fe4193"),
   "shapes":[  
      {  
         "shape":"square",
         "color":"black"
      },
      {  
         "shape":"circle",
         "color":"green"
      }
   ]
}

प्रश्न करें:

db.test.find({"shapes.color": "red"}, {"shapes.color": 1})

या

db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})

लौटे दस्तावेज़ (दस्तावेज़ 1) से मेल खाता है , लेकिन हमेशा सभी सरणी मदों के साथ shapes:

{ "shapes": 
  [
    {"shape": "square", "color": "blue"},
    {"shape": "circle", "color": "red"}
  ] 
}

हालाँकि, मैं केवल उस सारणी के साथ दस्तावेज़ (दस्तावेज़ 1) प्राप्त करना चाहता हूँ color=red:

{ "shapes": 
  [
    {"shape": "circle", "color": "red"}
  ] 
}

मैं यह कैसे कर सकता हूँ?

जवाबों:


416

MongoDB 2.2 का नया $elemMatchप्रोजेक्शन ऑपरेटर लौटाए गए दस्तावेज़ को बदलने के लिए एक और तरीका प्रदान करता है जिसमें केवल पहला मिलान किया गया shapesतत्व शामिल है:

db.test.find(
    {"shapes.color": "red"}, 
    {_id: 0, shapes: {$elemMatch: {color: "red"}}});

यह दिखाता है:

{"shapes" : [{"shape": "circle", "color": "red"}]}

2.2 में, आप इसका उपयोग भी कर सकते हैं $ projection operator, जहां $एक प्रक्षेपण वस्तु क्षेत्र का नाम क्वेरी से क्षेत्र के पहले मिलान सरणी तत्व के सूचकांक का प्रतिनिधित्व करता है। निम्नलिखित के समान परिणाम मिलते हैं:

db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});

MongoDB 3.2 अद्यतन

3.2 रिलीज के साथ शुरू करने के लिए, आप $filterप्रक्षेपण के दौरान एक सरणी को फ़िल्टर करने के लिए नए एकत्रीकरण ऑपरेटर का उपयोग कर सकते हैं , जिसमें केवल पहले के बजाय सभी मैचों को शामिल करने का लाभ है ।

db.test.aggregate([
    // Get just the docs that contain a shapes element where color is 'red'
    {$match: {'shapes.color': 'red'}},
    {$project: {
        shapes: {$filter: {
            input: '$shapes',
            as: 'shape',
            cond: {$eq: ['$$shape.color', 'red']}
        }},
        _id: 0
    }}
])

परिणाम:

[ 
    {
        "shapes" : [ 
            {
                "shape" : "circle",
                "color" : "red"
            }
        ]
    }
]

15
किसी भी समाधान अगर मैं चाहता हूँ कि यह हर तत्व है कि यह सिर्फ पहले के बजाय मेल खाता है?
स्टीव एनजी

मुझे डर है कि मैं मैंगो 3.0.X :-(
charliebrownie

@charliebrownie तब उपयोग करने वाले अन्य उत्तरों में से एक का उपयोग करें aggregate
जॉनीएचके

यह क्वेरी केवल सरणी "आकृतियों" को वापस करती है और यह अन्य क्षेत्रों को वापस नहीं करेगी। किसी को पता है कि अन्य क्षेत्रों को भी कैसे वापस करना है?
मार्क थिएन

1
यह भी काम करता है:db.test.find({}, {shapes: {$elemMatch: {color: "red"}}});
पॉल

97

MongoDB 2.2+ में नया एग्रीगेशन फ्रेमवर्क मैप / रिड्यूस का विकल्प प्रदान करता है। $unwindऑपरेटर अपने को अलग करने के लिए किया जा सकता shapesदस्तावेजों की एक धारा है कि मिलान किया जा सकता में सरणी:

db.test.aggregate(
  // Start with a $match pipeline which can take advantage of an index and limit documents processed
  { $match : {
     "shapes.color": "red"
  }},
  { $unwind : "$shapes" },
  { $match : {
     "shapes.color": "red"
  }}
)

का परिणाम:

{
    "result" : [
        {
            "_id" : ObjectId("504425059b7c9fa7ec92beec"),
            "shapes" : {
                "shape" : "circle",
                "color" : "red"
            }
        }
    ],
    "ok" : 1
}

6
@ जॉनीएचके: इस मामले में, $elemMatchएक और विकल्प है। मैं वास्तव में एक Google समूह प्रश्न के माध्यम से यहाँ आया था जहाँ $ elemMatch काम नहीं करेगा क्योंकि यह केवल प्रति दस्तावेज पहला मैच लौटाता है।
स्टेनी

1
धन्यवाद, मुझे उस सीमा के बारे में पता नहीं था, इसलिए यह जानना अच्छा है। आप जिस टिप्पणी का जवाब दे रहे हैं, उसे हटाने के लिए क्षमा करें, मैंने इसके बजाय एक और उत्तर पोस्ट करने का निर्णय लिया और लोगों को भ्रमित नहीं करना चाहता था।
जॉनीएचके

3
@ जॉनीएचके: कोई चिंता नहीं, सवाल के लिए अब तीन उपयोगी उत्तर हैं ;-)
स्टेनी

अन्य खोजकर्ताओं के लिए, इसके अलावा मैंने भी जोड़ने की कोशिश की { $project : { shapes : 1 } }- जो काम करने के लिए लग रहा था और सहायक होगा यदि संलग्न दस्तावेज बड़े थे और आप सिर्फ shapesप्रमुख मूल्यों को देखना चाहते थे ।
user1063287

2
@calmbird I ने प्रारंभिक $ मैच चरण को शामिल करने के लिए उदाहरण को अपडेट किया। यदि आप एक अधिक कुशल फीचर सुझाव में रुचि रखते हैं, तो मैं SERVER-6612 देखूंगा / बढ़ाऊंगा: एक प्रोजेक्शन में कई ऐरे वैल्यूज को प्रोजेक्‍ट करने में सपोर्ट करता है , जैसे MongoDB इश्यू ट्रैकर में $ एलएममैच प्रोजेक्‍ट स्‍पेसियर
स्टेनी

30

एक और इंटरेस्टिंग तरीका $ redact का उपयोग करना है , जो MongoDB 2.6 की नई एकत्रीकरण विशेषताओं में से एक है । यदि आप 2.6 का उपयोग कर रहे हैं, तो आपको एक $ खोल की आवश्यकता नहीं है, जो आपको बड़े सरणियों के कारण प्रदर्शन समस्याओं का कारण हो सकता है।

db.test.aggregate([
    { $match: { 
         shapes: { $elemMatch: {color: "red"} } 
    }},
    { $redact : {
         $cond: {
             if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
             then: "$$DESCEND",
             else: "$$PRUNE"
         }
    }}]);

$redact "दस्तावेजों में संग्रहीत जानकारी के आधार पर दस्तावेजों की सामग्री को प्रतिबंधित करता है" । तो यह केवल दस्तावेज़ के अंदर चलेगा । यह मूल रूप से आपके दस्तावेज़ को नीचे से ऊपर तक स्कैन करता है, और यह जाँचता है कि क्या यह आपकी ifस्थिति से मेल खाता है $cond, अगर वहाँ है तो यह या तो सामग्री ( $$DESCEND) या हटा ( $$PRUNE) रखेगा ।

ऊपर के उदाहरण में, पहले $matchपूरे shapesसरणी को लौटाता है , और $ redact इसे अपेक्षित परिणाम के लिए स्ट्रिप्स करता है।

ध्यान दें कि {$not:"$color"}आवश्यक है, क्योंकि यह शीर्ष दस्तावेज़ को भी स्कैन करेगा, और यदि शीर्ष स्तर पर $redactकोई colorफ़ील्ड नहीं मिलती है तो यह वापस आ जाएगी falseजो पूरे दस्तावेज़ को छीन सकती है जो हम नहीं चाहते हैं।


1
सही जवाब। जैसा कि आपने उल्लेख किया है कि डॉलर में बहुत से रैम की खपत होगी। इसलिए तुलना करने पर यह बेहतर होगा।
मनोज

मुझे एक शंका है। उदाहरण में, "आकार" एक सरणी है। क्या "$ redact" सभी ऑब्जेक्ट्स को "आकृतियों" सरणी में स्कैन करेगा ?? प्रदर्शन के संबंध में यह कैसे अच्छा होगा ??
मनोजप

यह सब नहीं, बल्कि आपके पहले मैच का परिणाम है। यही कारण है कि आप $matchअपने पहले कुल चरण के रूप में
डालते हैं

okkk .. अगर "रंग" फ़ील्ड पर एक इंडेक्स बनाया गया है, तब भी यह "आकृतियों" सरणी में सभी ऑब्जेक्ट्स को स्कैन करेगा ??? जो एक सरणी में कई वस्तुओं के मिलान का कुशल तरीका हो सकता है ???
मनोजप

2
प्रतिभाशाली! मुझे समझ नहीं आ रहा है कि यहाँ $ eq कैसे काम करता है। मैंने इसे मूल रूप से छोड़ दिया और यह मेरे लिए काम नहीं करता था। किसी तरह, यह मैच खोजने के लिए आकृतियों के सरणी में दिखता है, लेकिन क्वेरी कभी भी निर्दिष्ट नहीं करती है कि कौन सी सरणी को देखना है। जैसे, यदि दस्तावेजों में आकार थे और, उदाहरण के लिए, आकार; मैचों के लिए दोनों सरणियों में $ eq दिखेगा? क्या $ redact सिर्फ दस्तावेज़ के भीतर कुछ भी ढूंढ रहा है जो 'if' कंडीशन से मेल खाता है?
ओनसा

30

सावधानी: यह उत्तर एक समाधान प्रदान करता है जो उस समय प्रासंगिक था , इससे पहले कि MongoDB 2.2 और ऊपर की नई विशेषताओं को पेश किया गया था। यदि आप MongoDB के अधिक हालिया संस्करण का उपयोग कर रहे हैं, तो अन्य उत्तरों को देखें।

फ़ील्ड चयनकर्ता पैरामीटर पूर्ण गुणों तक सीमित है। इसका उपयोग किसी सरणी के भाग को चुनने के लिए नहीं किया जा सकता है, केवल पूरे सरणी के लिए। मैंने $ स्थितीय ऑपरेटर का उपयोग करने की कोशिश की , लेकिन यह काम नहीं किया।

सबसे आसान तरीका है कि ग्राहक में केवल आकृतियों को फ़िल्टर करें ।

यदि आपको वास्तव में MongoDB से सीधे सही आउटपुट की आवश्यकता है , तो आप आकृतियों को फ़िल्टर करने के लिए मैप-कम का उपयोग कर सकते हैं

function map() {
  filteredShapes = [];

  this.shapes.forEach(function (s) {
    if (s.color === "red") {
      filteredShapes.push(s);
    }
  });

  emit(this._id, { shapes: filteredShapes });
}

function reduce(key, values) {
  return values[0];
}

res = db.test.mapReduce(map, reduce, { query: { "shapes.color": "red" } })

db[res.result].find()

24

बेहतर आप सरणी तत्व में मेल खाने वाले तत्व का उपयोग कर सकते हैं $slice, यह एक सरणी में महत्वपूर्ण वस्तु को वापस करने में सहायक है।

db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})

$sliceजब आप तत्व के सूचकांक को जानते हैं तो मददगार होता है, लेकिन कभी-कभी आप चाहते हैं कि जो भी तत्व आपके मापदंड से मेल खाता हो। आप $ऑपरेटर के साथ मिलान तत्व वापस कर सकते हैं ।



12

मोंगोडब में खोजने के लिए वाक्य रचना है

    db.<collection name>.find(query, projection);

और दूसरी क्वेरी जो आपने लिखी है, वह है

    db.test.find(
    {shapes: {"$elemMatch": {color: "red"}}}, 
    {"shapes.color":1})

इसमें आपने $elemMatchऑपरेटर का उपयोग क्वेरी भाग में किया है, जबकि यदि आप इस ऑपरेटर को प्रोजेक्शन भाग में उपयोग करते हैं तो आपको वांछित परिणाम मिलेगा। आप अपनी क्वेरी नीचे लिख सकते हैं

     db.users.find(
     {"shapes.color":"red"},
     {_id:0, shapes: {$elemMatch : {color: "red"}}})

यह आपको वांछित परिणाम देगा।


1
यह मेरे लिए काम करता है। हालाँकि, ऐसा प्रतीत होता है कि "shapes.color":"red"क्वेरी पैरामीटर (खोज विधि का पहला पैरामीटर) आवश्यक नहीं है। आप इसे बदल सकते हैं {}और समान परिणाम प्राप्त कर सकते हैं ।
एरिक ओल्सन

2
@ErikOlson उपरोक्त मामले में आपका सुझाव सही है, जहां हमें सभी दस्तावेजों को खोजने की आवश्यकता है जो लाल रंग के साथ हैं और केवल उन पर प्रक्षेपण लागू करने के लिए। लेकिन मान लीजिए कि अगर किसी को उन सभी दस्तावेज़ों का पता लगाना है, जिनका रंग नीला है, लेकिन यह केवल उन आकृतियों वाले सरणी के तत्व को वापस करना चाहिए, जिनका रंग लाल है। इस मामले में उपरोक्त क्वेरी को किसी और द्वारा भी संदर्भित किया जा सकता है ..
विक्की

यह सबसे आसान लगता है, लेकिन मैं इसे काम नहीं कर सकता। यह केवल पहला मिलान करने वाला उपनिर्देशन लौटाता है।
न्यूमैन

8

जॉनीएचके को धन्यवाद ।

यहां मैं केवल कुछ और जटिल उपयोग जोड़ना चाहता हूं।

// Document 
{ 
"_id" : 1
"shapes" : [
  {"shape" : "square",  "color" : "red"},
  {"shape" : "circle",  "color" : "green"}
  ] 
} 

{ 
"_id" : 2
"shapes" : [
  {"shape" : "square",  "color" : "red"},
  {"shape" : "circle",  "color" : "green"}
  ] 
} 


// The Query   
db.contents.find({
    "_id" : ObjectId(1),
    "shapes.color":"red"
},{
    "_id": 0,
    "shapes" :{
       "$elemMatch":{
           "color" : "red"
       } 
    }
}) 


//And the Result

{"shapes":[
    {
       "shape" : "square",
       "color" : "red"
    }
]}

7

आपको केवल क्वेरी चलाने की आवश्यकता है

db.test.find(
{"shapes.color": "red"}, 
{shapes: {$elemMatch: {color: "red"}}});

इस क्वेरी का आउटपुट है

{
    "_id" : ObjectId("562e7c594c12942f08fe4192"),
    "shapes" : [ 
        {"shape" : "circle", "color" : "red"}
    ]
}

जैसा कि आप उम्मीद करते हैं कि यह रंग से मेल खाने वाले सरणी से सटीक क्षेत्र देगा: 'लाल'।


3

$ परियोजना के साथ यह अधिक उपयुक्त होगा अन्य बुद्धिमान मिलान तत्वों को दस्तावेज़ में अन्य तत्वों के साथ एक साथ जोड़ा जाएगा।

db.test.aggregate(
  { "$unwind" : "$shapes" },
  { "$match" : {
     "shapes.color": "red"
  }},
{"$project":{
"_id":1,
"item":1
}}
)

क्या आप बता सकते हैं कि यह इनपुट और आउटपुट सेट के साथ पूरा होता है?
अलेक्जेंडर मिल्स

2

इसी तरह आप कई के लिए पा सकते हैं

db.getCollection('localData').aggregate([
    // Get just the docs that contain a shapes element where color is 'red'
  {$match: {'shapes.color': {$in : ['red','yellow'] } }},
  {$project: {
     shapes: {$filter: {
        input: '$shapes',
        as: 'shape',
        cond: {$in: ['$$shape.color', ['red', 'yellow']]}
     }}
  }}
])

इस उत्तर वास्तव में पसंदीदा 4.x तरीका है: $matchतो, अंतरिक्ष में कटौती करने $filterआप क्या चाहते हैं के रखने के लिए, इनपुट क्षेत्र अधिलेखन (उपयोग उत्पादन $filterमैदान पर shapesकरने $projectके लिए पर वापस shapes। शैली ध्यान दें: के रूप में फ़ील्ड नाम का उपयोग करने के लिए सबसे अच्छा नहीं asतर्क क्योंकि है कि बाद के साथ भ्रम को जन्म दे सकता $$shapeहै और $shapeमैं पसंद करते हैं। zzके रूप में asक्षेत्र क्योंकि यह वास्तव में बाहर खड़ा है।
बज़ Moschetti

1
db.test.find( {"shapes.color": "red"}, {_id: 0})

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

1

एकत्रीकरण फ़ंक्शन का उपयोग करें और $projectदस्तावेज़ में विशिष्ट ऑब्जेक्ट फ़ील्ड प्राप्त करें

db.getCollection('geolocations').aggregate([ { $project : { geolocation : 1} } ])

परिणाम:

{
    "_id" : ObjectId("5e3ee15968879c0d5942464b"),
    "geolocation" : [ 
        {
            "_id" : ObjectId("5e3ee3ee68879c0d5942465e"),
            "latitude" : 12.9718313,
            "longitude" : 77.593551,
            "country" : "India",
            "city" : "Chennai",
            "zipcode" : "560001",
            "streetName" : "Sidney Road",
            "countryCode" : "in",
            "ip" : "116.75.115.248",
            "date" : ISODate("2020-02-08T16:38:06.584Z")
        }
    ]
}

0

हालाँकि यह सवाल 9.6 साल पहले पूछा गया था, लेकिन यह कई लोगों के लिए बहुत मददगार रहा है। आपके सभी प्रश्नों, संकेत और उत्तर के लिए आप सभी का धन्यवाद। यहाँ एक उत्तर से उठा .. मैंने पाया कि निम्नलिखित विधि का उपयोग मूल दस्तावेज में अन्य क्षेत्रों को प्रोजेक्ट करने के लिए भी किया जा सकता है। यह किसी के लिए उपयोगी हो सकता है।

निम्नलिखित दस्तावेज़ के लिए, यह पता लगाने की आवश्यकता थी कि क्या एक कर्मचारी (एम्प # 7839) के पास वर्ष 2020 के लिए अपना अवकाश इतिहास निर्धारित है। इतिहास को मूल कर्मचारी दस्तावेज के भीतर एक एम्बेडेड दस्तावेज़ के रूप में लागू किया जाता है।

db.employees.find( {"leave_history.calendar_year": 2020}, 
    {leave_history: {$elemMatch: {calendar_year: 2020}},empno:true,ename:true}).pretty()


{
        "_id" : ObjectId("5e907ad23997181dde06e8fc"),
        "empno" : 7839,
        "ename" : "KING",
        "mgrno" : 0,
        "hiredate" : "1990-05-09",
        "sal" : 100000,
        "deptno" : {
                "_id" : ObjectId("5e9065f53997181dde06e8f8")
        },
        "username" : "none",
        "password" : "none",
        "is_admin" : "N",
        "is_approver" : "Y",
        "is_manager" : "Y",
        "user_role" : "AP",
        "admin_approval_received" : "Y",
        "active" : "Y",
        "created_date" : "2020-04-10",
        "updated_date" : "2020-04-10",
        "application_usage_log" : [
                {
                        "logged_in_as" : "AP",
                        "log_in_date" : "2020-04-10"
                },
                {
                        "logged_in_as" : "EM",
                        "log_in_date" : ISODate("2020-04-16T07:28:11.959Z")
                }
        ],
        "leave_history" : [
                {
                        "calendar_year" : 2020,
                        "pl_used" : 0,
                        "cl_used" : 0,
                        "sl_used" : 0
                },
                {
                        "calendar_year" : 2021,
                        "pl_used" : 0,
                        "cl_used" : 0,
                        "sl_used" : 0
                }
        ]
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.