+5

Mongo đại cương

1. Cài đặt MongoDB

Trên window

Truy cập vào url: https://www.mongodb.com/try/download/community

  • Phiên bản Community là phiên bản dành cho cộng đồng miễn phí
  • Version: 5.0.2(current) - tại thời điểm tháng 9/2021
  • Platform: Windows
  • Package: msi

cài đặt mongoDB community server

Trên Ubuntu

// Cập nhật phiên bản phần mềm
sudo apt update

// Cài mongoDB
sudo apt install mongodb

// Kiểm tra mongoDB cài được chưa?
sudo systemctl status mongodb

2. Kiểm tra mongoDB

// Đối với window khi chưa cài biến môi trường,
// thì ta phải vào tận nơi cài đặt, mở cmd tại đây
C:\Program Files\MongoDB\Server\5.0\bin

// Khởi động mongoDB
mongo

// Kết quả
MongoDB shell version v5.0.2
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("4246f2ac-0933-47cd-9e7c-09b8730ed98e") }
MongoDB server version: 5.0.2
// Thoát mongoDB
exit

// Kết quả
bye

3. Thao tác với database

3-1. Xem danh sách

show dbs

// Kết quả
admin         0.000GB
config        0.000GB
local         0.000GB

3-2. Tạo DB mới hoặc sử dụng DB có sẵn

use <new_database>

// Ví dụ
use shopping_app

// Kết quả
switched to db shopping_app

3-3. Xem đang sử dụng DB nào

db

// Kết quả
shopping_app

3-4. Xóa DB

use <db_name>
db.dropDatabase()

// Ví dụ
use shopping_app
db.dropDatabase()

// Kết quả
> use shopping_app
switched to db shopping_app
> db.dropDatabase()
{ "ok" : 1 }

3-5. Import DB từ CSV

Kể từ Version 4.4.0, bạn phải cài đặt thêm MongoDB Database Tools để import, export data.

Link hướng dẫn: https://docs.mongodb.com/database-tools/installation/installation/

Link cài đặt: https://www.mongodb.com/try/download/database-tools

mongodb-tool.jpg

// Đối với window khi chưa cài biến môi trường,
// thì ta phải vào tận nơi cài đặt, mở cmd tại đây
C:\Program Files\MongoDB\Tools\100\bin

mongoimport --type csv -d <db_name> -c <collection_name> --headerline --drop /path/to/file.csv

--type: kiểu là csv
-d: tên database
-c: tên collection
--headerline: bỏ đi dòng đầu của file csv
--drop: nếu database đã tồn tại thì xóa và tạo mới
/path/to/file.csv: đường dẫn tới file csv

// Ví dụ
mongoimport --type csv -d shopping_app -c users --headerline --drop D:\backup\db-shopping-app.csv

3-6. Import DB từ JSON

mongoimport --type json -d <db_name> -c <collection_name> --drop /path/to/file.json

// Ví dụ
mongoimport --type json -d shopping_app -c users --drop D:\backup\db-shopping-app.json

3-7. Export DB

mongodump --db <db_name> --out /path/to
mongodump --db <db_name> --out /path/to --gzip

--out: chỗ lưu database

// Ví dụ
mongodump --db shopping_app --out D:\backupDB
mongodump --db shopping_app --out D:\backupDB --gzip

4. Collection

4-1. Xem collections

show collections

4-2. Thêm collection

db.category.insert({name: 'Laptop'})
// Hoặc
db.createCollection('Laptop')

// Kết quả
{ "ok" : 1 }

4-3. Sửa collection

db.<collection_name>.renameCollection('new_name')

// Ví dụ
db.users.renameCollection('Users')

// Kết quả
{ "ok" : 1 }

4-4. Xóa collection

db.<collection_name>.drop()

// Ví dụ
db.users.drop()

// Kết quả
true

4-5. Export collection ra json

mongoexport --db <db_name> --collection <collection_name> --out /path/to
// Ngắn gọn
mongoexport -d <db_name> -c <collection_name> -o /path/to


// Ví dụ
mongoexport -d shpping_app -c users -o "D:\backupDB\collectionUsers.json"

4-6. Export collection ra csv

mongoexport -d <db_name> -c <collection_name> --type csv -o /path/to -f id,first_name,last_name

-f: các field muốn export

// Ví dụ
mongoexport -d shopping_app -c users --type csv -o "D:\backupDB\collectionUsers.json" -f id,first_name,last_name

4-7. Import collection từ json

mongoimport -d <db_name> -c <collection_name> --file /path/to/file.json

4-8. Import collection từ csv

mongoimport --d <db_name> -c <collection_name> --type csv --file /path/to/file.csv

