Tài liệu này phác thảo các mẫu cơ bản để lưu trữ nhận xét do người dùng gửi trong hệ thống quản lý nội dung [CMS. ]
Tổng quan
MongoDB cung cấp một số cách tiếp cận khác nhau để lưu trữ dữ liệu như nhận xét của người dùng về nội dung từ CMS. Không có cách triển khai chính xác, nhưng có một số cách tiếp cận phổ biến và những cân nhắc đã biết cho từng cách tiếp cận. Nghiên cứu điển hình này khám phá các chi tiết triển khai và sự đánh đổi của từng tùy chọn. Ba mẫu cơ bản là
Lưu trữ mỗi bình luận trong riêng của mình
Cách tiếp cận này cung cấp tính linh hoạt cao nhất với chi phí phức tạp ở cấp độ ứng dụng bổ sung
Những triển khai này giúp hiển thị các nhận xét theo trình tự thời gian hoặc theo chuỗi và không hạn chế số lượng nhận xét được đính kèm với một đối tượng cụ thể
Nhúng tất cả các nhận xét vào tài liệu “cha mẹ”
Cách tiếp cận này cung cấp hiệu suất cao nhất có thể để hiển thị nhận xét với chi phí linh hoạt. cấu trúc của các nhận xét trong tài liệu kiểm soát định dạng hiển thị
注解
Vì , các tài liệu, bao gồm nội dung gốc và tất cả các nhận xét, không thể vượt quá 16 megabyte
Một thiết kế kết hợp, lưu trữ các nhận xét riêng biệt với "cha mẹ", nhưng tổng hợp các nhận xét vào một số ít tài liệu, trong đó mỗi tài liệu chứa nhiều nhận xét
Ngoài ra, hãy xem xét rằng các nhận xét có thể được tạo thành chuỗi, trong đó các nhận xét luôn là câu trả lời cho mục "gốc" hoặc một nhận xét khác, chứa các yêu cầu kiến trúc nhất định được thảo luận bên dưới
Một tài liệu cho mỗi bình luận
Lược đồ
Nếu bạn lưu trữ từng nhận xét trong tài liệu riêng của nó, các tài liệu trong bộ sưu tập nhận xét của bạn sẽ có cấu trúc như sau.
{ _id: ObjectId[...], discussion_id: ObjectId[...], slug: '34db', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }
Biểu mẫu này chỉ phù hợp để hiển thị nhận xét theo thứ tự thời gian. Cửa hàng bình luận
- trường discussion_id tham chiếu đến phần chính của cuộc thảo luận,
- mã định danh sên tương thích với URL,
- a đã đăng ,
- một tác giả tài liệu phụ chứa tham chiếu đến hồ sơ của người dùng trong id field and their name in the name field, and
- toàn bộ văn bản của nhận xét.
Để hỗ trợ nhận xét theo luồng, bạn có thể sử dụng cấu trúc hơi khác như sau
{ _id: ObjectId[...], discussion_id: ObjectId[...], parent_id: ObjectId[...], slug: '34db/8bda' full_slug: '2012.02.08.12.21.08:34db/2012.02.09.22.19.16:8bda', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }
cấu trúc này
- thêm trường parent_id lưu trữ nội dung của _id field of the parent comment,
- sửa đổi trường slug để giữ một đường dẫn gồm sên cha hoặc sên của cha mẹ và sên duy nhất của nhận xét này, và
- thêm trường full_slug kết hợp thông tin về sên và thời gian để dễ dàng sắp xếp tài liệu trong một cuộc thảo luận theo chuỗi theo ngày.
警告
MongoDB chỉ có thể lập chỉ mục. Điều này bao gồm tất cả dữ liệu trường, tên trường và không gian tên [i. e. tên cơ sở dữ liệu và tên bộ sưu tập. ] Điều này có thể trở thành sự cố khi bạn tạo chỉ mục của trường full_slug để hỗ trợ sắp xếp.
hoạt động
Phần này chứa thông tin tổng quan về các hoạt động phổ biến để tương tác với nhận xét được biểu thị bằng lược đồ trong đó mỗi nhận xét là của riêng nó
Tất cả các ví dụ trong tài liệu này sử dụng ngôn ngữ lập trình Python và PyMongo cho MongoDB, nhưng bạn có thể triển khai hệ thống này bằng bất kỳ ngôn ngữ nào bạn chọn. Đưa ra các lệnh sau tại trình bao Python tương tác để tải các thư viện cần thiết
>>> import bson >>> import pymongo
Đăng bình luận mới
Để đăng bình luận mới theo trình tự thời gian [i. e. không phân luồng], hãy sử dụng thao tác insert[] sau.
slug = generate_pseudorandom_slug[] db.comments.insert[{ 'discussion_id': discussion_id, 'slug': slug, 'posted': datetime.utcnow[], 'author': author_info, 'text': comment_text }]
Để chèn nhận xét cho hệ thống có nhận xét theo chuỗi, bạn phải tạo đường dẫn slug và . Xem thao tác sau. at insert. See the following operation:
posted = datetime.utcnow[] # generate the unique portions of the slug and full_slug slug_part = generate_pseudorandom_slug[] full_slug_part = posted.strftime['%Y.%m.%d.%H.%M.%S'] + ':' + slug_part # load the parent comment [if any] if parent_slug: parent = db.comments.find_one[ {'discussion_id': discussion_id, 'slug': parent_slug }] slug = parent['slug'] + '/' + slug_part full_slug = parent['full_slug'] + '/' + full_slug_part else: slug = slug_part full_slug = full_slug_part # actually insert the comment db.comments.insert[{ 'discussion_id': discussion_id, 'slug': slug, 'full_slug': full_slug, 'posted': posted, 'author': author_info, 'text': comment_text }]
Xem các bình luận được phân trang
Để xem các nhận xét không theo chuỗi, hãy chọn tất cả các nhận xét tham gia thảo luận và sắp xếp theo trường đã đăng . Ví dụ.
cursor = db.comments.find[{'discussion_id': discussion_id}] cursor = cursor.sort['posted'] cursor = cursor.skip[page_num * page_size] cursor = cursor.limit[page_size]
Vì trường full_slug chứa cả thông tin phân cấp [thông qua đường dẫn] và thông tin theo trình tự thời gian nên bạn có thể sử dụng cách sắp xếp đơn giản trên full_slug field to retrieve a threaded view:
cursor = db.comments.find[{'discussion_id': discussion_id}] cursor = cursor.sort['full_slug'] cursor = cursor.skip[page_num * page_size] cursor = cursor.limit[page_size]
也可以参考
, , và
lập chỉ mục
Để hỗ trợ các truy vấn trên một cách hiệu quả, hãy duy trì hai chỉ mục phức hợp, trên
- [``discussion_id , đã đăng ]`` và
- [``discussion_id , full_slug ]``
Đưa ra thao tác sau tại trình bao Python tương tác
>>> db.comments.ensure_index[[ .. ['discussion_id', 1], ['posted', 1]]] >>> db.comments.ensure_index[[ .. ['discussion_id', 1], ['full_slug', 1]]]
注解
Đảm bảo rằng bạn luôn sắp xếp theo phần tử cuối cùng trong chỉ mục tổng hợp để tối đa hóa hiệu suất của các truy vấn này
Truy xuất Nhận xét qua Liên kết Trực tiếp
Truy vấn
Để truy xuất trực tiếp một nhận xét mà không cần phải lật từng trang qua tất cả các nhận xét, bạn có thể chọn theo trường slug .
________số 8_______
Bạn có thể truy xuất một "thảo luận phụ" hoặc một nhận xét và tất cả các phần tử con của nó theo cách đệ quy bằng cách thực hiện truy vấn tiền tố biểu thức chính quy trên full_slug field:
import re subdiscussion = db.comments.find_one[{ 'discussion_id': discussion_id, 'full_slug': re.compile['^' + re.escape[parent_slug]] }] subdiscussion = subdiscussion.sort['full_slug']
lập chỉ mục
Vì bạn đã tạo chỉ mục trên { discussion_id. 1, full_slug. } để hỗ trợ truy xuất các cuộc thảo luận phụ, bạn có thể thêm hỗ trợ cho các truy vấn trên bằng cách thêm chỉ mục trên {< . discussion_id: 1 , sên. 1 } . Sử dụng thao tác sau trong trình bao Python.
{ _id: ObjectId[...], discussion_id: ObjectId[...], parent_id: ObjectId[...], slug: '34db/8bda' full_slug: '2012.02.08.12.21.08:34db/2012.02.09.22.19.16:8bda', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }0
Nhúng tất cả bình luận
Thiết kế này nhúng toàn bộ cuộc thảo luận của một chuỗi nhận xét bên trong chủ đề. Trong ví dụ này, tài liệu "chủ đề" chứa toàn bộ nội dung cho bất kỳ nội dung nào bạn đang quản lý
Lược đồ
Hãy xem xét tài liệu chủ đề nguyên mẫu sau.
{ _id: ObjectId[...], discussion_id: ObjectId[...], parent_id: ObjectId[...], slug: '34db/8bda' full_slug: '2012.02.08.12.21.08:34db/2012.02.09.22.19.16:8bda', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }1
Cấu trúc này chỉ phù hợp để hiển thị tất cả các bình luận theo trình tự thời gian vì nó nhúng các bình luận theo thứ tự thời gian. Mỗi tài liệu trong mảng trong bình luận chứa ngày, tác giả và văn bản của bình luận.
注解
Vì bạn đang lưu trữ các nhận xét theo thứ tự được sắp xếp, nên không cần phải duy trì sên cho mỗi nhận xét
Để hỗ trợ phân luồng bằng cách sử dụng thiết kế này, bạn cần nhúng nhận xét vào trong nhận xét, sử dụng cấu trúc giống như sau
{ _id: ObjectId[...], discussion_id: ObjectId[...], parent_id: ObjectId[...], slug: '34db/8bda' full_slug: '2012.02.08.12.21.08:34db/2012.02.09.22.19.16:8bda', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }2
Ở đây, trường trả lời trong mỗi nhận xét chứa các nhận xét phụ, trường này có thể chứa các nhận xét phụ.
注解
Trong thiết kế tài liệu nhúng, bạn từ bỏ một số tính linh hoạt về định dạng hiển thị, vì rất khó hiển thị nhận xét trừ khi bạn lưu trữ chúng trong MongoDB
Nếu trong tương lai, bạn muốn chuyển từ trình tự thời gian sang luồng hoặc từ luồng sang trình tự thời gian, thiết kế này sẽ khiến việc di chuyển đó trở nên khá tốn kém
警告
Hãy nhớ rằng các tài liệu có một. Nếu các cuộc thảo luận phổ biến lớn hơn 16 megabyte, thì việc tăng tài liệu bổ sung sẽ không thành công
Ngoài ra, khi tài liệu MongoDB phát triển đáng kể sau khi tạo, bạn sẽ gặp phải tình trạng phân mảnh bộ nhớ lớn hơn và hiệu suất cập nhật bị giảm sút trong khi MongoDB di chuyển tài liệu nội bộ
hoạt động
Phần này chứa thông tin tổng quan về các thao tác phổ biến để tương tác với nhận xét được biểu thị bằng giản đồ nhúng tất cả nhận xét của nội dung chủ đề hoặc "cha mẹ"
注解
Đối với tất cả các thao tác bên dưới, không cần bất kỳ chỉ mục mới nào vì tất cả các thao tác đều hoạt động trong tài liệu. Vì bạn sẽ truy xuất các tài liệu này theo trường _id , nên bạn có thể dựa vào chỉ mục mà MongoDB tự động tạo.
Đăng bình luận mới
Để đăng bình luận mới theo trình tự thời gian [i. e chưa theo luồng], bạn cần có bản cập nhật[] sau.
{ _id: ObjectId[...], discussion_id: ObjectId[...], parent_id: ObjectId[...], slug: '34db/8bda' full_slug: '2012.02.08.12.21.08:34db/2012.02.09.22.19.16:8bda', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }3
Toán tử chèn nhận xét vào mảng nhận xét theo đúng thứ tự thời gian. Đối với các cuộc thảo luận theo chuỗi, thao tác update[] phức tạp hơn. Để trả lời một bình luận, đoạn mã sau giả định rằng nó có thể truy xuất 'đường dẫn' dưới dạng danh sách các vị trí, cho bình luận gốc.
{ _id: ObjectId[...], discussion_id: ObjectId[...], parent_id: ObjectId[...], slug: '34db/8bda' full_slug: '2012.02.08.12.21.08:34db/2012.02.09.22.19.16:8bda', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }4
Điều này tạo tên trường có dạng trả lời. 0. trả lời. 2. là str_path rồi sử dụng giá trị này với toán tử để chèn nhận xét mới vào nhận xét chính replies array.
Xem các bình luận được phân trang
Để xem nhận xét trong thiết kế không theo luồng, bạn phải sử dụng toán tử $slice .
{ _id: ObjectId[...], discussion_id: ObjectId[...], parent_id: ObjectId[...], slug: '34db/8bda' full_slug: '2012.02.08.12.21.08:34db/2012.02.09.22.19.16:8bda', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }5
Để trả lại nhận xét được đánh số trang cho thiết kế theo luồng, bạn phải truy xuất toàn bộ tài liệu và đánh số trang nhận xét trong ứng dụng
{ _id: ObjectId[...], discussion_id: ObjectId[...], parent_id: ObjectId[...], slug: '34db/8bda' full_slug: '2012.02.08.12.21.08:34db/2012.02.09.22.19.16:8bda', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }6
Truy xuất Nhận xét qua Liên kết Trực tiếp
Thay vì truy xuất nhận xét thông qua sên như trên, ví dụ sau truy xuất nhận xét bằng cách sử dụng vị trí của chúng trong danh sách hoặc cây nhận xét
Theo thứ tự thời gian [i. e. không phân luồng], chỉ cần sử dụng toán tử $slice để trích xuất một nhận xét, như sau.
{ _id: ObjectId[...], discussion_id: ObjectId[...], parent_id: ObjectId[...], slug: '34db/8bda' full_slug: '2012.02.08.12.21.08:34db/2012.02.09.22.19.16:8bda', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }7
Đối với nhận xét theo luồng, bạn phải tìm đúng đường dẫn qua cây trong ứng dụng của mình, như sau
{ _id: ObjectId[...], discussion_id: ObjectId[...], parent_id: ObjectId[...], slug: '34db/8bda' full_slug: '2012.02.08.12.21.08:34db/2012.02.09.22.19.16:8bda', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }8
注解
Vì nhận xét của cha mẹ nhúng câu trả lời của con cái, thao tác này thực sự truy xuất toàn bộ cuộc thảo luận phụ cho nhận xét mà bạn đã truy vấn
Thấy
find_one[] .
Thiết kế lược đồ lai
Lược đồ
Trong “phương pháp kết hợp”, bạn sẽ lưu trữ nhận xét trong “nhóm” chứa khoảng 100 nhận xét. Xem xét tài liệu ví dụ sau
{ _id: ObjectId[...], discussion_id: ObjectId[...], parent_id: ObjectId[...], slug: '34db/8bda' full_slug: '2012.02.08.12.21.08:34db/2012.02.09.22.19.16:8bda', posted: ISODateTime[...], author: { id: ObjectId[...], name: 'Rick' }, text: 'This is so bogus .. ' }9
Mỗi tài liệu duy trì trang và số lượng dữ liệu chứa . comments array that holds the comments themselves.
注解
Việc sử dụng định dạng kết hợp làm cho việc lưu trữ các nhận xét theo luồng trở nên phức tạp và cấu hình cụ thể này không được đề cập trong tài liệu này
Ngoài ra, 100 bình luận là giới hạn mềm cho số lượng bình luận trên mỗi trang. Giá trị này là tùy ý. chọn một giá trị sẽ ngăn không cho kích thước tài liệu tối đa vượt quá 16MB, nhưng đủ lớn để đảm bảo rằng hầu hết các luồng nhận xét sẽ vừa với một tài liệu. Trong một số trường hợp, số lượng nhận xét trên mỗi tài liệu có thể vượt quá 100, nhưng điều này không ảnh hưởng đến tính chính xác của mẫu
hoạt động
Phần này chứa một số thao tác phổ biến mà bạn có thể sử dụng khi xây dựng một CMS sử dụng mô hình lưu trữ kết hợp này với các tài liệu chứa 100 trang nhận xét “. ”
Tất cả các ví dụ trong tài liệu này sử dụng ngôn ngữ lập trình Python và PyMongo cho MongoDB, nhưng bạn có thể triển khai hệ thống này bằng bất kỳ ngôn ngữ nào bạn chọn
Đăng bình luận mới
Đang cập nhật
Để đăng bình luận mới, bạn cần bình luận trên trang cuối cùng và số lượng bình luận của trang đó. Xem xét ví dụ sau truy vấn dựa trên trường discussion_id .
>>> import bson >>> import pymongo0
Thao tác find_and_modify[] là một ,. nếu MongoDB không thể tìm thấy tài liệu có số trang chính xác, thì find_and_modify[] will create it and initialize the new document with appropriate values for count and comments.
Để giới hạn số lượng nhận xét trên mỗi trang ở mức khoảng 100, bạn sẽ cần tạo các trang mới khi cần thiết. Thêm logic sau để hỗ trợ điều này
>>> import bson >>> import pymongo1
Thao tác update[] này bao gồm số trang đã biết cuối cùng trong truy vấn để ngăn tình trạng cạnh tranh trong đó số trang tăng gấp đôi . Nếu một quy trình khác tăng số lượng trang, thì cập nhật ở trên không làm gì cả.
lập chỉ mục
Để hỗ trợ find_and_modify[] và update[] operations, maintain a compound index on [discussion_id, page] in the comment_pages collection, by issuing the following operation at the Python/PyMongo console:
>>> import bson >>> import pymongo2
Xem các bình luận được phân trang
Hàm sau xác định cách đánh số trang bình luận với kích thước trang cố định [i. e. không phải với khoảng 100 tài liệu nhận xét trong ví dụ trên,] như ví dụ
>>> import bson >>> import pymongo3
Ở đây, toán tử $slice lấy nhận xét từ mỗi trang, nhưng chỉ khi điều này thỏa mãn skip requirement. For example: if you have 3 pages with 100, 102, 101, and 22 comments on each page, and you wish to retrieve comments where skip=300 và limit=50. Use the following algorithm:
SkipLimitDiscussion30050 {$slice. [ 300, 50 ] . 20050 matches nothing in page #1; subtract page #1’s count from skip and continue.20050{$slice. [ 200, 50 ] . 9850 matches nothing in page #2; subtract page #2’s count from skip and continue.9850{$slice. [ 98, 50 ] . 048 matches 2 comments in page #3; subtract page #3’s count from skip [saturating at 0], subtract 2 from limit, and continue.048{$slice. [ 0, 48 ] . 026Không còn trang nào nữa; . matches all 22 comments in page #4; subtract 22 from limit and continue.026There are no more pages; terminate loop.注解
Vì bạn đã có một chỉ mục trên [ discussion_id , trang] in your comment_pages collection, MongoDB can satisfy these queries efficiently.
Truy xuất Nhận xét qua Liên kết Trực tiếp
Truy vấn
Để truy xuất nhận xét trực tiếp mà không cần phân trang qua tất cả các trang trước đó của nhận xét, hãy sử dụng sên để tìm đúng trang, sau đó sử dụng logic ứng dụng để tìm nhận xét chính xác
>>> import bson >>> import pymongo4
lập chỉ mục
Để thực hiện truy vấn này một cách hiệu quả, bạn sẽ cần một chỉ mục mới trên discussion_id và nhận xét. các trường slug [tôi. e. { discussion_id. 1 nhận xét. sên. 1 } . ] Tạo chỉ mục này bằng thao tác sau trong bảng điều khiển Python/PyMongo.
>>> import bson >>> import pymongo5
sharding
Đối với tất cả các kiến trúc được thảo luận ở trên, bạn sẽ muốn trường discussion_id tham gia vào khóa phân đoạn, nếu bạn cần phân đoạn .
Đối với các ứng dụng sử dụng phương pháp "một tài liệu cho mỗi nhận xét", hãy cân nhắc sử dụng slug [hoặc . Đưa ra thao tác sau tại bảng điều khiển Python/PyMongo. , in the case of threaded comments] fields in the shard key to allow the instances to route requests by slug. Issue the following operation at the Python/PyMongo console:
>>> import bson >>> import pymongo6
Điều này sẽ trả về phản hồi sau
>>> import bson >>> import pymongo7
Trong trường hợp nhận xét được nhúng hoàn toàn vào nội dung gốc, việc xác định khóa phân đoạn nằm ngoài phạm vi của tài liệu này