Mối quan hệ MongoDB: nhúng hoặc tham chiếu?


524

Tôi mới biết về MongoDB - đến từ nền tảng cơ sở dữ liệu quan hệ. Tôi muốn thiết kế cấu trúc câu hỏi với một số nhận xét, nhưng tôi không biết nên sử dụng mối quan hệ nào để nhận xét: embedhay reference?

Một câu hỏi với một số ý kiến, như stackoverflow , sẽ có cấu trúc như thế này:

Question
    title = 'aaa'
    content = bbb'
    comments = ???

Đầu tiên, tôi muốn sử dụng các nhận xét được nhúng (tôi nghĩ embedđược khuyến nghị trong MongoDB), như thế này:

Question
    title = 'aaa'
    content = 'bbb'
    comments = [ { content = 'xxx', createdAt = 'yyy'}, 
                 { content = 'xxx', createdAt = 'yyy'}, 
                 { content = 'xxx', createdAt = 'yyy'} ]

Rõ ràng, nhưng tôi lo lắng về trường hợp này: Nếu tôi muốn chỉnh sửa một nhận xét được chỉ định, làm thế nào để tôi có được nội dung và câu hỏi của nó? Không có _idđể cho tôi tìm một, cũng không question_refđể tôi tìm câu hỏi của nó. (Tôi là người mới, đến nỗi tôi không biết có cách nào để làm điều này mà không _idquestion_ref.)

Tôi có phải sử dụng refkhông embed? Sau đó tôi phải tạo một bộ sưu tập mới cho ý kiến?


Tất cả các đối tượng Mongo được tạo bằng _ID, cho dù bạn có tạo trường hay không. Vì vậy, về mặt kỹ thuật, mỗi bình luận vẫn sẽ có một ID.
Robbie Guilfoyle

25
@RobbieGuilfoyle không đúng-- xem stackoverflow.com/a/11263912/347455
pennstatephil

13
Tôi đứng chính xác, cảm ơn @pennstatephil :)
Robbie Guilfoyle

4
Điều anh ta có thể có nghĩa là tất cả các đối tượng cầy mangut được tạo ra với một _id cho những người sử dụng khung này - xem các nhánh con của cầy mangut
Luca Steeb

1
Một cuốn sách rất hay để tìm hiểu các mối quan hệ mongo db là "Các mẫu thiết kế ứng dụng MongoDB - O'Reilly". Chương một, nói về quyết định này, để nhúng hoặc tham khảo?
Felipe Toledo

Câu trả lời:


769

Đây là một nghệ thuật hơn là một khoa học. Các tài liệu Mongo trên Schemas là một tài liệu tham khảo tốt, nhưng đây là một số điều cần xem xét:

  • Đặt càng nhiều càng tốt

    Niềm vui của cơ sở dữ liệu Tài liệu là nó giúp loại bỏ rất nhiều Joins. Bản năng đầu tiên của bạn nên đặt càng nhiều trong một tài liệu càng tốt. Bởi vì các tài liệu MongoDB có cấu trúc và bởi vì bạn có thể truy vấn một cách hiệu quả trong cấu trúc đó (điều này có nghĩa là bạn có thể lấy một phần tài liệu mà bạn cần, vì vậy kích thước tài liệu không nên làm bạn lo lắng nhiều) không cần phải bình thường hóa dữ liệu như bạn sẽ làm trong SQL. Cụ thể, bất kỳ dữ liệu nào không hữu ích ngoài tài liệu gốc của nó phải là một phần của cùng một tài liệu.

  • Dữ liệu riêng biệt có thể được tham chiếu từ nhiều nơi vào bộ sưu tập của riêng mình.

    Đây không phải là vấn đề "không gian lưu trữ" vì đây là vấn đề "nhất quán dữ liệu". Nếu nhiều bản ghi sẽ tham chiếu đến cùng một dữ liệu thì sẽ hiệu quả hơn và ít bị lỗi hơn khi cập nhật một bản ghi và giữ các tham chiếu đến nó ở những nơi khác.

  • Cân nhắc kích thước tài liệu

    MongoDB áp đặt giới hạn kích thước 4 MB (16 MB với 1,8) cho một tài liệu. Trong một thế giới GB dữ liệu nghe có vẻ nhỏ, nhưng nó cũng là 30 nghìn tweet hoặc 250 câu trả lời Stack Overflow điển hình hoặc 20 ảnh nhấp nháy. Mặt khác, đây là thông tin nhiều hơn nhiều so với một lần có thể muốn trình bày trên một trang web điển hình. Đầu tiên hãy xem xét những gì sẽ làm cho các truy vấn của bạn dễ dàng hơn. Trong nhiều trường hợp mối quan tâm về kích thước tài liệu sẽ được tối ưu hóa sớm.

  • Cấu trúc dữ liệu phức tạp:

    MongoDB có thể lưu trữ các cấu trúc dữ liệu lồng nhau sâu tùy ý, nhưng không thể tìm kiếm chúng một cách hiệu quả. Nếu dữ liệu của bạn tạo thành cây, rừng hoặc biểu đồ, bạn thực sự cần lưu trữ từng nút và các cạnh của nó trong một tài liệu riêng. (Lưu ý rằng có các cửa hàng dữ liệu được thiết kế riêng cho loại dữ liệu này mà người ta cũng nên xem xét)

    Nó cũng đã được chỉ ra rằng không thể trả lại một tập hợp các phần tử trong tài liệu. Nếu bạn cần chọn và chọn một vài bit của mỗi tài liệu, việc tách chúng ra sẽ dễ dàng hơn.

  • Tính nhất quán của dữ liệu

    MongoDB đánh đổi giữa hiệu quả và tính nhất quán. Quy tắc là các thay đổi đối với một tài liệu duy nhất luôn là nguyên tử, trong khi các cập nhật cho nhiều tài liệu không bao giờ được coi là nguyên tử. Cũng không có cách nào để "khóa" bản ghi trên máy chủ (bạn có thể xây dựng bản ghi này thành logic của máy khách bằng cách sử dụng trường "khóa"). Khi bạn thiết kế lược đồ của mình, hãy xem xét cách bạn sẽ giữ dữ liệu của mình nhất quán. Nói chung, bạn càng giữ trong tài liệu càng tốt.

