Làm cách nào để tôi thực hiện SQL Join tương đương trong MongoDB?


498

Làm cách nào để tôi thực hiện SQL Join tương đương trong MongoDB?

Ví dụ: bạn có hai bộ sưu tập (người dùng và nhận xét) và tôi muốn kéo tất cả các nhận xét với pid = 444 cùng với thông tin người dùng cho mỗi bộ sưu tập.

comments
  { uid:12345, pid:444, comment="blah" }
  { uid:12345, pid:888, comment="asdf" }
  { uid:99999, pid:444, comment="qwer" }

users
  { uid:12345, name:"john" }
  { uid:99999, name:"mia"  }

Có cách nào để kéo tất cả các nhận xét với một trường nhất định (ví dụ: ... find ({pid: 444})) và thông tin người dùng được liên kết với mỗi nhận xét trong một lần không?

Hiện tại, trước tiên tôi nhận được các bình luận phù hợp với tiêu chí của mình, sau đó tìm ra tất cả các uid trong tập kết quả đó, lấy các đối tượng người dùng và hợp nhất chúng với kết quả của bình luận. Có vẻ như tôi đang làm sai.


35
Câu trả lời cuối cùng cho câu hỏi này có lẽ là phù hợp nhất, vì MongoDB 3.2+ đã triển khai một giải pháp tham gia có tên là $ lookup. Nghĩ rằng tôi sẽ đẩy nó ở đây vì có lẽ không phải ai cũng sẽ đọc đến tận cùng. stackoverflow.com/a/33511166/2593330
thefourtheye

6
Đúng, $ tra cứu đã được giới thiệu trong MongoDB 3.2. Thông tin chi tiết có thể được tìm thấy tại docs.mongodb.org/master/reference/operator/aggregation/lookup/ mẹo
NDB

Câu trả lời:


306

Kể từ Mongo 3.2, câu trả lời cho câu hỏi này hầu hết không còn đúng nữa. Toán tử tra cứu $ mới được thêm vào đường dẫn tổng hợp về cơ bản là giống hệt với phép nối ngoài bên trái:

https://docs.mongodb.org/master/reference/operator/aggregation/lookup/#pipe._S_lookup

Từ các tài liệu:

{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}

Tất nhiên Mongo không phải là một cơ sở dữ liệu quan hệ và các nhà phát triển đang cẩn thận đề xuất các trường hợp sử dụng cụ thể cho $ tra cứu, nhưng ít nhất là vào ngày 3.2 hiện có thể tham gia với MongoDB.


@clayton: Thế còn hơn hai bộ sưu tập?
Dipen Dedania

1
@DipenDedania chỉ cần thêm các giai đoạn tra cứu $ bổ sung vào đường dẫn tổng hợp.
Clayton Gulick

Tôi không thể tham gia bất kỳ trường nào trong mảng trong bộ sưu tập bên trái với id tương ứng của nó trong bộ sưu tập bên phải. Có ai giúp tôi không ??
Prateek Singh

1
Tôi hơi bối rối về điều này - có cách nào để xác định rằng bạn chỉ muốn một số tài liệu nhất định trong bộ sưu tập "từ" hoặc nó tự động tham gia tất cả vào db cùng một lúc không?
dùng3413723

Chỉ tự hỏi liệu Spring Data MongoDB mới nhất có hỗ trợ cho 3.2 không?
gtiwari333

142

Trang này trên trang web mongodb chính thức giải quyết chính xác câu hỏi này:

https://mongodb-documentation.readthedocs.io/en/latest/ecystem/tutorial/model-data-for-ruby-on-rails.html

Khi chúng tôi hiển thị danh sách các câu chuyện của mình, chúng tôi sẽ cần hiển thị tên của người dùng đã đăng câu chuyện. Nếu chúng tôi đang sử dụng cơ sở dữ liệu quan hệ, chúng tôi có thể thực hiện tham gia trên người dùng và cửa hàng và nhận tất cả các đối tượng của chúng tôi trong một truy vấn duy nhất. Nhưng MongoDB không hỗ trợ các phép nối và đôi khi, đòi hỏi một chút không chuẩn hóa. Ở đây, điều này có nghĩa là lưu trữ thuộc tính 'tên người dùng'.

