Trong trường hợp nào sau đây bạn không nên nhúng tài liệu này vào tài liệu khác trong MongoDB?

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à

  1. 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ể

  2. 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

  3. 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 . 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

  1. (``discussion_id , đã đăng )`` và
  2. (``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. 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ì trangsố 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 pymongo
0

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 pymongo
1

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()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 pymongo
2

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 pymongo
3

Ở đâ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=300limit=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 pymongo
4

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_idnhậ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 pymongo
5

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 pymongo
6

Điều này sẽ trả về phản hồi sau

>>> import bson
>>> import pymongo
7

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

Khi nào chúng ta nên nhúng tài liệu này với tài liệu khác trong MongoDB?

Tài liệu MongoDB được nhúng hoặc lồng nhau là một tài liệu bình thường được lồng bên trong một tài liệu khác trong bộ sưu tập MongoDB. Tài liệu được nhúng đặc biệt hữu ích khi tồn tại mối quan hệ một-nhiều giữa các tài liệu .

MongoDB không phù hợp với những tình huống nào?

Một trong những nhược điểm của MongoDB là không hỗ trợ giao dịch . Mặc dù ngày càng có ít ứng dụng yêu cầu giao dịch nhưng vẫn có một số ứng dụng cần giao dịch để cập nhật nhiều tài liệu/bộ sưu tập. Nếu đó là chức năng cần thiết cho nhóm của bạn thì không nên sử dụng MongoDB.

Tại sao MongoDB sử dụng tài liệu nhúng?

Tài liệu nhúng là một cách hiệu quả và rõ ràng để lưu trữ dữ liệu liên quan, đặc biệt là dữ liệu thường xuyên được truy cập cùng nhau . Nói chung, khi thiết kế lược đồ cho MongoDB, bạn nên nhúng theo mặc định và chỉ sử dụng các tham chiếu và phép nối phía ứng dụng hoặc phía cơ sở dữ liệu khi chúng đáng giá.

Giới hạn của một tài liệu trong MongoDB là gì?

Kích thước tài liệu BSON tối đa là 16 megabyte . Kích thước tài liệu tối đa giúp đảm bảo rằng một tài liệu không thể sử dụng quá nhiều RAM hoặc quá nhiều băng thông trong quá trình truyền. Để lưu trữ tài liệu lớn hơn kích thước tối đa, MongoDB cung cấp GridFS API.