Đặt quan hệ MongoDB

Lược đồ linh hoạt của MongoDB cho phép nhiều mẫu khi mô hình hóa mối quan hệ giữa các thực thể. Ngoài ra, đối với nhiều trường hợp sử dụng, mô hình dữ liệu không chuẩn hóa (lưu trữ dữ liệu liên quan ngay trong một tài liệu) có thể là lựa chọn tốt nhất vì tất cả thông tin được lưu giữ ở một nơi, do đó ứng dụng yêu cầu ít truy vấn hơn để tìm nạp tất cả dữ liệu. Tuy nhiên, cách tiếp cận này cũng có những nhược điểm của nó, chẳng hạn như khả năng trùng lặp dữ liệu, tài liệu lớn hơn và kích thước tài liệu tối đa

Nói chung, MongoDB khuyên bạn nên sử dụng các mô hình dữ liệu được chuẩn hóa khi các ưu điểm của việc nhúng bị bỏ qua do tác động của sự trùng lặp. Trong bài đăng trên blog này, chúng tôi xem xét các khả năng khác nhau của việc liên kết các tài liệu với các tham chiếu thủ công và DBRef khi có nhu cầu làm việc với các mối quan hệ

DBRef là phần tử gốc của MongoDB để thể hiện các tham chiếu đến các tài liệu khác có định dạng rõ ràng

class Book {
    // ...
    private Publisher publisher;
}
5 chứa thông tin về cơ sở dữ liệu đích, bộ sưu tập và giá trị id của phần tử tham chiếu, phù hợp nhất để liên kết đến các tài liệu được phân phối trên các bộ sưu tập khác nhau

Mặt khác, các tham chiếu thủ công có cấu trúc đơn giản hơn (bằng cách chỉ lưu trữ id của tài liệu được tham chiếu), nhưng do đó, không linh hoạt khi nói đến các tham chiếu bộ sưu tập hỗn hợp

Sau khi thiết lập thuật ngữ, hãy giới thiệu các loại miền nổi tiếng, chẳng hạn như

class Book {
    // ...
    private Publisher publisher;
}
6 và
class Book {
    // ...
    private Publisher publisher;
}
7, và mối quan hệ rõ ràng của chúng với nhau

class Book {
    private String isbn13;
    private String title;
    private int pages;
}

class Publisher {
    private String name;
    private String arconym;
    private int foundationYear;
}

Nhúng

class Book {
    // ...
    private Publisher publisher;
}
7 vào trong mỗi và mọi
class Book {
    // ...
    private Publisher publisher;
}
6 không phải là một lựa chọn hấp dẫn, vì nó sẽ dẫn đến sao chép dữ liệu và tạo gánh nặng không cần thiết cho việc lưu trữ và khả năng bảo trì

class Book {
    // ...
    private Publisher publisher;
}

Mặc dù định dạng lưu trữ này cho phép cập nhật nguyên tử và mang lại sự linh hoạt nhất khi truy vấn các thuộc tính cụ thể, nhưng việc lặp lại thông tin

class Book {
    // ...
    private Publisher publisher;
}
7, như được hiển thị trong đoạn mã bên dưới, có thể không xứng đáng với chi phí này

________số 8

Điều tương tự cũng xảy ra với việc nhúng một bộ sưu tập

{
    "_id" : "617cfb",
    "isbn13" : "978-0345503800",
    "title" : "The Warded Man",
    "pages" : 432,
    "publisher" : {
        "name" : "Del Rey Books",
        "arconym" : "DRB",
        "foundationYear" : 1977
    }
}
1 trong
class Book {
    // ...
    private Publisher publisher;
}
7, điều này gây ra các tài liệu lớn không cần thiết. Bình thường hóa mô hình và làm việc với các tài liệu được liên kết có thể giảm thiểu sự cố

Bước đầu tiên là xác định hướng của mối quan hệ, để tìm ra phần nào của mối quan hệ cần giữ tham chiếu, nếu không phải cả hai. Quyết định này sẽ ảnh hưởng đến các tùy chọn tra cứu, lưu trữ và truy vấn của chúng tôi sau này

Liên kết với DBrefs

Trong trường hợp này,

class Book {
    // ...
    private Publisher publisher;
}
7 giữ các tham chiếu đến
{
    "_id" : "617cfb",
    "isbn13" : "978-0345503800",
    "title" : "The Warded Man",
    "pages" : 432,
    "publisher" : {
        "name" : "Del Rey Books",
        "arconym" : "DRB",
        "foundationYear" : 1977
    }
}
1 được liên kết. Ý tưởng là lưu trữ các tham chiếu đó dưới dạng một mảng trong tài liệu
class Book {
    // ...
    private Publisher publisher;
}
7

class Book {
    // ...
    private Publisher publisher;
}
4

