2016-01-23 16 views
37

Jaka jest składnia wykonywania wyszukiwania $ w polu będącym tablicą obiektów ObjectID zamiast pojedynczego ObjectID?

Przykład Zamówienie dokumentu:

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

Nie Zapytanie robocza:

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

pożądanego rezultatu

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

Czy mój przykład z dokumentu, aby nie wystarczająco jasne? czy chciałbyś mieć przykładowe dokumenty dla produktów? –

+0

SERWER-22881 będzie śledzić wykonywanie pracy tablicy zgodnie z oczekiwaniami (nie jako wartość dosłowną). –

Odpowiedz

63

Etap rurociąg $lookup agregacja nie będzie działać bezpośrednio z tablicy. Główną intencją tego projektu jest "lewe połączenie" jako typ "jeden do wielu" (lub naprawdę "odnośnik") na temat możliwych powiązanych danych. Ale wartość ma być pojedyncza, a nie tablica.

Dlatego należy "zdenormalizować" zawartość najpierw przed wykonaniem operacji $lookup, aby to zadziałało. A to oznacza, używając $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" } 
    }} 
]) 

After $lookup mecze każdego członka tablicy wynik jest sama tablica, więc znowu $unwind i $group do $push nowe tablice dla wyniku końcowego.

Należy zauważyć, że wszelkie dopasowania "left join", które nie zostaną znalezione, utworzą pustą tablicę dla "productObjects" na danym produkcie, a tym samym negują dokument dla elementu "product" po wywołaniu drugiego $unwind.

Chociaż bezpośrednia aplikacja do tablicy byłaby miła, tak właśnie to działa obecnie, dopasowując pojedynczą wartość do możliwej liczby.

Jak $lookup jest w zasadzie bardzo nowy, obecnie pracuje jako będzie zaznajomiony z tymi, którzy są zaznajomieni z mongoose jako „złej wersji Mans” metody .populate() oferowanych tam. Różnica polega na tym, że $lookup oferuje przetwarzanie "po stronie serwera" połączenia "w przeciwieństwie do klienta" i że część "dojrzałości" w $lookup jest obecnie niedostępna z tego, co oferuje .populate() (np. Interpolacja odnośnika bezpośrednio na tablicy) .

To jest właściwie przypisany numer do ulepszenia SERVER-22881, więc przy odrobinie szczęścia trafi do następnego wydania lub wkrótce.

Jako zasada projektowania, twoja obecna struktura nie jest ani dobra ani zła, ale podlega jedynie kosztom ogólnym podczas tworzenia dowolnego "sprzężenia". Jako taka obowiązuje podstawowa zasada MongoDB w początkach, gdzie jeśli "możesz" żyć z danymi "wstępnie połączonymi" w jednej kolekcji, najlepiej jest to zrobić.

Jedna ogólna zasada mówi, że intencją "połączenia" jest działanie w odwrotnym kierunku, niż pokazano tutaj. Zamiast więc zachowywać "powiązane identyfikatory" innych dokumentów w dokumencie "rodzica", ogólną zasadą, która najlepiej działa, jest sytuacja, w której "powiązane dokumenty" zawierają odniesienie do "rodzica".

Można więc powiedzieć, że "najlepiej działa" z "projektem relacji", który jest przeciwieństwem tego, jak coś takiego jak mangusta .populate() wykonuje sprzężenia po stronie klienta. Identyfikując "jeden" w obrębie każdego "wiele" zamiast tego, wystarczy wyciągnąć powiązane elementy bez konieczności wcześniejszej konfiguracji tablicy.

+0

Dziękuję, to działa! Czy jest to wskaźnik, że moje dane nie są poprawnie ustrukturyzowane/znormalizowane? –

+1

@JasonLin Nie tak straigtforward jak "dobry/zły", więc jest trochę więcej wyjaśnień dodanych do odpowiedzi. To zależy od tego, co ci odpowiada. –

+2

Obecna implementacja jest nieco niezamierzona. sensowne jest wyszukiwanie wszystkich wartości w tablicy lokalnego pola, nie ma sensu używać tej tablicy dosłownie, aby SERVER-22881 mógł ją śledzić. –

-1

Agregacja z $lookup i późniejsze $group jest dość kłopotliwe, więc jeśli (i to medium if) używasz węzeł & Mongoose lub biblioteka wsparcie z niektórych podpowiedzi w schemacie, można używać .populate() pobrać te dokumenty:

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") 
    ... 
1

użycie $ odpoczynek dostaniesz pierwszy obiekt zamiast tablicy obiektów

zapytania:

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

wynik:

{ 
    "_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" 
    } 
} 
Powiązane problemy