Những người theo chủ nghĩa thuần túy có thể cảm thấy không thoải mái, như thể chúng ta đang vi phạm một số luật phổ quát. Nhưng hãy nhớ rằng các bộ sưu tập MongoDB không tương đương với các bảng quan hệ; mỗi phục vụ một mục tiêu thiết kế độc đáo. Một bảng chuẩn hóa cung cấp một khối dữ liệu nguyên tử, bị cô lập. Tuy nhiên, một tài liệu đại diện chặt chẽ hơn đại diện cho một đối tượng. Trong trường hợp của một trang web tin tức xã hội, có thể lập luận rằng tên người dùng là nội tại của câu chuyện được đăng.


51
@dudelgrincen đó là một sự thay đổi mô hình từ cơ sở dữ liệu bình thường hóa và quan hệ. Mục tiêu của NoQuery là đọc và ghi từ cơ sở dữ liệu rất nhanh. Với BigData, bạn sẽ có các ứng dụng và máy chủ ngoại vi với số lượng thấp hơn trên DB. Bạn sẽ thực hiện hàng triệu giao dịch một giây. Giảm tải nặng từ cơ sở dữ liệu và đưa nó lên cấp ứng dụng. Nếu bạn cần phân tích sâu, bạn chạy một công việc tích hợp đưa dữ liệu của bạn vào cơ sở dữ liệu OLAP. Bạn không nên nhận được nhiều truy vấn sâu từ dbs OLTP của bạn.
Snowburnt

18
@dudelgrincen Tôi cũng nên nói rằng nó không dành cho mọi dự án hay thiết kế. Nếu bạn có một cái gì đó hoạt động trong cơ sở dữ liệu kiểu SQL tại sao lại thay đổi nó? Nếu bạn không thể xoa bóp lược đồ của bạn để làm việc với noQuery, thì đừng.
Snowburnt

9
Việc di chuyển và một lược đồ phát triển liên tục cũng dễ dàng hơn rất nhiều để quản lý trên hệ thống NoQuery.
justin

14
Điều gì sẽ xảy ra nếu người dùng có 3.540 bài đăng trên trang web và anh ta thay đổi tên người dùng trong hồ sơ? Mỗi bài đăng nên được cập nhật với tên người dùng mới?
Ivo Pereira

2
@IvoPereira Có và đó chính xác là lý do tại sao người ta nên tránh mô hình hóa dữ liệu theo cách này. Có một bài viết giải thích cùng một kịch bản và hậu quả của nó: Tại sao bạn không bao giờ nên sử dụng MongoDB
Omid

138

Chúng tôi có thể hợp nhất / tham gia tất cả dữ liệu bên trong chỉ một bộ sưu tập với một chức năng dễ dàng trong một vài dòng bằng cách sử dụng bảng điều khiển máy khách mongodb và bây giờ chúng tôi có thể thực hiện truy vấn mong muốn. Dưới đây là một ví dụ đầy đủ,

.- Tác giả:

db.authors.insert([
    {
        _id: 'a1',
        name: { first: 'orlando', last: 'becerra' },
        age: 27
    },
    {
        _id: 'a2',
        name: { first: 'mayra', last: 'sanchez' },
        age: 21
    }
]);

.- Thể loại:

db.categories.insert([
    {
        _id: 'c1',
        name: 'sci-fi'
    },
    {
        _id: 'c2',
        name: 'romance'
    }
]);

.- Sách

db.books.insert([
    {
        _id: 'b1',
        name: 'Groovy Book',
        category: 'c1',
        authors: ['a1']
    },
    {
        _id: 'b2',
        name: 'Java Book',
        category: 'c2',
        authors: ['a1','a2']
    },
]);

.- Cho mượn sách

db.lendings.insert([
    {
        _id: 'l1',
        book: 'b1',
        date: new Date('01/01/11'),
        lendingBy: 'jose'
    },
    {
        _id: 'l2',
        book: 'b1',
        date: new Date('02/02/12'),
        lendingBy: 'maria'
    }
]);

.- Phép thuật:

