在 node.js 中按名称获取 mongodb 集合
Get mongodb collection by name in node.js
我目前正在学习 node.js,这是我参与其中的第一个项目。它(据说)是一个简单的待办事项列表应用程序,其中有多个列表我可以 load/edit/save/remove。
在 todo_list.ejs 文件中,我有一个 div,其中列出了所有集合名称:
<div id="list_container" class="lists">
<ul id="col_list" class="collection-list">
<% lists.forEach(list => { %>
<li class="collection-list-item">
<div class="list-name-container">
<a href="/<%=list.name %>" class="list-link">
<span class="list-name" name="list_name"><%=list.name %></span>
</a>
</div>
</li>
<% }) %>
</ul>
</div>
看起来像这样:
当我点击列表的 link 时。我尝试使用以下代码加载新列表(这是一个 mongodb 集合):
app.route("/:list_name").get((req, res) => {
MongoClient.connect(process.env.DB_CONNECT, (err, db) => {
if(err) throw err;
var database = db.db("myFirstDatabase");
const cursor = database.collection(req.params.list_name).find({}); /* stuck here */
database.listCollections().toArray((err, collections) => {
if(err) throw err;
db.close();
collections.forEach(element => {
if(element.name == req.params.list_name){
current_list = element;
current_list_name = element.name;
}
});
task.find({}, (err, todo_tasks) => { /*currently using the model.find() method to list all the documents which always looks at the "tasks" collection*/
res.render("todo_list.ejs", { tasks: todo_tasks, lists: collections, curr_list: current_list_name });
});
});
});
});
我评论了上面代码中卡住的地方。我正在尝试按名称获取 mongodb 集合,然后将其所有内容加载到列表中,但我不知道如何按名称查找集合。通过阅读 node.js 文档,我找到了游标对象,它有大量的信息和属性,我不知道如何处理...
有没有一种简单的方法可以按名称查找集合并获取其文档列表?
编辑 1:
这是我添加任务的地方:
//ADD TASK TO LIST
app.post('/', async (req, res) => {
const tsk = new task({ /*the mongodb model for tasks*/
content: req.body.content,
deadline: req.body.deadline
});
try {
await tsk.save();
res.redirect("/");
} catch(e) {
console.log(e);
res.redirect("/");
}
});
试试这个,应该有用。
我所做的更改:-
- MongoDb
connect
回调函数改为async
.
- 在
database.collection(req.params.list_name).find({});
末尾添加toArray()
函数
- 并将上述功能制作成
await
.
您可以选择.then
或async/await
,由您决定!
app.route("/:list_name").get((req, res) => {
MongoClient.connect(process.env.DB_CONNECT,async (err, db) => {
if(err) throw err;
var database = db.db("myFirstDatabase");
const todo_tasks = await database.collection(req.params.list_name).find({}).toArray(); /* add '.toArray()' */
database.listCollections().toArray((err, collections) => {
if(err) throw err;
db.close();
collections.forEach(element => {
if(element.name == req.params.list_name){
current_list = element;
current_list_name = element.name;
}
});
res.render("todo_list.ejs", { tasks: todo_tasks, lists: collections, curr_list: current_list_name });
});
});
});
经过一些改进:-
app.route("/:list_name").get((req, res) => {
// Connecting to MongoDb database
MongoClient.connect(process.env.DB_CONNECT, async (err, db) => {
if (err) throw err;
// Choosing 'myFirstDatabase' database
const database = db.db("myFirstDatabase");
let todo_tasks = [];
let collections = [];
let current_list_name = "";
// Getting selected list items(todo tasks) to array
try {
todo_tasks = await database.collection(req.params.list_name).find({}).toArray(); // Change :- Add '.toArray()'
} catch (err) {
if (err) throw err;
}
// Getting collections names
try {
collections = await database.listCollections().toArray();
db.close();
} catch (err) {
if (err) throw err;
}
// Getting selected list details
collections.forEach(element => {
if (element.name === req.params.list_name) {
current_list = element; // I don't understand what this code here
current_list_name = element.name;
}
});
// Rendering front end
res.render("todo_list.ejs", {
tasks: todo_tasks,
lists: collections,
curr_list: current_list_name,
});
});
});
我不会解决此答案中的 EJS
部分,因为我不合格,而且您提供的代码似乎都不错。但是,我将审查后端部分。
此外,由于我不知道你有什么样的编码背景(如果有的话),这个答案将包含很多关于可能是简单概念的解释。
总结
从你的第二个代码片段中,有几件事需要讨论:
- 异步代码
- 数据库连接和概论
- 实际实施
- 代码构思
- [编辑]:Save/Edit 实现
根据 OP 的知识,还有很多要涵盖的内容,例如 try/catch
子句,MongoDB 模型验证,express 的用法 Router
等等,但我只会在需要时编辑我的答案。
异步代码
对于其余的答案,大部分代码将被 async/await
关键字包围。这些是代码正常工作所必需的。
基本上,JS 是一种为网络而生的语言,您有时需要等待网络或数据库请求完成,然后再执行任何其他操作。这就是 callbacks
、promises
或 async/await
语法(承诺的语法糖)派上用场的地方。
假设您需要像您的示例一样检索任务列表:
app.route("/:list_name").get((req, res) => {
MongoClient.connect(process.env.DB_CONNECT, (err, db) => {
if(err) throw err;
var database = db.db("myFirstDatabase");
const cursor = database.collection(req.params.list_name).find({}); /* stuck here */
console.log(cursor);
// ..........
});
});
JS 默认是异步的,如果你 运行 这段代码,cursor
很可能会 undefined。原因是代码不会等待 database.collection(.............
完成以继续执行。但是在前面提到的 callback/promises/async-await
的帮助下,我们的代码现在可以等到这条指令完成。
您可以在 async/await here and here, and see here 上阅读 MongoDB 示例也使用 async/await,但您将在以下部分中看到更多“实际”用法它。
请记住,您使用的内容(无论是回调、承诺还是 async/await 语法)完全取决于您和您的喜好。
数据库连接
按照当前编写的代码,每次用户单击列表中的任何项目时,都会建立到 MongoDB 的连接,并且该连接不属于路由处理程序。您的后端应用程序应该连接到数据库一次(至少对于这种情况,对于某些高级情况启动多个连接可能很有用),并在您的后端应用程序停止时关闭连接(通常不是这种情况API).
例如,Atlas
云数据库有 500 个连接的限制。这意味着,如果 501 位用户同时单击您的前端列表中的一个项目,最好的情况是有人没有得到他所要求的,但情况可能更糟。
对于这件事,你有几种选择。一种是使用一个框架来帮助您利用一些代码和样板,例如 Mongoose or work with the native MongoDB driver 我们将这样做,因为您似乎已经使用过它,而且我坚信首先使用最低层将让你更快地学习更高层次的框架。
现在,让我们来解决这个问题。我们想将数据库连接放在其他地方,它会被调用一次。同样,您可以使用多个选项,但我喜欢为它创建一个 class,并导出一个新实例以在我的代码中的任何位置执行我想要的操作。这是一个(非常)简单的例子,说明我的最小目标是什么:
mongo-client.js:
const { MongoClient } = require('mongodb');
class MongoCli {
constructor() {
let url = `mongodb://testuser:my_sup3r_passwOrd@127.0.0.1:27017/?authSource=my_database_name`;
this.client = new MongoClient(url, { useUnifiedTopology: true });
}
async init() {
if (this.client) {
await this.client.connect();
this.db = this.client.db('test');
} else
console.warn("Client is not initialized properly");
}
}
module.exports = new MongoCli();
实际实施
当然,这段代码自己不行,需要调用等待,before定义路由。所以,就在 app.route("/:list_name")............
之前,调用这个:await MongoCli.init();
.
这是我的(再次,真的)简单 server.js
的样子(我已经将 mongo-客户端代码与服务器分开):
const express = require('express');
const MongoCli = require('./mongo-cli.js');
const server = async () => {
const app = express();
await MongoCli.init();
app.route("/:list_name").get(async (req, res) => {
});
return app;
};
module.exports = server;
现在,让我们从头开始实现您真正想要的,a.k.a一旦用户点击任务主题,它将显示他点击的主题的所有任务:
const express = require('express');
const MongoCli = require('./mongo-cli.js');
const server = async () => {
const app = express();
await MongoCli.init();
app.route("/:list_name").get(async (req, res) => {
// we will query the collection specified by req.params.list_name
// then, .find({}) indicates we want all the results (empty filter)
// finally, we call .toArray() to transform a Cursor to a human-readable array
const tasks = await MongoCli.db.collection(req.params.list_name).find({}).toArray();
// making sure we got what we needed, you can remove the line below
console.log(tasks);
// return a HTTP 200 status code, along with the results we just queried
res.status(200).json(tasks);
});
return app;
};
module.exports = server;
很简单,对吧?
请记住,我的 server.js
可能看起来不像你的,因为有很多方法可以处理这个问题,开发人员可以找到他自己喜欢的方法,但你明白了。
代码构想
我们已经启动了 GET 路由,我们在调用路由时得到了结果,一切都很好! ... 不完全是。
如果我们有 1500 个任务主题,现在会发生什么?我们真的应该创建 1500 个不同的集合,知道一个任务由描述、状态、截止日期,最终是一个名字组成吗?当然,我们可以做到,但这并不意味着我们必须这样做。
相反,创建一个且唯一的集合 tasks
,并向其添加键 topic
怎么样?
考虑到上面的句子,现在的路线是这样的:
const express = require('express');
const MongoCli = require('./mongo-cli.js');
const server = async () => {
const app = express();
await MongoCli.init();
app.route("/:topic_wanted").get(async (req, res) => {
// we now know the collection is named 'tasks'
// then, .find({topic: req.params.topic_wanted}) indicates we want all the results where the key 'topic' corresponds to req.params.topic_wanted
// finally, we call .toArray() to transform a Cursor to a human-readable array
const tasks = await MongoCli.db.collection('tasks').find({topic: req.params.topic_wanted}).toArray();
// making sure we got what we needed
console.log(tasks);
// return a HTTP 200 OK, along with the results we just queried
res.status(200).json(tasks);
});
return app;
};
module.exports = server;
遗言
希望我没有跑题,希望我的回答能帮到你。
另外,我在写答案时看到您现在需要弄清楚如何 post 任务。如果您需要进一步 information/explanation 或什至 posting 任务的帮助,请在评论中告诉我。
编辑(添加):
Save/Edit 实现
看到您创建新任务的实现,我假设您已经在使用 mongoose
。不幸的是,在 Mongoose 中声明模型时,它会自动搜索(如果不存在则创建)与您声明的模型名称相同的集合,但小写和复数除外(see here 了解更多信息) .这意味着您不能声明一个 new task
并将其分配给一个名为“users”的集合。
这就是本回答的第 4 部分“代码概念”发挥作用的地方。否则,您编辑的代码没有“重大”缺陷。
我目前正在学习 node.js,这是我参与其中的第一个项目。它(据说)是一个简单的待办事项列表应用程序,其中有多个列表我可以 load/edit/save/remove。
在 todo_list.ejs 文件中,我有一个 div,其中列出了所有集合名称:
<div id="list_container" class="lists">
<ul id="col_list" class="collection-list">
<% lists.forEach(list => { %>
<li class="collection-list-item">
<div class="list-name-container">
<a href="/<%=list.name %>" class="list-link">
<span class="list-name" name="list_name"><%=list.name %></span>
</a>
</div>
</li>
<% }) %>
</ul>
</div>
看起来像这样:
当我点击列表的 link 时。我尝试使用以下代码加载新列表(这是一个 mongodb 集合):
app.route("/:list_name").get((req, res) => {
MongoClient.connect(process.env.DB_CONNECT, (err, db) => {
if(err) throw err;
var database = db.db("myFirstDatabase");
const cursor = database.collection(req.params.list_name).find({}); /* stuck here */
database.listCollections().toArray((err, collections) => {
if(err) throw err;
db.close();
collections.forEach(element => {
if(element.name == req.params.list_name){
current_list = element;
current_list_name = element.name;
}
});
task.find({}, (err, todo_tasks) => { /*currently using the model.find() method to list all the documents which always looks at the "tasks" collection*/
res.render("todo_list.ejs", { tasks: todo_tasks, lists: collections, curr_list: current_list_name });
});
});
});
});
我评论了上面代码中卡住的地方。我正在尝试按名称获取 mongodb 集合,然后将其所有内容加载到列表中,但我不知道如何按名称查找集合。通过阅读 node.js 文档,我找到了游标对象,它有大量的信息和属性,我不知道如何处理...
有没有一种简单的方法可以按名称查找集合并获取其文档列表?
编辑 1:
这是我添加任务的地方:
//ADD TASK TO LIST
app.post('/', async (req, res) => {
const tsk = new task({ /*the mongodb model for tasks*/
content: req.body.content,
deadline: req.body.deadline
});
try {
await tsk.save();
res.redirect("/");
} catch(e) {
console.log(e);
res.redirect("/");
}
});
试试这个,应该有用。
我所做的更改:-
- MongoDb
connect
回调函数改为async
. - 在
database.collection(req.params.list_name).find({});
末尾添加 - 并将上述功能制作成
await
.
toArray()
函数
您可以选择.then
或async/await
,由您决定!
app.route("/:list_name").get((req, res) => {
MongoClient.connect(process.env.DB_CONNECT,async (err, db) => {
if(err) throw err;
var database = db.db("myFirstDatabase");
const todo_tasks = await database.collection(req.params.list_name).find({}).toArray(); /* add '.toArray()' */
database.listCollections().toArray((err, collections) => {
if(err) throw err;
db.close();
collections.forEach(element => {
if(element.name == req.params.list_name){
current_list = element;
current_list_name = element.name;
}
});
res.render("todo_list.ejs", { tasks: todo_tasks, lists: collections, curr_list: current_list_name });
});
});
});
经过一些改进:-
app.route("/:list_name").get((req, res) => {
// Connecting to MongoDb database
MongoClient.connect(process.env.DB_CONNECT, async (err, db) => {
if (err) throw err;
// Choosing 'myFirstDatabase' database
const database = db.db("myFirstDatabase");
let todo_tasks = [];
let collections = [];
let current_list_name = "";
// Getting selected list items(todo tasks) to array
try {
todo_tasks = await database.collection(req.params.list_name).find({}).toArray(); // Change :- Add '.toArray()'
} catch (err) {
if (err) throw err;
}
// Getting collections names
try {
collections = await database.listCollections().toArray();
db.close();
} catch (err) {
if (err) throw err;
}
// Getting selected list details
collections.forEach(element => {
if (element.name === req.params.list_name) {
current_list = element; // I don't understand what this code here
current_list_name = element.name;
}
});
// Rendering front end
res.render("todo_list.ejs", {
tasks: todo_tasks,
lists: collections,
curr_list: current_list_name,
});
});
});
我不会解决此答案中的 EJS
部分,因为我不合格,而且您提供的代码似乎都不错。但是,我将审查后端部分。
此外,由于我不知道你有什么样的编码背景(如果有的话),这个答案将包含很多关于可能是简单概念的解释。
总结
从你的第二个代码片段中,有几件事需要讨论:
- 异步代码
- 数据库连接和概论
- 实际实施
- 代码构思
- [编辑]:Save/Edit 实现
根据 OP 的知识,还有很多要涵盖的内容,例如 try/catch
子句,MongoDB 模型验证,express 的用法 Router
等等,但我只会在需要时编辑我的答案。
异步代码
对于其余的答案,大部分代码将被 async/await
关键字包围。这些是代码正常工作所必需的。
基本上,JS 是一种为网络而生的语言,您有时需要等待网络或数据库请求完成,然后再执行任何其他操作。这就是 callbacks
、promises
或 async/await
语法(承诺的语法糖)派上用场的地方。
假设您需要像您的示例一样检索任务列表:
app.route("/:list_name").get((req, res) => {
MongoClient.connect(process.env.DB_CONNECT, (err, db) => {
if(err) throw err;
var database = db.db("myFirstDatabase");
const cursor = database.collection(req.params.list_name).find({}); /* stuck here */
console.log(cursor);
// ..........
});
});
JS 默认是异步的,如果你 运行 这段代码,cursor
很可能会 undefined。原因是代码不会等待 database.collection(.............
完成以继续执行。但是在前面提到的 callback/promises/async-await
的帮助下,我们的代码现在可以等到这条指令完成。
您可以在 async/await here and here, and see here 上阅读 MongoDB 示例也使用 async/await,但您将在以下部分中看到更多“实际”用法它。
请记住,您使用的内容(无论是回调、承诺还是 async/await 语法)完全取决于您和您的喜好。
数据库连接
按照当前编写的代码,每次用户单击列表中的任何项目时,都会建立到 MongoDB 的连接,并且该连接不属于路由处理程序。您的后端应用程序应该连接到数据库一次(至少对于这种情况,对于某些高级情况启动多个连接可能很有用),并在您的后端应用程序停止时关闭连接(通常不是这种情况API).
例如,Atlas
云数据库有 500 个连接的限制。这意味着,如果 501 位用户同时单击您的前端列表中的一个项目,最好的情况是有人没有得到他所要求的,但情况可能更糟。
对于这件事,你有几种选择。一种是使用一个框架来帮助您利用一些代码和样板,例如 Mongoose or work with the native MongoDB driver 我们将这样做,因为您似乎已经使用过它,而且我坚信首先使用最低层将让你更快地学习更高层次的框架。
现在,让我们来解决这个问题。我们想将数据库连接放在其他地方,它会被调用一次。同样,您可以使用多个选项,但我喜欢为它创建一个 class,并导出一个新实例以在我的代码中的任何位置执行我想要的操作。这是一个(非常)简单的例子,说明我的最小目标是什么:
mongo-client.js:
const { MongoClient } = require('mongodb');
class MongoCli {
constructor() {
let url = `mongodb://testuser:my_sup3r_passwOrd@127.0.0.1:27017/?authSource=my_database_name`;
this.client = new MongoClient(url, { useUnifiedTopology: true });
}
async init() {
if (this.client) {
await this.client.connect();
this.db = this.client.db('test');
} else
console.warn("Client is not initialized properly");
}
}
module.exports = new MongoCli();
实际实施
当然,这段代码自己不行,需要调用等待,before定义路由。所以,就在 app.route("/:list_name")............
之前,调用这个:await MongoCli.init();
.
这是我的(再次,真的)简单 server.js
的样子(我已经将 mongo-客户端代码与服务器分开):
const express = require('express');
const MongoCli = require('./mongo-cli.js');
const server = async () => {
const app = express();
await MongoCli.init();
app.route("/:list_name").get(async (req, res) => {
});
return app;
};
module.exports = server;
现在,让我们从头开始实现您真正想要的,a.k.a一旦用户点击任务主题,它将显示他点击的主题的所有任务:
const express = require('express');
const MongoCli = require('./mongo-cli.js');
const server = async () => {
const app = express();
await MongoCli.init();
app.route("/:list_name").get(async (req, res) => {
// we will query the collection specified by req.params.list_name
// then, .find({}) indicates we want all the results (empty filter)
// finally, we call .toArray() to transform a Cursor to a human-readable array
const tasks = await MongoCli.db.collection(req.params.list_name).find({}).toArray();
// making sure we got what we needed, you can remove the line below
console.log(tasks);
// return a HTTP 200 status code, along with the results we just queried
res.status(200).json(tasks);
});
return app;
};
module.exports = server;
很简单,对吧?
请记住,我的 server.js
可能看起来不像你的,因为有很多方法可以处理这个问题,开发人员可以找到他自己喜欢的方法,但你明白了。
代码构想
我们已经启动了 GET 路由,我们在调用路由时得到了结果,一切都很好! ... 不完全是。
如果我们有 1500 个任务主题,现在会发生什么?我们真的应该创建 1500 个不同的集合,知道一个任务由描述、状态、截止日期,最终是一个名字组成吗?当然,我们可以做到,但这并不意味着我们必须这样做。
相反,创建一个且唯一的集合 tasks
,并向其添加键 topic
怎么样?
考虑到上面的句子,现在的路线是这样的:
const express = require('express');
const MongoCli = require('./mongo-cli.js');
const server = async () => {
const app = express();
await MongoCli.init();
app.route("/:topic_wanted").get(async (req, res) => {
// we now know the collection is named 'tasks'
// then, .find({topic: req.params.topic_wanted}) indicates we want all the results where the key 'topic' corresponds to req.params.topic_wanted
// finally, we call .toArray() to transform a Cursor to a human-readable array
const tasks = await MongoCli.db.collection('tasks').find({topic: req.params.topic_wanted}).toArray();
// making sure we got what we needed
console.log(tasks);
// return a HTTP 200 OK, along with the results we just queried
res.status(200).json(tasks);
});
return app;
};
module.exports = server;
遗言
希望我没有跑题,希望我的回答能帮到你。 另外,我在写答案时看到您现在需要弄清楚如何 post 任务。如果您需要进一步 information/explanation 或什至 posting 任务的帮助,请在评论中告诉我。
编辑(添加):
Save/Edit 实现
看到您创建新任务的实现,我假设您已经在使用 mongoose
。不幸的是,在 Mongoose 中声明模型时,它会自动搜索(如果不存在则创建)与您声明的模型名称相同的集合,但小写和复数除外(see here 了解更多信息) .这意味着您不能声明一个 new task
并将其分配给一个名为“users”的集合。
这就是本回答的第 4 部分“代码概念”发挥作用的地方。否则,您编辑的代码没有“重大”缺陷。