4-9. Schema validation

db.createCollection('courses', {
  validator: {
    $jsonSchema: {
      bsonType: 'object', // binary json (javascript object notation)
      title: 'Validate Course Object',
      properties: {
        title: {
          bsonType: 'string',
          description: 'Title must be string'
        },
        hours: {
          bsonType: 'int',
          minimum: 3,
          maximum: 100,
          description: 'Hours must be between 3 and 100'
        },
        startDate: {
          bsonType: ['date', 'string'],
          description: 'Incorrect date type'
        },
        price: {
          bsonType: ['int', 'double'],
          description: 'Price must be a number'
        }
      }
    }
  }
})
// Xem thông tin validation
db.getCollectionInfos({ name: 'courses' })
// Update schema validation
db.runCommand({
  collmod: 'course', // collection modify
  validator: {
    $jsonSchema: {
      bsonType: 'object',
      properties: {
        hours: {
          bsonType: 'int',
          minimum: 1,
          maximum: 50,
          description: 'Hours must be between 1 and 50'
        }
      }
    }
  }
})

5. Query data

db.<collection_name>.find(query)
db.<collection_name>.findOne(query)

collection_name: tên của collection
find: trả về tất cả dữ liệu
findOne: trả về dữ liệu đầu tiên
query: là một object mô tả bên dưới

/*==============================*/
find({}) // tìm tất cả

{key: value} // So sánh bằng
{age: 69} // tìm với tuổi bằng 69

{key: {$ne: value}} // So sánh không bằng
{age: {$ne: 69}} // tìm với tuổi khác 69

{key: {$gt: value}} // Nhỏ hơn
{age: {$gt: 69}} // tìm với tuổi nhỏ hơn 69

{key: {$gte: value}} // Nhỏ hơn bằng
{age: {$gte: 69}} // tìm với tuổi nhỏ hơn bằng 69

{key: {$lt: value}} // Lớn hơn
{age: {$lt: 69}} // tìm với tuổi lớn hơn 69

{key: {$lte: value}} // Lớn hơn bằng
{age: {$lte: 69}} // tìm với tuổi lớn hơn bằng 69

{key: {$in: value}} // Nằm trong những giá trị này
{age: {$in: [36, 49, 50]}} // tìm với tuổi bằng 36-49-50

{key: {$nin: value}} // Không nằm trong những giá trị này
{age: {$nin: [36, 49, 50]}} // tìm với tuổi khác 36-49-50

{key: value} // Áp dụng được với regex

{email: /\.gmail$/} // tìm với email kết thúc bằng .gmail
{ip_address: /^1/} // tìm với ip bắt đầu bằng 1
// Show cho đẹp
db.<collection_name>.find(query).pretty()

// Đếm số bản ghi
db.<collection_name>.find(query).count()

// Trả về bản ghi đầu tiên
db.<collection_name>.find(query).limit(1)
db.<collection_name>.find(query)[0]

6. Multi-field query

Điều kiện AND

db.<collection_name>.find(
  { key1: value1, key2: value2 }
)

// tìm với điều kiện giới tính là 'Male' và tuổi lớn hơn 69
{gender: 'Male', age: {$lt: 69}}

Điều kiện OR

db.<collection_name>.find({ 
  $or: [
    { key1: value1 },
    { key2: value2 }
  ]
})

// tìm với điều kiện giới tính là 'Male' hoặc tuổi lớn hơn 69
{
  $or: [
    { gender: 'Male' },
    { age: {$lt: 69} }
  ]
}

7. Nested object

Cách tìm khi value dạng object

// document trong mongoDB
{
  ...
  profile: {
    city: 'Hanoi',
    work: '2B Company'
  }
}

// tìm với điều kiện profile city là Hanoi
db.<collection_name>.find({ profile.city: 'Hanoi' })

8. Array field

Cách tìm khi value dạng array

// document trong mongoDB
{
  ...
  languages: ['English', 'Japanese', 'Vietnamese']
}

// tìm với điều kiện languages là English
db.<collection_name>.find({ languages: 'English' })
// document trong mongoDB
{
  ...
  languages: ['English', 'Japanese', 'Vietnamese']
}

// tìm với điều kiện languages có chứa 2 giá trị
db.<collection_name>.find({ languages: { $size: 2 }})

9. Array of embedded documents

Cách tìm khi value dạng array các object

// document trong mongoDB
{
  ...
  pets: [
    {type: 'cat', name: 'Linnea'},
    {type: 'dog', name: 'Tom'},
    {type: 'cat', name: 'Kylie'}
  ]
}

