Ví dụ về mô hình dữ liệu MongoDB
Trong MongoDB, dữ liệu có lược đồ linh hoạt. Nó hoàn toàn khác với cơ sở dữ liệu SQL, nơi bạn phải xác định và khai báo lược đồ của bảng trước khi chèn dữ liệu. Bộ sưu tập MongoDB không thực thi cấu trúc tài liệu Show
Thách thức chính trong mô hình hóa dữ liệu là cân bằng giữa nhu cầu của ứng dụng, đặc điểm hiệu suất của công cụ cơ sở dữ liệu và các mẫu truy xuất dữ liệu Hãy xem xét những điều sau đây khi thiết kế lược đồ trong MongoDB
Ví dụ chúng ta hãy lấy một ví dụ về một khách hàng cần thiết kế cơ sở dữ liệu cho trang web của mình. Trang web của anh ấy có các yêu cầu sau Mỗi bài đăng là khác biệt (chứa tiêu đề, mô tả và url duy nhất) Mỗi bài đăng có thể có một hoặc nhiều thẻ Mỗi bài đăng có tên của nhà xuất bản và tổng số lượt thích Mỗi bài đăng có thể có 0 hoặc nhiều bình luận và các bình luận phải chứa tên người dùng, tin nhắn, thời gian dữ liệu và lượt thích QCon London (27-29 tháng 3 năm 2023). Áp dụng các xu hướng mới nổi phù hợp để giải quyết các thách thức kỹ thuật của bạn Mô hình hóa dữ liệu. Hệ thống thương mại điện tử mẫu với MongoDB Thích In Dấu trang 06 Tháng sáu 2012 10 phút đọc qua
Viết cho InfoQTham gia cộng đồng chuyên gia. Tăng khả năng hiển thị của bạn.Phát triển sự nghiệp của bạn. Tìm hiểu thêm Mục tiêu của bài viết này là chứng minh một mô hình dữ liệu cho một ứng dụng thương mại điện tử đơn giản tập trung vào một vài cấu trúc chính có trong các hệ thống thương mại điện tử điển hình. Chúng bao gồm danh mục sản phẩm, giỏ hàng và đơn đặt hàng. Tất cả mã mẫu đều bằng JavaScript sử dụng trình bao MongoDB nhưng bạn có thể điều chỉnh tất cả các phần của mã nguyên mẫu này cho bất kỳ trình điều khiển MongoDB nào có sẵn Tổng quanCác hệ thống thương mại điện tử đơn giản là điểm khởi đầu tốt để lập mô hình dữ liệu với cơ sở dữ liệu tài liệu như MongoDB. Các ví dụ này dễ dàng chứng minh các khái niệm cốt lõi về phát triển ứng dụng với MongoDB và chứa một số mẫu mà bạn có thể sử dụng lại trong các miền vấn đề khác. MongoDB cho phép bạn sắp xếp dữ liệu của mình trong "tài liệu BSON" mà bạn có thể coi là tài liệu "JSON đã nhập". Một tài liệu nguyên mẫu đơn giản có thể giống như sau
Nội dung được tài trợ có liên quanNhà tài trợ liên quanGặp gỡ CockroachDB Serverless - Cơ sở dữ liệu SQL phát triển cao nhất trên hành tinh. Bắt đầu miễn phí và mở rộng quy mô khi bạn phát triển. Bắt đầu ngay lập tức Trong cơ sở dữ liệu quan hệ, bạn có thể diễn đạt điều này bằng hai bảng. Bảng đầu tiên sẽ chứa thông tin chi tiết của người đó như họ và tên, trong khi bảng thứ hai sẽ chứa thông tin chi tiết liên quan đến mọi người, với khóa ngoại trở lại bảng người MongoDB có một tập hợp phong phú các toán tử truy vấn và toán tử cập nhật cho phép bạn truy cập tài liệu dễ dàng và cũng thực hiện cập nhật nguyên tử trên các trường, mảng hoặc tài liệu con của tài liệu Yêu cầuPhiên bản MongoDB đang chạy và shell db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});1 Tất cả các ví dụ đều sử dụng trình bao để tránh làm sao lãng mô hình với các chi tiết triển khai ngôn ngữ cụ thể. Bạn có thể triển khai tất cả các ví dụ và mẫu trong bài viết này bằng trình điều khiển cho MongoDB và ngôn ngữ bạn chọn Những ví dụ này cố tình tránh xử lý lỗi phức tạp hơn để tập trung vào các hoạt động cơ bản như truy cập và sửa đổi dữ liệu Các thành phầnSimsong vừa ra mắt dòng điện thoại OneDroid hoàn toàn mới của họ trước sự đón nhận háo hức của thị trường tiêu dùng. Nhiệm vụ của bạn là xây dựng một hệ thống thương mại điện tử để tận dụng cơ hội to lớn này và nguồn hàng chúng tôi đã phân bổ Danh mục sản phẩmBước đầu tiên là thiết kế lược đồ cho trang web. Xem xét một lược đồ sản phẩm ban đầu { sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } } Mô hình dữ liệu này lưu trữ các chi tiết vật lý như thông tin sản xuất và vận chuyển dưới dạng tài liệu được nhúng trong tài liệu sản phẩm lớn hơn, điều này có ý nghĩa vì các chi tiết vật lý này là các tính năng độc đáo của sản phẩm. Điều này mang lại cho tài liệu "địa phương dữ liệu mạnh", cho phép ánh xạ dễ dàng trong môi trường hướng đối tượng Để chèn tài liệu vào bộ sưu tập sản phẩm, hãy sử dụng các lệnh sau mongo use ecommerce db.products.insert({ sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } }) Lệnh đầu tiên (mongo) khởi động bảng điều khiển mongodb và kết nối với bảng điều khiển Mongo DB cục bộ trên localhost và cổng 27017. Cái tiếp theo chọn cơ sở dữ liệu thương mại điện tử (sử dụng thương mại điện tử) và cái thứ ba chèn tài liệu sản phẩm vào bộ sưu tập sản phẩm. Về sau, tất cả các lệnh đều giả sử bạn đang ở trong trình bao Mongo DB bằng cơ sở dữ liệu thương mại điện tử Mô hình dữ liệu sản phẩm có một mã hàng duy nhất xác định sản phẩm, tiêu đề, mô tả, số lượng hàng trong kho và thông tin giá cả về mặt hàng Tất cả các sản phẩm có danh mục. Trong trường hợp của Simsong One, đó là điện thoại 15G và cũng có bộ thu FM. Do đó, Sản phẩm này thuộc cả hai loại db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});2 và db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});3. Thêm các danh mục vào tài liệu hiện có, với thao tác db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});4 sau db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }}); Để hỗ trợ các truy vấn hiệu quả bằng cách sử dụng trường db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});5, hãy thêm chỉ mục vào trường danh mục cho bộ sưu tập db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});6 db.products.ensureIndex({categories:1 }) Điều này trả về tất cả các sản phẩm cho một danh mục cụ thể bằng cách sử dụng chỉ mục và biểu thức chính quy được neo. Miễn là biểu thức chính quy phân biệt chữ hoa chữ thường và được neo, MongoDB sẽ sử dụng chỉ mục để trả về truy vấn. Ví dụ: tìm nạp tất cả các sản phẩm trong danh mục bắt đầu bằng db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});7 { sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } }1 Để có thể cung cấp danh sách tất cả các sản phẩm trong danh mục, hãy sửa đổi mô hình dữ liệu bằng bộ sưu tập tài liệu cho từng danh mục. Trong bộ sưu tập này, mỗi tài liệu đại diện cho một danh mục và chứa đường dẫn cho danh mục đó trong cây danh mục. Những tài liệu này sẽ giống như sau { sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } }2 Chèn tài liệu vào bộ sưu tập db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});5 và thêm chỉ mục vào bộ sưu tập này { sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } }4 Có hai con đường trong mỗi loại. điều này cho phép ứng dụng sử dụng cùng một phương pháp để tìm tất cả các danh mục cho một danh mục gốc cụ thể như được sử dụng để tìm sản phẩm theo danh mục. Ví dụ: để trả về tất cả các danh mục phụ của danh mục "di động", hãy sử dụng truy vấn sau { sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } }5 Điều này sẽ trả về các tài liệu sau { sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } }6 Sử dụng các giá trị db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});9 này, ứng dụng có thể sử dụng phương pháp này để truy cập cây danh mục và trích xuất nhiều danh mục con hơn với một truy vấn được hỗ trợ chỉ mục. Hơn nữa, ứng dụng có thể lấy tất cả các tài liệu cho một danh mục cụ thể bằng cách sử dụng giá trị db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});9 này xe đẩyGiỏ hàng trong hệ thống thương mại điện tử, cho phép người dùng đặt trước các mặt hàng từ kho và giữ chúng cho đến khi họ thanh toán và thanh toán cho các mặt hàng đó. Ứng dụng phải đảm bảo rằng tại bất kỳ thời điểm nào, không có nhiều mặt hàng trong giỏ hàng hơn số lượng có trong kho và nếu người dùng từ bỏ giỏ hàng, ứng dụng phải trả lại các mặt hàng từ giỏ hàng về kho mà không mất dấu vết của bất kỳ đối tượng nào. Lấy tài liệu sau, mô hình giỏ hàng { sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } }9 Mảng db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});6 chứa danh sách sản phẩm khách hàng dự định mua. Sử dụng thao tác db.products.ensureIndex({categories:1 })2 sau để tạo giỏ hàng mongo use ecommerce db.products.insert({ sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } })0 Nếu hàng tồn kho có 99 mặt hàng, sau thao tác này, hàng tồn kho sẽ có 97 mặt hàng. Để ngăn "bán quá mức", ứng dụng phải chuyển các mặt hàng từ kho vào giỏ hàng. Để hỗ trợ các hoạt động này, các ứng dụng phải thực hiện một loạt các bản cập nhật và có thể "khôi phục" các thay đổi nếu xảy ra sự cố. Bắt đầu bằng cách thêm một sản phẩm vào giỏ hàng của khách hàng với thao tác sau mongo use ecommerce db.products.insert({ sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } })1 Sau đó, kiểm tra để đảm bảo rằng khoảng không quảng cáo có thể hỗ trợ thêm sản phẩm vào giỏ hàng của khách hàng mongo use ecommerce db.products.insert({ sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } })2 Hoạt động này chỉ thành công nếu có đủ hàng tồn kho và ứng dụng phải phát hiện thành công hay thất bại của hoạt động. Gọi db.products.ensureIndex({categories:1 })3 để lấy kết quả của lần cập nhật đã thử mongo use ecommerce db.products.insert({ sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } })3 } Nếu db.products.ensureIndex({categories:1 })4 sai trong tài liệu kết quả, thao tác không thành công và ứng dụng phải "quay lại" nỗ lực thêm sản phẩm vào giỏ hàng của người dùng. Mẫu này đảm bảo rằng ứng dụng không thể có nhiều sản phẩm trong giỏ hàng hơn khoảng không quảng cáo có sẵn Ngoài việc thêm các đối tượng vào giỏ hàng, còn có một số hoạt động liên quan đến giỏ hàng mà ứng dụng phải có khả năng hỗ trợ
Chuỗi hoạt động tiếp theo cho phép ứng dụng đảm bảo rằng các giỏ hàng được cập nhật và ứng dụng có đủ khoảng không quảng cáo để trang trải nó. Cập nhật giỏ hàng với số lượng mới, sử dụng thao tác db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});4 sau mongo use ecommerce db.products.insert({ sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } })4 Lưu ý rằng có một số biến được xác định gọi là số lượng mới, số lượng cũ và số lượng_delta để chứa số lượng mới và số lượng trước đó trong giỏ hàng cũng như đồng bằng cần được yêu cầu từ kho hàng Bây giờ, hãy xóa mặt hàng bổ sung khỏi kho, cập nhật số lượng mặt hàng trong giỏ hàng mongo use ecommerce db.products.insert({ sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } })5 Đảm bảo ứng dụng có đủ khoảng không quảng cáo cho hoạt động. Nếu không có đủ khoảng không quảng cáo, ứng dụng phải khôi phục hoạt động cuối cùng. Thao tác sau đây kiểm tra lỗi bằng cách sử dụng db.products.ensureIndex({categories:1 })3 và khôi phục thao tác nếu nó trả về lỗi mongo use ecommerce db.products.insert({ sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } })6 Nếu người dùng từ bỏ quy trình mua hàng hoặc giỏ hàng trở nên cũ kỹ và hết thời gian chờ, ứng dụng phải trả lại nội dung giỏ hàng cho kho hàng. Thao tác này yêu cầu một vòng lặp tìm tất cả các giỏ hàng đã hết hạn hoặc bị hủy, sau đó trả lại nội dung của từng giỏ hàng cho kho hàng. Bắt đầu bằng cách tìm tất cả các xe đẩy đủ "cũ" và sử dụng một thao tác giống như sau mongo use ecommerce db.products.insert({ sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } })7 Hoạt động này đưa tất cả các sản phẩm trong mỗi giỏ hàng và đưa chúng trở lại kho và xóa số nhận dạng giỏ hàng khỏi mảng db.products.ensureIndex({categories:1 })7 trong tài liệu sản phẩm. Khi ứng dụng đã trả lại tất cả các mặt hàng vào kho, ứng dụng sẽ đặt trạng thái của giỏ hàng thành hết hạn Thủ tục thanh toánKhi người dùng nhấp vào nút "xác nhận" trong phần thanh toán của ứng dụng, ứng dụng sẽ tạo một tài liệu "đặt hàng" phản ánh toàn bộ đơn đặt hàng. Hãy xem xét các hoạt động sau đây mongo use ecommerce db.products.insert({ sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } })8 Đối với cơ sở dữ liệu quan hệ, bạn có thể cần lập mô hình này dưới dạng một tập hợp các bảng. cho các đơn đặt hàng, vận chuyển, theo dõi và thanh toán. Sử dụng MongoDB, người ta có thể tạo một tài liệu độc lập, dễ hiểu và chỉ cần ánh xạ vào một ứng dụng hướng đối tượng. Sau khi chèn tài liệu này, ứng dụng phải đảm bảo hàng tồn kho được cập nhật trước khi hoàn tất thanh toán. Bắt đầu bằng cách đặt giỏ hàng là xong, với thao tác sau mongo use ecommerce db.products.insert({ sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } })9 Sử dụng thao tác sau để xóa số nhận dạng giỏ hàng khỏi tất cả các bản ghi sản phẩm db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});0 Bằng cách sử dụng "multi-update", là đối số cuối cùng trong phương thức db.products.update({sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }});4, thao tác này sẽ cập nhật tất cả các tài liệu phù hợp trong một nhóm thao tác Phần kết luậnKhả năng tài liệu phong phú đảm bảo hoạt động nguyên tử trong MongoDB giúp có thể lập mô hình nhiều ứng dụng khác nhau trong MongoDB. Ngay cả những yêu cầu khắt khe của các ứng dụng thông thường như hệ thống thương mại điện tử cũng có thể thực hiện được trong cơ sở dữ liệu tài liệu. Mô hình dữ liệu này (i. e. "schema design,") rất hữu ích để phát triển các ứng dụng xung quanh bất kỳ hệ thống tài nguyên bị hạn chế nào, không chỉ các hệ thống thương mại điện tử Mối quan hệ phù hợp chặt chẽ giữa mã ứng dụng hướng đối tượng và tài liệu dẫn đến các mô hình dữ liệu đơn giản hơn và ít mã keo hơn giữa hệ thống lưu trữ dữ liệu và mã cấp ứng dụng Giới thiệu về tác giảChristian Kvalheim, nút. js và nhà phát triển trình điều khiển cho Mongodb đã làm việc với tư cách là nhà phát triển và Giám đốc Kỹ thuật trong 12 năm qua. Anh ấy đã làm việc cho Thoughtworks, IMSI, Xing và giờ là 10gen trên nhiều nền tảng công nghệ khác nhau. Bên cạnh nút. js, anh ấy dành thời gian của mình cho Erlang, Java và CLấy cảm hứng từ nội dung này?Trở thành biên tập viên cho InfoQ là một trong những quyết định sáng suốt nhất trong sự nghiệp của tôi. Nó đã thử thách tôi và giúp tôi trưởng thành theo nhiều cách. Chúng tôi muốn có nhiều người tham gia nhóm của chúng tôi Thomas Betts Trưởng ban biên tập, Thiết kế và kiến trúc phần mềm @InfoQ; Viết cho InfoQ Xếp hạng bài viết nàynhận con nuôi Phong cách Đã liên hệ với tác giả Nội dung này nằm trong chủ đề AI, ML & Data Engineeringchủ đề liên quan
Nội dung liên quan
Bản tin InfoQTổng hợp nội dung của tuần trước trên InfoQ được gửi vào thứ Ba hàng tuần. Tham gia cộng đồng hơn 250.000 nhà phát triển cấp cao. Xem một ví dụ Nhập địa chỉ email của bạn Chọn quốc gia của bạn Tôi đồng ý với InfoQ. com xử lý dữ liệu của tôi như được giải thích trong Thông báo về quyền riêng tư này. Chúng tôi bảo vệ quyền riêng tư của bạn xin chào người lạBạn cần Đăng ký tài khoản InfoQ hoặc Đăng nhập hoặc đăng nhập để gửi bình luận. Nhưng còn rất nhiều điều đằng sau việc đăng ký Tận dụng tối đa trải nghiệm InfoQ Hãy cho chúng tôi biết bạn nghĩ gì
html được phép. a,b,br,blockquote,i,li,pre,u,ul,p Gửi email trả lời cho bất kỳ tin nhắn nào của tôi trong chủ đề này Nhận xét của cộng đồngchủ đề xem
|