进行数据库操作过程中,我们在有些特殊情况下可能会使用到类似于关系型数据库中的联表查询,MongoDB 3.2版本在聚合查询中给我我们提供了一个新的操作:lookup,可以把多个数据表关联起来进行查询,这样可以很方便的在一次查询中对多个相关表的数据进行过滤,最终查询出我们想要的数据。下面我来简单介绍下lookup的基本操作。

使用方法

准备数据

分别插入四条user数据和四条product数据,并检索出两条user数据和两条product数据组织成两条order数据插入到order表中(所有示例均使用mongoose进行操作数据库,以下不作说明)

const mongoose = require('mongoose'),
    User = mongoose.model('user'),// mongoose数据模型为user,真正的数据库表名为users
    Product = mongoose.model('product'),// mongoose数据模型为product,真正的数据库表名为products
    Order = mongoose.model('order');// mongoose数据模型为order,真正的数据库表名为orders

const users = [{
    age: 18,
    name: 'user1',
    sex: 'female',
  }, {
    age: 20,
    name: 'user2',
    sex: 'male',
  }, {
    age: 22,
    name: 'user3',
    sex: 'female',
  }, {
    age: 28,
    name: 'user4',
    sex: 'male',
  }],
  products = [{
    name: 'instance noddles',
    price: 5
  }, {
    name: 'milk',
    price: 6
  }, {
    name: 'tee',
    price: 50
  }, {
    name: 'gun',
    price: 1200
  }];

Promise.all([
  Product.insertMany(products),
  User.insertMany(users)
]).then(insertResults => {

  Promise.all([
    User.find({name: {$in: ['user1', 'user3']}}),// 查找用户名为 user1 和 user3 的用户
    Product.find({price: {$in: [5, 1200]}})// 查找价格为 5 和 1200 的面条和枪的商品数据
  ]).then(findResults => {
    const [usersInfo, productsInfo] = findResults;
    let orders = [];

    usersInfo.forEach(user => {
      productsInfo.some(product => {
        if ((user.name === 'user1' && product.price === 1200) ||
          (user.name === 'user3' && product.price === 5)) {
          orders.push({
            userId: user._id,
            productId: product._id
          });
          return true;
        } else {
          return false;
        }
      });
    });

    Order.insertMany(orders, done);

  }).catch(err => {
    done(err);
  });
}).catch(err => {
  done(err);
});

两张表关联

在Orders表中查询出20岁以上的用户的订单信息

Order.aggregate([{
  $lookup: {
    from: 'users',
    localField: 'userId',
    foreignField: '_id',
    as: 'user'
  }
}, {
  $unwind: '$user'
}, {
  $match: {
    'user.age': {$gt: 20}
  }
}]).then(results => {
  // process results
}).catch(err => {
  // process error
});

查询结果为:

[ { _id: 59e34faa626aeef690861752,
    updated: 2017-10-15T12:08:10.048Z,
    created: 2017-10-15T12:08:10.048Z,
    __v: 0,
    userId: 59e34fa9626aeef69086174f,
    productId: 59e34fa9626aeef690861749,
    user: 
     { _id: 59e34fa9626aeef69086174f,
       updated: 2017-10-15T12:08:09.392Z,
       created: 2017-10-15T12:08:09.392Z,
       __v: 0,
       age: 22,
       name: 'user3',
       sex: 'female' } } ]

说明:

  • from 是需要关联的另一张的表名字。在我们这里就是需要关联的users表,注意是另一张表在数据库中的名字,而不是你的mongoose里面的数据模型的名字
  • localField 是当前表(Order)需要关联的字段
  • foreignField 是目标表中的关联字段
  • as 是把目标表查询结果作为当前表的一个属性。你会看到查询出的结果中真正的user信息是挂在user这个属性下的
  • $unwind操作是把数组拆分为单独的文档。如果没有$unwind这一步,我们的结果中user属性就是一个数组,而不是一个对象了,具体的使用细节请查看官方文档
  • $match 操作是匹配我的的查询条件。注意这里如果要匹配关联表中的属性,需要使用到lookup中as的值然后加上.<target attr>进行匹配,在我的另一篇文章中也对match进行了简单的介绍

三张表关联

在Orders表中查询出年龄大于10岁并且购买的物品价格大于等于1200的订单信息

Order.aggregate([{
  $lookup: {
    from: 'users',
    localField: 'userId',
    foreignField: '_id',
    as: 'user'
  }
}, {
  $lookup: {
    from: 'products',
    localField: 'productId',
    foreignField: '_id',
    as: 'product'
  }
}, {
  $unwind: '$user'
}, {
  $unwind: '$product'
}, {
  $match: {
    'user.age': {$gt: 10},
    'product.price': {$gte: 1200}
  }
}]).then(result => {
  // process result
}).catch(err => {
  // process err
});

查询结果:

[ { _id: 59e34faa626aeef690861751,
    updated: 2017-10-15T12:08:10.048Z,
    created: 2017-10-15T12:08:10.048Z,
    __v: 0,
    userId: 59e34fa9626aeef69086174d,
    productId: 59e34fa9626aeef69086174c,
    user: 
     { _id: 59e34fa9626aeef69086174d,
       updated: 2017-10-15T12:08:09.391Z,
       created: 2017-10-15T12:08:09.391Z,
       __v: 0,
       age: 18,
       name: 'user1',
       sex: 'female' },
    product: 
     { _id: 59e34fa9626aeef69086174c,
       updated: 2017-10-15T12:08:09.386Z,
       created: 2017-10-15T12:08:09.386Z,
       __v: 0,
       name: 'gun',
       price: 1200 } } ]

把这个查询条件和查询结果和上一条的进行对比,很明显,聚合中多关联了一个products表,结果中也多出了一个produc属性

总结

从这两个简单的用法看来,lookup结合起其它一些聚合查询操作,能够很方便的在数据库中进行多表联查。

相关文档

Enjoy IT