Tại sao Mongoose có cả lược đồ và mô hình?


92

Hai loại đối tượng dường như gần gũi với nhau đến mức cả hai đều cảm thấy thừa. Có ích gì khi có cả lược đồ và mô hình?

Câu trả lời:


61

Thường thì cách dễ nhất để trả lời loại câu hỏi này là với một ví dụ. Trong trường hợp này, ai đó đã làm điều đó cho tôi :)

Hãy xem ở đây:

http://rawberg.com/blog/nodejs/mongoose-orm-nested-models/

CHỈNH SỬA: Bài viết gốc (như đã đề cập trong các bình luận) dường như không còn tồn tại, vì vậy tôi đang sao chép nó bên dưới. Nó có bao giờ quay lại không, hoặc nếu nó mới chuyển đi, vui lòng cho tôi biết.

Nó cung cấp một mô tả chi tiết về việc sử dụng các lược đồ trong các mô hình trong mongoose và tại sao bạn muốn làm điều đó, đồng thời cũng chỉ cho bạn cách đẩy các tác vụ qua mô hình trong khi lược đồ là tất cả về cấu trúc, v.v.

Bài gốc:

Hãy bắt đầu với một ví dụ đơn giản về việc nhúng một lược đồ bên trong một mô hình.

var TaskSchema = new Schema({
    name: String,
    priority: Number
});

TaskSchema.virtual('nameandpriority')
    .get( function () {
        return this.name + '(' + this.priority + ')';
    });

TaskSchema.method('isHighPriority', function() {
    if(this.priority === 1) {
        return true;
    } else {
        return false;
    }
}); 

var ListSchema = new Schema({
    name: String,
    tasks: [TaskSchema]
});

mongoose.model('List', ListSchema);

var List = mongoose.model('List');

var sampleList = new List({name:'Sample List'});

Tôi đã tạo một TaskSchemađối tượng mới với thông tin cơ bản mà một nhiệm vụ có thể có. Thuộc tính ảo Mongoose được thiết lập để kết hợp tên và mức độ ưu tiên của Nhiệm vụ một cách thuận tiện. Tôi chỉ chỉ định một getter ở đây nhưng những setter ảo cũng được hỗ trợ.

Tôi cũng đã xác định một phương thức nhiệm vụ đơn giản được gọi isHighPriorityđể chứng minh cách các phương thức hoạt động với thiết lập này.

Trong ListSchemađịnh nghĩa, bạn sẽ nhận thấy cách khóa tác vụ được cấu hình để chứa một mảng TaskSchemađối tượng. Khóa tác vụ sẽ trở thành một ví dụ trong DocumentArrayđó cung cấp các phương pháp đặc biệt để xử lý các tài liệu Mongo được nhúng.

Hiện tại, tôi chỉ truyền ListSchemađối tượng vào mongoose.model và để lại TaskSchema. Về mặt kỹ thuật, không cần thiết phải biến nó TaskSchemathành một mô hình chính thức vì chúng tôi sẽ không lưu nó vào bộ sưu tập của riêng nó. Sau đó, tôi sẽ chỉ cho bạn cách nó không gây hại gì nếu bạn làm vậy và nó có thể giúp tổ chức tất cả các mô hình của bạn theo cùng một cách, đặc biệt là khi chúng bắt đầu mở rộng nhiều tệp.

Với Listthiết lập mô hình, hãy thêm một vài nhiệm vụ vào nó và lưu chúng vào Mongo.

var List = mongoose.model('List');
var sampleList = new List({name:'Sample List'});

sampleList.tasks.push(
    {name:'task one', priority:1}, 
    {name:'task two', priority:5}
);

sampleList.save(function(err) {
    if (err) {
        console.log('error adding new list');
        console.log(err);
    } else {
        console.log('new list successfully saved'); 
    }
});

