program tip

배열의 ObjectId에 대한 $ lookup

radiobox 2020. 9. 21. 07:34
반응형

배열의 ObjectId에 대한 $ lookup


단일 ObjectId가 아닌 ObjectId의 배열 인 필드에서 $ lookup을 수행하는 구문은 무엇입니까?

주문 문서 예 :

{
  _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>}
  ],
}

$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가 호출 될 때 "제품"요소에 대한 문서가 무효화됩니다 .

배열에 직접 적용하는 것이 좋지만 단일 값을 가능한 많은 수에 일치시켜 현재 작동하는 방식입니다.

As $lookup is basically very new, it currently works as would be familiar to those who are familiar with mongoose as a "poor mans version" of the .populate() method offered there. The difference being that $lookup offers "server side" processing of the "join" as opposed to on the client and that some of the "maturity" in $lookup is currently lacking from what .populate() offers ( such as interpolating the lookup directly on an array ).

This is actually an assigned issue for improvement SERVER-22881, so with some luck this would hit the next release or one soon after.

디자인 원칙에 따라 현재 구조는 좋거나 나쁘지 않지만 "조인"을 만들 때 오버 헤드가 발생합니다. 따라서 처음에는 MongoDB의 기본 원칙이 적용됩니다. 여기서 하나의 컬렉션에 "사전 조인 된"데이터로 "살 수있는"경우 그렇게하는 것이 가장 좋습니다.

$lookup일반적인 원칙 으로 말할 수있는 다른 한 가지는 여기에서 "조인"의 의도가 여기에 표시된 것과 다른 방식으로 작동하는 것입니다. 따라서 "상위"문서 내에서 다른 문서의 "관련 ID"를 유지하는 대신 "관련 문서"에 "상위"에 대한 참조가 포함되어있는 경우 가장 잘 작동하는 일반적인 원칙이 있습니다.

따라서 $lookup몽구스와 같은 .populate()것이 클라이언트 측 조인을 수행 하는 방식의 반대 인 "관계 설계"로 "가장 잘 작동"한다고 할 수 있습니다 . 대신 각 "다"내에서 "하나"를 식별하면 $unwind먼저 배열 을 사용할 필요없이 관련 항목을 가져옵니다.


$lookup집계 파이프 라인 단계는 NOW (3.3.4 버전) 배열과 직접 작동합니다.

참조 : 로컬 (다중) 값 배열과 외래 (단일) 값 간 조회


pipeline스테이지를 사용하여 하위 문서 배열에 대한 검사를 수행 할 수도 있습니다.

다음은 python(죄송합니다 나는 뱀 사람입니다) 를 사용한 예 입니다.

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

The catch here is to match all objects in the ObjectId array (foreign _id that is in local field/prop products).

You can also clean up or project the foreign records with additional stages, as indicated by the comment above.


use $unwind you will get the first object instead of array of objects

query:

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

result:

{
    "_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"
    }
}

Aggregating with $lookup and subsequent $group is pretty cumbersome, so if (and that's a medium if) you're using node & Mongoose or a supporting library with some hints in the schema, you could use a .populate() to fetch those documents:

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

참고URL : https://stackoverflow.com/questions/34967482/lookup-on-objectids-in-an-array

반응형