Quan hệ nhiều nhiều trong mongodb

Published Sep 20th, 2019 4:14 p.m. 4 min read

I. Lời mở đầu

NoSQL và GraphQL chắc không còn xa lạ gì với mọi người nữa. Nói một cách đơn giản thì:

  • NoSQL là No Structured Query Language hay còn gọi là truy vấn không có quan hệ (no relationship).
  • GraphQL là ngôn ngữ hỗ trợ chúng ta quản lý việc quản lý việc trả về của dữ liệu, vì sao lại cần quản lý việc trả về, ví dụ đơn giản, khi làm việc với REST API, việc bạn gửi một URL request server trả về dữ liệu của một group trong đó chứa sẵn users mà user đó đã tham gia, nhưng bạn chỉ cần tên, id hay avatar của user là được, nhưng bên phía server lại trả về hết tất cả những giá trị có trong user đó, như vậy sẽ gây nặng dữ liệu trả về, gây giảm hiệu năng của ứng dụng. GraphQL sinh ra để giải quyết các vấn đề này, vẫn còn rất nhiều vấn đề mà bên REST API không thể giải quyết được thì GraphQL sẽ hỗ trợ.

II. Bài toán

  • Ta có 2 bảng là users và groups. Ta có mối quan hệ của chúng là một user có thể tham gia nhiều group và một group có thể có nhiều user
  • Nếu dùng SQL thì ta sẽ tạo thêm một bảng trung gian để đưa mối quan hệ nhiều nhiều này thành quan hệ 1 nhiều
  • Nhưng trong NoSQL ta không cần phải tạo thêm một bảng như vậy, ta chỉ việc lưu nó dưới dạng attribute của một object, ví dụ// Group group { id: 1, name: "Group", description: "Group Description", users: [ { id: 1, name: "Trung", email: "[email protected]", avatar: "Avatar.jpg", role: "Superman" }, { id: 1, name: "Linh", email: "[email protected]", avatar: "Picture.jpg", role: "Louis Lane" } ], } // User user { id: 1, name: "Trung", email: "[email protected]", avatar: "Avatar.jpg", role: "Superman", groups: [ { id: 1, name: "Group", description: "Group Description", }, ], }
    • Làm như trên thì việc truy vấn để lấy các groups trong user hay users trong group trở nên dễ dàng hơn
    • Tưởng chừng bài toán tới đây đã được giả quyết, nhưng KHÔNG! Nó lại sinh ra 2 vấn đề mới:
      • Nếu như lưu dữ liệu như trên thì việc phình data là chuyện tất yếu, và việc sau này một group có thể chứa tới 1.000, 10.000 hay 100.000 là rất dễ xảy ra, nếu ta lưu như trên thì việc record này sẽ trở lên rất nặng và chậm chạm
      • Vậy ta chuyển qua cách lưu theo ID, nhưng lưu theo ID thì ta lại phải viết thêm một hàm query tách biệt để có thể lấy ra những users trong group hay groups trong user. Lắc nhắc và cực kỳ phiền phức (các bạn làm một app nhỏ thử là sẽ thấy :v)
      • Vì vậy GraphQL hỗ trợ ta việc này

