mongoose(Node.js上でMongoDBを扱うためのライブラリ)を使うときに少し困ったので、二つのサイトを参考にして良いとこ取りをしてみました。すべてのコードを書くと、ちょっとしたwebアプリが出来上がります。ご指摘があればコメント欄にお願いします。
1.準備
VScodeを使いました。軽くて機能も豊富なのでお勧めです。
準備は以下の通りです。無い場合も簡単に入れられます。
- mongoose
- MongoDB
- node.js
VScodeを使う場合、拡張機能は以下のものを入れておきます。
- EJS language support
- ESLint
- MongoDB for VS Code
2.構成
VScodeのexpress-generatorというツールを使うと、Expressのアプリケーションの構造の原型が自動生成されるのでそこにフォルダ、ファイルを追加していきます。次のようなコードで生成します。
>npm install-g express-generator
>express --view="ejs" mongoImplement
>cd mongoImplement
mongoImplement>npm installmongoImplement>npm install--save mongodb mongoose
Image may be NSFW.
Clik here to view.
このような感じです。重要なのは5つのJSファイル(routes/users.jsは今回は使いません)と2つのejsファイルです。
3.コード
7つのファイルを載せます。分かりづらいかもしれませんがVScode上に並べると明快なはずです。。
データベースへの接続方法が少しややこしいのですが、規模の大きめなアプリを作る場合には整理整頓されていてわかりやすいってなると思いました。ときどき上のディレクトリ構造を見ながら読んでみてください。
module.exports={url:"mongodb://localhost:27017/omochi"};/*
27017はMngoDBのデフォルトのポート番号。何もいじってなければ27017で大丈夫です。
その下にデータベース名を書きます(データベースが無くても、名付けるだけで勝手に作成されます)。
エクスポート(exports)することにより、ほかの場所でも使えるようになります。
*/
constdbConfig=require("../config/db.config.js");//上のファイルのモジュールを読み込み。相対パスはお手元の構造に合うように指定してください。constmongoose=require("mongoose");//mongooseを読み込みmongoose.Promise=global.Promise;/*
非同期処理にpromiseを使います。
Promiseについてはhttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
*/constdb={};db.mongoose=mongoose;db.url=dbConfig.url;module.exports=db;//dbをエクスポート。これで他の場所でも使えます
以上で、「db」はお手元のMongoDBのデータベース(この例だとomochi)に接続されることになります。
つづいてマスターファイルです。
constexpress=require('express');//constとvarはどちらでもOKconstpath=require('path');constindexRouter=require('./routes/index');constusersRouter=require('./routes/users');//ルーティング設定のため、インポート。最後の2行と関連があります。詳しくはhttps://expressjs.com/ja/guide/routing.htmlconstapp=express();constdb=require("./models");//先ほどのファイルでエクスポートしたdbdb.mongoose.connect(db.url,{//urlはdb.config.jsでエクスポートした、お手元のMongoDBのデータベースへのパスuseNewUrlParser:true,//omajinaiuseUnifiedTopology:true//omajinai}).then(()=>{console.log("Connected to the database!");//接続に成功するとシェルに表示されます}).catch(err=>{console.log("Cannot connect to the database!",err);//接続に失敗するとシェルに表示されますprocess.exit();});//then,catchは非同期処理の成功時、失敗時に呼び出される関数です。詳しく「promise then catch」等で検索// view engine setupapp.set('views',path.join(__dirname,'views'));app.set('view engine','ejs');//app.useは、動的なページを作成するにあたって遷移先のファイルが何であれ実行してほしいときに使います。app.use(logger('dev'));app.use(express.json());app.use(express.urlencoded({extended:false}));app.use(express.static(path.join(__dirname,'public')));//この辺の設定はexpress-generatorで自動生成。app.use('/',indexRouter);app.use('/users',usersRouter);//ルーターは機能ごとにファイルを分けるのに役立ちます。module.exports=app;
これでどのファイルでもデータベースに接続できるようになりました。
続いてmongooseの特性の一つであるスキーマ定義を行います。
constmongoose=require('mongoose');constSchema=mongoose.Schema;constclientSchema=newSchema({userName:{type:String,required:true},email:{type:String,required:true},//requiredはバリデータ。空文字をエラーにしたい場合に使います。},{timestamps:true,//ある値が生成された時間を記録するもの。記録したい場合に書きます。});constClient=mongoose.model('Client',clientSchema);/*Clientモジュールを定義。clientSchemaというスキーマモデルを呼び出し、"clients"というコレクションが出来上がります。
('Client'の複数形?勝手にclientsになってました。コレクションという名前はNoSQLでの用語で、MySQLなどのRDBでいうテーブルにあたります。)
*/module.exports=Client;
スキーマ定義をまとめてファイルに書いておくと開発などもしやすそうです。
routes/indexファイルを書きます。indexファイルは先ほどのapp.jsファイルでルーティング設定していることを思い出しましょう。
constexpress=require('express');constrouter=express.Router();constClient=require('../models/models');//Clientのスキーマモデルをインポート/* GET home page. */router.get('/',function(req,res,next){res.render('newuser',{title:'Add New User'});// views/newuser.ejsのtitleに文字を代入。});/* GET New User page. */router.get('/newuser',function(req,res){//redirect用res.render('newuser',{title:'Add New User'});});/* GET Userlist page. */router.get('/userlist',function(req,res){Client.find()//最初にインポートしておくとこれだけで使えます.then(docs=>res.render('userlist',{"userlist":docs})).catch(err=>res.send("There was a problem adding the information to the database."));});/*
引数docsはfindした検索結果です。適当な単語で大丈夫ですが、行の最後の単語と同じにします。
res.renderの中の('userlist',{"userlist" : docs} )は、userlist.ejs(一つ目の引数)に書かれたuserlist(二つ目の引数)にdocsを代入するという意味です。
*//* POST to Add User Service */router.post('/adduser',function(req,res){constuserName=req.body.username;//フロントで<input>を使うとreq.body.nameに格納されます。nameは<input>内のname="username"の箇所constemail=req.body.useremail;// Submit to the DBconstnewClient=newClient({userName,email,});/*
↑ がmongooseを使うときのデータの追加方法です。スキーマ定義してあるのでこれだけでいいのですね。
mongooseを使わない場合と違い、データ追加の命令をコールバック関数の外に書くのがポイントです。
*/newClient.save().then(()=>res.redirect('userlist')).catch(err=>res.send("There was a problem adding the information to the database."));});module.exports=router;
最後にフロントを書きます。
<!DOCTYPE html>
<html>
<head>
<title>Add User</title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<form id="formAddUser" name="adduser" method="post" action="/adduser">
<input id="inputUserName" type="text" placeholder="username" name="username" />
<input id="inputUserEmail" type="text" placeholder="email" name="useremail" />
<button id="btnSubmit" type="submit">Submit</button>
</form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>User List</title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1>User List</h1>
<ul>
<%
var list = '';
for (i = 0; i < userlist.length; i++) {
list += '<li><a href="mailto:' + userlist[i].email + '">' + userlist[i].userName + '</a></li>';
}
%>
<%- list %>
</ul>
</body>
</html>
「<%-」と「<%=」は、htmlの要素として出力したいか、文字として出力したいかで使い分けます
最後に以下のコードで実行し、ブラウザでhttp://localhost:3000
などで検索すると二つのページを行き来できます(動作確認済み)。入力した内容はMongoDBにたまってゆきます。MongoDBCompassを使うと、視覚的に分かりやすくデータが表示されます(MySQLのようなテーブル型で、等)。
mongoImplement>npm start
以上でコードは終わりです。お疲れさまでした。MongoDB,Expressに加えてReactやpandas,scikit-learnを使うと本格的なアプリ開発になります。