Pobiera tylko element zapytany w tablicy obiektów w kolekcji MongoDB

Załóżmy, że masz następujące dokumenty w mojej kolekcji:

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

Do zapytania:

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

Lub

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

Zwraca dopasowany dokument (dokument 1), Ale zawsze ze wszystkimi elementami tablicy w shapes:

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

Jednak chciałbym uzyskać dokument (dokument 1) tylko z tablicą zawierającą color=red:

{ "shapes": 
  [
    {"shape": "circle", "color": "red"}
  ] 
}
Jak mogę to zrobić?
Author: Neil Lunn, 2010-10-21

10 answers

MongoDB 2.2 nowy $elemMatch Operator projekcji zapewnia inny sposób zmiany zwracanego dokumentu tak, aby zawierał tylko pierwszy dopasowany shapes element:

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

Zwraca:

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

W 2.2 można to również zrobić za pomocą $ projection operator, gdzie $ w polu Nazwa obiektu projekcji reprezentuje indeks pierwszego pasującego elementu tablicy z zapytania. Poniższe wyniki zwracają te same wyniki Co powyżej:

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

MongoDB 3.2 Update

Począwszy od wydania 3.2, możesz użyć nowego $filter operator agregacji do filtrowania tablicy podczas projekcji, który ma tę zaletę, że zawiera wszystkie dopasowania, zamiast tylko pierwszego.

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
    }}
])

Wyniki:

[ 
    {
        "shapes" : [ 
            {
                "shape" : "circle",
                "color" : "red"
            }
        ]
    }
]
 324
Author: JohnnyHK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-25 18:04:43

Nowy Framework agregacji W MongoDB 2.2+ stanowi alternatywę dla Map/Reduce. Na $unwind operator może być użyty do oddzielenia tablicy shapes W Strumień dokumentów, który można dopasować:

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"
  }}
)

Wyniki w:

{
    "result" : [
        {
            "_id" : ObjectId("504425059b7c9fa7ec92beec"),
            "shapes" : {
                "shape" : "circle",
                "color" : "red"
            }
        }
    ],
    "ok" : 1
}
 91
Author: Stennie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-28 08:41:36

Uwaga: Ta odpowiedź dostarcza rozwiązania, które było istotne w tym czasie, zanim wprowadzono nowe funkcje MongoDB 2.2 i up. Zobacz inne odpowiedzi, jeśli używasz nowszej wersji MongoDB.

Parametr wyboru pola jest ograniczony do pełnych właściwości. Nie można go użyć do wybrania części tablicy, tylko całej tablicy. Próbowałem użyć $ operator pozycyjny , ale to nie zadziałało.

Najprostszym sposobem jest aby po prostu filtrować kształty w kliencie .

Jeśli naprawdę potrzebujesz poprawnego wyjścia bezpośrednio z MongoDB, możesz użyć Map-reduce do filtrowania kształtów.

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()
 28
Author: Niels van der Rest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-19 18:42:45

Innym interesującym sposobem jest użycie $redact, która jest jedną z nowych funkcji agregacji MongoDB 2.6 . Jeśli używasz 2.6, nie potrzebujesz $unwind, który może powodować problemy z wydajnością, jeśli masz duże tablice.

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

$redact "ogranicza treść dokumentów na podstawie informacji przechowywanych w samych dokumentach" . Więc będzie działać tylko wewnątrz dokumentu . Zasadniczo skanuje twój dokument do na dole i sprawdza, czy pasuje do twojego if warunku, który jest w $cond, jeśli jest dopasowanie, zatrzyma zawartość($$DESCEND) lub usunie ($$PRUNE).

W powyższym przykładzie, first $match zwraca całą tablicę shapes, a $redact usuwa ją do oczekiwanego wyniku.

Zauważ, że {$not:"$color"} jest konieczne, ponieważ skanuje również górny dokument, a jeśli $redact nie znajdzie pola color na górnym poziomie, to zwróci false, które może pozbawić cały dokument czego nie chcemy.

 27
Author: anvarik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-04 08:31:35

Lepiej można odpytywać w pasującym elemencie tablicy używając {[1] } czy jest pomocne zwrócenie znaczącego obiektu w tablicy.

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

$slice jest pomocny, gdy znasz indeks elementu, ale czasami chcesz niezależnie od tego, który element tablicy odpowiada twoim kryteriom. Możesz zwrócić pasujący element z operatorem $.

 16
Author: Naren Dran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-18 09:14:37

Składnia find w mongodb to

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

I drugie zapytanie, które napisałeś, to jest

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

W tej części użyłeś operatora $elemMatch w części zapytania, podczas gdy jeśli użyjesz tego operatora w części projekcji, otrzymasz pożądany rezultat. Możesz zapisać swoje zapytanie jako

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

To da Ci pożądany rezultat.

 11
Author: Vicky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-31 08:19:11
 db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})

wyjścia

{

   "shapes" : [ 
       {
           "shape" : "circle",
           "color" : "red"
       }
   ]
}
 11
Author: Viral Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-07 06:25:02

Dzięki JohnnyHK.

Tutaj chcę tylko dodać trochę bardziej skomplikowanego użycia.

// 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
Author: Eddy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:50

Wystarczy uruchomić zapytanie

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

Wyjście tego zapytania to

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

Zgodnie z oczekiwaniami wyświetli dokładne pole z tablicy pasujące do koloru: 'red'.

 5
Author: Vaibhav Patil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-10 07:50:37

Wraz z $project będzie bardziej odpowiednie inne mądre pasujące elementy będą łączone razem z innymi elementami w dokumencie.

db.test.aggregate(
  { "$unwind" : "$shapes" },
  { "$match" : {
     "shapes.color": "red"
  }},
{"$project":{
"_id":1,
"item":1
}}
)
 2
Author: shakthydoss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-09 15:45:13