使用Express/MongoDB创建永久链接

使用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

假设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: '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
进行查询),您应该可以更深入一层。我喜欢这样。我稍后会试试,然后告诉你。