Tạo Collection lưu trữ Document JSON với cấu trúc động
Bước đầu tiên là tạo một Collection mới trong Database hiện tại để lưu trữ dữ liệu dạng Document. Chúng ta sẽ thiết lập Collection này không có Schema cứng (Schema-free) để linh hoạt chấp nhận các trường dữ liệu khác nhau.
Mục đích là xây dựng nơi chứa dữ liệu sản phẩm với các thuộc tính động, ví dụ: một sản phẩm có thể có màu sắc, kích thước, hoặc thông số kỹ thuật khác nhau tùy loại.
Sử dụng ArangoShell để tạo Collection với tên "products". Kết quả mong đợi là thông báo thành công và Collection xuất hiện trong danh sách.
arangosh
db._create("products")
Đoạn code trên khởi tạo Collection "products" với cấu hình mặc định (Shard factor 1, Replication factor 1). Sau khi chạy, bạn sẽ thấy thông báo { "result": true }.
Để xác minh, hãy liệt kê các Collection hiện có. Kết quả phải hiển thị "products" trong mảng "collections".
db._collections()
Đưa vào một vài Document mẫu với cấu trúc khác nhau để chứng minh tính linh hoạt. Document thứ nhất là sản phẩm điện tử, Document thứ hai là sản phẩm thời trang.
db.products.save({
"_key": "laptop-x1",
"type": "electronics",
"name": "Gaming Laptop X1",
"specs": { "cpu": "i9", "ram": "32GB", "gpu": "RTX 4090" },
"price": 2500,
"stock": 15
})
db.products.save({
"_key": "shirt-s01",
"type": "fashion",
"name": "Summer Shirt",
"sizes": ["S", "M", "L", "XL"],
"colors": ["Red", "Blue"],
"price": 25,
"stock": 100
})
Khi chạy lệnh này, ArangoDB sẽ trả về đối tượng Document đã được lưu, bao gồm cả khóa tự động sinh "_id" (ví dụ: products/laptop-x1). Việc này chứng tỏ Collection chấp nhận cả object lồng nhau (specs) và mảng (sizes, colors).
Verify bằng cách truy xuất tất cả dữ liệu vừa lưu. Kết quả phải trả về mảng chứa 2 Document trên.
db.products.toArray()
Sử dụng Hash Index và Geo Index để tăng tốc tìm kiếm
Sau khi có dữ liệu, việc tiếp theo là tối ưu hóa truy vấn. Chúng ta sẽ tạo Hash Index cho trường "type" để lọc nhanh theo danh mục, và Geo Index cho trường "location" để tìm kiếm theo vị trí địa lý.
Hash Index hoạt động hiệu quả khi cần tìm kiếm chính xác một giá trị (equality check). Geo Index (Haversine) dùng để tính khoảng cách trên mặt cầu, cần dữ liệu định dạng [kinh độ, vĩ độ].
Tạo Hash Index trên trường "type" của Collection "products". Kết quả là một đối tượng Index chứa loại "hash" và trường "type".
db.products.ensureIndex({
type: "hash",
fields: ["type"]
})
Thêm dữ liệu địa lý vào các Document hiện có để chuẩn bị cho Geo Index. Chúng ta cập nhật thêm trường "location" với định dạng [longitude, latitude].
db.products.update("laptop-x1", { location: [106.6297, 10.8231] })
db.products.update("shirt-s01", { location: [105.8441, 21.0285] })
Việc này cập nhật 2 Document. Kết quả là mỗi Document trả về { "updated": 1 } hoặc { "updated": 0 } nếu đã tồn tại, nhưng quan trọng là trường "location" đã có giá trị số thực.
Bây giờ tạo Geo Index trên trường "location". Lưu ý: Geo Index chỉ hoạt động với mảng 2 phần tử [lon, lat].
db.products.ensureIndex({
type: "geo",
fields: ["location"]
})
Verify kết quả bằng cách liệt kê tất cả Index trong Collection "products". Bạn phải thấy 3 Index: Index mặc định (_key), Hash Index (type), và Geo Index (location).
db.products.indexes()
Thực hiện truy vấn tìm kiếm sử dụng Hash Index. Tìm tất cả sản phẩm loại "electronics".
FOR doc IN products
FILTER doc.type == "electronics"
RETURN doc
Kết quả chỉ trả về Document laptop-x1. Để kiểm tra xem Index có được dùng không, hãy chạy lệnh EXPLAIN trước query.
EXPLAIN
FOR doc IN products
FILTER doc.type == "electronics"
RETURN doc
Trong phần output của EXPLAIN, tìm phần "indexes". Bạn sẽ thấy "type" được liệt kê, chứng tỏ Hash Index đã kích hoạt.
Thực hiện truy vấn Geo Index: Tìm các sản phẩm trong bán kính 100km quanh Hà Nội (tọa độ 106.6297, 10.8231).
FOR doc IN products
FILTER doc.location && geoWithin(doc.location, {
center: [106.6297, 10.8231],
radius: 100000
})
RETURN {
name: doc.name,
distance: geoDistance(doc.location, [106.6297, 10.8231])
}
Kết quả trả về danh sách sản phẩm nằm trong bán kính 100km kèm theo khoảng cách tính toán (đơn vị mét). Đây là ứng dụng thực tế của Geo Index.
Tích hợp lưu trữ Key-Value (ArangoDB Lite) cho dữ liệu đơn giản
ArangoDB hỗ trợ chế độ Key-Value (KV) trong cùng một Collection mà không cần chuyển đổi sang Database khác. Điều này giúp lưu trữ các cặp Key-Value đơn giản với hiệu năng cao hơn Document đầy đủ.
Chúng ta sẽ tạo một Collection mới "config_kv" và cấu hình nó để lưu trữ dữ liệu dạng KV. Dữ liệu này sẽ chỉ gồm Key và Value, không có các trường metadata phức tạp.
Tạo Collection "config_kv" với tùy chọn "isSmart" hoặc cấu hình mặc định, nhưng quan trọng là chúng ta sẽ sử dụng phương thức .save() với định dạng đặc biệt hoặc .update() để thao tác như KV store.
db._create("config_kv")
Lưu trữ cấu hình hệ thống đơn giản: Key là tên biến, Value là nội dung. Ví dụ: lưu phiên bản API hoặc cờ bật tắt tính năng.
db.config_kv.save({
"_key": "api_version",
"value": "v2.1.0"
})
db.config_kv.save({
"_key": "maintenance_mode",
"value": false
})
db.config_kv.save({
"_key": "max_upload_size",
"value": 10485760
})
Khi chạy lệnh này, ArangoDB lưu Document nhưng chúng ta chỉ quan tâm đến cặp Key-Value. Trong thực tế, để tối ưu hiệu năng KV, bạn có thể dùng ArangoDB Shell để truy xuất trực tiếp qua Key.
Truy xuất giá trị theo Key nhanh chóng. Thay vì quét toàn bộ Collection, dùng hàm .get() hoặc .byKey() để lấy giá trị.
db.config_kv.get("api_version")
Kết quả trả về toàn bộ Document: { "_key": "api_version", "_id": "config_kv/api_version", "value": "v2.1.0" }. Để lấy thuần Value, dùng truy vấn AQL.
FOR doc IN config_kv
FILTER doc._key == "maintenance_mode"
RETURN doc.value
Kết quả trả về giá trị boolean: false. Thao tác này nhanh hơn nhiều so với tìm kiếm trên Collection chứa hàng triệu Document phức tạp.
Cập nhật giá trị Key-Value. Ví dụ: bật chế độ bảo trì.
db.config_kv.update("maintenance_mode", { value: true })
Verify bằng cách đọc lại giá trị "maintenance_mode". Kết quả phải trả về true.
FOR doc IN config_kv
FILTER doc._key == "maintenance_mode"
RETURN doc.value
Thực hiện truy vấn kết hợp giữa Document và Graph
Bước cuối cùng là kết hợp dữ liệu Document (sản phẩm) và Graph (mối quan hệ) trong cùng một luồng xử lý. Chúng ta sẽ tạo Collection Edge để liên kết sản phẩm với danh mục (Category).
Mục đích là xây dựng mối quan hệ "thuộc về" (belongs_to). Một sản phẩm (Document) sẽ có cạnh (Edge) nối đến một danh mục (Document). Chúng ta sẽ truy vấn để lấy sản phẩm kèm thông tin danh mục.
Tạo Collection Edge "product_categories" để lưu trữ các cạnh nối giữa "products" và "categories".
db._create("product_categories", { isEdgeCollection: true })
Tạo Collection "categories" để chứa các danh mục hàng hóa (ví dụ: Điện tử, Thời trang).
db._create("categories")
Thêm dữ liệu vào Collection "categories".
db.categories.save({
"_key": "electronics",
"name": "Thiết bị Điện tử",
"description": "Laptop, điện thoại, phụ kiện"
})
db.categories.save({
"_key": "fashion",
"name": "Thời trang",
"description": "Quần áo, giày dép, phụ kiện"
})
Đạo tạo các cạnh (Edge) nối từ sản phẩm trong "products" đến danh mục trong "categories". Edge sẽ có cấu trúc { "_from": products/..., "_to": categories/... }.
db.product_categories.save({
"_from": "products/laptop-x1",
"_to": "categories/electronics",
"relation": "belongs_to"
})
db.product_categories.save({
"_from": "products/shirt-s01",
"_to": "categories/fashion",
"relation": "belongs_to"
})
Verify kết quả bằng cách liệt kê các Edge. Kết quả phải hiển thị 2 cạnh nối đã tạo.
db.product_categories.toArray()
Thực hiện truy vấn kết hợp: Lấy thông tin sản phẩm và thông tin danh mục liên quan thông qua Graph. Đây là điểm mạnh của ArangoDB Multi-Model.
FOR p, e, c IN 1..1 ANY "products/laptop-x1" product_categories
RETURN {
product: {
name: p.name,
type: p.type,
price: p.price
},
category: {
name: c.name,
description: c.description
},
relation: e.relation
}
Kết quả trả về một object duy nhất chứa thông tin sản phẩm laptop-x1, thông tin danh mục "Thiết bị Điện tử", và loại quan hệ "belongs_to". Query này đã thực hiện join tự động giữa Collection Document và Edge Collection.
Để mở rộng, hãy viết query lấy tất cả sản phẩm trong danh mục "fashion" kèm thông tin danh mục.
FOR c, e, p IN 1..1 OUTBOUND "categories/fashion" product_categories
RETURN {
category: c.name,
product: p.name,
price: p.price,
specs: p.specs || p.sizes
}
Kết quả trả về mảng chứa các sản phẩm thuộc danh mục thời trang, bao gồm cả thông tin chi tiết của sản phẩm (specs hoặc sizes tùy loại). Điều này chứng tỏ khả năng truy vấn linh hoạt giữa Document và Graph trong cùng một mô hình dữ liệu.
Verify bằng cách kiểm tra cấu trúc dữ liệu trả về. Bạn sẽ thấy object có trường "category", "product", "price" và "specs" (hoặc "sizes") đã được gộp chung trong một kết quả.
Điều hướng series:
Mục lục: Series: Triển khai Database Multi-Model với ArangoDB trên Ubuntu 24.04
« Phần 3: Xây dựng mô hình dữ liệu Graph và thực hiện truy vấn
Phần 5: Cấu hình cụm ArangoDB Cluster (Multi-Server) trên Ubuntu »