Trong đoạn mã trên, thuộc tính sách được chú thích bằng

{
    "_id" : "617cfb",
    "isbn13" : "978-0345503800",
    "title" : "The Warded Man",
    "pages" : 432,
    "publisher" : {
        "name" : "Del Rey Books",
        "arconym" : "DRB",
        "foundationYear" : 1977
    }
}
6. Điều này khuyên lớp ánh xạ Dữ liệu mùa xuân lưu trữ các phần tử của thuộc tính dưới dạng các phần tử
{
    "_id" : "617cfb",
    "isbn13" : "978-0345503800",
    "title" : "The Warded Man",
    "pages" : 432,
    "publisher" : {
        "name" : "Del Rey Books",
        "arconym" : "DRB",
        "foundationYear" : 1977
    }
}
7 gốc của MongoDB, giống như sau

class Book {
    // ...
    private Publisher publisher;
}
7

Sử dụng chú thích

{
    "_id" : "617cfb",
    "isbn13" : "978-0345503800",
    "title" : "The Warded Man",
    "pages" : 432,
    "publisher" : {
        "name" : "Del Rey Books",
        "arconym" : "DRB",
        "foundationYear" : 1977
    }
}
6 cho phép chúng tôi giảm dung lượng lưu trữ bằng cách không lặp lại tất cả thông tin
class Book {
    // ...
    private Publisher publisher;
}
7 trong Sách, điều này tốt. Tuy nhiên, phương pháp này có nhược điểm của nó.
class Book {
    // ...
    private Publisher publisher;
}
6 không còn chứa thông tin về nhà xuất bản, điều này có thể ảnh hưởng đến các truy vấn tra cứu
{
    "_id" : "617cfb",
    "isbn13" : "978-0345503800",
    "title" : "The Warded Man",
    "pages" : 432,
    "publisher" : {
        "name" : "Del Rey Books",
        "arconym" : "DRB",
        "foundationYear" : 1977
    }
}
1 theo thuộc tính của
class Book {
    // ...
    private Publisher publisher;
}
7. Thay vào đó, việc thiếu tham chiếu ngược từ
class Book {
    // ...
    private Publisher publisher;
}
6 đến nhà xuất bản cũng sẽ ảnh hưởng đến hiệu suất khi tra cứu
class Book {
    // ...
    private Publisher publisher;
}
7 cho một
class Book {
    // ...
    private Publisher publisher;
}
6 nhất định, vì bây giờ chúng ta phải đưa ra một truy vấn đối với bộ sưu tập
class Book {
    // ...
    private Publisher publisher;
}
7 khớp với một
class Book {
    // ...
    private Publisher publisher;
}
47 so với trường
class Book {
    // ...
    private Publisher publisher;
}
48 của một nhà xuất bản, thay vào đó . Ngoài ra, mảng
class Book {
    // ...
    private Publisher publisher;
}
48 trong
class Book {
    // ...
    private Publisher publisher;
}
7 sử dụng một đối tượng phức tạp lưu trữ nhiều thông tin hơn mức cần thiết, trong khi một tham chiếu thủ công chỉ sử dụng id là đủ, vì tất cả các đối tượng tham chiếu được giữ trong cùng một bộ sưu tập đích

May mắn thay, có nhiều cách để cải thiện, bắt đầu bằng việc thêm tham chiếu ngược vào

class Book {
    // ...
    private Publisher publisher;
}
7, (ví dụ: bằng
class Book {
    // ...
    private Publisher publisher;
}
72 của nó)

class Book {
    private String isbn13;
    private String title;
    private int pages;
}

class Publisher {
    private String name;
    private String arconym;
    private int foundationYear;
}
3

Liên kết với Tài liệu tham khảo thủ công

Tiếp theo, hãy chuyển từ DBRef sang các tham chiếu thủ công để lưu trữ bộ sưu tập các tham chiếu

class Book {
    // ...
    private Publisher publisher;
}
6. Bước rõ ràng là xóa chú thích
{
    "_id" : "617cfb",
    "isbn13" : "978-0345503800",
    "title" : "The Warded Man",
    "pages" : 432,
    "publisher" : {
        "name" : "Del Rey Books",
        "arconym" : "DRB",
        "foundationYear" : 1977
    }
}
6 và thay thế
class Book {
    // ...
    private Publisher publisher;
}
75 bằng
class Book {
    // ...
    private Publisher publisher;
}
76, như trong đoạn trích bên dưới

class Book {
    private String isbn13;
    private String title;
    private int pages;
}

class Publisher {
    private String name;
    private String arconym;
    private int foundationYear;
}
8

class Book {
    private String isbn13;
    private String title;
    private int pages;
}

class Publisher {
    private String name;
    private String arconym;
    private int foundationYear;
}
9

