mongoose 的关联操作 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wangweifeng

mongoose 的关联操作

  •  
  •   wangweifeng 2017 年 9 月 20 日 3519 次点击
    这是一个创建于 3138 天前的主题,其中的信息可能已经有所发展或是发生改变。

    mongoose-ref

    实现代码 github 最近在做一个项目涉及到 mongoose 的关联查询等等,之前做的 mysql,postgresql 比较多,而 mongoose 用的都是比较简单的存储数据,简单查询等等。 刚开始涉及 ref 还是有点小晕的,查询了相关资源,也可以模模糊糊做出来,但是会有各种报错。痛下决心研究透彻点,晚上熬夜到 2 点终于有点小眉目了。

    • node v8.5.0
    • mongodb
    • 结合一个接口理解
    • 结合 promise-async-await
    • 一般采用 mvc 模式,本文就直接在 express 里直接写了

    1. 首先建立了一个 mongoose-ref 项目

    直接使用了 express -e mongoose-ref

    2.在 routes/index 里连接 mongodb 数据库

    const mOngoose= require('mongoose'); mongoose.connect('mongodb://localhost:27017/ref'); 

    3.建立 4 个模型,用户:User,城市:City,省份:State,国家:Country

    同时建立关联 user->city->state->country

     const Schema = mongoose.Schema; const ObjectId = Schema.Types.ObjectId; const UserSchema = new Schema({ username: { type: String }, userpwd: { type: String }, userage: { type: Number }, city: { type: Schema.Types.ObjectId, ref: 'City' }, }); const CitySchema = new Schema({ name: { type: String }, state: { type: Schema.Types.ObjectId, ref: 'State' } }); const StateSchema = new Schema({ name: { type: String }, country: { type: Schema.Types.ObjectId, ref: 'Country' } }); const CountrySchema = new Schema({ name: { type: String } }); const User = mongoose.model('User', UserSchema); const City = mongoose.model('City', CitySchema); const State = mongoose.model('State', StateSchema); const Country = mongoose.model('Country', CountrySchema); 

    4.主要采用 promise-async-async 进行逻辑处理

    首先创建一个 user_getCountryList 函数,如下代码

     const user_getCountryList = async function (req, res) { console.log("/v1/ref start -->" + JSON.stringify(req.body)); try { const respOndData= { status: res.statusCode, data: {}, error: {} }; const username = req.body.username; const userpwd = req.body.userpwd; const userage = req.body.userage; const usercityname = req.body.usercityname; const userstatename = req.body.userstatename; const usercountryname = req.body.usercountryname; const userInfoCountry = await findUserCountry({ name: usercountryname }, usercountryname);//查看国家 const userInfoState = await findUserState({ name: userstatename }, userstatename);//查看州 const userInfoCity = await findUserCity({ name: usercityname }, usercityname);//查看城市 const userInfo = await findUser({ username: username, }, username,userpwd,userage);//查看用户信息 const updateInfoUser = await updateUser({ _id: userInfo },userInfoCity);//更新用户信息 const updateInfoCity = await updateCity({ _id: userInfoCity }, userInfoState);//更新城市信息 const updateInfoState = await updateState({ _id: userInfoState }, userInfoCountry);//更新州信息 return res.json(respondData); } catch (error) { //错误处理 console.log("userCity error -->" + JSON.stringify(error)); respondData.error = error; return res.json(respondData); } } 

    首先查看传入的国家在 country 中有没有,加入有,返回_id,没有就创建传入的国家名,并返回_id,查看 findUserCountry 函数对应的逻辑

     const findUserCountry = async function (cnd, country) { console.log("findUserCountry start --> " + JSON.stringify(cnd)); return new Promise(function (resolve, reject) { Country.findOne(cnd, function (error, data) { console.log("findUserCountry findOne data --> " + JSON.stringify(data)); if (error) { return reject(error); } if (data) { return resolve(data._id); } else { const userCountry = new Country({ name: country }); userCountry.save(function (err, data) { if (err) { console.log("userCountry.save err-->" + JSON.stringify(err)); return reject(err); } console.log("userCountry-->" + JSON.stringify(data)); return resolve(data._id); }); } }); }) } 

    同理传入的州,城市,用户信息以同样的方式返回_id

    接下来就要进行关联 user->city->state->country

    通俗的说就是在 User 表中 city 保存 City 表中所需要的_id;也就是之前返回的_id 这时就可以用到,可以参考 updateUser 函数

     const updateUser = async function (cnd, cityid) { console.log("updateUser start --> " + JSON.stringify(cnd)); return new Promise(function (resolve, reject) { User.update(cnd, { $set: { city: cityid } }, function (error, data) { console.log("updateUser findOne data --> " + JSON.stringify(data)); if (error) { return reject(error); } return resolve(data); }); }) } 

    可以使用 postman 模拟数据,如图: Postman 模拟接口请求 这时就把 City 对应的_id 写进了 User 表中,可以查看表,如图: User 表中数据 City 表中数据 同理 user->city->state->country 数据都可以写进不同的表中。

    5.使用 populate 关联查询

    当传入 username 时,使用 populate 关联查询,可以查询出这个人的所以信息

     User.find({ username: user_name }) .populate('city') .exec(function (err, docs) { City.find({ _id: docs[0].city._id }) .populate('state') .exec(function (err, doc) { State.find({ _id: doc[0].state._id }) .populate('country') .exec(function (err, result) { const userInfo = {}; userInfo.username = docs[0].username; userInfo.userpwd = docs[0].userpwd; userInfo.userage = docs[0].userage; userInfo.usercity = doc[0].name; userInfo.userstate = result[0].name; userInfo.usercountry = result[0].country.name; respondData.data.push(userInfo); return res.json(respondData); }) }) }); 

    使用 postman 模拟接口如下 Postman 模拟接口关联查询

    当然这个关联查询也可以使用 promise-async-await 不过有时候看着这回调,层层包含还挺好看,或者这也是 js 的一大美感呢

    3 条回复    2017-09-22 12:44:53 +08:00
    wangweifeng
        1
    wangweifeng  
    OP
       2017 年 9 月 20 日
    为什么 Markdown 在这里看起来那么怪呢
    qiuyk
        2
    qiuyk  
       2017 年 9 月 22 日
    我记得 mongoose 如果 exec 不传 cb 就返回 promise 的吧 既然都用上了 async/await 了 就彻底一点吧
    wangweifeng
        3
    wangweifeng  
    OP
       2017 年 9 月 22 日
    @qiuyk 可以彻底的,最后一句话说了,层层包含还挺好看,或者这也是 js 的一大美感呢,哈哈
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5223 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 68ms UTC 08:12 PVG 16:12 LAX 01:12 JFK 04:12
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86