BETA

インサート機能追加 - nodejsからmongodbの操作を少しづつ改良してみる

投稿日:2018-12-07
最終更新:2018-12-07
  • 前回の記事はこちらです。

  • 前回の記事で、名前や年齢、体重、身長別のリストが取りだせるようになったのですが、今回は、追加機能の実装です。

  • 現状は、一度に追加するか、全部削除するという大雑把な処理だったのですが、一件づつ追加する機能を追加したいと思います。

  • 今回は、複数の値をやり取りするため、ブラウザー用のhttpクライアント fetch を使います。

  • fetch については、使う機能だけの説明になりますので、以前の記事も参考にしてもらえたらと思います。
  • 流れとしては、追加用の画面からデータを入力して、fetchクライアントを通して、expressサーバーのURLスイッチ /api/insertonepost にデータを渡して monogoコレクションメソッドで処理をします。

  • 1件追加用のコレクションメソッドは、まだないのでコレクションメソッドから作って行きます。

  • ついでに、コレクションメソッドをコールバック仕様から、asyncを使うタイプに書き直ししました。

  • libAsync.js

const assert = require('assert')  
const { state } = require('../config.js')  
const colName = 'documents'  

if ( state.debug ) console.info('-- libAsync log --')  


module.exports.deleteManyDocuments = async(db) => {  
  const collection = db.collection(colName)  
  return await collection.deleteMany({})  
}  

module.exports.insertManyDocuments = async(db) => {  
  const collection = db.collection(colName)  
  return await collection.insertMany([  
    { name: 'toto', age: 25, weight: 65, height: 176 },  
    { name: 'momo', age: 27, weight: 50, height: 163 },  
    { name: 'koko', age: 25, weight: 87, height: 189 },  
    { name: 'nattou', age: 25, weight: 47, height: 110 },  
    { name: 'hotaru', age: 25, weight: 65, height: 179 },  
    { name: 'takuma', age: 55, weight: 78, height: 183 },  
  ])  
}  

module.exports.findDocuments = async(db) => {  
  const collection = db.collection(colName)  
  return await collection  
    .find({})  
    .project({ '_id': 0})  
    .limit(100)  
    .toArray()  
}  

module.exports.searchDocuments = async({ db, findsData }) => {  
  const collection = db.collection(colName)  
    const value = Object.entries(findsData)[0][1]  
    const key   = Object.entries(findsData)[0][0]  
    const fixFindsData = {}  
    const type = fixFindsData[key] = (value - 0)  
    // console.log('NaN or integer: ', type)  
    const input = type ? fixFindsData : findsData  
  return await collection  
        .find(input)  
        .project({ '_id': 0})  
        .limit(100)  
        .toArray()  

}  

module.exports.insertOneDocumentPost= async({ db, insertOneData }) => {  
    const collection = db.collection(colName)  
    const arrayInsertOneData = Object.entries(insertOneData)  
  const fixInsertOneData = {}  
  for(let item of arrayInsertOneData) {  
    key = item[0]  
    value = item[1]  
    fixInsertOneData[key] = (value - 0)  
  }  
  delete fixInsertOneData.name  
  const input = { ...insertOneData, ...fixInsertOneData }  

    if (state.debug) {  
    console.log('input: ', input)  
    }  

  return collection.insertOne( input )  
}  
  • 抜粋です。
module.exports.insertOneDocumentPost= async({ db, insertOneData }) => {  
    const collection = db.collection(colName)  
    const arrayInsertOneData = Object.entries(insertOneData)  
  const fixInsertOneData = {}  
  for(let item of arrayInsertOneData) {  
    key = item[0]  
    value = item[1]  
    fixInsertOneData[key] = (value - 0)  
  }  
  delete fixInsertOneData.name  
  const input = { ...insertOneData, ...fixInsertOneData }  

    if (state.debug) {  
    console.log('input: ', input)  
    }  

  return collection.insertOne( input )  
}  
  • 今回の1件追加の機能の心臓部分で、insertOneData をオブジェクトリテラルの形で受け取り、insertOneコレクションメソッドに渡していますが、受け取るデータは数字が文字扱いになってしまうため、検索時のときの同じような処理をしていますが、今回はオブジェクトなので、オブジェクトの結合で処理しています。 結構手間かかるので、次の実装でも手間だったら、データはすべて文字列だったという、仕様変更をしたいと思います(笑)

  • fetch からのデータを処理するのは、index.js です。

  • index.js

