MongoDB 공부 및 적용 : CRUD 구현 및 Transaction

정재헌·2023년 3월 28일
0

Database

목록 보기
4/6

MongoDB를 이용한 프로젝트를 진행하게 되어, MongoDB에 대한 공부를 함께 진행 중에 있다. 처음에는 좀 더 편하게 이해하기 위하여 MongoDB를 사용한 회사들의 기술 블로그를 바탕으로 공부를 하였으며, 이제는 좀 더 깊은 이해를 위해서 공식 문서를 바탕으로 블로그를 작성해보고자 한다.

첫 번째 MongoDB 블로그에서, MongoDB 기본 설정과 MongoDB의 장점들에 대허 간략하게 알아 보았다. 두 번째에는, MongoDB에서 이야기하는 좋은 데이터 모델링과 MongoDB 내 relation type인 embedding과 referencing에 대해서 알아보았다. 오늘 세 번째 시간에는, Node.js application에서의 MongoDB CRUD operation과 transaction에 대해 알아보고자 한다.

BSON

MongoDB CRUD에서 빠질 수 없는 내용이 바로 BSON이다. MongoDB는 JSON형태로 보이지만 실제 저장 시에는 BSON 형태로 저장된다고 알려져 있기 때문이다. BSON 형태로 저장되는 이유는 총 3가지의 장점이 있기 때문이다.

  • Optimized for storage, retrieval, and transmission across the wire.
  • More secure than plain text JSON. (JSON는 삽입 공격에 취약하다고 한다.)
  • More data types than JSON.

Inserting a document in Node.js applications

insertOne() / insertMany()

const dbname = "bank"
const collection_name = "accounts"
 
const accountsCollection = client.db(dbname).collection(collection_name)

const sampleAccount = {
 account_holder: "Linus Torvalds",
 account_id: "MDB829001337",
 account_type: "checking",
 balance: 50352434,
}

const main = async () => {
 try {
   await connectToDatabase()
   // insertOne method is used here to insert the sampleAccount document
   let result = await accountsCollection.insertOne(sampleAccount)
   console.log(`Inserted document: ${result.insertedId}`)
 } catch (err) {
   console.error(`Error inserting document: ${err}`)
 } finally {
   await client.close()
 }
}
 
main()
const dbname = "bank"
const collection_name = "accounts"
 
const accountsCollection = client.db(dbname).collection(collection_name)

const sampleAccounts = [
 {
   account_id: "MDB011235813",
   account_holder: "Ada Lovelace",
   account_type: "checking",
   balance: 60218,
 },
 {
   account_id: "MDB829000001",
   account_holder: "Muhammad ibn Musa al-Khwarizmi",
   account_type: "savings",
   balance: 267914296,
 },
]
 
const main = async () => {
 try {
   await connectToDatabase()
   let result = await accountsCollection.insertMany(sampleAccounts)
   console.log(`Inserted ${result.insertedCount} documents`)
   console.log(result)
 } catch (err) {
   console.error(`Error inserting documents: ${err}`)
 } finally {
   await client.close()
 }
}

main()

Querying a MongoDB collection in Node.js applications

find() / findOne() + query operator(help refine our search)

필터링은 네트워크, 디스크와 같은 서버 자원의 애플리케이션 사용을 최적화한다.

const dbname = "bank"
const collection_name = "accounts"
 
const accountsCollection = client.db(dbname).collection(collection_name)

// Document used as a filter for the find() method
const documentsToFind = { balance: { $gt: 4700 } }
 
const main = async () => {
 try {
   await connectToDatabase()
   // find() method is used here to find documents that match the filter
   let result = accountsCollection.find(documentsToFind)
   let docCount = accountsCollection.countDocuments(documentsToFind)
   await result.forEach((doc) => console.log(doc))
   console.log(`Found ${await docCount} documents`)
 } catch (err) {
   console.error(`Error finding documents: ${err}`)
 } finally {
   await client.close()
 }
}

main()
const dbname = "bank"
const collection_name = "accounts"
 
const accountsCollection = client.db(dbname).collection(collection_name)

// Document used as a filter for the findOne() method
const documentToFind = { _id: ObjectId("62a3638521a9ad028fdf77a3") }

const main = async () => {
 try {
   await connectToDatabase()
   // findOne() method is used here to find a the first document that matches the filter
   let result = await accountsCollection.findOne(documentToFind)
   console.log(`Found one document`)
   console.log(result)
 } catch (err) {
   console.error(`Error finding document: ${err}`)
 } finally {
   await client.close()
 }
}

main()

Updating documents in Node.js applications

updateOne() / updateMany() : update the value of an existing field. Or add a new field and a new value.

  • 배열 값의 필드를 업데이트 할 경우 : $push / $pull / $pop operator를 사용한다.
  • _id field is immutable and cannot be overwritten.
const dbname = "bank"
const collection_name = "accounts"

const accountsCollection = client.db(dbname).collection(collection_name)

const documentToUpdate = { _id: ObjectId("62d6e04ecab6d8e130497482") }

const update = { $inc: { balance: 100 } }