Thuộc tính task trên phiên bản Listmodel ( simpleList) của chúng ta hoạt động giống như một mảng JavaScript thông thường và chúng ta có thể thêm các tác vụ mới vào đó bằng cách sử dụng push. Điều quan trọng cần lưu ý là các tác vụ được thêm vào dưới dạng các đối tượng JavaScript thông thường. Đó là một sự khác biệt tinh tế có thể không trực quan ngay lập tức.

Bạn có thể xác minh từ Mongo shell rằng danh sách và nhiệm vụ mới đã được lưu vào mongo.

db.lists.find()
{ "tasks" : [
    {
        "_id" : ObjectId("4dd1cbeed77909f507000002"),
        "priority" : 1,
        "name" : "task one"
    },
    {
        "_id" : ObjectId("4dd1cbeed77909f507000003"),
        "priority" : 5,
        "name" : "task two"
    }
], "_id" : ObjectId("4dd1cbeed77909f507000001"), "name" : "Sample List" }

Bây giờ chúng ta có thể sử dụng ObjectIdđể kéo lên Sample Listvà lặp lại các tác vụ của nó.

List.findById('4dd1cbeed77909f507000001', function(err, list) {
    console.log(list.name + ' retrieved');
    list.tasks.forEach(function(task, index, array) {
        console.log(task.name);
        console.log(task.nameandpriority);
        console.log(task.isHighPriority());
    });
});

Nếu bạn chạy đoạn mã cuối cùng đó, bạn sẽ gặp lỗi cho biết tài liệu nhúng không có phương thức isHighPriority. Trong phiên bản Mongoose hiện tại, bạn không thể truy cập trực tiếp các phương thức trên lược đồ nhúng. Có một vé mở để sửa nó và sau khi đặt câu hỏi cho Mongoose Google Group, manimal45 đã đăng một bài giải hữu ích để sử dụng ngay bây giờ.

List.findById('4dd1cbeed77909f507000001', function(err, list) {
    console.log(list.name + ' retrieved');
    list.tasks.forEach(function(task, index, array) {
        console.log(task.name);
        console.log(task.nameandpriority);
        console.log(task._schema.methods.isHighPriority.apply(task));
    });
});

Nếu bạn chạy mã đó, bạn sẽ thấy kết quả sau trên dòng lệnh.

Sample List retrieved
task one
task one (1)
true
task two
task two (5)
false

Với suy nghĩ về công việc đó, hãy biến nó TaskSchemathành một mô hình Mongoose.

mongoose.model('Task', TaskSchema);

var Task = mongoose.model('Task');

var ListSchema = new Schema({
    name: String,
    tasks: [Task.schema]
});

mongoose.model('List', ListSchema);

var List = mongoose.model('List');

Các TaskSchemađịnh nghĩa là giống như trước đây vì vậy tôi rời nó ra. Sau khi nó trở thành một mô hình, chúng ta vẫn có thể truy cập đối tượng Schema bên dưới của nó bằng cách sử dụng ký hiệu dấu chấm.

Hãy tạo một danh sách mới và nhúng hai phiên bản mô hình Nhiệm vụ vào trong đó.

var demoList = new List({name:'Demo List'});

var taskThree = new Task({name:'task three', priority:10});
var taskFour = new Task({name:'task four', priority:11});

demoList.tasks.push(taskThree.toObject(), taskFour.toObject());

demoList.save(function(err) {
    if (err) {
        console.log('error adding new list');
        console.log(err);
    } else {
        console.log('new list successfully saved'); 
    }
});

Khi chúng tôi nhúng các phiên bản mô hình Nhiệm vụ vào Danh sách, chúng tôi đang kêu gọi toObjecthọ chuyển đổi dữ liệu của họ thành các đối tượng JavaScript đơn giản mà đối tượng List.tasks DocumentArrayđang mong đợi. Khi bạn lưu các phiên bản mô hình theo cách này, các tài liệu nhúng của bạn sẽ chứa ObjectIds.