Đối với những gì bạn đang mô tả, tôi sẽ nhúng các bình luận và cung cấp cho mỗi bình luận một trường id với ObjectID. ObjectID có dấu thời gian được nhúng trong đó để bạn có thể sử dụng nó thay vì được tạo nếu bạn muốn.


1
Tôi muốn thêm vào câu hỏi OP: Mô hình nhận xét của tôi chứa tên người dùng và liên kết đến hình đại diện của anh ấy. Điều gì sẽ là cách tiếp cận tốt nhất, xem xét người dùng có thể sửa đổi tên / hình đại diện của mình?
1102018

5
Về 'Cấu trúc dữ liệu phức tạp', có vẻ như có thể trả về một tập hợp con các phần tử trong tài liệu bằng cách sử dụng khung tổng hợp (thử $ relax).
Eyal Roth

4
Errr, Kỹ thuật này không phải là possibel hoặc không được biết đến rộng rãi trong MongoDB vào đầu năm 2012. Với sự phổ biến của câu hỏi này, tôi sẽ khuyến khích bạn viết câu trả lời cập nhật của riêng bạn. Tôi sợ rằng tôi đã rời khỏi sự phát triển tích cực trên MongoDB và tôi không ở vị trí tốt để giải quyết nhận xét của bạn trong bài viết gốc của mình.
John F. Miller

54
16MB = 30 triệu tweet? Mens khoảng 0,5 byte mỗi tweet?!
Paolo

8
Vâng, có vẻ như tôi đã bị giảm 1000 và một số người thấy điều này quan trọng. Tôi sẽ chỉnh sửa bài viết. WRT 560byte mỗi tweet, khi tôi sử dụng nó vào năm 2011, twitter vẫn được gắn với tin nhắn văn bản và chuỗi Ruby 1.4; nói cách khác vẫn chỉ ký tự ASCII.
John F. Miller

39

Nói chung, nhúng là tốt nếu bạn có mối quan hệ một-một hoặc nhiều-nhiều giữa các thực thể và tham chiếu là tốt nếu bạn có nhiều mối quan hệ nhiều-nhiều.


10
bạn có thể vui lòng thêm một liên kết tham khảo? Cảm ơn.
db80

Làm thế nào để bạn tìm thấy một nhận xét cụ thể với thiết kế này từ một đến nhiều?
Mauricio Past dáng


29

Nếu tôi muốn chỉnh sửa một nhận xét được chỉ định, làm thế nào để có được nội dung và câu hỏi của nó?