const main = async () => {
  try {
    await connectToDatabase()
    let result = await accountsCollection.updateOne(documentToUpdate, update)
    result.modifiedCount === 1
      ? console.log("Updated one document")
      : console.log("No documents updated")
  } catch (err) {
    console.error(`Error updating document: ${err}`)
  } finally {
    await client.close()
  }
}

main()
const database = client.db(dbname);
const bank = database.collection(collection_name);

const documentsToUpdate = { account_type: "checking" };

const update = { $push: { transfers_complete: "TR413308000" } }

const main = async () => {
  try {
    await connectToDatabase()
    let result = await accountsCollection.updateMany(documentsToUpdate, update)
    result.modifiedCount > 0
      ? console.log(`Updated ${result.modifiedCount} documents`)
      : console.log("No documents updated")
  } catch (err) {
    console.error(`Error updating documents: ${err}`)
  } finally {
    await client.close()
  }
}

main()

Deleting documents in Node.js applications

deleteOne() / deleteMany()

삭제된 document 수와 같은 정보를 검색할 수 있는 결과를 object로 return

const dbname = "bank"
const collection_name = "accounts"

const accountsCollection = client.db(dbname).collection(collection_name)

const documentToDelete = { _id: ObjectId("62d6e04ecab6d8e13049749c") }

const main = async () => {
  try {
    await connectToDatabase()
    let result = await accountsCollection.deleteOne(documentToDelete)
    result.deletedCount === 1
      ? console.log("Deleted one document")
      : console.log("No documents deleted")
  } catch (err) {
    console.error(`Error deleting documents: ${err}`)
  } finally {
    await client.close()
  }
}

main()
const dbname = "bank"
const collection_name = "accounts"

const accountsCollection = client.db(dbname).collection(collection_name)

const documentsToDelete = { balance: { $lt: 500 } }

const main = async () => {
 try {
   await connectToDatabase()
   let result = await accountsCollection.deleteMany(documentsToDelete)
   result.deletedCount > 0
     ? console.log(`Deleted ${result.deletedCount} documents`)
     : console.log("No documents deleted")
 } catch (err) {
   console.error(`Error deleting documents: ${err}`)
 } finally {
   await client.close()
 }
}
 
main()

Creating MongoDB transactions in Node.js applications

Transactions
commit or rollback, 모두 성공 또는 모두 실패 (=Atomicity)
ex) 금융 거래, 온라인 쇼핑 카트 등

RDBMS뿐만 아니라 NoSQL인 MongoDB에서도 transaction 기능을 사용할 수 있다.

  • ACID(Atomicity, Consistency, Isolation, Durability) : 데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 성질을 가리키는 약어.
  • isolation levels in transaction: 동시에 여러 트랜잭션이 실시될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 허용할지 말지를 결정할 수 있다.

Transaction 과정

  • Start a client session
  • Define the transaction options (optional)
  • Define the sequence of operations to perform inside the transactions
  • Release resources used by the transaction

*Multi-document transactions have a 60-second time limit.

  1. Create variables used in the transaction.
// Collections
const accounts = client.db("bank").collection("accounts")
const transfers = client.db("bank").collection("transfers")

// Account information
let account_id_sender = "MDB574189300"
let account_id_receiver = "MDB343652528"
let transaction_amount = 100
  1. Start a new session.
const session = client.startSession()
  1. Begin a transaction with the WithTransaction() method on the session.
const transactionResults = await session.withTransaction(async () => {
  // Operations will go here
})
  1. Update the balace field of the sender's account by decrementing the transaction_amount from the balance field.
const senderUpdate = await accounts.updateOne(
  { account_id: account_id_sender },
  { $inc: { balance: -transaction_amount } },
  { session }
)
  1. Update the balance field of the receiver's account by incrementing the transaction_amount to the balance field.
const receiverUpdate = await accounts.updateOne(
  { account_id: account_id_receiver },
  { $inc: { balance: transaction_amount } },
  { session }
)
  1. Create a transfer document and insert it into the transfers collection.
const transfer = {
  transfer_id: "TR21872187",
  amount: 100,
  from_account: account_id_sender,
  to_account: account_id_receiver,
}

const insertTransferResults = await transfers.insertOne(transfer, { session })
  1. Update the transfers_complete array of the sender's account by adding the transfer_id to the array.
const updateSenderTransferResults = await accounts.updateOne(
  { account_id: account_id_sender },
  { $push: { transfers_complete: transfer.transfer_id } },
  { session }
)
  1. Update the transfers_complete array of the receiver's account by adding the transfers_id to the array.
const updateReceiverTransferResults = await accounts.updateOne(
  { account_id: account_id_receiver },
  { $push: { transfers_complete: transfer.transfer_id } },
  { session }
)
  1. Log a message regarding the success or failure of the transaction.
if (transactionResults) {
  console.log("Transaction completed successfully.")
} else {
  console.log("Transaction failed.")
}
  1. Catch any errors and close the session.
} catch (err) {
  console.error(`Transaction aborted: ${err}`)
  process.exit(1)
} finally {
  await session.endSession()
  await client.close()
}
profile
백엔드 개발자

0개의 댓글