使用Express/MongoDB创建永久链接
假设LMS应用程序具有以下模式:使用Express/MongoDB创建永久链接,mongodb,express,permalinks,Mongodb,Express,Permalinks,假设LMS应用程序具有以下模式: const CourseSchema = new mongoose.Schema({ name: { type: String, required: true }, code: { type: String, required: true, unique: 1, uppercase: 1 }, // ex. CSCA48 quizzes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Qui
const CourseSchema = new mongoose.Schema({
name: { type: String, required: true },
code: { type: String, required: true, unique: 1, uppercase: 1 }, // ex. CSCA48
quizzes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Quiz' }]
});
const QuizSchema = new mongoose.Schema({
name: { type: String, required: true }, // ex. 1a
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }]
});
const QuestionSchema = new mongoose.Schema({
number: { type: String, required: true }, // ex. 4
question: { type: String, required: true },
type: { type: String },
choices: [String],
answers: [String]
});
我按照RESTfulAPI准则构建(或尝试构建)了应用程序。比如说,
GET /courses/[ObjectID]
GET /courses/[ObjectID]/quizzes
GET /courses/[ObjectID]/quizzes/[ObjectID]
GET /courses/[ObjectID]/quizzes/[ObjectID]/questions
router.param('courseID', controllers.Course.getCourseByParam);
router.param('quizID', controllers.Quiz.getQuizByParam);
router.param('questionID', controllers.Question.getQuestionByParam);
router.get('/courses', controllers.Course.getCourses);
router.get('/courses/:courseID', controllers.Course.getCourse);
router.get('/courses/:courseID/quizzes', controllers.Quiz.getQuizzes);
router.get('/courses/:courseID/quizzes/:quizID/questions', controllers.Question.getQuestions);
我之所以使用ObjectID,是因为它们是唯一的,并且可以很容易地检索对象——特别是使用。比如说,
GET /courses/[ObjectID]
GET /courses/[ObjectID]/quizzes
GET /courses/[ObjectID]/quizzes/[ObjectID]
GET /courses/[ObjectID]/quizzes/[ObjectID]/questions
router.param('courseID', controllers.Course.getCourseByParam);
router.param('quizID', controllers.Quiz.getQuizByParam);
router.param('questionID', controllers.Question.getQuestionByParam);
router.get('/courses', controllers.Course.getCourses);
router.get('/courses/:courseID', controllers.Course.getCourse);
router.get('/courses/:courseID/quizzes', controllers.Quiz.getQuizzes);
router.get('/courses/:courseID/quizzes/:quizID/questions', controllers.Question.getQuestions);
但是现在,我想知道是否有一种方法不使用ObjectID,以使URL更友好/可读
GET /courses/[code]/quizzes/[name]/questions/[number]
ex. /courses/CSCA48/quizzes/1a/questions/4
我最担心的是,尽管课程code
是唯一的,但测验name
和问题number
却不是唯一的。例如,/courses/CSCA48/quizzes/1b/questions/4
对于不同的测验是不同的问题。因此,除非我弄错了,否则实际上不可能使用app.param()
。因此,我需要以某种方式确保测验属于匹配的课程,然后问题属于匹配的测验
我并不是真的在寻找代码,只是一个关于如何实现(或不实现)的一般想法。由于组合
[code]-[name]-[number]
是唯一的(而且[code]
也很重要),您应该能够运行一个查询,从而提出正确的问题:
Course.findOne({ code : req.params.code })
.populate({
path : 'quizzes',
match : { name : req.params.name },
populate : {
path : 'questions',
match : { number : req.params.number }
}
})
或者,您可以向
QuestionSchema
添加两个字段,以反映问题所属的课程和测验,在这种情况下,您只需运行一个查询即可找到正确的问题文档(填充需要3个查询)。组合是否唯一?@robertklep Yes,可能是这样。它们可能是一种独特的组合,但如果检索结果将依赖于使用“填充路径”(这基本上是除了任何基本请求之外,至少还有两个对MongoDB的查询),那么这是一个非常糟糕的主意,您应该重新考虑设计。不,这是不一样的。如果您查看提供给您的答案,它会使用.populate()
和匹配
选项来查询名称
(例如),而不是对象ID
,后者是课程
模型本身中实际存在的唯一对象。然后类似地,在测验
中“加入”的详细信息中添加编号
,以匹配问题
。这现在变成了三个查询,而不是一个查询,这是最佳的。您需要打开调试mongoose.set('debug',true)
和实际发出的查询。要点是您应该尽量避免发出“连接”来匹配条件。事实上,.populate()
有点“老掉牙”。MongoDB现在可以执行$lookup
,这更有效,因为它实际上是在服务器上执行的。但尽管如此,您还是应该进行建模,以便通过单个请求检索实际需要的信息。这通常意味着通过更好的建模,能够从一个集合中检索,而不依赖于“连接的模拟”,也不真正依赖于“任何可以避免的连接”。有趣的第一个解决方案。我假设这只适用于2个嵌套级别?例如,[code]-[name]-[number]-[anothervar]
需要执行第二个解决方案?@Mikey如果您向QuestionSchema
添加一个填充字段(然后使用req.params.anothervar
进行查询),您应该可以更深入一层。我喜欢这样。我稍后会试试,然后告诉你。