前面陆续学习了一下 node、npm、模块,也稍尝试 Express,感觉得换一个思路加快进行。
比如笔者对前端的开发已较熟悉,如果领导给一个内部小网站
的需求,难道说你得给我配置一个后端
?
(资料图)
又不是做一个复杂的后端,只是简单的数据存储(增删改查)、上传文件、下载csv或excel,无需考虑性能、稳定性、负载均衡等等,怎么就不能做
目标实现简单后台的开发和部署
Express 项目生成器生成项目可通过应用生成器工具 express-generator 可以快速创建一个应用的骨架
创建项目文件夹 spug-back-end,进入项目后执行 npx express-generator
:
Administrator@ /e/spug-back-end$ npx express-generatornpm WARN exec The following package was not found and will be installed: express-generator@4.16.1npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.) warning: the default view engine will not be jade in future releases warning: use `--view=jade" or `--help" for additional options create : public\ create : public\javascripts\ create : public\images\ create : public\stylesheets\ create : public\stylesheets\style.css create : routes\ create : routes\index.js create : routes\users.js create : views\ create : views\error.jade create : views\index.jade create : views\layout.jade create : app.js create : package.json create : bin\ create : bin\www install dependencies: $ npm install run the app: $ DEBUG=spug-back-end:* npm start
生成如下内容:
Administrator@ /e/spug-back-end$ lltotal 5-rw-r--r-- 1 Administrator 197121 1075 Apr 14 15:06 app.jsdrwxr-xr-x 1 Administrator 197121 0 Apr 14 15:06 bin/-rw-r--r-- 1 Administrator 197121 301 Apr 14 15:06 package.jsondrwxr-xr-x 1 Administrator 197121 0 Apr 14 15:06 public/drwxr-xr-x 1 Administrator 197121 0 Apr 14 15:06 routes/drwxr-xr-x 1 Administrator 197121 0 Apr 14 15:06 views/
Tip: 对于较老的 Node 版本(8.2.0 以下),请通过 npm 将 Express 应用程序生成器安装到全局环境中并使用
$ npm install -g express-generator$ express
根据上文提示安装依赖 npm install
:
Administrator@ /e/spug-back-end$ npm installnpm WARN deprecated constantinople@3.0.2: Please update to at least constantinople 3.1.1npm WARN deprecated transformers@2.1.0: Deprecated, use jstransformernpm WARN deprecated jade@1.11.0: Jade has been renamed to pug, please install the latest version of pug instead of jadeadded 99 packages, and audited 100 packages in 22s1 package is looking for funding run `npm fund` for details8 vulnerabilities (1 low, 4 high, 3 critical)To address all issues (including breaking changes), run: npm audit fix --forceRun `npm audit` for details.
全局安装 nodemon(在Node.js应用程序开发过程中使用的简单监视器脚本),编码过程中无需重启 node 服务即可生效:
PS E:\spug-back-end> npm i -g nodemonchanged 32 packages, and audited 33 packages in 1s3 packages are looking for funding run `npm fund` for detailsfound 0 vulnerabilities
修改启动脚本:
"version": "0.0.0", "private": true, "scripts": {- "start": "node ./bin/www"+ "start": "nodemon ./bin/www"
通过 npm run start
本地启动服务:
PS E:\spug-back-end> npm run start> spug-back-end@0.0.0 start> nodemon ./bin/www[nodemon] 2.0.22[nodemon] to restart at any time, enter `rs`[nodemon] watching path(s): *.*[nodemon] watching extensions: js,mjs,json[nodemon] starting `node ./bin/www`
入口文件 /bin/www.js
默认使用的是 3000 端口(var port = normalizePort(process.env.PORT || "3000");
)
浏览器访问 http://localhost:3000/
,页面显示:
ExpressWelcome to Express
项目解读模板views
目录中存放的是 jade 模板
文件:
Administrator@ /e/spug-back-end/views (master)$ lserror.jade index.jade layout.jade
首页
对应的模板是 index.jade:
Administrator@ /e/spug-back-end/views (master)$ cat index.jadeextends layoutblock content h1= title p Welcome to #{title}
Express 生成器默认使用 jade 模板,对前端不是很友好,笔者通过 npx express-generator --view=ejs
选用 ejs
模板(对前端更友好)重新创建。
首页对应模板 index.ejs:
<%= title %> <%= title %>
Welcome to <%= title %>
入口文件bin/www.js
是应用的入口文件。
核心代码如下:
// 加载 app.jsvar app = require("../app");var http = require("http");var port = normalizePort(process.env.PORT || "3000");app.set("port", port);// 创建 http servervar server = http.createServer(app);server.listen(port);
完整代码如下:
#!/usr/bin/env node/** * Module dependencies. */var app = require("../app");var debug = require("debug")("spug-back-end:server");var http = require("http");/** * Get port from environment and store in Express. */var port = normalizePort(process.env.PORT || "3000");app.set("port", port);/** * Create HTTP server. */var server = http.createServer(app);/** * Listen on provided port, on all network interfaces. */server.listen(port);server.on("error", onError);server.on("listening", onListening);/** * Normalize a port into a number, string, or false. */function normalizePort(val) { var port = parseInt(val, 10); if (isNaN(port)) { // named pipe return val; } if (port >= 0) { // port number return port; } return false;}/** * Event listener for HTTP server "error" event. */function onError(error) { if (error.syscall !== "listen") { throw error; } var bind = typeof port === "string" ? "Pipe " + port : "Port " + port; // handle specific listen errors with friendly messages switch (error.code) { case "EACCES": console.error(bind + " requires elevated privileges"); process.exit(1); break; case "EADDRINUSE": console.error(bind + " is already in use"); process.exit(1); break; default: throw error; }}/** * Event listener for HTTP server "listening" event. */function onListening() { var addr = server.address(); var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port; debug("Listening on " + bind);}
Tip: 和我们之前创建的最简单的服务器类似
app.js入口文件中引入 app.js,完整代码如下:
var createError = require("http-errors");var express = require("express");var path = require("path");var cookieParser = require("cookie-parser");var logger = require("morgan");var indexRouter = require("./routes/index");var usersRouter = require("./routes/users");// Creates an Express applicationvar app = express();// view engine setup// 模板引擎相关代码app.set("views", path.join(__dirname, "views"));app.set("view engine", "ejs");app.use(logger("dev"));// for parsing application/jsonapp.use(express.json());// for parsing application/x-www-form-urlencodedapp.use(express.urlencoded({ extended: false }));app.use(cookieParser());// 放开静态资源app.use(express.static(path.join(__dirname, "public")));// 定义两个路由。一个返回页面一个返回后端数据// app.use([path,] callback [, callback...]) - 在指定路径挂载指定的一个或多个中间件函数:当请求路径的基匹配路径时,执行中间件函数。app.use("/", indexRouter);app.use("/users", usersRouter);// catch 404 and forward to error handlerapp.use(function(req, res, next) { next(createError(404));});// error handlerapp.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get("env") === "development" ? err : {}; // render the error page res.status(err.status || 500); res.render("error");});module.exports = app;
和我们之前用 Express 实现的报名系统 非常类似。这里创建了一个 Express 应用,并定义了两个示例路由:
var indexRouter = require("./routes/index");var usersRouter = require("./routes/users");// 返回页面app.use("/", indexRouter);// 返回后端数据app.use("/users", usersRouter);
路由app.js 使用了两个路由。内容如下:
// routes/index.jsvar express = require("express");var router = express.Router();/* GET home page. */router.get("/", function(req, res, next) { res.render("index", { title: "Express" });});module.exports = router;
// routes/users.jsvar express = require("express");var router = express.Router();/* GET users listing. */router.get("/", function(req, res, next) { res.send("respond with a resource");});module.exports = router;
浏览器访问 http://localhost:3000/users
,页面显示 respond with a resource
。
数据库通常会安装到 linux 中,这里以 ubuntu 为例,通过 apt-get install mongodb
即可安装,非常方便:
// 笔者刚已执行root@linux:/home/pjl# apt-get install mongodbReading package lists... DoneBuilding dependency treeReading state information... Donemongodb is already the newest version (1:3.6.9+really3.6.8+90~g8e540c0b6d-0ubuntu5.3).0 upgraded, 0 newly installed, 0 to remove and 150 not upgraded.
数据库现在已经启动,我们通过mongo -version
验证安装成功:
root@linux:/home/pjl# mongo -versionMongoDB shell version v3.6.8git version: 8e540c0b6db93ce994cc548f000900bdc740f80aOpenSSL version: OpenSSL 1.1.1f 31 Mar 2020allocator: tcmallocmodules: nonebuild environment: distarch: x86_64 target_arch: x86_64
mongodb 配置文件是 /etc/mongodb.conf
。请注意下面两项配置:
// 远程连接#bind_ip = 127.0.0.1bind_ip = 0.0.0.0// 开机自启动#auth = trueauth = true
MongoDB shell输入 mongo 即可进入 MongoDB shell
(操作mongo):
root@linux:/home/pjl# mongoMongoDB shell version v3.6.8connecting to: mongodb://127.0.0.1:27017Implicit session: session { "id" : UUID("aedc6541-4a67-4e60-8eb4-d1325c82d061") }MongoDB server version: 3.6.8Welcome to the MongoDB shell.For interactive help, type "help".For more comprehensive documentation, see http://docs.mongodb.org/Questions? Try the support group http://groups.google.com/group/mongodb-userServer has startup warnings:2023-04-15T14:34:32.327+0800 I STORAGE [initandlisten]2023-04-15T14:34:32.327+0800 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine2023-04-15T14:34:32.327+0800 I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem2023-04-15T14:34:34.861+0800 I CONTROL [initandlisten]2023-04-15T14:34:34.861+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.2023-04-15T14:34:34.861+0800 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.2023-04-15T14:34:34.861+0800 I CONTROL [initandlisten]> help db.help() help on db methods db.mycoll.help() help on collection methods sh.help() sharding helpers rs.help() replica set helpers help admin administrative help help connect connecting to a db help help keys key shortcuts help misc misc things to know help mr mapreduce show dbs show database names show collections show collections in current database show users show users in current database show profile show most recent system.profile entries with time >= 1ms show logs show the accessible logger names show log [name] prints out the last segment of log in memory, "global" is default use set current database db.foo.find() list objects in collection foo db.foo.find( { a : 1 } ) list objects in foo where a == 1 it result of the last line evaluated; use to further iterate DBQuery.shellBatchSize = x set default number of items to display on shell exit quit the mongo shell> show dbsadmin 0.000GBconfig 0.000GBlocal 0.000GB
通过 help
可以查看帮助,通过show dbs
发现现在有三个数据库。
通过 use 可以创建或切换数据库。比如:
// use - 创建数据库 pjl_db。如果存在则直接切换> use pjl_dbswitched to db pjl_db// db - 目前操作的是 pjl_db 数据库> dbpjl_db
由于新建的数据库 pjl_db 什么都没有,所以通过 show dbs
显示不出来,可通过 createCollection 创建表,再次查询即可显示该数据库。比如
// 新建数据库未能展示> show dbsadmin 0.000GBconfig 0.000GBlocal 0.000GB// 通过 tab 建有提示> db.createdb.createCollection( db.createRole( db.createUser( db.createView(// 创建表 users。这里叫集合> db.createCollection("users"){ "ok" : 1 }// 再次查询则可显示新建数据库 pjl_db> show dbsadmin 0.000GBconfig 0.000GBlocal 0.000GBpjl_db 0.000GB
Tip:db.createCollection("pjl_db", {size: 1024*1024, capped: true, max: 1000})
- 创建 pjl_db 数据库,同时这个数据库最多 1M,记录数只能有1000条,在多一条则把第一条给替代。db.createCollection("users")
则不作限制。
通过 db.dropDatabase() 即可删除数据库。比如:
> db.dropDatabase(){ "dropped" : "pjl_db", "ok" : 1 }> show dbsadmin 0.000GBconfig 0.000GBlocal 0.000GB
新增、查看和删除表通过 db.createCollection("users")
新建 users 表。比如:
// 创建表 users。这里叫集合> db.createCollection("users"){ "ok" : 1 }// 再次查询则可显示新建数据库 pjl_db> show dbsadmin 0.000GBconfig 0.000GBlocal 0.000GBpjl_db 0.000GB
db.getCollectionNames - 查看有哪些表db.tableName.drop - 删除某张表示例:
// 新建两张表: table-b、table-c> db.createCollection("table-b"){ "ok" : 1 }> db.createCollection("table-c"){ "ok" : 1 }// tab 提示> db.getCollectiondb.getCollection( db.getCollectionInfos( db.getCollectionNames(// 有哪些表> db.getCollectionNames()[ "table-b", "table-c", "users" ]// 删除table-b表失败> db.table-b.drop()2023-04-15T15:17:10.232+0800 E QUERY [thread1] ReferenceError: b is not defined :@(shell):1:1// 删除table-b表成功> db["table-b"].drop()true> db.getCollectionNames()[ "table-c", "users" ]
表格-新增数据通过 db.users.save
可以向 users 表中单条、批量插入数据。甚至字段名不同,字段数量不同也能插入。请看示例:
// 给 users 表插入一条数据 {"name": "pjl", age: 18}> db.users.save({"name": "pjl", age: 18})WriteResult({ "nInserted" : 1 })// 查询users表> db.users.find(){ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }// 批量插入数据> db.users.save([{"name": "pjl2", age: 19}, {"name": "pjl3", age: 20}])BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 2, "nUpserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ ]})// 批量插入成功> db.users.find(){ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }// 换字段名和字段长度,也能插入成功> db.users.save({"name2": "pjl", age2: 18, tel: 131111111})WriteResult({ "nInserted" : 1 })> db.users.find(){ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643a529a73f16a13ae248f46"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }
Tip:向 mongo 表中插入数据非常自由,什么字段、什么类型都可以。
表格-删除数据通过 db.users.remove({age2:18})
可以删除 age2=18 的数据,通过 db.users.remove({})
删除 users 表中所有数据。请看示例:
// 目前有4条数据> db.users.find(){ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643a529a73f16a13ae248f46"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }// 可以删除 age2=18 的数据> db.users.remove({age2:18})WriteResult({ "nRemoved" : 1 })> db.users.find(){ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }// 删除 users 表中所有数据是 `db.users.remove({})`。`db.users.remove()` 会报错> db.users.remove()2023-04-16T09:10:55.859+0800 E QUERY [thread1] Error: remove needs a query :DBCollection.prototype._parseRemove@src/mongo/shell/collection.js:357:1DBCollection.prototype.remove@src/mongo/shell/collection.js:382:18@(shell):1:1// 删除 users 表中所有数据> db.users.remove({})WriteResult({ "nRemoved" : 3 })> db.users.find()>
表格-修改数据通过 db.users.update({name:"pjl2"}, {$set: {age: 20}})
修改 name=pjl2 的数据,将 age 改为 20。
Tip:直接 db.users.update({name:"pjl"}, {age: 19})
会替换整条数据。
\$inc
指增加,如果需要减去,则将数字改成负数。
示例如下:
> db.users.find(){ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "name" : "pjl", "age" : 18 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }// 修改 name=pjl 的数据,将 age改为19> db.users.update({name:"pjl"}, {age: 19})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })// 替换了条数据> db.users.find(){ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }// 通过 $set 成功更改 age 而不影响其他字段> db.users.update({name:"pjl2"}, {$set: {age: 20}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.users.find(){ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 20 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }// 给 age 增加1> db.users.update({name:"pjl2"}, {$inc: {age: 1}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.users.find(){ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 21 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }// 给 age 减去1> db.users.update({name:"pjl2"}, {$inc: {age: -1}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.users.find(){ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 20 }{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }
表格-查询数据首先清空 users 表,并插入6条数据。
find通过 db.users.find()
查询所有数据:
> db.users.find(){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }>
大于小于$gt - 大于$gte - 大于等于$lt - 小于$lte - 小于等于// 查询 age 大于 50 的数据> db.users.find({age:{$gt: 50}}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }// 查询 age 大于 69 的数据> db.users.find({age:{$gt: 69}})// 查询 age 大于等于 69 的数据> db.users.find({age:{$gte: 69}}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }// age 小于 20 > db.users.find({age:{$lt: 20}}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }// 查询 age 大于 10,小于 40 的数据> db.users.find({age:{$gt:10, $lt:40}}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }>
单条件db.users.find({name: "jia"})
- 查询 name 等于
jia 的数据db.users.find({name: /jia/})
- 查询 name 中包含
jia 的数据db.users.find({name: /jia$/})
- 查询 name 中以 jia 结尾
的数据// 查询 name 等于 jia 的数据> db.users.find({name: "jia"}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }// 查询 name 中包含 jia 的数据> db.users.find({name: /jia/}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }// 查询 name 中以 jia 结尾的数据> db.users.find({name: /jia$/}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }
多条件db.users.find({$or: [{age: 19}, {name: "jia"}]})
查询 age=19 或 name=jia
// 查询 age=19 或 name=jia> db.users.find({$or: [{age: 19}, {name: "jia"}]}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }
列过滤db.users.find({}, {name: 1})
- 只显示 name 这个字段。默认主键回返回。1 是显示,0指不显示db.users.find({}, {name: 1, age: 1, _id: 0})
- 只要 name 和 age 字段,主键不要// 只显示 name 这个字段。默认主键回返回> db.users.find({}, {name: 1}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali" }// 只显示 name 这个字段,主键也不要> db.users.find({}, {name: 1, _id: 0}){ "name" : "peng" }{ "name" : "jia" }{ "name" : "li" }{ "name" : "pengjia" }{ "name" : "pengjiali" }{ "name" : "jiali" }// 只要 name 和 age 字段,主键不要> db.users.find({}, {name: 1, age: 1, _id: 0}){ "name" : "peng", "age" : 19 }{ "name" : "jia", "age" : 29 }{ "name" : "li", "age" : 39 }{ "name" : "pengjia", "age" : 49 }{ "name" : "pengjiali", "age" : 59 }{ "name" : "jiali", "age" : 69 }
排序通过 db.users.find().sort({age: -1})
执行 age 逆序,正序则为1。
// age 逆序> db.users.find({}, {age:1}).sort({age: -1}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "age" : 69 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "age" : 59 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "age" : 49 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "age" : 39 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "age" : 29 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "age" : 19 }// age 正序> db.users.find({}, {age:1}).sort({age: 1}){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "age" : 19 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "age" : 29 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "age" : 39 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "age" : 49 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "age" : 59 }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "age" : 69 }
分页分页可以通过 skip 和 limit实现。例如 db.users.find().skip(2).limit(2)
查询第二页。跳过2条,查询2条。
> db.users.find(){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }// 查询前3条> db.users.find().limit(3){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }// 第一页。跳过0条,查询2条> db.users.find().skip(0).limit(2){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }// 第二页。跳过2条,查询2条> db.users.find().skip(2).limit(2){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }// 第三页。跳过4条,查询2条> db.users.find().skip(4).limit(2){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }// 先 age 逆序,在取2条> db.users.find().sort({age: -1}).skip(0).limit(2){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }
通过 count() 查询记录数,例如 db.users.find({name: "jia"}).count()
// 总共 6 条> db.users.find().count()6// 符合条件的共 1 条> db.users.find({name: "jia"}).count()1> db.users.find({name: /jia/}).count()4
db.users.findOne()
- 查询第一条
// 查询第一条> db.users.findOne(){ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231"}
项目目录划分程序那么复杂,不可能全写在一起,笔者做如下分层:
路由层
- 匹配路由,调用控制层控制层
- 取得前端请求数据,加工处理,比如调用数据库(services层),在返回数据给前端服务层
- 引用创建的数据库模型,对数据进行增删改查模型层
- 创建数据库模型(这里是创建表)以用户
模块为例,在这4层中分别创建如下 js:
- services // 服务层 - UserService.js // User 模块的服务层- routes // 路由 - UserRouter.js // User 模块的路由。比如登录、登录- models // 模型层 - UserModel.js // User Model- controllers // 控制层 - UserController.js
连接数据库前面我们操作数据库是直接进入 mongo shell 操作:
// 创建并切换到数据库 pjl_dbuse pjl_db// 创建 users 表(这里叫集合)db.createCollection("users")
现在我们需要通过 node 来操作数据库。
启动mongo服务通过 mongod --dbpath=/var/lib/mongodb --bind_ip=0.0.0.0 --port=27017
启动mongo服务(未设置数据库密码)。其中 0.0.0.0
用户远程连接,端口是 27017
。
如果需要后台启动(关闭终端服务也不会停止),需要指定日志路径,就像这样:
root@linux:/home/pjl# mongod --dbpath=/var/lib/mongodb --fork --logpath=/var/log/mongodb/mongodb.log --bind_ip=0.0.0.0 --port=27017about to fork child process, waiting until server is ready for connections.forked process: 133354child process started successfully, parent exiting
查看 mongod 进程:
root@linux:/home/pjl# ps aux |grep mongodroot 133354 7.5 1.5 976348 62496 ? Sl 09:46 0:00 mongod --dbpath=/var/lib/mongodb --fork --logpath=/var/log/mongodb/mongodb.log --bind_ip=0.0.0.0 --port=27017root 133383 0.0 0.0 12120 716 pts/0 S+ 09:47 0:00 grep --color=auto mongodroot@linux:/home/pjl#
Tip:还可以通过指定配置文件启动:
root@linux:/home/pjl# mongod -f /etc/mongodb.conf// 没反应,通过 `tail -10 日志` 能看到输出
另起窗口进入 mongo shell,运行 show dbs
报错如下:
// 笔者将配置文件中端口改为 27027root@linux:/var/log/mongodb# mongo --port=27027MongoDB shell version v3.6.8connecting to: mongodb://127.0.0.1:27027/Implicit session: session { "id" : UUID("dc184887-824d-474a-a942-3d42ff1a21bf") }MongoDB server version: 3.6.8> show dbs2023-04-21T09:52:04.824+0800 E QUERY [thread1] Error: listDatabases failed:{ "ok" : 0, "errmsg" : "there are no users authenticated", "code" : 13, "codeName" : "Unauthorized"} :_getErrorWithCode@src/mongo/shell/utils.js:25:13Mongo.prototype.getDBs@src/mongo/shell/mongo.js:67:1shellHelper.show@src/mongo/shell/utils.js:860:19shellHelper@src/mongo/shell/utils.js:750:15@(shellhelp2):1:1>
网友说是 安装mongo数据库时,配置文件中加了安全权限的设置
,解决请看这里
通过 npm i mongoose -D
安装 mongoose —— Mongoose is a MongoDB object modeling tool)。
通过 mongoose 操作数据库非常方便
连接数据库连接数据库很简单,只需要先启动数据库服务,然后在启动应用前连接数据库即可。代码如下:
// bin/www.jsvar http = require("http");+ // 引入数据库+ require("../config/db.config")var port = normalizePort(process.env.PORT || "3000");
// config/db.config.jsconst mongoose = require("mongoose")// mongo服务 ipmongoose.connect("mongodb://192.168.1.223:27017/pjl_db")mongoose.connection.once("open", () => { console.log("数据库连接成功")})
通过 mongo shell 查到目前只有3个数据库:
> show dbsadmin 0.000GBconfig 0.000GBlocal 0.000GB
启动应用,控制台输出 数据库连接成功
:
PS E:\spug-back-end> npm run start> spug-back-end@0.0.0 start> nodemon ./bin/www[nodemon] 2.0.22[nodemon] to restart at any time, enter `rs`[nodemon] watching path(s): *.*[nodemon] watching extensions: js,mjs,json[nodemon] starting `node ./bin/www`数据库连接成功
发现 pjl_db 数据库自动被创建。
> show dbsadmin 0.000GBconfig 0.000GBlocal 0.000GBpjl_db 0.000GB
Tip:有了数据库,还得需要表才能对数据进行增、删、改、查。在 mongoose 中,需要创建一个 model(模型),可以把他当做一张表(或一条记录),比如下文登录
模块的 UserModel.js
就是一个 model,向 model 插入一条数据时,mongoose 会自动创建一张名为 users 的表(或集合)。
这里我们完成系统登录模块的开发。
app.jsapp.js 引入用户路由。
// app.jsvar indexRouter = require("./routes/index");var usersRouter = require("./routes/users");+ const UserRouter = require("./routes/UserRouter")app.use("/users", usersRouter);+ app.use(UserRouter);
UserRouter.js定义登录路由(/user/login
),路由匹配成功,进入控制层处理。
// routes/UserRouter.js// 用户路由var express = require("express");var router = express.Router();const UserController = require("../controllers/UserController.js")/* POST users listing. */router.post("/user/login", UserController.login);module.exports = router;
UserController.js控制层调用服务层,如果数据库能通过用户名和密码查询到该用户则表明登录成功,否则返回用户名密码不匹配
。
// controllers/UserController.jsconst UserService = require("../services/UserService")const UserModel = require("../models/UserModel")const UserController = { login: async (req, res) => { // req.body - 例如 {"username":"pjl","password":"123456"} console.log("res.body", JSON.stringify(res.body)) var result = await UserService.login(req.body) if(result.length === 0){ res.send({ code: "-1", error: "用户名密码不匹配" }) }else{ res.send({ code: "0", error: "" }) } }}module.exports = UserController
UserService.js模型层通过 model 查找数据库
// services/UserService.jsconst UserModel = require("../models/UserModel.js")const UserService = { login: async ({username, password}) => { return UserModel.find({ username, password }) }}module.exports = UserService
UserModel.jsmongoose 通过 model 创建表。
// models/UserModel.js// model 与表一一对应const mongoose = require("mongoose")const Schema = mongoose.Schema// 通过 schema 限制一下集合(表),否则什么都能传入,太自由了不好const Usertype = { username: String, password: String, // 性别 gender: Number, // 头像 avatar: String, // 角色 role: Number, // 管理员1,编辑2}const UserModel = mongoose.model("user", new Schema(Usertype))module.exports = UserModel
测试这里笔者在 git bash 中使用 curl
(客户端的url) 模拟登录(post 用户名+密码):
Administrator@ ~/Desktop$ curl -X POST -d "username=pjl" -d "password=123456" http://localhost:3000/user/login % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed100 76 100 48 100 28 1425 831 --:--:-- --:--:-- --:--:-- 2533{"code":"-1","error":"用户名密码不匹配"}
由于 users 表中没有数据,当然也就查询不到(返回 {"code":"-1","error":"用户名密码不匹配"}
)。
笔者手动插入该条用户信息:
> use pjl_dbswitched to db pjl_db> db.users.save({username: "pjl", password: "123456"})WriteResult({ "nInserted" : 1 })
再次登录就能查询到 {"code":"0","error":""}
:
Administrator@ ~/Desktop$ curl -X POST -d "username=pjl" -d "password=123456" http://localhost:3000/user/login % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed100 51 100 23 100 28 1730 2106 --:--:-- --:--:-- --:--:-- 5100{"code":"0","error":""}
党的二十大报告提出:“健全共建共治共享...
构建新发展格局是把握未来发展主动权的战...
本届进博会上,以“智慧医疗构建健康管理...
流畅的轮廓、大气的车身、强劲的电动引擎...
作为一家新型实体企业,京东连续5年参与进...
在韩国,中国工商银行首尔分行联合使领馆...
图①:在进博会食品及农产品展区,工作人...
本报北京11月8日电全国人大常委会副委员长...
新华社北京11月8日电全国人大常委会副委员...
教育是国之大计、党之大计。习近平总书记...
习近平总书记在给“中国好人”李培生、胡...
越是深入了解新时代10年伟大变革,就越能...
夯实基础、补齐短板、凝聚合力,造就大批...
冰清玉洁、精金良玉、玉振金声、玉树临风...
一个个“人不负青山,青山定不负人”的故...
晨风吹拂。山东省费县费城街道王家庄村广...
本报嘉兴11月8日电(记者金歆、窦瀚洋)8...
作为世界互联网大会国际组织成立后的首届...
继续在线上举办国家展,为观众带来沉浸式...
新华社北京11月8日电学习贯彻党的二十大精...
新华社北京11月8日电(记者成欣)11月8日...
新华社北京11月8日电11月8日,国务委员兼...
“中国国际进口博览会为阿中两国提供了理...
第五届中国国际进口博览会上,处处可见绿...
新华社埃及沙姆沙伊赫11月8日电(记者姚兵...
“连续5年如期举办进博会,是中国践行互利...
举办进博会是中国主动向世界开放市场的重...
“不够喝还可以来添哦。”第五届进博会新...
全球首发、亚洲首展、中国首秀……第五届...
习近平主席在第五届中国国际进口博览会开...