Ví dụ về mã hoàn chỉnh có sẵn dưới dạng ý chính . Hy vọng rằng những cách giải quyết này sẽ giúp mọi thứ suôn sẻ hơn khi Mongoose tiếp tục phát triển. Tôi vẫn còn khá mới với Mongoose và MongoDB, vì vậy vui lòng chia sẻ các giải pháp và mẹo tốt hơn trong phần nhận xét. Chúc bạn lập mô hình dữ liệu vui vẻ!


3
Thông thường, bạn không nên gửi các liên kết trần trụi như một câu trả lời cho các câu hỏi được đăng trên SO vì liên kết có thể ngừng hoạt động (như trong trường hợp này). Ít nhất sao chép / quá khứ và trích dẫn các phần có liên quan của bài viết mà bạn liên kết đến.
Behrang Saeedzadeh

1
thực hiện - nó vẫn còn trong bộ nhớ cache của Google, do đó tương đối đơn giản
Adam Comerford

1
Đối với bản ghi, sự cố phương pháp tài liệu nhúng đã được khắc phục: github.com/LearnBoost/mongoose/issues/249#ref-commit-e18077a
Dakota

5
Tôi không cố gắng làm mưa làm gió trong cuộc diễu hành của bất kỳ ai, nhưng câu trả lời này giống như một bài hướng dẫn: trả lời bằng cách nào, nhưng không phải tại sao. Mặc dù có ít phiếu bầu hơn, nhưng tôi thấy câu trả lời sau hữu ích hơn nhiều: stackoverflow.com/a/22950402/26331
aaaidan

2
Tôi đã xem câu trả lời đó (và đã ủng hộ nó), câu trả lời này đã được trả lời và chấp nhận hơn 2 năm trước đó. Tôi rất vui vì có một câu trả lời hay hơn được tìm thấy, không có mưa trong cuộc diễu hành của bất kỳ ai và đã có một liên kết đến câu trả lời mà bạn đã tham khảo trong phần bình luận của câu hỏi kể từ tháng 2 năm 2015 nên tôi không cảm thấy cần phải tự liên kết nó
Adam Comerford

54

Schema là một đối tượng xác định cấu trúc của bất kỳ tài liệu nào sẽ được lưu trữ trong bộ sưu tập MongoDB của bạn; nó cho phép bạn xác định các loại và trình xác thực cho tất cả các mục dữ liệu của bạn.

Mô hình là một đối tượng cho phép bạn truy cập dễ dàng vào bộ sưu tập được đặt tên, cho phép bạn truy vấn bộ sưu tập và sử dụng Lược đồ để xác thực bất kỳ tài liệu nào bạn lưu vào bộ sưu tập đó. Nó được tạo bằng cách kết hợp một Lược đồ, một Kết nối và một tên bộ sưu tập.

Ban đầu được viết bởi Valeri Karpov, MongoDB Blog


5

Tôi không nghĩ rằng câu trả lời được chấp nhận thực sự trả lời cho câu hỏi đã được đặt ra. Câu trả lời không giải thích tại sao Mongoose lại quyết định yêu cầu nhà phát triển cung cấp cả biến Schema và Model. Ví dụ về một khuôn khổ trong đó họ đã loại bỏ sự cần thiết của nhà phát triểnđể xác định lược đồ dữ liệu là django - một nhà phát triển viết lên các mô hình của họ trong tệp models.py và để nó vào khung để quản lý lược đồ. Lý do đầu tiên mà tôi nghĩ đến tại sao họ làm điều này, dựa trên kinh nghiệm của tôi với django, là dễ sử dụng. Có lẽ quan trọng hơn là nguyên tắc KHÔ (không lặp lại chính mình) - bạn không cần phải nhớ cập nhật lược đồ khi bạn thay đổi mô hình - django sẽ làm điều đó cho bạn! Rails cũng quản lý lược đồ dữ liệu cho bạn - một nhà phát triển không trực tiếp chỉnh sửa lược đồ, nhưng thay đổi nó bằng cách xác định các chuyển đổi thao tác với lược đồ.