var express = require('express');  
var router = express.Router();  
const MongoClient = require('mongodb').MongoClient  
const assert = require('assert')  
const { state } = require('../config.js')  

const url = 'mongodb://localhost:27017/myproject'  
const dbName = 'myproject'  

const client = new MongoClient(url)  
const {  
  insertOneDocumentPost,  
  insertManyDocuments,  
  findDocuments,  
  searchDocuments,  
  deleteManyDocuments,  
} = require('./libAsync.js')  
// } = require('./libCallback.js')  

if ( state.debug ) {  
  console.log('-- index.js log --')  
}  





client.connect(err => {  
    assert.equal(null, err)  
    console.log("Connected successfully to server")  
  const db = client.db(dbName)  
  let result = null  

  // test api  
  router.get('/api/gettest', (req, res) => {  
    console.log(req.query)  
    res.send(req.query)  
  })  
  router.post('/api/posttest', (req, res) => {  
    console.log(req.body)  
    res.send(req.body)  
  })  

  // html  
  router.get('/', function(req, res, next) {  
    console.log(req.query)  
    res.render('index', { title: 'mongodb Tool' });  
  });  

  router.get('/user_insert', (req, res, next) => {  
    res.render('user_insert' )  
  })  

  router.get('/user_search', (req, res, next) => {  
    res.render('user_search' )  
  })  

  // api  
    router.get('/api/deletemany', (req, res) => {  
    console.log(req.query)  
    deleteManyDocuments(db)  
      .then(result => {  
        console.log(result.result)  
      })  
      .catch(e => console.error(e.message))  
        res.send('delete done')  
  })  

    router.post('/api/insertonepost', (req, res) => {  
    const insertOneData = req.body  
    if (state.debug) {  
      console.log('api/insertonepost: ', insertOneData)  
    }  
    insertOneDocumentPost({ db, insertOneData })  
      .then(result => {  
        console.log(result.result)  
      })  
      .catch(e => console.error(e.message))  

  })  

    router.get('/api/insertmany', (req, res) => {  
    console.log(req.query)  
    insertManyDocuments(db)  
      .then(result => {  
        console.log('api/insertmany result: ', result.resul)  
      })  
      .catch(e => console.error(e.message))  
        res.send('insertmany done')  
  })  

    router.get('/api/find', (req, res) => {  
    findDocuments(db)  
      .then(docs => {  
        if(state.debug) console.log('api/find: result ', docs)  
        res.render('user_list', { userList: docs })  
      })  
      .catch(e => console.error(e.message))  

  })  

    router.get('/api/search', (req, res) => {  
        const findsData = req.query  
    console.log(req.query)  
    searchDocuments({db, findsData })  
      .then(docs => {  
        if (state.debug) console.log('api/search: ', docs)  
        res.render('user_list', { userList: docs})  
      })  
      .catch(e => console.error(e.message))  
  })  

})  


module.exports = router;  
  • 抜粋
router.get('/user_insert', (req, res, next) => {  
  res.render('user_insert' )  
})  

router.post('/api/insertonepost', (req, res) => {  
  const insertOneData = req.body  
  if (state.debug) {  
    console.log('api/insertonepost: ', insertOneData)  
  }  
  insertOneDocumentPost({ db, insertOneData })  
    .then(result => {  
      console.log(result.result)  
    })  
    .catch(e => console.error(e.message))  
})  
  • /user_insert は、入力画面ようです。

  • /api/insertonepost は、html側 fetchクライアントのデータ受け渡し場所です。
  • 受け取ったデータを、先程作った insertOneDocument 関数で処理します。

  • fetch 側を作る前に、expressサーバーで処理できることを確認します。

  • 今回は、GETリクエストではなく、POSTリクエストを使うため、ブラウザーのURL欄は使えません。 そのためターミナル用のhttpクライアントを使います。
  • ターミナルでは、curl が有名ですが、初心者には少し扱いづらいので代わりに HTTPie を使います。 HTTPie 検索して入れてみてください。

  • テスト用の /api/posttest を用意していますので、そこで 動作確認をしてみます。

http POST http://localhost:3000/api/posttest name=Pie  age:=23 weight:=46 height:=167  
HTTP/1.1 200 OK  
Connection: keep-alive  
Content-Length: 48  
Content-Type: application/json; charset=utf-8  
Date: Thu, 06 Dec 2018 13:30:07 GMT  
ETag: W/"30-Yj1oW0CPhF/4wXdIqv4fP0rVcd4"  
X-Powered-By: Express  