// tìm với điều kiện pets type là cat
db.<collection_name>.find({ 
  pets.type: 'cat'
})

// Hoặc
db.<collection_name>.find({ 
  pets: {
    $elementMatch: { type: 'cat' }
  }
})

10. Query empty field

Cách tìm khi document không có field

// data
{first_name: 'ken', last_name: 'trung'}
{first_name: 'thu', last_name: 'thuy', age: 18}

// tìm với điều kiện có field age
db.<collection_name>.find({ age: null })

// Hoặc
db.<collection_name>.find({ age: { $exists: false }})

11. $where

Giá trị nhận vào của $where là string

db.<collection_name>.find({ $where: 'javascript expression' })

// Ví dụ
db.<collection_name>.find({ first_name: 'ken' })
db.<collection_name>.find({ $where: 'this.first_name === "ken"' })
db.<collection_name>.find({ $where: 'this.first_name === this.last_name' })

12. Pagination

// trả về số lượng, không trả về dữ liệu
db.<collection_name>.count(query)

// bỏ qua X phần tử và chỉ lấy Y phần tử
db.<collection_name>.find(query).skip(x).limit(y)

13. Sort

db.<collection_name>.find(query).sort({ field: -1 })

1: ascending order (tăng dần)
-1: descending order (giảm dần)

14. CRUD

db.<collection_name>.save()
db.<collection_name>.insert(data)
db.<collection_name>.insertMany(datas)
db.<collection_name>.find(query)
db.<collection_name>.findOne(query)
db.<collection_name>.updateOne(query, data)
db.<collection_name>.updateMany(query, data)
db.<collection_name>.delete(query)
db.<collection_name>.deleteOne(query)
// insert-document.js
db.users.insert({
  first_name: 'ken',
  last_name: 'trung'
})
load('script/insert-document.js')
var user = {}
user.first_name = 'ken'
user.last_name = 'trung'
db.<collection_name>.save(user)

// Tìm document có _id là gì, sau đó update trường first_name
db.<collection_name>.updateOne(
  { _id: ... },
  { $set: {first_name: 'ken' }}
)

// Tìm document có _id là gì, sau đó update language nằm trong settings
// Trường settings là array và ta chỉ muốn update 1 trường trong đó
db.<collection_name>.updateOne(
  { _id: ... },
  { $set: {settings.languages: 'vn' }}
)

15. Atomic operator

// $inc: tăng - giảm số lượng
// $push: thêm 1 data vào mảng
// $pull: xóa 1 data ra khỏi mảng
// $addToSet: nếu chưa có thì thêm, có rồi thì chỉ update

db.<collection_name>.updateOne(
  {_id: ...},
  {$inc: {viewCount + 1}}
)

16. MongoDB Schema Design

16-1. Quan hệ 1:1

  • Một tác giả có thể có nhiều địa chỉ
  • Nhưng một tác giả chỉ sống tại một địa chỉ
const author = {
  name: 'kentrung',
  age: 36
}
const address = {
  street: '169E ngõ 1102 đường Giáp Bát, Hoàng Mai, Hà Nội',
  city: 'HN'
}
  • Cách 1: Sử dụng kiểu embedded => tối ưu
const author = {
  name: 'kentrung',
  age: 36,
  address: {
    street: '169E ngõ 1102 đường Giáp Bát, Hoàng Mai, Hà Nội',
    city: 'HN'
  }
}
// truy vấn mất 1 lần query
author.findOne({ name: 'kentrung' })
  • Cách 2: Sử dụng kiểu references
const author = {
  name: 'kentrung',
  age: 36,
  authorId: 1
}
const address = {
  authorId: 1,
  street: '169E ngõ 1102 đường Giáp Bát, Hoàng Mai, Hà Nội',
  city: 'HN'
}
// truy vấn mất 2 lần query
author.findOne({ name: 'kentrung' }) // get authorId
address.findOne({ authorId: 1 }) // get address with authorId

16-2. Quan hệ 1 : N

  • Một tác giả có thể có nhiều comments
const author = {
  name: 'kentrung',
  age: 36
}
const comment1 = {
  content: 'Bài viết hay quá bạn ơi',
  createdAt: new Date().getTime()
}
const comment2 = {
  content: 'Like đầu tiên',
  createdAt: new Date().getTime()
}
  • Cách 1: Sử dụng embedded => ko tốt
    • Một document trong MongoDB không thể lớn hơn 16MB, nếu quá nhiều comments thì author quá lớn
    • Khi bạn xóa hay sửa comments trong author này rất chậm
    • Phân trang khó, phải lấy hết rồi skip, limit