Để thêm một

class Book {
    // ...
    private Publisher publisher;
}
6 mới vào trường
class Book {
    // ...
    private Publisher publisher;
}
78 của
class Book {
    // ...
    private Publisher publisher;
}
7, chúng ta có thể sử dụng câu lệnh sau

class Book {
    // ...
    private Publisher publisher;
}
3

Theo cách tiếp cận này sẽ tối ưu hóa định dạng lưu trữ và đưa ra tuyên bố rất rõ ràng về kiểu dữ liệu được sử dụng trong cả mô hình miền cũng như cơ sở dữ liệu. Tuy nhiên, chỉ

class Book {
    // ...
    private Publisher publisher;
}
78 không cung cấp cho bạn ngữ cảnh của bộ sưu tập để tra cứu các giá trị có trong trường
class Book {
    // ...
    private Publisher publisher;
}
78

Liên kết bằng cách sử dụng Tài liệu tham khảo thủ công khai báo

Bắt đầu với Spring Data MongoDB 3. 3. 0, các tham chiếu thủ công có thể được thể hiện theo cách khai báo bằng cách sử dụng chú thích

class Book {
    private String isbn13;
    private String title;
    private int pages;
}

class Publisher {
    private String name;
    private String arconym;
    private int foundationYear;
}
32

class Book {
    // ...
    private Publisher publisher;
}
7

Theo mặc định, điều này yêu cầu lớp ánh xạ trích xuất giá trị

class Book {
    // ...
    private Publisher publisher;
}
72 của thực thể được tham chiếu để lưu trữ, tự tải tài liệu được tham chiếu khi đọc

class Book {
    // ...
    private Publisher publisher;
}
0

Bởi vì lớp ánh xạ nhận thức được liên kết giữa các tài liệu, các câu lệnh cập nhật, chẳng hạn như câu lệnh được hiển thị trước đó, phát hiện liên kết và trích xuất id để lưu trữ

class Book {
    // ...
    private Publisher publisher;
}
1

Ngoài ra, tham chiếu ngược từ

class Book {
    // ...
    private Publisher publisher;
}
6 đến
class Book {
    // ...
    private Publisher publisher;
}
7 có thể được lập mô hình theo cách này. Trong trường hợp này, có thể trì hoãn việc truy xuất nhà xuất bản cho đến khi truy cập thuộc tính lần đầu tiên để tránh sự chậm trễ khi tải háo hức

class Book {
    // ...
    private Publisher publisher;
}
2

Bằng cách sử dụng các liên kết khai báo, giờ đây chúng tôi có thể duy trì chức năng lập bản đồ trong khi tối ưu hóa cho việc lưu trữ. Tuy nhiên, chúng ta cần cẩn thận khi thêm các phiên bản

class Book {
    // ...
    private Publisher publisher;
}
6 mới, vì chúng cũng cần được thêm vào trường
class Book {
    // ...
    private Publisher publisher;
}
48 của
class Book {
    // ...
    private Publisher publisher;
}
7, để thiết lập liên kết

class Book {
    // ...
    private Publisher publisher;
}
3

Đoạn mã trên phác thảo rất rõ bản chất phi nguyên tử khi làm việc với các liên kết giữa các tài liệu, có thể yêu cầu chạy các hoạt động trong Giao dịch

Tham khảo kiểu một-nhiều

Tùy thuộc vào nhu cầu của ứng dụng của bạn, có thể đảo ngược mối quan hệ giữa

class Book {
    // ...
    private Publisher publisher;
}
6 và
class Book {
    // ...
    private Publisher publisher;
}
7 để phần tử liên kết chỉ được lưu trữ trong các tài liệu
class Book {
    // ...
    private Publisher publisher;
}
6. Điều này cho phép bạn lưu trữ
{
    "_id" : "617cfb",
    "isbn13" : "978-0345503800",
    "title" : "The Warded Man",
    "pages" : 432,
    "publisher" : {
        "name" : "Del Rey Books",
        "arconym" : "DRB",
        "foundationYear" : 1977
    }
}
1 mà không phải suy nghĩ về việc cập nhật tài liệu
class Book {
    // ...
    private Publisher publisher;
}
7, như chúng ta đã thấy trong đoạn trích trước. Để làm được như vậy, chúng ta cần làm hai việc. Đầu tiên, chúng ta cần yêu cầu lớp ánh xạ bỏ qua các liên kết lưu trữ từ
class Book {
    // ...
    private Publisher publisher;
}
7 đến
class Book {
    // ...
    private Publisher publisher;
}
6 và thứ hai, cập nhật truy vấn tra cứu khi truy xuất liên kết
{
    "_id" : "617cfb",
    "isbn13" : "978-0345503800",
    "title" : "The Warded Man",
    "pages" : 432,
    "publisher" : {
        "name" : "Del Rey Books",
        "arconym" : "DRB",
        "foundationYear" : 1977
    }
}
1

