【问题标题】:Mongodb query on reference document参考文档上的MongoDB查询
【发布时间】:2019-08-04 14:43:00
【问题描述】:

我在猫鼬中定义了两个架构,如下所示,

模型:文章

var articleSchema = new mongoose.Schema({
    sequenceId: String
    title: String,
    abstract: String,
    authors: [String],
    date: Date,
    words: [{
        selectedWord: String,
        details: { type: Schema.Types.ObjectId, ref: 'Word' }
    }],
    otherTags: [String]
})

模型:字

var wordSchema = new mongoose.Schema({
    preferredWord: String,
    synonyms: [String],
    category: String
})

现在我正在尝试为以下场景获取 2 组结果,

  1. 获取所有在 selectedWord、preferredWord 或 同义词 中包含“wordAbc”和/或“wordXyz”的文章
  2. 获取数据库中所有文章的selectedWord、preferredWord和同义词中的所有唯一词

使用猫鼬执行查询的最佳/有效方式是什么?

对于第一个结果,我尝试了部分查询,但收到 CastError 消息,

Article.find({})
.populate( 'words', null, { 'details': {$in: ['wordAbc', 'wordXyz']}} )
.exec(function(err, docs) {});

【问题讨论】:

    标签: arrays node.js mongodb mongoose


    【解决方案1】:

    我认为你可以使用聚合管道实现你想要的两件事。

    1. 获取所有包含“wordAbc”和/或“wordXyz”的文章 selectedWord、preferredWord 或同义词

    首先您需要填充words数组的details字段中的所有单词,然后匹配基于selectedWord、preferredWord或synonyms的文章。

    可以这样实现:

    Article.aggregate([{
        $unwind : {
            path :"$words",
            preserveNullAndEmptyArrays :true
        }
    },{
        $lookup : {
            from : "words",
            localField : "words.details",
            foreignField : "_id",
            as : "words.details"
        }
    },{
        $unwind : {
            path : "$words.details",
            preserveNullAndEmptyArrays : true
        }
    },{
        $match : {
            $or : [{
                "words.selectedWord" : {$in: ['wordAbc', 'wordXyz']}
            },{
                "words.details.preferredWord" : {$in: ['wordAbc', 'wordXyz']}
            },{
                "words.details.synonyms" : {$in: ['wordAbc', 'wordXyz']}
            }]
        }
    },{
        $group : {
            _id : "$_id",
            title : {$first : "$title"},
            abstract : {$first : "$abstract"},
            authors : {$first : "$authors"},
            date : {$first : "$date"},
            words: {$push : "$words"},
            otherTags: {$first : "$otherTags"}
        }
    }])
    
    1. 获取 selectedWord、preferredWord 和同义词中的所有唯一词 数据库中的所有文章

    在这种情况下,你将不得不unwindwords数组,然后populatewords.details来自words集合,然后unwindsynonyms数组,这样我们就可以创建一组@ 987654339@、preferredWord和synonyms跨越所有articles,最后在聚合管道的最后阶段将所有唯一词组成一整套。

    可以这样实现:

    Article.aggregate([{
        $project : {
            words : "$words"
        }
    },{
        $unwind : "$words" 
    },{
        $lookup : {
            from : "words",
            localField : "words.details",
            foreignField : "_id",
            as : "words.details"
        }
    },{
        $unwind : "$words.details"
    },{
        $unwind : "$words.details.synonyms"
    },{
        $project : {
            selectedWord : "$words.selectedWord",
            preferredWord : "$words.details.preferredWord",
            synonyms : "$words.details.synonyms"
        }
    },{
        $group : {
            _id : "null",
            selectedWord : {$addToSet  :"$selectedWord"},
            preferredWord : {$addToSet  :"$preferredWord"},
            synonyms : {$addToSet  :"$synonyms"}
        }
    },{
        $project : {
            commonWords : {$setUnion : ["$selectedWord","$preferredWord","$synonys"]}
        }
    }])
    

    第二次聚合的解释。

    1. $project : 我们只想要单词,所以我保留了所有文章的单词字段,并从管道中删除了所有其他不必要的字段。
    2. $unwind : 我们需要展开 words 数组,以便我们可以在管道的下一阶段从 words 集合中 $lookup words.details
    3. $lookup :从单词集合中填充详细信息。
    4. $unwind :由于 $lookup 返回一个数组,我们需要展开它以使其成为一个对象
    5. $unwind : unwind words.details.synonyms,这样我们就可以将它们分组并在管道的下一阶段创建唯一单词数组,在这个阶段,聚合管道中的单个文档将如下所示:

      {
          words : {
              selectedWord :"someword",
              details : {
                  preferredWord : "otherword",
                  synonym : "synonymword"
              }
          }
      }
      
    6. $project:我们需要这个扁平化对象结构。在此阶段之后,管道中的单个文档将如下所示:

      {
           selectedWord :"someword",
           preferredWord : "otherword",
           synonym : "synonymword"
      }
      
    7. $group:将所有selectedWord合并为一个数组,preferredWord合并为一个数组,同义词在一个数组中,$addToSet用于删除重复对象

    8. $project : 结合所有 3 个数组并创建一个唯一单词数组

    有关 mongoDB 使用的所有运算符的详细信息,请阅读相应的文档。

    $setUnion documentation

    $addToSet documentation

    $project documentation

    $unwind documentation

    Documentation of all the operators of mongodb aggregation pipeline

    希望对你有帮助

    【讨论】:

    • 感谢 Ravi 的逐步说明。这正是我想要的。
    • 要补充一点,'$' 符号需要从 $_id 行中删除:"null",否则代码将通过 MongoError: The field '$_id' must be a accumulator对象
    • 哦,是的,很抱歉打错了,我会马上修改。
    • 当所有查询字段、selectedWord、详细信息、preferredWord 和同义词中的值退出时,您的查询工作正常。但是,如果任何字段数据为空,则查询 1 和 2 都不包含该对象。如何使这些字段在查询中成为可选字段?
    • 您也可以使用preserveNullAndEmptyArrays:true 和$unwind 来保留空数组。我会相应地更新答案。
    猜你喜欢
    • 2015-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多