Bạn có thể truy vấn bằng tài liệu phụ : db.question.find({'comments.content' : 'xxx'}).

Điều này sẽ trả về toàn bộ tài liệu Câu hỏi. Để chỉnh sửa nhận xét đã chỉ định, sau đó bạn phải tìm nhận xét trên máy khách, thực hiện chỉnh sửa và lưu lại vào DB.

Nói chung, nếu tài liệu của bạn chứa một mảng các đối tượng, bạn sẽ thấy rằng các đối tượng phụ đó sẽ cần phải được sửa đổi phía máy khách.


4
Điều này sẽ không hoạt động nếu hai bình luận có nội dung giống hệt nhau. người ta có thể lập luận rằng chúng tôi cũng có thể thêm tác giả vào truy vấn tìm kiếm, điều này vẫn không hiệu quả nếu tác giả đưa ra hai nhận xét giống hệt nhau có cùng nội dung
Steel Brain

@SteelBrain: nếu anh ấy đã giữ chỉ số nhận xét, ký hiệu chấm có thể giúp ích. xem stackoverflow.com/a/33284416/1587329
serv-inc

13
Tôi không hiểu làm thế nào câu trả lời này có 34 upvote, nhiều người thứ hai nhận xét cùng một điều mà toàn bộ hệ thống sẽ phá vỡ. Đây là một thiết kế hoàn toàn khủng khiếp và không bao giờ nên được sử dụng. Cách @user thực hiện là cách để đi
user2073973

21

Chà, tôi hơi muộn nhưng vẫn muốn chia sẻ cách tạo lược đồ của mình.

Tôi có các lược đồ cho tất cả mọi thứ có thể được mô tả bằng một từ, giống như bạn sẽ làm điều đó trong OOP cổ điển.

VÍ DỤ

  • Bình luận
  • Tài khoản
  • Người dùng
  • Bài viết trên blog
  • ...

Mọi lược đồ có thể được lưu dưới dạng Tài liệu hoặc Subdocument, vì vậy tôi khai báo điều này cho mỗi lược đồ.

Tài liệu:

  • Có thể được sử dụng như một tài liệu tham khảo. (Ví dụ: người dùng đã nhận xét -> nhận xét có tham chiếu "được tạo bởi" cho người dùng)
  • Là một "Root" trong ứng dụng của bạn. (Ví dụ: blogpost -> có một trang về blogpost)

Phân ngành:

  • Chỉ có thể được sử dụng một lần / không bao giờ là một tài liệu tham khảo. (Ví dụ: Nhận xét được lưu trong blogpost)
  • Không bao giờ là một "Root" trong ứng dụng của bạn. (Nhận xét chỉ hiển thị trong trang blogpost nhưng trang vẫn là về blogpost)

20

Tôi đã xem qua bài thuyết trình nhỏ này trong khi tự mình nghiên cứu câu hỏi này. Tôi đã ngạc nhiên về cách nó được đặt ra, cả thông tin và cách trình bày về nó.

http://openmymind.net/Multipl-Collections-Versus-Embedded-Document

Nó tóm tắt:

Theo nguyên tắc chung, nếu bạn có nhiều [tài liệu con] hoặc nếu chúng lớn, một bộ sưu tập riêng biệt có thể là tốt nhất.

Các tài liệu nhỏ hơn và / hoặc ít hơn có xu hướng phù hợp tự nhiên để nhúng.


11
Bao nhiêu là a lot? 3? 10? 100? Có gì large? 1kb? 1 MB? 3 lĩnh vực? 20 lĩnh vực? Là gì smaller/ fewer?
Traxo

1
Đó là một câu hỏi hay và tôi không có câu trả lời cụ thể. Bài thuyết trình tương tự bao gồm một slide có nội dung "Một tài liệu, bao gồm tất cả các tài liệu và mảng được nhúng của nó, không thể vượt quá 16 MB", do đó có thể là điểm cắt của bạn hoặc chỉ đi với những gì có vẻ hợp lý / thoải mái cho tình huống cụ thể của bạn. Trong dự án hiện tại của tôi, phần lớn các tài liệu được nhúng dành cho các mối quan hệ 1: 1 hoặc 1: nhiều trong đó các tài liệu được nhúng thực sự đơn giản.
Chris Bloom

Xem thêm nhận xét hàng đầu hiện tại của @ john-f-miller, trong khi không cung cấp số cụ thể cho ngưỡng có chứa một số gợi ý bổ sung sẽ giúp hướng dẫn quyết định của bạn.
Chris Bloom