Một lý do mà tôi có thể hiểu rằng Mongoose sẽ tách lược đồ và mô hình là các trường hợp mà bạn muốn xây dựng một mô hình từ hai lược đồ. Một kịch bản như vậy có thể phức tạp hơn mức đáng để quản lý - nếu bạn có hai lược đồ được quản lý bởi một mô hình, tại sao chúng không phải là một lược đồ?

Có lẽ câu hỏi ban đầu là một di tích của hệ thống cơ sở dữ liệu quan hệ truyền thống. Trong thế giới NoSQL / Mongo, có lẽ lược đồ linh hoạt hơn một chút so với MySQL / PostgreSQL, và do đó, việc thay đổi lược đồ là việc làm phổ biến hơn.


Như thể lược đồ so với mô hình là không đủ Tự lặp lại, bạn sẽ gặp nhiều trùng lặp hơn khi cố gắng duy trì giao diện TypeScript phù hợp và thậm chí nhiều hơn khi tạo lược đồ GraphQL.
Dan Dascalescu

0

Để hiểu tại sao? bạn phải hiểu Mongoose thực sự là gì?

Chà, mongoose là một thư viện mô hình dữ liệu đối tượng cho MongoDB và Node JS, cung cấp mức độ trừu tượng cao hơn. Vì vậy, nó hơi giống mối quan hệ giữa Express và Node, vì vậy Express là một lớp trừu tượng so với Node thông thường, trong khi Mongoose là một lớp trừu tượng trên trình điều khiển MongoDB thông thường.

Thư viện mô hình dữ liệu đối tượng chỉ là một cách để chúng ta viết mã Javascript sau đó sẽ tương tác với cơ sở dữ liệu. Vì vậy, chúng tôi chỉ có thể sử dụng trình điều khiển MongoDB thông thường để truy cập cơ sở dữ liệu của mình, nó sẽ hoạt động tốt.

Nhưng thay vào đó, chúng tôi sử dụng Mongoose vì nó cung cấp cho chúng tôi nhiều chức năng hơn, cho phép phát triển các ứng dụng của chúng tôi nhanh hơn và đơn giản hơn.

Vì vậy, một số tính năng mà Mongoose cung cấp cho chúng tôi các lược đồ để mô hình hóa dữ liệu và mối quan hệ của chúng tôi, xác thực dữ liệu dễ dàng, một API truy vấn đơn giản, phần mềm trung gian, v.v.

Trong Mongoose, một lược đồ là nơi chúng ta lập mô hình dữ liệu của mình, nơi chúng ta mô tả cấu trúc của dữ liệu, giá trị mặc định và xác thực, sau đó chúng ta lấy lược đồ đó và tạo một mô hình từ nó, một mô hình về cơ bản là một trình bao bọc xung quanh lược đồ, cho phép chúng tôi thực sự giao tiếp với cơ sở dữ liệu để tạo, xóa, cập nhật và đọc tài liệu.

nhập mô tả hình ảnh ở đây

Hãy tạo một mô hình từ một lược đồ.

const tourSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'A tour must have a name'],
    unique: true,
  },
  rating: {
    type: Number,
    default: 4.5,
  },
  price: {
    type: Number,
    required: [true, 'A tour must have a price'],
  },
});
//tour model
const Tour = mongoose.model('Tour', tourSchema);

Theo quy tắc, chữ cái đầu tiên của tên model phải được viết hoa.

Hãy tạo phiên bản của mô hình mà chúng tôi đã tạo bằng cách sử dụng mongoose và lược đồ. tương tác với cơ sở dữ liệu của chúng tôi.

const testTour = new Tour({ // instance of our model
  name: 'The Forest Hiker',
  rating: 4.7,
  price: 497,
});
 // saving testTour document into database
testTour
  .save()
  .then((doc) => {
    console.log(doc);
  })
  .catch((err) => {
    console.log(err);
  });

Vì vậy, có cả schama và modle cầy mangut làm cho cuộc sống của chúng tôi dễ dàng hơn.

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.