db.books.find().forEach(
    function (newBook) {
        newBook.category = db.categories.findOne( { "_id": newBook.category } );
        newBook.lendings = db.lendings.find( { "book": newBook._id  } ).toArray();
        newBook.authors = db.authors.find( { "_id": { $in: newBook.authors }  } ).toArray();
        db.booksReloaded.insert(newBook);
    }
);

.- Nhận dữ liệu thu thập mới:

db.booksReloaded.find().pretty()

.- Phản ứng :)

{
    "_id" : "b1",
    "name" : "Groovy Book",
    "category" : {
        "_id" : "c1",
        "name" : "sci-fi"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        }
    ],
    "lendings" : [
        {
            "_id" : "l1",
            "book" : "b1",
            "date" : ISODate("2011-01-01T00:00:00Z"),
            "lendingBy" : "jose"
        },
        {
            "_id" : "l2",
            "book" : "b1",
            "date" : ISODate("2012-02-02T00:00:00Z"),
            "lendingBy" : "maria"
        }
    ]
}
{
    "_id" : "b2",
    "name" : "Java Book",
    "category" : {
        "_id" : "c2",
        "name" : "romance"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        },
        {
            "_id" : "a2",
            "name" : {
                "first" : "mayra",
                "last" : "sanchez"
            },
            "age" : 21
        }
    ],
    "lendings" : [ ]
}

Tôi hy vọng dòng này có thể giúp bạn.


2
Tôi đang tự hỏi nếu cùng một mã này có thể được chạy bằng học thuyết mongodb?
abbood

4
Điều gì xảy ra khi một trong các đối tượng tham chiếu được cập nhật? Có phải bản cập nhật đó tự động phản ánh trong đối tượng cuốn sách? Hay vòng lặp đó cần phải chạy lại?
balupton

14
Điều này là tốt miễn là dữ liệu của bạn là nhỏ. Nó sẽ mang từng nội dung sách đến khách hàng của bạn và sau đó tìm nạp từng danh mục, cho vay và tác giả từng người một. Thời điểm sách của bạn ở hàng ngàn, điều này sẽ đi rất chậm. Một kỹ thuật tốt hơn có lẽ sẽ là sử dụng đường ống tổng hợp và xuất dữ liệu đã hợp nhất thành một bộ sưu tập riêng. Hãy để tôi trở lại với nó một lần nữa. Tôi sẽ thêm một câu trả lời.
Sandeep Giri

Bạn có thể điều chỉnh thuật toán của bạn với ví dụ khác này không? stackoverflow.com/q/32718079/287948
Peter Krauss

1
@SandeepGiri làm thế nào tôi có thể thực hiện đường ống tổng hợp vì tôi có dữ liệu thực sự chuyên sâu trong bộ sưu tập riêng biệt cần tham gia ??
Yassine Abdul-Rahman

38

Bạn phải làm theo cách bạn mô tả. MongoDB là một cơ sở dữ liệu không liên quan và không hỗ trợ tham gia.


4
Có vẻ hiệu suất sai khôn ngoan đến từ nền máy chủ sql, nhưng nó có thể không tệ với db tài liệu?
terjetyl

3
cũng từ nền tảng máy chủ sql, tôi sẽ đánh giá cao MongoDB lấy 'tập kết quả' (với các trường được trả về đã chọn) làm đầu vào cho một truy vấn mới trong một lần, giống như các truy vấn lồng nhau trong SQL
Stijn Sanders

1
@terjetyl Bạn phải thực sự lên kế hoạch cho nó. Những lĩnh vực nào bạn sẽ trình bày ở mặt trước, nếu đó là số lượng giới hạn trong một chế độ xem riêng lẻ thì bạn lấy đó làm tài liệu nhúng. Điều quan trọng là không cần phải tham gia. Nếu bạn muốn phân tích sâu, bạn làm điều đó sau khi thực tế trong cơ sở dữ liệu khác. Chạy một công việc biến đổi dữ liệu thành một khối OLAP để có hiệu suất tối ưu.
Snowburnt

4
Từ phiên bản mongo 3.2 còn lại tham gia được hỗ trợ.
Somnath Muluk

18

