MongoDB를 이용한 프로젝트를 진행하게 되어, MongoDB에 대한 공부를 함께 진행 중에 있다. 처음에는 좀 더 편하게 이해하기 위하여 MongoDB를 사용한 회사들의 기술 블로그를 바탕으로 공부를 하였으며, 이제는 좀 더 깊은 이해를 위해서 공식 문서를 바탕으로 블로그를 작성해보고자 한다.
첫 번째 MongoDB 블로그에서, MongoDB 기본 설정과 MongoDB의 장점들에 대허 간략하게 알아 보았다. 두 번째에는, MongoDB에서 이야기하는 좋은 데이터 모델링과 MongoDB 내 relation type인 embedding과 referencing에 대해서 알아보았다. 오늘 세 번째 시간에는, Node.js application에서의 MongoDB CRUD operation과 transaction에 대해 알아보고자 한다.
MongoDB CRUD에서 빠질 수 없는 내용이 바로 BSON이다. MongoDB는 JSON형태로 보이지만 실제 저장 시에는 BSON 형태로 저장된다고 알려져 있기 때문이다. BSON 형태로 저장되는 이유는 총 3가지의 장점이 있기 때문이다.
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()
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()
updateOne() / updateMany() : update the value of an existing field. Or add a new field and a new value.
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()
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()
Transactions
commit or rollback, 모두 성공 또는 모두 실패 (=Atomicity)
ex) 금융 거래, 온라인 쇼핑 카트 등
RDBMS뿐만 아니라 NoSQL인 MongoDB에서도 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.
// 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
const session = client.startSession()
const transactionResults = await session.withTransaction(async () => {
// Operations will go here
})
const senderUpdate = await accounts.updateOne(
{ account_id: account_id_sender },
{ $inc: { balance: -transaction_amount } },
{ session }
)
const receiverUpdate = await accounts.updateOne(
{ account_id: account_id_receiver },
{ $inc: { balance: transaction_amount } },
{ session }
)
const transfer = {
transfer_id: "TR21872187",
amount: 100,
from_account: account_id_sender,
to_account: account_id_receiver,
}
const insertTransferResults = await transfers.insertOne(transfer, { session })
const updateSenderTransferResults = await accounts.updateOne(
{ account_id: account_id_sender },
{ $push: { transfers_complete: transfer.transfer_id } },
{ session }
)
const updateReceiverTransferResults = await accounts.updateOne(
{ account_id: account_id_receiver },
{ $push: { transfers_complete: transfer.transfer_id } },
{ session }
)
if (transactionResults) {
console.log("Transaction completed successfully.")
} else {
console.log("Transaction failed.")
}
} catch (err) {
console.error(`Transaction aborted: ${err}`)
process.exit(1)
} finally {
await session.endSession()
await client.close()
}