III. Giải quyết bài toán với GraphQL

  • Nếu bạn có tìm hiểu về GraphQL thì sẽ biết rằng trong GraphQL có 2 phần là
    • ĐỊnh nghĩa (gồm Type, Query, Mutation)
    • Trả về (gồm Resolver)
  • Đầu tiên trong phần Type của GraphQL ta định nghĩa như sautype Group { id:String! # Chấm than có nghĩa là bắt buộc phải có giá trị này name:String! desciption:String userIds:[String] # Ở đây định nghĩa để lưu những giá trị id của users users:[User] # Đây là giá trị trả về cho Client, không lưu dữ liệu, chức năng chỉ có trả về mảng user } type User { id:String! email:String! name:String! avatar:String role:String groupIds:[String] groups:[Group] }
  • Ở resolver ta định query các giá trị để trả vềGroup: { users: (obj, args, context, info) => // function query dữ liệu users theo group } User: { groups: (obj, args, context, info) => // function query dữ liệu groups theo user }

IV. Kết

Như vậy dữ liệu vẫn được trả về như cách ta mong muốn là khi get user hay group, nếu ta muốn thì nó vẫn trả về được list các user hoặc group. Mong rằng bài viết này giúp ích được phần nào cho mọi người về việc kết hợp giữa NoSQL với GraphQL và có thêm một cách để giải quyết vấn đề quan hệ nhiều nhiều trong NoSQL

All Rights Reserved

Các quan hệ của mongodb là các NoSQL tương đương với liên kết trong cơ sở dữ liệu quan hệ. Tuy chúng đều thực hiện những mục đích tương tự nhau, xuất hiện ở những trường hợp tương tự nhau nhưng bản chất về hành vi xử lý của chúng lại khác nhau hoàn toàn.

NoSQL relations vs RDBMS associations

Các Relation (quan hệ) liên kết dữ liệu từ model này với model khác. Sâu hơn nữa thì đây chính là những gì dành các RDBMS liên kết. Tùy nhiên, cái cách mà dữ liệu được liên kết trong NoSQL lại hoàn toàn khác. Trong cơ sở dữ liệu quan hệ truyền thống, các quan hệ được dùng để liên kết giữa bảng này với các bảng khác. MongoDB và các cơ sở dữ liệu NoSQL tương đương là các "document-oriented" (tức là không có bảng nào cả mà chỉ là những bản ghi thôi). Vậy thay vào đó, các liên kết được vẽ ra ở giữa những "bản ghi".

ActiveRecord

class Post < ActiveRecord::Base belongs_to :author has_many :comments end

Mongoid (referencing)

class Post include Mongoid::Document belongs_to :author has_many :comments end

Mongoid (embedding)

class Post include Mongoid::Document embedded_in :author embeds_many :comments end

Tất cả những class trên về cơ bản đều thực hiện liên kết giữa 1 author có nhiều bài post, 1 bài posts lại có nhiều comments. Nhìn vào đoạn code trên ta có thể thấy

embeds_many <=> has_many

embeds_in <=> belongs_to

Nhưng điều này đã làm đơn giản hóa nó đi.

ActiveRecord

Cơ sở dữ liệu quan hệ sẽ xử lý các liên kết thông qua khóa ngoại than chiếu tới 1 hàng duy nhất của bảng khác. Ví dụ: một post sẽ có một author tham chiếu ID của bản ghi trong bảng authors mà nó thuộc về. Nếu một author có nhiều post, nhiều bản ghi post sẽ có cùng tác giả. author không có post_ids.

Relational database schema

Mongoid References

Có một cách cơ sở dữ liệu NoQuery xử lý một đến nhiều mối quan hệ là thông qua các mối quan hệ. Tham chiếu tương tự như các association trong cơ sở dữ liệu quan hệ hơn là nhúng (phần này sẽ được giải thích ở bên dưới). Mối quan hệ cũng sử dụng các khóa ngoại, tuy nhiên chúng trỏ từ document này sang document khác, thay vì một bản ghi trong một bảng này đến một bản ghi trong một bảng khác.

// An author document. { "_id" : ObjectId("4d3ed089fb60ab534684b7e9"), "name" : "Pat Whitrock" } // A post document. { "_id" : ObjectId("4d3ed089fb60ab534684b7e0"), "author_id" : ObjectId("4d3ed089fb60ab534684b7e9"), "name" : "Mongo Stuff" } // A comment document. { "_id" : ObjectId("4d3ed089fb60ab534684b7e8"), "post_id" : ObjectId("4d3ed089fb60ab534684b7e0"), "content" : "Lots of stuff about Mongo." }

Mongoid Embeds

Một cách khác để cơ sở dữ liệu NoQuery xử lý loại mối quan hệ này với nhiều mối quan hệ là thông qua việc nhúng các document. Một document được nhúng trong một document khác, dẫn đến cơ bản là một hash (hàm băm) khổng lồ. Ví dụ: author sẽ nhúng nhiều tài liệu bài đăng và mỗi tài liệu bài đăng sẽ nhúng nhiều comment. Mỗi author là một hash chứa một mảng các post, mỗi document chứa một mảng các comment.

// An author document. { "_id" : ObjectId("4d3ed089fb60ab534684b7e9"), "name" : "Pat Whitrock", "posts" : [ // An embedded post document. { "_id" : ObjectId("4d3ed089fb60ab534684b7e0"), "name" : "Mongo Stuff", "comments" : [ // An embedded comment document. { "_id" : ObjectId("4d3ed089fb60ab534684b7e8"), "content" : "Lots of stuff about Mongo." } ] } ] }

Embedding vs Referencing

Tại sao Mongo cần có nhiều cách để xác định cùng một mối quan hệ với nhiều mối quan hệ khi các ORM như ActiveRecord chỉ yêu cầu một? Cả nhúng và tham chiếu đều là các lựa chọn hợp lý, nhưng mỗi lựa chọn phục vụ nhiều hơn cho các trường hợp sử dụng cụ thể. Có một vài điều sẽ cần được xem xét trước khi đưa ra quyết định.

Dữ liệu của bạn sẽ được kết nối từ nhiều điểm? Nếu bạn cần truy cập dữ liệu của mình từ nhiều điểm, có lẽ bạn nên sử dụng Referencing. Nếu dữ liệu của bạn chỉ hữu ích liên quan đến tài liệu gốc của nó, thì Embedding là cách để đi. Cũng quan trọng để xem xét là tính nhất quán dữ liệu và kích thước tài liệu. Các document MongoDB có thể bị giới hạn ở kích thước tối đa 4 MB, tuy nhiên, rất khó có thể đây là vấn đề bạn sẽ gặp phải sớm.

Source: //pat-whitrock.github.io/blog/2014/05/07/mongodb-relations/

Chủ đề