Như những người khác đã chỉ ra rằng bạn đang cố gắng tạo một cơ sở dữ liệu quan hệ từ bất kỳ cơ sở dữ liệu quan hệ nào mà bạn thực sự không muốn làm nhưng dù sao đi nữa, nếu bạn có một trường hợp bạn phải làm điều này thì đây là một giải pháp bạn có thể sử dụng. Trước tiên chúng tôi thực hiện tìm kiếm foreach trên bộ sưu tập A (hoặc trong trường hợp người dùng của bạn) và sau đó chúng tôi lấy từng mục làm đối tượng sau đó chúng tôi sử dụng thuộc tính đối tượng (trong trường hợp của bạn là uid) để tra cứu trong bộ sưu tập thứ hai của chúng tôi (trong trường hợp bình luận của bạn) có thể tìm thấy nó sau đó chúng tôi có một trận đấu và chúng tôi có thể in hoặc làm một cái gì đó với nó. Hy vọng điều này sẽ giúp bạn và chúc may mắn :)

db.users.find().forEach(
function (object) {
    var commonInBoth=db.comments.findOne({ "uid": object.uid} );
    if (commonInBoth != null) {
        printjson(commonInBoth) ;
        printjson(object) ;
    }else {
        // did not match so we don't care in this case
    }
});

Điều này có tìm thấy mục chúng tôi hiện đang lặp không?
Skarlinski

18

Với sự kết hợp đúng của $ lookup , $ project$ match , bạn có thể tham gia các bảng đối số trên nhiều tham số. Điều này là do chúng có thể bị xiềng xích nhiều lần.

Giả sử chúng ta muốn làm theo ( tham khảo )

SELECT S.* FROM LeftTable S
LEFT JOIN RightTable R ON S.ID =R.ID AND S.MID =R.MID WHERE R.TIM >0 AND 
S.MOB IS NOT NULL

Bước 1: Liên kết tất cả các bảng

bạn có thể $ tra cứu bao nhiêu bảng tùy thích.

$ tra cứu - một cho mỗi bảng trong truy vấn

$ bung ra - vì dữ liệu được chuẩn hóa không chính xác, các dữ liệu khác được gói trong các mảng

Mã Python ..

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"}

                        ])

Bước 2: Xác định tất cả các điều kiện

$ project : xác định tất cả các câu điều kiện ở đây, cộng với tất cả các biến bạn muốn chọn.

Mã Python ..

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"},

                        # define conditionals + variables

                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }}
                        ])

Bước 3: Tham gia tất cả các điều kiện

$ khớp - tham gia tất cả các điều kiện bằng OR hoặc AND, v.v. Có thể có bội số của các điều kiện này.

$ project : xác định tất cả các điều kiện

Mã Python ..

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "$R"},

                        # define conditionals + variables

                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }},

                        # join all conditionals

                        {"$match": {
                          "$and": [
                            {"R.TIM": {"$gt": 0}}, 
                            {"MOB": {"$exists": True}},
                            {"midEq": {"$eq": True}}
                        ]}},

                        # undefine conditionals

                        {"$project": {
                          "midEq": 0
                        }}

                        ])

Khá nhiều sự kết hợp của các bảng, điều kiện và phép nối có thể được thực hiện theo cách này.


17

Dưới đây là ví dụ về "tham gia" * Bộ sưu tập Diễn viênPhim :

https://github.com/mongodb/cookbook/blob/master/content/potypes/pOLL.txt

Nó sử dụng .mapReduce()phương pháp

* tham gia - một cách khác để tham gia vào cơ sở dữ liệu định hướng tài liệu


19
-1, Đây KHÔNG phải là nối dữ liệu từ hai bộ sưu tập. Nó đang sử dụng dữ liệu từ một bộ sưu tập (các tác nhân) xoay quanh dữ liệu. Vì vậy, những thứ từng là khóa bây giờ là giá trị và giá trị bây giờ là khóa ... rất khác so với THAM GIA.
Evan Teran

12
Đây chính xác là những gì bạn phải làm, MongoDB không phải là quan hệ mà là định hướng tài liệu. MapReduce cho phép chơi với dữ liệu với hiệu suất lớn (bạn có thể sử dụng cụm vv ....) nhưng ngay cả đối với các trường hợp đơn giản, nó rất hữu ích!
Thomas Decaux

14