const author = {
  name: 'kentrung',
  age: 36,
  comments: [
    {
      content: 'Bài viết hay quá bạn ơi',
      createdAt: new Date().getTime()
    },
    {
      content: 'Like đầu tiên',
      createdAt: new Date().getTime()
    }
  ]
}
  • Cách 2: Sử dụng references
  • Nếu có 100.000 comments thì phân trang không hiệu quả cho dù đánh index
// collection author
const author = {
  name: 'kentrung',
  age: 36,
  authorId: 1
}

// collection comments
{
  authorId: 1,
  content: 'Bài viết hay quá bạn ơi',
  createdAt: new Date().getTime()
},
{
  authorId: 1,
  content: 'Like đầu tiên',
  createdAt: new Date().getTime()
}
// collection comments
{
  authorId: 1,
  content: 'Bài viết hay quá bạn ơi',
  createdAt: new Date().getTime(),
  page: 1,
  count: 1000,
  comments: [...1000comment]
},
{
  authorId: 1,
  content: 'Like đầu tiên',
  createdAt: new Date().getTime(),
  page: 2,
  count: 2000,
  comments: [1001...2000comment]
}

16-3. Quan hệ N - N

Tham khảo: https://nentang.vn/app/edu/khoa-hoc/co-so-du-lieu/co-so-du-lieu-mysql/lessons/tao-bang-table-theo-quan-he-nhieu-nhieu-n-n-su-dung-heidisql

Step 1: Vẽ mô hình thực thể ER và xem xét Quan hệ giữa 2 table giang_vienlop_hoc

  • Ta có Quan hệ thực tế giữa Giảng viên và Lớp học như sau:
  • Một Giảng viên thì có thể cùng lúc tham gia giảng dạy cho một hoặc nhiều Lớp học.
  • Một Lớp học thì có thể có một hoặc nhiều Giảng viên giảng dạy.
  • Mô hình Thực thể ER mô tả Quan hệ giữa 2 table như sau:

Mô hình Thực thể ER

Step 2: Đặt tên cho Mối quan hệ Nhiều - Nhiều (N-N)

Trong thực tế, để xác định hay ghi nhận quá trình công tác / hoặc phân công giảng dạy cho Giảng viên (cũng có thể gọi là sắp Thời khóa biểu) cho Giảng viên. Cán bộ phòng Đào tạo thường sẽ có 1 quyển sổ được đặt tên là bảng "Phân công Công tác/Giảng dạy" để ghi nhận Giảng viên nào? dạy Lớp học nào?

SQL_HowTo_Design_Many_To_Many_2.png

Step 3: Tiến hành tạo bảng (table) trung gian và chuyển mối liên hệ từ Nhiều - Nhiều (N-N) thành Một-Nhiều (1-N)

  • Theo quy tắc thiết kế Cơ sở dữ liệu, khi có Quan hệ Nhiều - Nhiều (N-N) xuất hiện, Cơ sở dữ liệu sẽ:
  • Sinh thêm Bảng (table) trung gian.
  • Bảng (table) trung gian sẽ có tên là tên của Mối quan hệ Nhiều - Nhiều (N-N). Thường đặt tên là Quá trình / Tham gia / Phân công, ...
  • Trong bảng (table) trung gian sẽ có đầy đủ khóa ngoại (Foreign key - FK) liên kết đến table Master.
  • Trong bảng (table) trung gian sẽ có thêm các cột dùng để lưu trữ thông tin bổ sung/làm rõ nghĩa Mối quan hệ Nhiều - Nhiều (N-N). Tuy nhiên, các cột này không bắt buộc (tùy vào thiết kế của bạn)
  • Dựa theo quy tắc trên, chúng ta có mô hình ER mới như sau:

SQL_HowTo_Design_Many_To_Many_3.png

Step 4: lưu ý các Ràng buộc Logic về Nghiệp vụ lưu trữ dữ liệu

  • Thông thường, trong bất kỳ hệ thống nào, cũng có tồn tại các quy tắc ràng buộc về mặt lưu trữ. Theo ví dụ trên, ta có ràng buộc logic về nghiệp vụ lưu trữ dữ liệu như sau:
  • Phải đảm bảo rằng một Giảng viên không được dạy cùng 1 Lớp học (tức là dữ liệu ghi nhận trong table phan_cong_cong_tac không được trùng nhau).
  • Hay nói cách khác một Lớp học không thể cùng lúc có nhiều Giảng viên giảng dạy được (trong 1 tiết học cụ thể, không thể có nhiều hơn 1 Giảng viên cùng đứng trên lớp?!)
  • Từ ràng buộc logic về nghiệp vụ Lưu trữ của hệ thống yêu cầu như trên, chúng ta cần thiết kế lại mô hình ER như sau:

Cách giải quyết 1:

  • Tiến hành tạo ràng buộc Khóa chính (Primary key - PK) bao gồm 2 cột (gv_id, lop_id)

SQL_HowTo_Design_Many_To_Many_4.png

Cách giải quyết 2:

  • Tạo thêm 1 cột ID làm khóa chính (Primary key - PK), kiểu số nguyên, tự tăng trong table phan_cong_cong_tac
  • Tạo khóa Duy nhất (Unique) bao gồm 2 cột (gv_id, lop_id) để đảm bảo rằng không thể có cặp dữ liệu (gv_id và lop_id) trùng lặp nhau.

SQL_HowTo_Design_Many_To_Many_5.png

Mở rộng thêm bảng (table) mon_hoc

  • Ta có mối quan hệ thực tế giữa 3 thực thể Giảng viên, Lớp học và Môn học như sau:
  • Một Giảng viên thì có thể cùng lúc tham gia Giảng dạy cho 1 hoặc nhiều Lớp học.
  • Một Lớp học thì có thể có 1 hoặc nhiều Giảng viên giảng dạy.
  • Một Lớp học thì có thể học 1 hoặc nhiều Môn học khác nhau (như Toán, Lý, Hóa, Lập trình Web, Thiết kế CSDL, ...)
  • Một Môn học có thể được dạy ở 1 hoặc nhiều Lớp học khác nhau

Một số ví dụ Quan hệ Nhiều - Nhiều (N-N) thực tế:

  • Một Sản phẩm có thể cùng lúc thuộc 1 hoặc nhiều Chuyên mục <-> Một Chuyên mục có thể có 1 hoặc nhiều Sản phẩm.
  • Một Nhân viên có thể cùng lúc Chấm công nhiều loại hình (Hành chính, Ngoài giờ, Ngày nghỉ, Lễ/Tết, ...) <-> Một loại hình Chấm công có thể có nhiều Nhân viên.
  • Một mẫu Kết quả Xét nghiệm có thể có 1 hoặc nhiều Tiêu chí <-> Một tiêu chí có thể có 1 hoặc nhiều Mẫu Kết quả Xét nghiệm.
  • Một phòng Khách sạn có thể có 1 hoặc nhiều Khách thuê <-> Một Khách thuê có thể thuê 1 hoặc nhiều phòng ...

17. Cài mongoDB qua Docker

version: '3.1'

services:
  mongo: # service name
    image: mongo # Dùng image của mongo version mới nhất hoặc dùng mongo:5.0.22-windowsservercore => version cố định
    container_name: mongo-container # 1 service 1 container
    ports:
      - 27018:27017 # Truy cập vào port 27018 thì map với port 27017
    volumes: # database đang chứa ở đâu
      - './mongodb:/data/db' # Thư mục hiện tại 
    restart: always # Nếu bị crash thì auto restart container
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: examplePassword

  mongo-express:
    image: mongo-express
    container_name: mongo-express-container
    restart: always
    ports:
      - 8082:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: examplePassword
      ME_CONFIG_MONGODB_URL: mongodb://root:example@mongo:27017/

#docker-compose -f mongo-compose.yml up --detach

18. Một số ví dụ về find

Sample data: https://atlas-education.s3.amazonaws.com/sampledata.archive