Phần ban đầu khá dễ dàng, áp dụng thêm chú thích

class Book {
    private String isbn13;
    private String title;
    private int pages;
}

class Publisher {
    private String name;
    private String arconym;
    private int foundationYear;
}
87 cho thuộc tính
class Book {
    // ...
    private Publisher publisher;
}
48. Phần khác yêu cầu chúng tôi cập nhật thuộc tính
class Book {
    private String isbn13;
    private String title;
    private int pages;
}

class Publisher {
    private String name;
    private String arconym;
    private int foundationYear;
}
89 của chú thích
class Book {
    private String isbn13;
    private String title;
    private int pages;
}

class Publisher {
    private String name;
    private String arconym;
    private int foundationYear;
}
32 bằng truy vấn tùy chỉnh

class Book {
    // ...
    private Publisher publisher;
}
4

Trong đoạn mã trên, chúng tôi sử dụng hỗ trợ biểu thức trong trình phân tích cú pháp truy vấn của Dữ liệu mùa xuân. Khi làm như vậy, chúng tôi có thể truy cập tài liệu thô

class Book {
    // ...
    private Publisher publisher;
}
7 bằng cách sử dụng thuộc tính
class Book {
    private String isbn13;
    private String title;
    private int pages;
}

class Publisher {
    private String name;
    private String arconym;
    private int foundationYear;
}
92 và có thể trích xuất mã định danh của nó để sau đó sử dụng nó khi truy vấn bộ sưu tập
class Book {
    // ...
    private Publisher publisher;
}
6 để tìm các phần tử phù hợp

Chú thích cuối

Có một gốc tổng hợp duy nhất với dữ liệu được nhúng có nhiều lợi thế. Tuy nhiên, điều quan trọng là phải hiểu cách mô hình hóa các mối quan hệ một khi những lợi thế đó bị thay thế bởi các mối quan tâm khác, chẳng hạn như kích thước lưu trữ hoặc khả năng hoạt động. Chúng tôi đã thấy rằng, bằng cách chuyển từ phương pháp nhúng sang DBRefs và chuyển sang tham chiếu thủ công, chúng tôi có thể giảm kích thước bộ nhớ. Tuy nhiên, chúng tôi phải giải quyết các vấn đề khác, chẳng hạn như các thay đổi ảnh hưởng đến nhiều tài liệu và các tùy chọn truy vấn hạn chế.

class Book {
    private String isbn13;
    private String title;
    private int pages;
}

class Publisher {
    private String name;
    private String arconym;
    private int foundationYear;
}
32 có thể là một công cụ mạnh mẽ cho phép bạn thể hiện và tùy chỉnh các liên kết giữa các tài liệu. Bạn có thể tìm hiểu thêm về nó trong Tài liệu tham khảo của chúng tôi

Tuy nhiên, trước khi bạn rời đi, vui lòng luôn ghi nhớ rằng các liên kết giữa các tài liệu yêu cầu các máy chủ khứ hồi bổ sung. Do đó, hãy đảm bảo có sẵn các chỉ mục hỗ trợ tra cứu của bạn. Một bộ sưu tập các tài liệu được liên kết được tải hàng loạt, khôi phục thứ tự trên cơ sở nỗ lực tốt nhất trong bộ nhớ của ứng dụng của bạn

Ngoài ra, hãy luôn tự hỏi cái nào là tốt nhất cho ứng dụng của bạn?

MongoDB có thể xử lý các mối quan hệ không?

Mối quan hệ thực thi tính toàn vẹn của dữ liệu trong cơ sở dữ liệu quan hệ. Tuy nhiên, không có mối quan hệ nào giữa các tài liệu trong MongoDB và các cơ sở dữ liệu NoSQL khác . Kết quả là, các tài liệu được khép kín. Có một số cách tiếp cận khác nhau để mô hình hóa các mối quan hệ tài liệu này.

Làm cách nào bạn có thể triển khai 1 đến nhiều mối quan hệ trong MongoDB?

Trong MongoDB, quan hệ một-một, một-nhiều và nhiều-nhiều có thể được triển khai theo hai cách. Sử dụng tài liệu nhúng . Sử dụng tài liệu tham khảo của bộ sưu tập khác.

Các mối quan hệ được thể hiện như thế nào trong MongoDB?

Trong MongoDB, một mối quan hệ thể hiện cách các loại tài liệu khác nhau có liên quan logic với nhau. Các mối quan hệ như một-một, một-nhiều, v.v. , có thể được biểu diễn bằng cách sử dụng hai mô hình khác nhau. Mô hình tài liệu nhúng . Mô hình tham chiếu .