Bạn có thể tham gia hai bộ sưu tập trong Mongo bằng cách sử dụng tra cứu được cung cấp trong phiên bản 3.2. Trong trường hợp của bạn, truy vấn sẽ là

db.comments.aggregate({
    $lookup:{
        from:"users",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

hoặc bạn cũng có thể tham gia với sự tôn trọng với người dùng thì sẽ có một chút thay đổi như được đưa ra dưới đây.

db.users.aggregate({
    $lookup:{
        from:"comments",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

Nó sẽ hoạt động giống như tham gia trái và phải trong SQL.


11

Nó phụ thuộc vào những gì bạn đang cố gắng làm.

Hiện tại bạn đã thiết lập nó như một cơ sở dữ liệu chuẩn hóa, điều này là tốt, và cách bạn đang làm nó là phù hợp.

Tuy nhiên, có những cách khác để làm điều đó.

Bạn có thể có một bộ sưu tập bài đăng có nhiều bình luận cho mỗi bài đăng với các tham chiếu đến người dùng mà bạn có thể truy vấn lặp đi lặp lại để có được. Bạn có thể lưu trữ tên người dùng với các bình luận, bạn có thể lưu trữ tất cả chúng trong một tài liệu.

Điều thú vị với NoQuery là nó được thiết kế cho các lược đồ linh hoạt và đọc và viết rất nhanh. Trong trang trại Dữ liệu lớn điển hình, cơ sở dữ liệu là nút cổ chai lớn nhất, bạn có ít công cụ cơ sở dữ liệu hơn so với ứng dụng và máy chủ ngoại vi ... chúng đắt hơn nhưng mạnh hơn, dung lượng ổ cứng tương đối rẻ. Bình thường hóa xuất phát từ khái niệm cố gắng tiết kiệm không gian, nhưng nó đi kèm với chi phí khiến cơ sở dữ liệu của bạn thực hiện các Joins phức tạp và xác minh tính toàn vẹn của các mối quan hệ, thực hiện các hoạt động xếp tầng. Tất cả đều tiết kiệm cho các nhà phát triển một số vấn đề đau đầu nếu họ thiết kế cơ sở dữ liệu đúng cách.

Với NoQuery, nếu bạn chấp nhận rằng sự dư thừa và dung lượng lưu trữ không phải là vấn đề vì chi phí của chúng (cả về thời gian xử lý cần thiết để cập nhật và chi phí ổ cứng để lưu trữ thêm dữ liệu), việc không chuẩn hóa không phải là vấn đề (đối với mảng nhúng trở thành Hàng trăm ngàn mặt hàng có thể là một vấn đề hiệu suất, nhưng hầu hết thời gian đó không phải là vấn đề). Ngoài ra, bạn sẽ có một số ứng dụng và máy chủ ngoại vi cho mỗi cụm cơ sở dữ liệu. Yêu cầu họ thực hiện các thao tác nặng và để các máy chủ cơ sở dữ liệu bám vào đọc và viết.

TL; DR: Những gì bạn đang làm là tốt, và có nhiều cách khác để làm điều đó. Kiểm tra các mẫu mô hình dữ liệu của tài liệu mongodb để biết một số ví dụ tuyệt vời. http://docs.mongodb.org/manual/data-modeling/


8
"Bình thường hóa xuất phát từ khái niệm cố gắng tiết kiệm không gian" Tôi đặt câu hỏi này. Bình thường hóa IMHO xuất phát từ khái niệm tránh dư thừa. Giả sử bạn lưu tên của người dùng cùng với blogpost. Nếu cô ấy kết hôn thì sao? Trong một mô hình không được chuẩn hóa, bạn sẽ phải lội qua tất cả các bài viết và thay đổi tên. Trong một mô hình chuẩn hóa, bạn thường thay đổi MỘT bản ghi.
DanielKhan

@DanielKhan ngăn chặn sự dư thừa và tiết kiệm không gian là những khái niệm tương tự, nhưng về phân tích lại tôi đồng ý, sự dư thừa là nguyên nhân gốc rễ cho thiết kế này. Tôi sẽ tua lại. Cảm ơn đã lưu ý.
Snowburnt

11

Có một đặc điểm kỹ thuật là rất nhiều trình điều khiển hỗ trợ được gọi là DBRef.

DBRef là một đặc tả chính thức hơn để tạo tài liệu tham khảo giữa các tài liệu. DBRefs (nói chung) bao gồm tên bộ sưu tập cũng như id đối tượng. Hầu hết các nhà phát triển chỉ sử dụng DBRef nếu bộ sưu tập có thể thay đổi từ tài liệu này sang tài liệu tiếp theo. Nếu bộ sưu tập được tham chiếu của bạn sẽ luôn giống nhau, các tài liệu tham khảo thủ công được nêu ở trên sẽ hiệu quả hơn.

Lấy từ Tài liệu MongoDB: Mô hình dữ liệu> Tham chiếu mô hình dữ liệu> Tham chiếu cơ sở dữ liệu


11

$ tra cứu (tổng hợp)

Thực hiện phép nối ngoài bên trái với một bộ sưu tập chưa được bảo vệ trong cùng một cơ sở dữ liệu để lọc các tài liệu từ bộ sưu tập đã tham gia của LĐ để xử lý. Đối với mỗi tài liệu đầu vào, giai đoạn tra cứu $ sẽ thêm một trường mảng mới có các thành phần là các tài liệu phù hợp từ bộ sưu tập đã tham gia vào. Giai đoạn $ tra cứu chuyển các tài liệu được định hình lại này sang giai đoạn tiếp theo. Giai đoạn $ tra cứu có các cú pháp sau:

Trận đấu bình đẳng

Để thực hiện so khớp bằng nhau giữa một trường từ các tài liệu đầu vào với một trường từ các tài liệu của bộ sưu tập đã tham gia vào bộ phận cộng đồng, thì giai đoạn tra cứu $ có cú pháp sau:

{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}

Hoạt động sẽ tương ứng với câu lệnh giả SQL sau:

SELECT *, <output array field>
FROM collection
WHERE <output array field> IN (SELECT <documents as determined from the pipeline>
                               FROM <collection to join>
                               WHERE <pipeline> );

URL Mongo


truy vấn phụ hoàn toàn khác với tham gia, nếu bảng bên trái của bạn rất lớn, truy vấn phụ có nghĩa là mỗi hàng phải tự thực hiện một truy vấn. nó sẽ trở nên rất chậm tham gia rất nhanh trong sql.
yww325

8

Trước 3.2.6 , Mongodb không hỗ trợ tham gia truy vấn như mysql. giải pháp dưới đây phù hợp với bạn.

 db.getCollection('comments').aggregate([
        {$match : {pid : 444}},
        {$lookup: {from: "users",localField: "uid",foreignField: "uid",as: "userData"}},
   ])

4

Bạn có thể chạy các truy vấn SQL bao gồm tham gia trên MongoDB với mongo_fdw từ Postgres.


3

MongoDB không cho phép tham gia, nhưng bạn có thể sử dụng các plugin để xử lý việc đó. Kiểm tra plugin mongo-tham gia. Đó là điều tốt nhất và tôi đã sử dụng nó. Bạn có thể cài đặt nó bằng cách sử dụng npm trực tiếp như thế này npm install mongo-join. Bạn có thể kiểm tra các tài liệu đầy đủ với các ví dụ .

(++) công cụ thực sự hữu ích khi chúng ta cần tham gia (N) bộ sưu tập

(-) chúng ta có thể áp dụng các điều kiện chỉ ở cấp cao nhất của truy vấn

Thí dụ

var Join = require('mongo-join').Join, mongodb = require('mongodb'), Db = mongodb.Db, Server = mongodb.Server;
db.open(function (err, Database) {
    Database.collection('Appoint', function (err, Appoints) {

        /* we can put conditions just on the top level */
        Appoints.find({_id_Doctor: id_doctor ,full_date :{ $gte: start_date },
            full_date :{ $lte: end_date }}, function (err, cursor) {
            var join = new Join(Database).on({
                field: '_id_Doctor', // <- field in Appoints document
                to: '_id',         // <- field in User doc. treated as ObjectID automatically.
                from: 'User'  // <- collection name for User doc
            }).on({
                field: '_id_Patient', // <- field in Appoints doc
                to: '_id',         // <- field in User doc. treated as ObjectID automatically.
                from: 'User'  // <- collection name for User doc
            })
            join.toArray(cursor, function (err, joinedDocs) {

                /* do what ever you want here */
                /* you can fetch the table and apply your own conditions */
                .....
                .....
                .....


                resp.status(200);
                resp.json({
                    "status": 200,
                    "message": "success",
                    "Appoints_Range": joinedDocs,


                });
                return resp;


            });

    });

2

Bạn có thể làm điều đó bằng cách sử dụng đường dẫn tổng hợp, nhưng thật khó để tự viết nó.

Bạn có thể sử dụng mongo-join-queryđể tạo đường dẫn tổng hợp tự động từ truy vấn của bạn.

Đây là cách truy vấn của bạn sẽ như thế nào:

const mongoose = require("mongoose");
const joinQuery = require("mongo-join-query");

joinQuery(
    mongoose.models.Comment,
    {
        find: { pid:444 },
        populate: ["uid"]
    },
    (err, res) => (err ? console.log("Error:", err) : console.log("Success:", res.results))
);

Kết quả của bạn sẽ có đối tượng người dùng trong uidtrường và bạn có thể liên kết nhiều cấp độ sâu như bạn muốn. Bạn có thể điền tham chiếu đến người dùng, tham chiếu đến Nhóm, tham chiếu đến một thứ khác, v.v.

Tuyên bố miễn trừ trách nhiệm : Tôi đã viết mongo-join-queryđể giải quyết vấn đề chính xác này.


0

playORM có thể làm điều đó cho bạn bằng cách sử dụng S-SQL (SQL có thể mở rộng) chỉ cần thêm phân vùng để bạn có thể tham gia trong các phân vùng.


-2

Không, có vẻ như bạn không làm sai. MongoDB tham gia là "phía khách hàng". Khá giống như bạn nói:

Hiện tại, trước tiên tôi nhận được các bình luận phù hợp với tiêu chí của mình, sau đó tìm ra tất cả các uid trong tập kết quả đó, lấy các đối tượng người dùng và hợp nhất chúng với kết quả của bình luận. Có vẻ như tôi đang làm sai.

1) Select from the collection you're interested in.
2) From that collection pull out ID's you need
3) Select from other collections
4) Decorate your original results.

Đây không phải là một phép nối "thực sự", nhưng nó thực sự hữu ích hơn nhiều so với một phép nối SQL vì bạn không phải xử lý các hàng trùng lặp cho các phép nối hai mặt, thay vào đó bạn trang trí bộ đã chọn ban đầu.

Có rất nhiều điều vô nghĩa và FUD trên trang này. Hóa ra 5 năm sau MongoDB vẫn là một thứ.


'bạn không phải đối phó với các hàng trùng lặp cho "nhiều" tham gia hai bên' - không biết ý của bạn là gì. Bạn có thể làm rõ?
Đánh dấu Amery

1
@MarkAmery, chắc chắn rồi. Trong SQL, mối quan hệ nn sẽ trả về các hàng trùng lặp. Vd: Bạn bè. Nếu Bob là bạn của Mary và Jane, bạn sẽ nhận được 2 hàng cho Bob: Bob, Mary và Bob, Jane. 2 Bobs là một lời nói dối, chỉ có một Bob. Với sự tham gia của phía khách hàng, bạn có thể bắt đầu với Bob và trang trí theo cách bạn thích: Bob, "Mary và Jane". SQL cho phép bạn thực hiện điều này với các truy vấn con, nhưng đó là hoạt động trên máy chủ db có thể được thực hiện trên máy khách.
Michael Cole

-3

Tôi nghĩ, nếu bạn cần các bảng dữ liệu chuẩn hóa - Bạn cần thử một số giải pháp cơ sở dữ liệu khác.

Nhưng tôi đã nói rằng sự châm biếm cho MOngo trên Git Nhân tiện , trong mã chèn - nó có tên phim, nhưng ID của phim noi .

Vấn đề

Bạn có một bộ sưu tập Diễn viên với một loạt các Phim họ đã thực hiện.

Bạn muốn tạo một bộ sưu tập Phim với một loạt các Diễn viên.

Một số dữ liệu mẫu

 db.actors.insert( { actor: "Richard Gere", movies: ['Pretty Woman', 'Runaway Bride', 'Chicago'] });
 db.actors.insert( { actor: "Julia Roberts", movies: ['Pretty Woman', 'Runaway Bride', 'Erin Brockovich'] });

Giải pháp

Chúng ta cần lặp qua từng bộ phim trong tài liệu Diễn viên và phát riêng từng Phim.

Việc bắt ở đây là trong giai đoạn giảm. Chúng tôi không thể phát ra một mảng từ pha khử, vì vậy chúng tôi phải xây dựng một mảng Actors bên trong tài liệu "giá trị" được trả về.

Mật mã
map = function() {
  for(var i in this.movies){
    key = { movie: this.movies[i] };
    value = { actors: [ this.actor ] };
    emit(key, value);
  }
}

reduce = function(key, values) {
  actor_list = { actors: [] };
  for(var i in values) {
    actor_list.actors = values[i].actors.concat(actor_list.actors);
  }
  return actor_list;
}

Lưu ý cách Act_list thực sự là một đối tượng javascript có chứa một mảng. Cũng lưu ý rằng bản đồ phát ra cấu trúc tương tự.

Chạy phần sau để thực thi bản đồ / thu nhỏ, xuất nó vào bộ sưu tập "trục" và in kết quả:

printjson (db.actors.mapReduce (bản đồ, thu nhỏ, "trục")); db.p Pivot.find (). forEach (printjson);

Dưới đây là kết quả đầu ra mẫu, lưu ý rằng "Pretty Woman" và "Runaway Bride" có cả "Richard Gere" và "Julia Roberts".

{ "_id" : { "movie" : "Chicago" }, "value" : { "actors" : [ "Richard Gere" ] } }
{ "_id" : { "movie" : "Erin Brockovich" }, "value" : { "actors" : [ "Julia Roberts" ] } }
{ "_id" : { "movie" : "Pretty Woman" }, "value" : { "actors" : [ "Richard Gere", "Julia Roberts" ] } }
{ "_id" : { "movie" : "Runaway Bride" }, "value" : { "actors" : [ "Richard Gere", "Julia Roberts" ] } }


Lưu ý rằng hầu hết nội dung của câu trả lời này (tức là bit bằng tiếng Anh dễ hiểu) được sao chép từ sách nấu ăn MongoDB tại liên kết GitHub mà người trả lời đã cung cấp.
Đánh dấu Amery

-4

Chúng tôi có thể hợp nhất hai bộ sưu tập bằng cách sử dụng truy vấn phụ mongoDB. Dưới đây là ví dụ, Nhận xét--

`db.commentss.insert([
  { uid:12345, pid:444, comment:"blah" },
  { uid:12345, pid:888, comment:"asdf" },
  { uid:99999, pid:444, comment:"qwer" }])`

Người dùng--

db.userss.insert([
  { uid:12345, name:"john" },
  { uid:99999, name:"mia"  }])

Truy vấn phụ MongoDB cho THAM GIA--

`db.commentss.find().forEach(
    function (newComments) {
        newComments.userss = db.userss.find( { "uid": newComments.uid } ).toArray();
        db.newCommentUsers.insert(newComments);
    }
);`

Nhận kết quả từ Bộ sưu tập mới được tạo--

db.newCommentUsers.find().pretty()

Kết quả--

`{
    "_id" : ObjectId("5511236e29709afa03f226ef"),
    "uid" : 12345,
    "pid" : 444,
    "comment" : "blah",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f2"),
            "uid" : 12345,
            "name" : "john"
        }
    ]
}
{
    "_id" : ObjectId("5511236e29709afa03f226f0"),
    "uid" : 12345,
    "pid" : 888,
    "comment" : "asdf",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f2"),
            "uid" : 12345,
            "name" : "john"
        }
    ]
}
{
    "_id" : ObjectId("5511236e29709afa03f226f1"),
    "uid" : 99999,
    "pid" : 444,
    "comment" : "qwer",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f3"),
            "uid" : 99999,
            "name" : "mia"
        }
    ]
}`

Hy vọng điều này sẽ giúp.


7
Tại sao bạn về cơ bản sao chép câu trả lời gần một năm tuổi này? stackoverflow.com/a/22739813/4186945
hackel
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.