// Hiển thị bản ghi đầu tiên trong collection 'movies'
db.movies.find().limit(1)
---
{
  _id: ObjectId("573a1390f29313caabcd42e8"),
  plot: 'A group of bandits stage a brazen train hold-up, only to find a determined posse hot on their heels.',
  genres: ['Short', 'Western'],
  runtime: 11,
  cast: [
    'A.C. Abadie',
    "Gilbert M. 'Broncho Billy' Anderson",
    'George Barnes',
    'Justus D. Barnes'
  ],
  poster: 'https://m.media-amazon.com/images/M/MV5BMTU3NjE5NzYtYTYyNS00MDVmLWIwYjgtMmYwYWIxZDYyNzU2XkEyXkFqcGdeQXVyNzQzNzQxNzI@._V1_SY1000_SX677_AL_.jpg',
  title: 'The Great Train Robbery',
  fullplot: "Among the earliest existing films in American cinema - notable as the first film that presented a narrative story to tell - it depicts a group of cowboy outlaws who hold up a train and rob the passengers. They are then pursued by a Sheriff's posse. Several scenes have color included - all hand tinted.",
  languages: ['English'],
  released: 1903-12-01T00:00:00.000Z,
  directors: ['Edwin S. Porter'],
  rated: 'TV-G',
  awards: {wins: 1, nominations: 0, text: '1 win.'},
  lastupdated: '2015-08-13 00:27:59.177000000',
  year: 1903,
  imdb: {rating: 7.4, votes: 9847, id: 439},
  countries: ['USA'],
  type: 'movie',
  tomatoes: {
    viewer: {rating: 3.7, numReviews: 2559, meter: 75},
    fresh: 6,
    critic: {rating: 7.6, numReviews: 6, meter: 100},
    rotten: 0,
    lastUpdated: 2015-08-08T19:16:10.000Z
  },
  num_mflix_comments: 0
}
// Hiển thị bản ghi đầu tiên trong collection 'movies'
// Nhưng chỉ hiển thị 2 trường plot và genres
db.movies.find({}, {plot: 1, genres: 1, _id: 0}).limit(1)
---
{
  plot: 'A group of bandits stage a brazen train hold-up, only to find a determined posse hot on their heels.',
  genres: ['Short', 'Western']
}
// Show movies which has year in [2000, 2018]
// year nằm trong khoảng từ 2000 đến 2018
db.movies.find({year: {$gte: 2000, $lte: 2018}}, {year: 1, plot: 1, _id: 1})
// which movies types we have
// Có những loại movie nào
db.movies.distinct('type')
---
[ 'movie', 'series' ]
// Tìm những movies có tomatoes viewer rating >= 4
db.movies.find({
  "tomatoes.viewer.rating": {$gte: 4}
})
// What is the maximum of 'tomatoes.viewer.rating'
// sort giảm dần
db.movies.find().sort({'tomatoes.viewer.rating': -1}).limit(1)
// What is the minimum of 'tomatoes.viewer.rating'
// sort giảm dần
db.movies.find({
  'tomatoes.viewer.rating': {$exists: true}
}).sort({'tomatoes.viewer.rating': 1}).limit(1)
// Tìm những movies có director là William K.L. Dickson hoặc Edwin S. Porter hoặc cả 2
db.movies.find({
  director: {
    $in: ['William K.L. Dickson', 'Edwin S. Porter']
  }
})
// Show all movies has only 1 director
db.movies.find({
  director: { $size: 1 }
})
// Tìm với id
db.movies.find({
  _id: ObjectId("573a1390f29313caabcd5a93")
})
// Director là một mảng các giá trị
// Tính số lượng của director này
db.movies.find({}, {
  title: 1,
  directors: 1,
  numberOfDirector: { $size: '$directors' }
})
// Hiển thị first page, 5 items
db.movies.find().skip(0).limit(5)

// second page, 5 items
db.movies.find().skip(5).limit(5)

19. Một số ví dụ về aggregate

  • Aggregate (tổng hợp) something better than find
  • Nguyên tắc pipeline, làm việc tuần tự, việc 1 tìm kiếm. việc 2 sửa kết quả tìm kiếm, việc 3 làm gì đó, việc 4 làm gì đó
  • Hay gọi là các stage 1, stage 2, stage 3...
// Tìm với ID
// Chỉ hiển thị title - year - director
db.movies.aggregate([
  //stage 1
  {
    $match: {
      _id: ObjectId("573a1390f29313caabcd5a93")
    }
  },
  //stage 2
  {
    $project: {
      title: 1,
      year: 1,
      director: 1
    }
  }
])
// Tìm với ID
// Count số lượng bản ghi
db.movies.aggregate([
  {
    $match: {
      _id: ObjectId("573a1390f29313caabcd5a93")
    }
  },
]).toArray().length
// Check điều kiện AND
db.movies.aggregate([
  {
    $match: {
      $and: [
        {
          'imdb.votes': 884
        },
        {
          type: 'movie'
        },
        {
          year: 1917
        }
      ]
    }
  },
])
// Tìm những movie có nhiều comments
db.movies.aggregate([
  {
    $group: {
      _id: "$movie_id",
      numberOfComments: {
        $count: {}
      }
    }
  },
])
// Show luôn movie đã tìm thấy ở trên
db.movies.aggregate([
  {
    $group: {
      _id: "$movie_id",
      numberOfComments: {
        $count: {}
      }
    }
  },
  {
    $lookup: {
      from: "movies",
      localField: "_id", // _id là ở stage trên
      foreignField: "_id",
      as: "detailMovie"
    }
  }
])
// Tìm field text có chứa Itaque magni
// Dùng Regex
// . kí tự bất kì
// * có hoặc không
// i không phân biệt hoa thường
// Phía trước hoặc sau chuỗi cần tìm có hoặc không có kí tự bất kì
db.movies.aggregate([
  {
    $match: {
      text: /.*Itaque magni.*/i,
    }
  }
])
// Tìm field text bắt đầu bằng Itaque magni
// text starts with ...
db.movies.aggregate([
  {
    $match: {
      text: /^Itaque magni.*/i,
    }
  }
])
// Tìm field text kết thúc bằng Itaque magni
// text ends with ...
db.movies.aggregate([
  {
    $match: {
      text: /.*Itaque magni$/i,
    }
  }
])
// Sort kết quả tìm kiếm trường 'name'
db.movies.aggregate([
  {
    $match: {
      text: /.*Itaque magni.*/i,
    }
  },
  {
    $sort: {
      name: -1
    }
  }
])
// Thêm fields vào stage
db.movies.aggregate([
  {
    $match: {
      writers: {$exists: true}
    }
  },
  {
    $addFields: {
      numberOfWriters: {$size: "$writers"}
    }
  }
])

