MongoDB+mongoose
MongoDB
最近良くNode.jsのmongooseでMongoDBをよく使う。
aggregateってMongoDBだと使ったことがなかったので少し触ってみた。
aggregateはDjangoのaggregateと同様に何でもできそうな感じがある。
aggregateでデータ件数が多いデータを処理するときは最初にmatchを書くといいようだ。
簡単なフィルターならpopulateの方がわかりやすくて良いなと思った。
Node.jsは個人的にあまり好きになれない…。
docker compose
# Use root/example as user/password credentials services: mongo: image: mongo:7.0 restart: always environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: example ports: - 27017:27017 mongo-express: image: mongo-express restart: always ports: - 8081:8081 environment: ME_CONFIG_MONGODB_ADMINUSERNAME: root ME_CONFIG_MONGODB_ADMINPASSWORD: example ME_CONFIG_MONGODB_URL: mongodb://root:example@mongo:27017/ ME_CONFIG_BASICAUTH: false
ソース
const express = require("express"); const mongoose = require("mongoose"); const cors = require("cors"); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(cors()); const dburl = "mongodb://root:example@localhost:27017/mongo-db?authSource=admin"; mongoose .connect(dburl) .then(() => { console.log("DB connected"); }) .catch((err) => { console.log(err); }); // insert data const Schema = mongoose.Schema; const commentSchema = new mongoose.Schema({ content: { type: String, required: true }, author: { type: String, required: true }, }); const Comment = mongoose.model("Comment", commentSchema); const blogSchema = new Schema( { title: { type: String, required: true, }, content: { type: String, required: true, }, comments: [{ type: mongoose.Schema.Types.ObjectId, ref: "Comment" }], }, { timestamps: true } ); const Blog = mongoose.model("Blog", blogSchema); async function insertData() { const blog = new Blog({ title: "Blog 1", content: "This is blog 1", }); const comment1 = new Comment({ content: "This is the first comment", author: "User1", }); await comment1.save(); const comment2 = new Comment({ content: "This is the second comment.", author: "User2", }); await comment2.save(); blog.comments = [comment1._id, comment2._id]; const res = await blog.save(); console.log(res); } async function filterData() { const blogWithComments = await Blog.find().populate("comments"); console.log(`all data`); console.log(`comment author: ${blogWithComments[0].comments[0].author}`); console.log(`comment author: ${blogWithComments[0].comments[1].author}`); console.log("populate filter data"); const populateFilter = await Blog.find().populate({ path: "comments", match: { author: "User1" }, }); if (populateFilter == {}) { console.error("no match"); } else if (populateFilter.length == 1) { console.log(`comment author: ${populateFilter[0].comments[0].author}`); } else { console.log(`comment author: ${populateFilter[0].comments[0].author}`); console.log(`comment author: ${populateFilter[0].comments[1].author}`); } console.log("aggregate filter data"); const aggregateFilter = await Blog.aggregate([ { $lookup: { from: "comments", // MongDBのコレクション名を指定する localField: "comments", // blogSchemaのフィールド名を指定 foreignField: "_id", // commentsのリレーションはcommentsのどのキーに対応するのかを指定 as: "allMatchedComments", // 結果を入れるキー名 }, }, { $addFields: { filteredComments: { $filter: { input: "$allMatchedComments", // フィルタ対象の配列(今回は結果を入れるキー名で指定したキー名を入れる) as: "singleComment", // 配列の各要素を参照する変数名 cond: { $eq: ["$$singleComment.author", "User1"] }, // フィルターする条件 }, }, }, }, ]); if (aggregateFilter == {}) { console.error("no match"); } else if (aggregateFilter.length == 1) { console.log( `comment author: ${aggregateFilter[0].filteredComments[0].author}` ); } else { console.log( `comment author: ${aggregateFilter[0].filteredComments[0].author}` ); console.log( `comment author: ${aggregateFilter[0].filteredComments[1].author}` ); } } (async () => { await insertData(); await filterData(); })();