16

Tôi biết điều này khá cũ nhưng nếu bạn đang tìm câu trả lời cho câu hỏi của OP về cách chỉ trả lại nhận xét được chỉ định, bạn có thể sử dụng toán tử $ (truy vấn) như thế này:

db.question.update({'comments.content': 'xxx'}, {'comments.$': true})

4
Điều này sẽ không hoạt động nếu hai bình luận có nội dung giống hệt nhau. người ta có thể lập luận rằng chúng tôi cũng có thể thêm tác giả vào truy vấn tìm kiếm, điều này vẫn không hiệu quả nếu tác giả đưa ra hai nhận xét giống hệt nhau với cùng một nội dung
Steel Brain

1
@SteelBrain: Chơi tốt thưa ngài, chơi tốt.
JakeStrang

12

Có, chúng tôi có thể sử dụng tài liệu tham khảo trong tài liệu. Để điền vào tài liệu khác giống như sql tôi tham gia. Trong mongo db họ không tham gia để ánh xạ một đến nhiều tài liệu về mối quan hệ. Thay vào đó chúng tôi có thể sử dụng dân số để thực hiện kịch bản của mình ..

var mongoose = require('mongoose')
  , Schema = mongoose.Schema

var personSchema = Schema({
  _id     : Number,
  name    : String,
  age     : Number,
  stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});

var storySchema = Schema({
  _creator : { type: Number, ref: 'Person' },
  title    : String,
  fans     : [{ type: Number, ref: 'Person' }]
});

Dân số là quá trình tự động thay thế các đường dẫn được chỉ định trong tài liệu bằng (các) tài liệu từ các bộ sưu tập khác. Chúng tôi có thể điền vào một tài liệu, nhiều tài liệu, đối tượng đơn giản, nhiều đối tượng đơn giản hoặc tất cả các đối tượng được trả về từ một truy vấn. Hãy xem xét một số ví dụ.

Tốt hơn bạn có thể nhận thêm thông tin vui lòng truy cập: http://mongoosejs.com/docs/population.html


5
Mongoose sẽ đưa ra một yêu cầu riêng cho từng trường dân cư. Điều này khác với SQL THAM GIA khi chúng được thực hiện trên máy chủ. Điều này bao gồm lưu lượng truy cập thêm giữa máy chủ ứng dụng và máy chủ mongodb. Một lần nữa, bạn có thể cân nhắc điều này khi bạn tối ưu hóa. Tuy nhiên, anwser của bạn vẫn đúng.
Tối đa

6

Trên thực tế, tôi khá tò mò tại sao không ai nói về các thông số kỹ thuật của UML. Một nguyên tắc nhỏ là nếu bạn có tổng hợp, thì bạn nên sử dụng tài liệu tham khảo. Nhưng nếu nó là một thành phần, thì khớp nối mạnh hơn và bạn nên sử dụng các tài liệu nhúng.

Và bạn sẽ nhanh chóng hiểu tại sao nó hợp lý. Nếu một đối tượng có thể tồn tại độc lập với cha mẹ, thì bạn sẽ muốn truy cập nó ngay cả khi cha mẹ không tồn tại. Vì bạn không thể nhúng nó vào cha mẹ không tồn tại, bạn phải làm cho nó sống trong cấu trúc dữ liệu của chính nó. Và nếu cha mẹ tồn tại, chỉ cần liên kết chúng lại với nhau bằng cách thêm một ref của đối tượng trong cha mẹ.

Không thực sự biết sự khác biệt giữa hai mối quan hệ là gì? Đây là một liên kết giải thích chúng: Tổng hợp và Thành phần trong UML


Tại sao -1? Xin vui lòng đưa ra một lời giải thích sẽ làm rõ lý do
Bonjour123


1

Nếu tôi muốn chỉnh sửa một nhận xét được chỉ định, làm thế nào để tôi có được nội dung và câu hỏi của nó?

Nếu bạn đã theo dõi số lượng bình luận và chỉ số của bình luận bạn muốn thay đổi, bạn có thể sử dụng toán tử dấu chấm ( ví dụ SO ).

Bạn có thể làm f.ex.

db.questions.update(
    {
        "title": "aaa"       
    }, 
    { 
        "comments.0.contents": "new text"
    }
)

(như một cách khác để chỉnh sửa các bình luận bên trong câu hỏi)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.