20. MongoDB - ZendVN

20-1. Một số thuật ngữ trong No-SQL

  • Ràng buộc - Relational
  • Không ràng buộc - Non relational
  • Khả năng mở rộng - High scalability
  • Khả năng mở rộng theo chiều dọc - Vertical scalable (scale up nâng cấp máy tính CPU, RAM, SSD...)
  • Khả năng mở rộng theo chiều ngang - Horizontal scalable (scale out thêm máy tính)
  • Phân tán dữ liệu - Distributed data
  • Triển khai linh hoạt - Deployment flexibility
  • Tính sẵn sàng - High availability
  • Nhất quán cuối - Eventual consistency
  • Lưu trữ tốt - Durability (vừa lưu trên ram vừa lưu trên SSD)

20-2. So sánh NoSQL - SQL

Tính năng SQL NoSQL
Hiệu xuất Kém hơn NoSQL vì khi truy vấn nó phải tính toán, kiểm tra và xử lý các mối quan hệ giữa các bảng Tốt hơn SQL vì nó bỏ qua các ràng buộc
Mở rộng theo chiều ngang Có thể thực hiện được nhưng sẽ rất phức tạp nếu đã tồn tại CSDL trong DB Mở rộng dễ dàng
Tốc độ read/write Kém hơn NoSQL vì phải đảm bảo tính ràng buộc dữ liệu giữa các bảng. Nếu sử dụng nhiều server phải bảo toàn tính nhất quán về dữ liệu ở các server với nhau Tốc độ nhanh hơn SQL vì bỏ qua ràng buộc. Vì dữ liệu được lưu trong RAM sau đó mới đẩy xuống SSD và có tính nhất quán cuối
Phần cứng Đòi hỏi phần cứng cao Không đòi hỏi quá cao
Thay đổi số node trong hệ thống Khi thêm hoặc xóa 1 node cần phải shutdown hệ thống trong 1 khoảng thời gian Không cần phải shutdown hệ thống
Truy vấn báo cáo Dễ dàng Không tốt
Mở rộng dữ liệu Khi muốn bổ xung thêm cột cho 1 bảng thì ta phải khai báo trước Không cần khai báo trước
Ứng dụng Xây dựng những hệ thống có quan hệ chặt chẽ và cần tính đồng nhất về dữ liệu như tài chính, ngân hàng, chứng khoán... Xây dựng những hệ thống lưu trữ thông tin lớn, không quá quan trọng về vấn đề đồng nhất dữ liệu trong 1 thời gian nhất định như báo trí, mạng xã hội, diễn đàn, shopping...

20-3. NoSQL có thay thế SQL?

  • Không thay thế
  • Hỗ trợ cho nhau

20-4. Các loại NoSQL

  • Key/value stores: sử dụng bảng băm (hash-table) nơi có một khóa duy nhất và một con trỏ chỉ đến một mục dữ liệu cụ thể. Các mô hình key/value thì đơn giản và dễ thực hiện nhưng nó không hiệu quả khi bạn truy vấn hay update dữ liệu. Examples: couchDB, Dynamo, MemcacheDB, OrientDB, redis...

Key-value Store

  • Column family stores: lưu trữ và xử lý một lượng lớn dữ liệu trên nhiều server. Chúng lưu trữ dữ liệu trong nhiều cột trong mỗi dòng với key cho từng dòng. Column families là một nhóm các dữ liệu liên quan được truy cập cùng với nhau. Examples: Hadoop/HBase - Apache, BigTable - Google, Cassandra - Facebook/Apache...

column family stores.png

  • Document databases: Mô hình 1 collection chứa nhiều document. Trong document chứa nhiều cặp key/value và các cặp key/value được phép lồng nhau. Mỗi document sẽ có 1 key để phân biệt. Examples: MongoDB, OrientDB...

