猫鼬自己填充自定义查询

Posted

技术标签:

【中文标题】猫鼬自己填充自定义查询【英文标题】:mongoose own populate with custom query 【发布时间】:2018-08-03 06:25:33 【问题描述】:

我正在尝试在 mongoose 中创建自定义查询方法 - 类似于 mongoose 的 populate() 函数。我有以下两个简单的模式:

const mongoose = require('mongoose')

const bookSchema = new mongoose.Schema(
    title: String,
    author: type: mongoose.Schema.Types.ObjectId, required: true, ref: 'Author'
, versionKey: false)

const authorSchema = new mongoose.Schema(
    name: String
, versionKey: false)

现在,我要检索作者信息以及作者所写的书籍。据我所知,猫鼬提供了自定义查询,因此我的想法是编写一个自定义查询函数,例如:

authorSchema.query.populateBooks = function () 
    // semi-code:
    return forAll(foundAuthors).findAll(books)

现在,要获取所有作者和所有书籍,我可以简单地运行:

authorModel.find().populateBooks(console.log)

这应该是这样的:

[ name: "Author 1", books: ["Book 1", "Book 2"], name: "Author 2", books: ["Book 3"]  ]

不幸的是,它不起作用,因为我不知道如何访问之前在我的 populateBooks 函数中选择的作者列表。我在自定义查询功能中需要的是之前选择的文档的集合。 例如,authorModel.find() 已经返回了作者列表。在 populateBooks() 中,我需要遍历此列表以查找所有作者的所有书籍。任何人都知道我可以如何访问这个集合,或者是否有可能?

【问题讨论】:

【参考方案1】:

populate:“填充是用其他集合中的文档自动替换文档中指定路径的过程”(来自我链接的文档)。

根据您的问题,您不是在寻找人口。您的查询是一个简单的查询(以下代码是为了实现您最后给出的示例结果。请注意,您的 books 字段的值是一个字符串数组,我假设这些是标题)。另外,请注意以下代码将适用于您已经提供的模型,但这是一个糟糕的实现,我建议反对 - 出于多种原因:效率、优雅、潜在错误(例如,具有相同姓名的作者),见代码后的注释:

Author.find(, function(err, foundAuthors)
    if (err)
        console.log(err); //handle error
    
    //now all authors are objects in foundAuthors
    //but if you had certain search parameters, foundAuthors only includes those authors
    var completeList = [];
    for (i=0;i<foundAuthors.length;i++)
        completeList.push(name: foundAuthors[i].name, books: []);
    
    Book.find().populate("author").exec(function(err, foundBooks)
        if (err)
            console.log(err); //handle err
        
        for (j=0;j<foundBooks.length;j++)
            for (k=0;k<completeList.length;k++)
                if (completeList[k].name === foundBooks[j].author.name)
                    completeList[k].books.push(foundBooks[j].title);
                
            
        
        //at this point, completeList has exactly the result you asked for
    );
);

但是,正如我所说,我建议不要使用此实现,这是基于您已经提供的代码而没有更改它。

我建议更改您的作者架构以包含书籍属性:

var AuthorSchema = new mongoose.Schema(
    books: [
        type: mongoose.Schema.Types.ObjectId,
        ref: "Book"
    ]
    //all your other code for the schema
);

并将所有书籍添加到各自的作者。这样,您需要做的就是获取一组对象,每个对象都包含一个作者,他的所有书籍都是一个查询:

Author.find().populate("books").exec(function(err, foundAuthors)
    //if there's no err, then foundAuthors is an array of authors with their books
);

这比我之前给出的可能的解决方案更简单、更高效、更优雅和更有效,基于您已经存在的代码而不需要更改它。

【讨论】:

感谢您的想法。在推荐的实现中,我保存了两次引用,这在我看来不是一个好方法,而且 BSON 大小限制了实现。作者/书籍模式是为了说明问题。实际上,我目前正在生成 2000 个(在年底增加并预计 5000 个)新条目,这相当于每天 2000 个新书。因此,每位作者的书籍数量在近 2 年后都在增长并达到 16 MB。所以,数组实现是不合适的。 好吧,那么你可以使用第一种方法。可以通过在数组的对象(author: "name", books: [] 对象)中包含作者 _ids 来解决相同的作者姓名问题(如果存在的话),然后使用搜索正确的索引_id 而不是名称。这也将消除对书籍查询使用填充方法的需要 我发现了另一个更好的选择,使用 mongoose 4.5 中引入的 virtuals,请参阅:mongoosejs.com/docs/populate.html#populate-virtuals 嗯,不知道。很好的发现

以上是关于猫鼬自己填充自定义查询的主要内容,如果未能解决你的问题,请参考以下文章

在猫鼬上自定义 json 输出

CAD如何自定义填充图案

Django:ModelForm 使用自定义查询预填充复选框

如何在猫鼬中返回自定义错误?

使用 2 个字段的猫鼬自定义验证

SQLite SELECT查询中的自定义字段填充后不会成为DataTable中的DateTime列?