mongodb Aggregation聚合操作之$lookup

在上一篇mongodb Aggregation聚合操作之$out中详细介绍了mongodb聚合操作中的$out使用以及参数细节。本篇将开始介绍Aggregation聚合操作中的$lookup操作。

说明:

mongodb多表连接的操作,对同一数据库中的未分片集合执行左外连接,从“已联接”集合中筛选文档以进行处理。对于每个输入文档,$lookup阶段添加一个新的数组字段,该字段的元素是来自“已加入”集合的匹配文档。

1. 精确匹配连接


语法:

1.精确匹配:

{

   $lookup:

     {

       from: <collection to join>,

       localField: <field from the input documents>,

       foreignField: <field from the documents of the "from" collection>,

       as: <output array field>

     }

}


参数讲解:

from:指定要与之执行连接的同一数据库中的集合。无法分片从集合。有关详细信息,请参见分片集合限制。

localField:指定从文档输入到$lookup阶段的字段。$lookup对from集合的文档中的localField与foreignField执行相等匹配。如果输入文档不包含localField,则$lookup将该字段视为具有null值,以便进行匹配。

foreignField:指定from集合中的文档中的字段。$lookup在输入文档中的foreignField与localField执行相等匹配。如果from集合中的文档不包含foreignField, $lookup将该值作为null进行匹配

as:指定要添加到输入文档的新数组字段的名称。新的数组字段包含来自from集合的匹配文档。如果指定的名称已经存在于输入文档中,则覆盖现有字段。


该操作将对应于以下伪sql语句:

SELECT *, <output array field>

FROM collection

WHERE <output array field> IN (SELECT *

                               FROM <collection to join>

                               WHERE <foreignField>= <collection.localField>);

1.1. 示例

1.1.1. 使用$lookup执行一个等式联接


初始化数据:

db.orders.insert([

   { "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },

   { "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },

   { "_id" : 3  }

])

db.inventory.insert([

   { "_id" : 1, "sku" : "almonds", description: "product 1", "instock" : 120 },

   { "_id" : 2, "sku" : "bread", description: "product 2", "instock" : 80 },

   { "_id" : 3, "sku" : "cashews", description: "product 3", "instock" : 60 },

   { "_id" : 4, "sku" : "pecans", description: "product 4", "instock" : 70 },

   { "_id" : 5, "sku": null, description: "Incomplete" },

   { "_id" : 6 }

])


示例:

db.orders.aggregate([

   {

     $lookup:

       {

         from: "inventory",

         localField: "item",

         foreignField: "sku",

         as: "inventory_docs"

       }

  }

])


结果:


{

    "_id" : 1.0,

    "item" : "almonds",

    "price" : 12.0,

    "quantity" : 2.0,

    "inventory_docs" : [

        {

            "_id" : 1.0,

            "sku" : "almonds",

            "description" : "product 1",

            "instock" : 120.0

        }

    ]

}


{

    "_id" : 2.0,

    "item" : "pecans",

    "price" : 20.0,

    "quantity" : 1.0,

    "inventory_docs" : [

        {

            "_id" : 4.0,

            "sku" : "pecans",

            "description" : "product 4",

            "instock" : 70.0

        }

    ]

}


{

    "_id" : 3.0,

    "inventory_docs" : [

        {

            "_id" : 5.0,

            "sku" : null,

            "description" : "Incomplete"

        },

        {

            "_id" : 6.0

        }

    ]

}

1.1.2. 对数组使用$lookup


初始化数据:

db.classes.insert( [

   { _id: 1, title: "Reading is ...", enrollmentlist: [ "giraffe2", "pandabear", "artie" ], days: ["M", "W", "F"] },

   { _id: 2, title: "But Writing ...", enrollmentlist: [ "giraffe1", "artie" ], days: ["T", "F"] }

])

db.members.insert( [

   { _id: 1, name: "artie", joined: new Date("2016-05-01"), status: "A" },

   { _id: 2, name: "giraffe", joined: new Date("2017-05-01"), status: "D" },

   { _id: 3, name: "giraffe1", joined: new Date("2017-10-01"), status: "A" },

   { _id: 4, name: "panda", joined: new Date("2018-10-11"), status: "A" },

   { _id: 5, name: "pandabear", joined: new Date("2018-12-01"), status: "A" },

   { _id: 6, name: "giraffe2", joined: new Date("2018-12-01"), status: "D" }

])


示例:

db.classes.aggregate([

   {

      $lookup:

         {

            from: "members",

            localField: "enrollmentlist",

            foreignField: "name",

            as: "enrollee_info"

        }

   }

])


结果:


{

    "_id" : 1.0,

    "title" : "Reading is ...",

    "enrollmentlist" : [

        "giraffe2",

        "pandabear",

        "artie"

    ],

    "days" : [

        "M",

        "W",

        "F"

    ],

    "enrollee_info" : [

        {

            "_id" : 1.0,

            "name" : "artie",

            "joined" : ISODate("2016-05-01T00:00:00.000Z"),

            "status" : "A"

        },

        {

            "_id" : 5.0,

            "name" : "pandabear",

            "joined" : ISODate("2018-12-01T00:00:00.000Z"),

            "status" : "A"

        },

        {

            "_id" : 6.0,

            "name" : "giraffe2",

            "joined" : ISODate("2018-12-01T00:00:00.000Z"),

            "status" : "D"

        }

    ]

}


{

    "_id" : 2.0,

    "title" : "But Writing ...",

    "enrollmentlist" : [

        "giraffe1",

        "artie"

    ],

    "days" : [

        "T",

        "F"

    ],

    "enrollee_info" : [

        {

            "_id" : 1.0,

            "name" : "artie",

            "joined" : ISODate("2016-05-01T00:00:00.000Z"),

            "status" : "A"

        },

        {

            "_id" : 3.0,

            "name" : "giraffe1",

            "joined" : ISODate("2017-10-01T00:00:00.000Z"),

            "status" : "A"

        }

    ]

}

1.1.3. 使用$lookup 和 $mergeObjects


初始化数据:

db.orders.insert([

   { "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },

   { "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 }

])

db.items.insert([

  { "_id" : 1, "item" : "almonds", description: "almond clusters", "instock" : 120 },

  { "_id" : 2, "item" : "bread", description: "raisin and nut bread", "instock" : 80 },

  { "_id" : 3, "item" : "pecans", description: "candied pecans", "instock" : 60 }

])


示例:

下面的操作首先使用$lookup阶段按下面的操作首先使用$lookup阶段按item字段连接两个集合,然后使用$replaceRoot中的$mergeObjects从项和订单中合并已连接的文档:字段连接两个集合,然后使用$replaceRoot中的$mergeObjects从项和订单中合并已连接的文档:

db.orders.aggregate([

   {

      $lookup: {

         from: "items",

         localField: "item",    // field in the orders collection

         foreignField: "item",  // field in the items collection

         as: "fromItems"

      }

   },

   {

      $replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$fromItems", 0 ] }, "$$ROOT" ] } }

   },

   { $project: { fromItems: 0 } }

])


结果:

{

    "_id" : 1.0,

    "item" : "almonds",

    "description" : "almond clusters",

    "instock" : 120.0,

    "price" : 12.0,

    "quantity" : 2.0

}

{

    "_id" : 2.0,

    "item" : "pecans",

    "description" : "candied pecans",

    "instock" : 60.0,

    "price" : 20.0,

    "quantity" : 1.0

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容