{  
    "age": 23,  
    "height": 167,  
    "name": "Pie",  
    "weight": 46  
}  
  • JSON形式でデータが帰って来ています。 問題なさそうなので 本番用の /api/insertonepost に投げてみます。
http POST http://localhost:3000/api/insertonepost name=pie  age:=23 weight:=46 height:=167  
  • リスト画面で確認すると、正常に書き込まれています。

  • サーバー側は、出来ましたので、クライアント側を作って行きます。
  • user_insert.pug

extends layout  

block content  
  h1 UserInsertPage  

  .container  
    .insert-area  
      label Name :  
        input.user_insert_input#userInsertName(type="text")  
      label Age :  
        input.user_insert_input#userInsertAge(type="number")  
      label Height :  
        input.user_insert_input#userInsertHeight(type="number")  
      label Weight :  
        input.user_insert_input#userInsertWeight(type="number")  
    #btn.btn.insertbtn Insert  
    #resetBtn.btn.insertbtn Reset  
    #userInsertResult.result  
  script(src="./javascripts/userInsert.js")  
  • userInsert.js
document.addEventListener('DOMContentLoaded', () => {  
  'use strict'  

  console.log('-- userInsertjs log --')  

  const userInsertName = document.getElementById('userInsertName')  
  const userInsertAge = document.getElementById('userInsertAge')  
  const userInsertWeight = document.getElementById('userInsertWeight')  
  const userInsertHeight = document.getElementById('userInsertHeight')  
  const btn = document.getElementById('btn')  
  const resetBtn = document.getElementById('resetBtn')  
  const userInsertResult = document.getElementById('userInsertResult')  

  resetBtn.addEventListener('click', () => {  
        userInsertResult.textContent = ''  
        userInsertName.value = ''  
        userInsertAge.value = ''  
        userInsertHeight.value = ''  
        userInsertWeight.value = ''  

  })  

  btn.addEventListener('click', () => {  

    const name = userInsertName.value  
    const age = Number(userInsertAge.value)  
    const weight = Number(userInsertWeight.value)  
    const height = Number(userInsertHeight.value)  


    const objData = {  
      name: name,  
      age: age,  
      height: height,  
      weight: weight  
    }  
    const data = new URLSearchParams(objData)  
    console.log('objData: ', objData)  
    console.log('URLserchParams data: ', data)  

    const options = {  
      method: 'POST',  
      body : data,  
    }  

    const main = async() => {  
      try {  
        let res = await fetch('http://localhost:3000/api/insertonepost', options)  
        if (!res.ok) throw new Error(response.statusText)  
        console.log('res: ', res)  

      } catch(e) {  
        console.error('エラー', e.message)  
      }  
    }  
    main()  
        userInsertResult.textContent = 'Done!'  



    // fetch('http://localhost:3000/api/insertonepost', options)  
    //   .then(response => {  
    //     if (!response.ok) {  
    //       throw Error(response.statusText)  
    //     }  
    //     return response  
    //   })  
    //   .then(response => {  
    //     console.log(response)  
    //   })  
    //   .catch(err => console.error(err))  

  })  

})  
  • main.js でまとめて書くのをやめて、画面ごとにJSファイルを書くように改めました。
  • fetch がプロミス仕様なので、async 風に記載しました。
  • 送る前に、Number関数で数字にしてから、オブジェクトリテラルにして送っていますが、expressサーバー側では、文字列になってました....
├── app.js  
├── bin  
│ └── www  
├── config.js  
├── package.json  
├── public  
│ ├── images  
│ ├── javascripts  
│ │ ├── main.js  
│ │ ├── userInsert.js  
│ │ └── userSearch.js  
│ └── stylesheets  
│     ├── style.css  
│     ├── style.css.map  
│     └── style.sass  
├── routes  
│ ├── index.js  
│ ├── libAsync.js  
│ ├── libCallback.js  
│ └── users.js  
├── views  
│ ├── _menu.pug  
│ ├── error.pug  
│ ├── index.pug  
│ ├── layout.pug  
│ ├── user_find.pug  
│ ├── user_insert.pug  
│ ├── user_list.pug  
│ └── user_search.pug  
└── yarn.lock  
  • 他にも、ちょっとずつ手直ししていますが、githubのコミットで確認してもらえればと思います。

  • 今回は、ここまでにしたいと思います。

  • あとは、修正と個別削除ができるようにしたいですね。

  • github からの差分です。

  • コミット: step03.001
git reset --hard d7295d90  
git pull  
git reset --hard 0fc5ec90f  
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@atoris1192の技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!