Я разрабатывал API, который использует несколько MongoDB для хранения определенных c данных, которые будут использоваться в разных приложениях.
Основная проблема, с которой я столкнулся, заключается в правильном соединении двух или более MongoDB и затем заполните поля mongoose.Schema.Types.ObjectId
соответствующими данными, хранящимися в отдельной MongoDB.
Это подход, который я нашел в inte rnet, но он не сработал, как ожидалось.
Модели в базе данных 1:
Модели / Post. js
const mongoose = require('mongoose');
var db = mongoose.createConnection(process.env.ATLAS_URI_BLOG, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
});
const PostSchema = new mongoose.Schema({
id: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
category: {
type: String,
required: true,
},
slug: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
tags: {
type: [],
required: false,
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment',
required: false,
}
],
cover: {
type: mongoose.Schema.Types.ObjectId,
ref: 'PostCover',
required: true,
},
howManyRead: {
type: Number,
required: true,
default: 0,
},
publishedOn: {
type: Date,
default: Date.now,
},
updatedOn: {
type: Date,
default: null,
},
});
const Post = db.model('Post', PostSchema);
module.exports = Post;
Модели / PostCover. js
const mongoose = require('mongoose');
const aws = require('aws-sdk');
const fs = require('fs');
const path = require('path');
const {
promisify,
} = require('util');
var db = mongoose.createConnection(process.env.ATLAS_URI_BLOG, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
});
const s3 = new aws.S3();
const PostCoverSchema = new mongoose.Schema({
id: String,
name: String,
size: Number,
key: String,
url: String,
createdAt: {
type: Date,
default: Date.now,
},
});
PostCoverSchema.pre('save', function () {
if (!this.url) {
this.url = `https://${process.env.BUCKET_NAME}.s3.amazonaws.com/${this.key}`;
}
});
PostCoverSchema.pre('remove', function () {
if (process.env.STORAGE_TYPE === 's3') {
return s3
.deleteObject({
Bucket: process.env.BUCKET_NAME,
Key: this.key,
})
.promise()
.then((response) => {
console.log(response.status);
})
.catch((response) => {
console.log(response.status);
});
}
return promisify(fs.unlink)(
path.resolve(__dirname, '..', '..', 'tmp', 'uploads', this.key),
);
});
const PostCover = db.model('PostCover', PostCoverSchema);
module.exports = PostCover;
Модели в базе данных 2 :
models / User. js
const mongoose = require('mongoose');
var db2 = mongoose.createConnection(process.env.ATLAS_URI_USER, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
});
const UserSchema = new mongoose.Schema({
id: {
type: String,
require: true,
},
name: {
type: String,
required: false,
},
email: {
type: String,
required: false,
},
username: {
type: String,
required: false,
},
password: {
type: String,
required: false,
},
profileImage: {
type: mongoose.Schema.Types.ObjectId,
ref: 'UserProfileImage',
required: false,
},
isAdmin: {
type: Boolean,
require: true,
},
origin: {
type: String,
required: true,
},
posts: [
{
type: mongoose.Schema.Types.ObjectId,
required: false,
}
],
quote: {
type: String,
required: false,
},
following: {
type: [],
required: false,
},
followers: {
type: [],
required: false,
},
socialMedia: {
github: {
type: String,
required: false,
},
linkedin: {
type: String,
required: false,
},
twitter: {
type: String,
required: false,
},
},
createdOn: {
type: Date,
default: Date.now,
},
});
const User = db2.model('User', UserSchema);
module.exports = User;
models / UserProfileImage. js
/* eslint-disable no-console */
/* eslint-disable func-names */
const mongoose = require('mongoose');
const aws = require('aws-sdk');
const fs = require('fs');
const path = require('path');
const {
promisify,
} = require('util');
var db2 = mongoose.createConnection(process.env.ATLAS_URI_USER, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
});
const s3 = new aws.S3();
const UserProfileImageSchema = new mongoose.Schema({
id: String,
name: String,
size: Number,
key: String,
url: String,
origin: String,
createdAt: {
type: Date,
default: Date.now,
},
});
UserProfileImageSchema.pre('save', function () {
if (this.origin !== 'Github') {
if (!this.url) {
this.url = `${process.env.APP_URL}/files/${this.key}`;
}
}
});
UserProfileImageSchema.pre('remove', function () {
if (process.env.STORAGE_TYPE === 's3') {
return s3
.deleteObject({
Bucket: process.env.BUCKET_NAME,
Key: this.key,
})
.promise()
.then((response) => {
console.log(response.status);
})
.catch((response) => {
console.log(response.status);
});
}
return promisify(fs.unlink)(
path.resolve(__dirname, '..', '..', 'tmp', 'uploads', this.key),
);
});
module.exports = db2.model('UserProfileImage', UserProfileImageSchema);
Следующий файл - это маршрут, который должен запрашивать базы данных. и заполните соответствующие поля соответствующими данными.
routs / blog. js
const express = require('express');
const app = express();
app.use(cors());
const Post = require('../models/blog/Post');
const PostCover = require('../models/blog/PostCover');
const User = require('../models/user/User');
// My first failing approach. It doesn't populate the fields `cover`, `author` and `profileImage`, it returns `null` instead.
app.get('/home/articles', async (req, res) => {
const postsList = [];
Post.find({
type: 'Article',
})
.sort({ publishedOn: -1 })
.limit(6)
.populate('cover')
.populate({
path: 'author',
populate: {
path: 'profileImage',
model: 'UserProfileImage',
},
})
.then((posts) => {
posts.map((post) => {
postsList.push({
id: post.id,
title: post.title,
slug: post.slug,
category: post.category,
cover: post.cover,
author: post.author,
publishedOn: post.publishedOn,
updateOn: post.updateOn,
});
});
res.status(200).send(postsList);
})
.catch((err) => {
res.json({
err,
});
});
});
const getAuthor = async (authorId) => {
const user = await User.findOne({
_id: authorId
})
const userImage = await UserProfileImage.findOne({
_id: user.profileImage
});
return {
socialMedia: user.socialMedia,
posts: user.posts,
following: user.following,
followers: user.followers,
_id: user._id,
id: user.id,
name: user.name,
email: user.email,
quote: user.quote,
username: user.username,
password: user.password,
profileImage: userImage,
isAdmin: user.isAdmin,
origin: user.origin,
createdOn: user.createdOn,
__v: user.__v,
}
}
const getCover = async (coverId) => {
try {
const cover = await PostCover.findOne({
_id: coverId,
});
return cover
} catch(err) {
console.log(err);
return {}
}
}
// My second work around but it doesn't work well.
app.get('/home/articles', async (req, res) => {
let postsList = [];
try {
const posts = await Post.find({
type: 'Article',
})
.sort({ publishedOn: -1 })
.limit(6)
let postsLen = posts.length;
posts.map(async (post, i) => {
postsList.push({
tags: post.tags,
comments: post.comments,
howManyRead: post.howManyRead,
updatedOn: post.updatedOn,
_id: post._id,
id: post.id,
type: post.type,
category: post.category,
title: post.title,
slug: post.slug,
cover: await getCover(post.cover),
content: post.content,
author: await getAuthor(post.author),
publishedOn: post.publishedOn,
__v: post.__v
})
if (postsLen === i + 1) {
res.json(postsList)
}
});
} catch(err) {
console.log(err)
}
});
Таким образом, с этим подходом в итоге получается, что я могу подключиться и запросить из разных баз данных индивидуально. Но я не могу заполнить поля mongoose.Schema.Types.ObjectId
, основанные на данных из отдельной базы данных, с помощью команды .populate()
, даже те из той же базы данных, что и поле cover
в models/Post.js
.
Как мне поступить с этой проблемой?