MongoDB findOne - 返回值

3

我需要通过调用一个函数从“用户”集合中获取用户的ID,并返回它的值。

fetchId = (name) => {
        User.findOne({name: name}, (err, user) => {
            return user._id;
        });
    };

但是这个实现返回了null。如何修复它?

是否触发了任何错误?你能在return user._id上面打印 console.log(err) 吗? - Cata John
控制台打印响应良好的ID值,返回-否。 - Adam Jakś
2个回答

6

按照您的示范,如果您不想使用承诺(promises),您可以从调用者传递一个回调函数,并在获取结果后调用回调函数,因为对Mongo的调用是异步的。

fetchId = (name, clb) => {
  User.findOne({name: name}, (err, user) => {
    clb(user._id);
  });
};

fetchId("John", id => console.log(id));

否则,您可以使用基于 Promise 的机制省略第一个回调并将 Promise 返回给调用方。
fetchId = name => {
  return User.findOne({name: name}).then(user => user.id);
}; 


fetchId("John")
 .then(id => console.log(id));

好的,但是如何使用带有返回值语法的第一个代码片段呢?例如: { creator_id: fetchId("Ian Corby", id => { /* 返回id */ } ) } - Adam Jakś
@AdamJakś - 有两件事情可以从一个函数(fetchId)中返回, 它的结果取决于异步调用(findOne):(a)什么也不返回,这是你的代码和本答案第一个想法所返回的,(b)一个promise,这是本答案的第二个建议。 - danh
@AdamJakś 建议使用 Promise 更好,一旦掌握它,可以了解一下 async/await,这是 Promise 的一个小改进,允许您使用您正在寻找的语法。 - danh
@Karim,你从哪里得到 user => user.id 代码片段中的 user 对象?(第二个例子);) - Adam Jakś
这是在调用数据库后,User.findOne 返回的 Promise 的结果。 - Karim

0
第三种方法是@Karim的回答中建议2的变体(非常好)。 如果OP希望编写代码,就好像结果是从异步代码中分配的一样,那么一个改进就是将fetchId声明为asyncawait其结果...
fetchId = async (name) => {
    return User.findOne({name: name}).then(user => user.id);
};

let someId = await fetchId("John");
console.log(id)

编辑

对于对象中的任何方法(包括属性getter),只要是异步执行的,调用代码都需要注意并相应地采取行动。

这往往会向上扩散到依赖于调用者及其调用者等的系统中的所有内容。我们无法避免这种情况,也没有语法修复方案(语法是编译器看到的)。这是物理学:需要更长时间的事情就需要更长时间。我们可以使用语法部分隐藏复杂性,但我们仍然受到额外复杂性的限制。

将此应用于您的问题,假设我们有一个表示User的对象,该对象由mongo远程存储。最简单的方法是将内存中的用户对象视为未准备好,直到完成异步获取(findOne)操作。

在这种方法下,调用者只需要记住一件额外的事情:在使用未准备好的用户之前,让它准备好。以下代码采用了async / await样式语法,这是最现代化的语法风格,并且最大程度地隐藏 - 但不能消除:-(异步复杂性...

class MyMongoUser {
    // after new, this in-memory user is not ready
    constructor(name) {
        this.name = name;
        this.mongoUser = null;  // optional, see how we're not ready?
    }

    // callers must understand: before using, tell it to get ready!
    async getReady() {
        this.mongoUser = await myAsyncMongoGetter();
        // if there are other properties that are computed asynchronously, do those here, too
    }

    async myAsyncMongoGetter() {
        // call mongo
        const self = this;
        return User.findOne({name: self.name}).then(result => {
            // grab the whole remote object. see below
            self.mongoUser = result;
        });
    }

    // the remaining methods can be synchronous, but callers must
    // understand that these won't work until the object is ready
    mongoId() {
        return (this.mongoUser)? this.mongoUser._id : null;
    }

    posts() {
        return [ { creator_id: this.mongoId() } ];
    }
}

注意,我们不仅仅是从用户那里获取mongo_id,而是将整个mongo对象保存下来。除非这会占用大量内存,否则我们可以让它挂在那里,以便获取任何远程存储的属性。

以下是调用者的样子...

let joe = new MyMongoUser('joe');
console.log(joe.posts()) // isn't ready, so this logs [ { creator_id: null } ];
await joe.getReady();
console.log(joe.posts()) // logs [ { creator_id: 'the mongo id' } ];

是的,但问题是我需要实现这样的语法: value = fetchId('John'); 所以函数fetchId应该返回value(console.log可以正常工作,但返回不行);) - Adam Jakś
@AdamJakś - 它只能返回空值或一个Promise以便稍后完成。它无法返回findOne()的结果,因为在fetchId返回时还没有运行。在回调方法中,回调返回什么并不重要。回调是在findOne()运行之后调用的。 - danh
好的,感谢您详细的解释。但是 - 如果我需要在对象属性的值中使用此函数怎么办?就像这样:{ posts [ { content: 'lorem', creator_id: <fetchId function use> } ]。您认为这是可能的吗?如何将creator_id键赋予用户ID值?;) - Adam Jakś
@AdamJakś - 我明白你的困扰。请查看我对答案所做的广泛修改。 - danh
非常感谢您的详细解释。我通过向value属性添加一个函数来实现了这段代码,就像这样:posts:[ { creator_id: fetchId('john'); } ],在函数体中,我添加了您提出的调用者代码建议中的代码: async fetchId(name) { let john = new MyMongoUser(name); await john.getReady(); john.mongoId(); }但问题现在是控制台返回了一个错误: Post validation failed: creator_id: Cast to String failed for value "Promise { <pending> }" at path "creat or_id"。在post模式中,我将creator_id设置为字符串。解析字符串不是一个解决方案。 - Adam Jakś
@AdamJakś - 很抱歉,我可能没有表达清楚。我又编辑了一次。帖子结构不应包括调用fetchId(请注意,我的建议中没有任何名为“fetchId”的方法)。我希望这对你有所帮助,但我认为我没有更多要补充的了。也许在继续进行项目之前,可以先阅读更多关于Promise和异步代码的内容。 - danh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接