Document databases.png

  • Graph databases: sử dụng câu trúc graph để lưu trữ dữ liệu, các record dữ liệu trong graph database được gọi là Nodes, các node kết nối với nhau bằng quan hệ Relationships. Nodes và Relationships đều có các thuộc tính Properties. Các Nodes có thể phân thành các group để dễ quản lý nhờ vào Label. Examples: Neo4J, Sones, Core Data...

graph databases.png

20-5. Single Field Indexes

// Liệt kê các thành phố theo chiều giảm dần dân số
db.zips.find().sort({ pop: -1 })

// Liệt kê các thành phố có dân số nhỏ hơn 4546, sắp xếp giảm dần
db.zips.find({
  pop: {  $lt: 4546 }
}).sort({ pop: -1 })
// Liệt kê các thành phố có dân số bằng 4546
db.zips.find({ pop: 4546 })

// Xem cách hoạt động của câu query
db.zips.find({ pop: 4546 }).explain('executionStats')

--- output ---
...
"executionStats" : {
    "executionSuccess" : true,
    "nReturned" : 2.0, // số lượng trả về
    "executionTimeMillis" : 11.0, // thời gian thực thi 11ms
    "totalKeysExamined" : 0.0,
    "totalDocsExamined" : 29353.0, // tổng số document quét qua (quét toàn bộ)
    "executionStages" : {
        "stage" : "filter",
        "planNodeId" : 1.0,
        "nReturned" : 2.0,
        "executionTimeMillisEstimate" : 12.0,
        "opens" : 1.0,
        "closes" : 1.0,
        "saveState" : 29.0,
        "restoreState" : 29.0,
        "isEOF" : 1.0,
        "numTested" : 29353.0,
        "filter" : "traverseF(s4, lambda(l1.0) { ((l1.0 == s7) ?: false) }, false) ",
        "inputStage" : {
            "stage" : "scan",
            "planNodeId" : 1.0,
            "nReturned" : 29353.0,
            "executionTimeMillisEstimate" : 12.0,
            "opens" : 1.0,
            "closes" : 1.0,
            "saveState" : 29.0,
            "restoreState" : 29.0,
            "isEOF" : 1.0,
            "numReads" : 29353.0,
            "recordSlot" : 5.0,
            "recordIdSlot" : 6.0,
            "fields" : [
                "pop"
            ],
            "outputSlots" : [
                NumberLong(4)
            ]
        }
    }
},
...
// Tạo index cho trường pop với sắp xếp dữ liệu tăng dần
db.zips.createIndex({ pop: 1 })

--- output ---
{
    "numIndexesBefore" : 1.0,
    "numIndexesAfter" : 2.0,
    "note" : "all indexes already exist",
    "ok" : 1.0
}


// Xem danh sách index
db.zips.getIndexes()

--- output ---
// có 2 index là _id và pop
[
    {
        "v" : 2.0,
        "key" : {
            "_id" : 1.0
        },
        "name" : "_id_"
    },
    {
        "v" : 2.0,
        "key" : {
            "pop" : 1.0
        },
        "name" : "pop_1"
    }
]

// Thực hiện lại liệt kê các thành phố có dân số bằng 4546
db.zips.find({ pop: 4546 })

// Xem cách query
db.zips.find({ pop: 4546 }).explain('executionStats')

--- output ---
"executionTimeMillis" : 0.0, // gần như là 0 ms
"totalKeysExamined" : 2.0,
"totalDocsExamined" : 2.0, // tổng số document quét qua là 2, ko phải quét toàn bộ

20-6. Xóa index

// Xem danh sách index
db.zips.getIndexes()

--- output ---
[
    {
        "v" : 2.0,
        "key" : {
            "_id" : 1.0
        },
        "name" : "_id_"
    },
    {
        "v" : 2.0,
        "key" : {
            "pop" : 1.0
        },
        "name" : "pop_1"
    }
]


// Xóa index pop thì lấy ở trường name
db.zips.dropIndex('pop_1')

20-7. Compound indexes

Tạo index với nhiều field

db.zips.createIndex({ pop: 1, state: 1 })

20-8. Unique Indexes

Tạo index để khi insert không bị trùng nhau

db.users.createIndex({ user_id: 1 }, { unique: true })

20-9. Sparse Indexes

Tạo index để khác null

db.users.find({
  department: { $exists: true }
}).sort({ department: 1 })

db.users.createIndex({ department: 1 }, { sparse: true })

db.users.find().sort({ deparment: 1 }).hint({ deparment: 1 })

20-10. TTL Indexes: thời gian tồn tại một index nào đó

20-11. Text indexes

20-12. Patial indexes


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí