LoginSignup
0
0

More than 1 year has passed since last update.

nodejs + express + mongoDB で複数のCollectionからまとめてデータを取り出す-lookup-

Posted at

はじめに

下のデータのように、
worksからデータを取り出し、user_idを基準してusersのデータも一緒に取り出したい。
しかし、非同期処理によってデータを順番に取り出して結合するなど、
まとめてデータを取り出すとき、MongoDBはMySQLと比べて扱にくい感がある。
いろいろ調べたところ、mongoDBもMySQLのleft joinのような使い方があるようです。

Data

users
{
  "user_name": "king",
  "age": 20,
  "id": "admin",
  "pw": "1234",
  "role": "admin",
  "department": "A"
}
works
{
  "title": "to be hero",
  "due_date": "2022-12-15",
  "create_datetime": "2022-12-03 19:59:39",
  "user_id": "admin"
}

まずlookupで取り出してみる

db.collection("works").aggregate([
    {
        $lookup: {
            from: "users",
            localField: "user_id",
            foreignField: "id",
            as: "unserInfo"
        },
    },
]).toArray((err, result) => {
    console.log(result)
    res.render('./works/works.ejs', { works: result })
})

from: 対象のコレクション
localField: 結合に使われる基準のコレクションのフィールド (ここではworks)
foreignField: 結合に使われる対象のコレクションのフィールド(ここではusers)
as: 対象のコレクションのドキュメントの保存名

[
  {
    _id: new ObjectId("638b2c1b6c66bbaeee6c0aa6"),
    title: 'to be hero',
    due_date: '2022-12-15',
    create_datetime: '2022-12-03 19:59:39',
    user_id: 'admin',
    unserInfo: [ 
     {
      _id: new ObjectId("6388ca58dbdf448d85e9e159"),
      user_name: 'king',
      age: 20,
      id: 'admin',
      pw: '1234',
      role: 'admin',
      department: 'A'
    } 
    ]
  }
]

workのデータに加えてusersの情報をunserInfoとして取り出した。
しかし、unserInfoの中にlist型としてusersのobjectデータが入っている。
これだと使いにくいので、次の$unwindでobjectをリストから取り出す。

リストからとりだす

db.collection("works").aggregate([
    {
        $lookup: {
            from: "users",
            localField: "user_id",
            foreignField: "id",
            as: "unserInfo"
        },
    },
    //追加
    { $unwind: "$unserInfo" },
]).toArray((err, result) => {
    res.render('./works/works.ejs', { works: result })
})
[
  {
    _id: new ObjectId("638b2c1b6c66bbaeee6c0aa6"),
    title: 'to be hero',
    due_date: '2022-12-15',
    create_datetime: '2022-12-03 19:59:39',
    user_id: 'admin',
    unserInfo: {
      _id: new ObjectId("6388ca58dbdf448d85e9e159"),
      user_name: 'king',
      age: 20,
      id: 'admin',
      pw: '1234',
      role: 'admin',
      department: 'A'
    }
  }
]

$unwindを使うことでリストから取り出した。
もっと欲を言えば、オブジェクトからも取り出したい。
$projectをオブジェクトの中から取り出す。

必要なフィールドを結合する

db.collection("works").aggregate([
    {
        $lookup: {
            from: "users",
            localField: "user_id",
            foreignField: "id",
            as: "unserInfo"
        },
    },
    { $unwind: "$unserInfo" },
    {
        //追加
        $project: {
            _id: 1,
            create_datetime: 1,
            user_id: 1,
            user_name: "$unserInfo.user_name",
            role: "$unserInfo.role",
        }
    }
]).toArray((err, result) => {
    res.render('./works/works.ejs', { works: result })
})
[
  {
    _id: new ObjectId("638b2c1b6c66bbaeee6c0aa6"),
    create_datetime: '2022-12-03 19:59:39',
    user_id: 'admin',
    user_name: 'king',
    role: 'admin'
  }
]

基準となる元のフィールドは1を指定することで表示することができる。
結合するデータは、その新・旧のフィールドを指定する必要がある。
しかし、指定なしですべてのフィールドを並べてくれる方法はまだないようです。

おわりに

残念ながらまだmysqlのような完璧なleft join はない模様
sql文がそのまま使えないのが、mongoDBの欠点として感じる。
しかし、inner joinのような使い方ができるのであれば、開発スピードが増す気がする。

関連

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0