在mongoose查询中随机播放子文档

时间:2014-05-25 11:02:33

标签: node.js mongodb mongoose

我有以下模特:

问题模型

var OptionSchema = new Schema({
    correct : {type:Boolean, default:false}
    value : String
});

var QuestionSchema = new Schema({
    value  : String
    , choices : [OptionSchema]
    , quiz : {type:ObjectId, ref:'quizzes'}
    , createdOn : {type:Date, default:Date.now}
    ...
});

var Question = mongoose.model('questions', QuestionSchema);

测验模型

var QuizSchema = new Schema({
name : String
, questions : [{type:ObjectId, ref:'questions'}]
,company : {type:ObjectId, ref:'companies'}
...
});

var Quiz = mongoose.model('quizzes', QuizSchema);

公司模式

var CompanySchema = new Schema({
name :String
...
});

我希望每个查询对每个问题的choices进行随机播放,我按照以下方式进行操作:

shuffle = function(v){
    //+ Jonas Raoni Soares Silva
    //@ http://jsfromhell.com/array/shuffle [rev. #1]

    for(var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x);
    return v;
};

app.get('/api/companies/:companyId/quizzes', function(req, res){

    var Query = Quiz.find({company:req.params.companyId});
    Query.populate('questions');
    Query.exec(function(err, docs){
        docs.forEach(function(doc) {
        doc.questions.forEach(function(question) {
            question.choices = shuffle(question.choices);
            })
        });
        res.json(docs);
    });

});

我的问题是: 我可以将choices数组随机化而不循环浏览所有文档吗?正如我现在所做的那样?

2 个答案:

答案 0 :(得分:0)

问题的实质归结为"我可以随机播放结果并让MongoDB为我做这项工作吗?"。嗯,是的,你可以,但重要的是要记住,"填充"不再是你的朋友帮助你这样做了,你需要完成自己做的工作。

这个问题的一小部分是我们将要切换到#34;你的客户方" shuffle" mapReduce是为了处理"选择"在服务器上。只是为了踢,我添加了一种技术来改变你的问题"以及:

  var Query = Quiz.findOne({ company: "5382a58bb7ea27c9301aa9df" });
  Query.populate('company', 'name -_id');
  Query.exec(function(err,quiz) {

    var shuffle = function(v) {
      for(var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x);
    };


    if (err)
      throw err;

    var raw = quiz.toObject();

    shuffle( raw.questions );

    Question.mapReduce(
      {
        map: function() {

          shuffle( this.choices );

          var found = -1;
          for ( var n=0; n<inputs.length; n++ ) {
            if ( this._id.toString() == inputs[n].toString() ) {
              found = n;
              break;
            }
          }

          emit( found, this );

        },

        reduce: function() {},

        scope: { inputs: raw.questions, shuffle: shuffle },

        query: { "_id": { "$in": raw.questions } }

      },
      function(err,results) {
        if (err)
          throw err;

        raw.questions = results.map(function(x) {
          return x.value;
        });

        console.log( JSON.stringify( raw, undefined, 4 ) );
      }
    );

  });

所以这个的关键部分不是允许&#34;填充&#34;要将所有相关的问题信息提取到您的架构对象中,您正在使用mapReduce进行手动替换。

请注意&#34;架构文档&#34;必须转换为普通对象,这是由.toObject()调用在那里完成的,以便我们可以替换&#34;问题&#34;与某些与模式类型不匹配的东西。

我们给mapReduce一个查询,通过简单地传入&#34;问题&#34;来从模型中选择所需的问题。数组作为匹配_id的参数。真的没有什么直接不同于什么&#34;填充&#34;在幕后为你做的,只是我们要处理&#34;合并&#34;手动

&#34; shuffle&#34;函数现在在服务器上执行,因为它被声明为var,我们可以通过&#34;范围&#34;和&#34;选项&#34;轻松传入。数组在发出之前将被洗牌,并最终返回。

我说的其他可选项是我们也是&#34;洗牌&#34;问题,这只是通过调用&#34; shuffle&#34;只考虑&#34;问题&#34;的_id值数组,然后将其传递到&#34;范围&#34;。注意到这也是通过$in传递给查询的,但仅此一项并不能保证返回订单。

这里使用的技巧是mapReduce在&#34; map&#34;阶段,必须&#34;发出&#34;所有键按升序排列到后期。因此,通过将当前_id值与其位置的位置作为&#34;输入&#34;的索引值进行比较。从范围的数组然后有一个位置顺序,可以作为&#34;键&#34;这里要尊重已经完成的洗牌的顺序。

&#34;合并&#34;然后很简单,因为我们只是替换&#34;问题&#34;使用mapReduce返回的值的数组。这里的.map()数组函数有一些帮助,可以清除mapReduce返回内容的结果。

除了您的&#34;选项&#34;现在实际上是在服务器上而不是通过循环进行洗牌,这应该可以让您了解如何自定义填充&#34;用于其他功能,例如&#34;切片&#34;和&#34;分页&#34;引用的数组&#34;问题&#34;如果这是其他你可能想看的东西。

答案 1 :(得分:0)

shuffle = function(v){
    //+ Jonas Raoni Soares Silva
    //@ http://jsfromhell.com/array/shuffle [rev. #1]

    for(var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x);
    return v;
};

app.get('/api/companies/:companyId/quizzes', function(req, res){

    var Query = Quiz.find({company:req.params.companyId});
    Query.populate('questions');
    Query.exec(function(err, docs){
    
    var raw = docs.toObject();
    
    //shuffle choices
    raw.questions.map(el => shuffle(el.choices))
    
    //if you need to shuffle the questions too
    shuffle(raw.questions);
    
    //if you need to limit the output questions, especially when ouput questions needs to be a subset of a pool of questions
    raw.questions.splice(limit);

        res.json(raw); // output quiz with shuffled questions and answers
    });

});