ฉันจะทำการเชื่อมต่อ SQL เข้ากับ MongoDB ได้อย่างไร


498

ฉันจะทำการเชื่อมต่อ SQL เข้ากับ MongoDB ได้อย่างไร

ตัวอย่างเช่นสมมติว่าคุณมีสองคอลเล็กชัน (ผู้ใช้และความคิดเห็น) และฉันต้องการดึงความคิดเห็นทั้งหมดด้วย pid = 444 พร้อมกับข้อมูลผู้ใช้สำหรับแต่ละรายการ

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"  }

มีวิธีดึงความคิดเห็นทั้งหมดด้วยฟิลด์ที่แน่นอน (เช่น ... find ({pid: 444})) และข้อมูลผู้ใช้ที่เกี่ยวข้องกับความคิดเห็นแต่ละรายการในครั้งเดียวหรือไม่

ในขณะนี้ฉันได้รับความคิดเห็นแรกซึ่งตรงกับเกณฑ์ของฉันจากนั้นหา uid ทั้งหมดในชุดผลลัพธ์นั้นรับวัตถุผู้ใช้และรวมเข้ากับผลลัพธ์ของความคิดเห็น ดูเหมือนว่าฉันกำลังทำผิด


35
คำตอบสุดท้ายของคำถามนี้น่าจะเกี่ยวข้องมากที่สุดเนื่องจาก MongoDB 3.2+ นำวิธีการเข้าร่วมที่เรียกว่า $ lookup มาใช้ คิดว่าฉันจะกดที่นี่เพราะบางทีทุกคนอาจไม่อ่านที่ด้านล่าง stackoverflow.com/a/33511166/2593330
thefourtheye

6
ถูกต้องมีการแนะนำการค้นหา $ใน MongoDB 3.2 รายละเอียดสามารถดูได้ที่docs.mongodb.org/master/reference/operator/aggregation/lookup/…
NDB

คำตอบ:


306

ในฐานะของ Mongo 3.2 คำตอบสำหรับคำถามนี้ส่วนใหญ่ไม่ถูกต้องอีกต่อไป ตัวดำเนินการค้นหา $ ใหม่ที่เพิ่มไปยังไปป์ไลน์การรวมจะเหมือนกับการเข้าร่วมด้านนอกด้านซ้าย:

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

จากเอกสาร:

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

แน่นอน Mongo ไม่ใช่ฐานข้อมูลเชิงสัมพันธ์และ devs นั้นระมัดระวังในการแนะนำกรณีการใช้งานเฉพาะสำหรับการค้นหา $ แต่อย่างน้อย 3.2 จากการเข้าร่วมเป็นไปได้ด้วย MongoDB


@clayton: แล้วมีอีกสองคอลเลคชั่นล่ะ?
Dipen Dedania

1
@DipenDedania เพียงเพิ่มขั้นตอนการค้นหา $ เพิ่มเติมไปยังขั้นตอนการรวม
Clayton Gulick

ฉันไม่สามารถเข้าร่วมฟิลด์ใด ๆ ในอาเรย์ในคอลเลกชันด้านซ้ายด้วยรหัสที่สอดคล้องกันในด้านขวา collection.can ใครช่วยฉันได้บ้าง ??
Prateek Singh

1
ฉันสับสนเล็กน้อยเกี่ยวกับเรื่องนี้ - มีวิธีใดที่จะระบุว่าคุณต้องการเฉพาะเอกสารบางอย่างในคอลเลกชัน "จาก" หรือไม่หรือจะเข้าร่วมทั้งหมดในฐานข้อมูลโดยอัตโนมัติหรือไม่?
3413723

เพียงแค่สงสัยว่า Spring Data MongoDB ล่าสุดรองรับ 3.2 หรือไม่?
gtiwari333

142

หน้านี้ในเว็บไซต์ mongodb อย่างเป็นทางการตอบคำถามนี้อย่างแน่นอน :

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

เมื่อเราแสดงรายการเรื่องราวของเราเราจะต้องแสดงชื่อของผู้ใช้ที่โพสต์เรื่องราว หากเราใช้ฐานข้อมูลเชิงสัมพันธ์เราสามารถทำการเข้าร่วมกับผู้ใช้และร้านค้าและรับวัตถุทั้งหมดของเราในแบบสอบถามเดียว แต่ MongoDB ไม่รองรับการรวมดังนั้นบางครั้งอาจต้องใช้การทำให้เป็นปกติเล็กน้อย นี่หมายถึงการแคชแอตทริบิวต์ 'ชื่อผู้ใช้'

นักสอนเชิงสัมพันธ์อาจรู้สึกไม่สบายใจอยู่แล้วราวกับว่าเรากำลังฝ่าฝืนกฎหมายสากล แต่ขอจำไว้ว่าการรวบรวม MongoDB นั้นไม่เทียบเท่ากับตารางเชิงสัมพันธ์ แต่ละอันมีวัตถุประสงค์การออกแบบที่เป็นเอกลักษณ์ ตารางที่ทำให้เป็นมาตรฐานให้กลุ่มข้อมูลที่แยกอะตอม อย่างไรก็ตามเอกสารแสดงถึงวัตถุในภาพรวมอย่างใกล้ชิดยิ่งขึ้น ในกรณีของไซต์ข่าวโซเชียลสามารถแย้งได้ว่าชื่อผู้ใช้นั้นเป็นเนื้อแท้กับเรื่องราวที่โพสต์


51
@dudelgrincen มันเป็นกระบวนทัศน์เปลี่ยนจากฐานข้อมูลปกติและเชิงสัมพันธ์ เป้าหมายของ NoSQL คือการอ่านและเขียนจากฐานข้อมูลอย่างรวดเร็ว ด้วย BigData คุณจะมีแอพพลิเคชั่นและเซิร์ฟเวอร์ส่วนหน้าที่มีตัวเลขต่ำกว่าในฐานข้อมูล คุณคาดว่าจะทำธุรกรรมหลายล้านรายการต่อวินาที ลดการยกของหนักจากฐานข้อมูลและวางลงในระดับแอปพลิเคชัน หากคุณต้องการการวิเคราะห์เชิงลึกคุณสามารถรันงานการรวมที่ทำให้ข้อมูลของคุณเป็นฐานข้อมูล OLAP คุณไม่ควรได้รับข้อความค้นหาจำนวนมากจาก OLTP dbs ของคุณ
Snowburnt

18
@dudelgrincen ฉันควรจะบอกว่ามันไม่ได้สำหรับทุกโครงการหรือการออกแบบ หากคุณมีบางอย่างที่ทำงานในฐานข้อมูลชนิด SQL ทำไมต้องเปลี่ยน หากคุณไม่สามารถนวดสคีมาของคุณเพื่อทำงานกับ noSQL ก็ไม่ได้
Snowburnt

9
การโยกย้ายและสกีมาที่มีการพัฒนาอยู่ตลอดเวลานั้นง่ายต่อการจัดการบนระบบ NoSQL
justin

14
จะเกิดอะไรขึ้นหากผู้ใช้มีการโพสต์ 3.540 ในเว็บไซต์และเขาจะเปลี่ยนชื่อผู้ใช้ของเขาในโปรไฟล์? ควรโพสต์ทุกครั้งด้วยชื่อผู้ใช้ใหม่หรือไม่
Ivo Pereira

2
@IvoPereira ใช่และนั่นเป็นสาเหตุที่เราควรหลีกเลี่ยงการสร้างแบบจำลองข้อมูลด้วยวิธีนี้ มีบทความที่อธิบายสถานการณ์เดียวกันและผลที่ตามมา: ทำไมคุณไม่ควรใช้ MongoDB
Omid

138

เราสามารถรวม / เข้าร่วมข้อมูลทั้งหมดภายในหนึ่งคอลเลกชันที่มีฟังก์ชั่นที่ง่ายในไม่กี่บรรทัดโดยใช้คอนโซลลูกค้า mongodb และตอนนี้เราสามารถทำการค้นหาที่ต้องการ ด้านล่างเป็นตัวอย่างที่สมบูรณ์

.- ผู้แต่ง:

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

.- หมวดหมู่:

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

.- หนังสือ

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

.- จองสินเชื่อ

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'
    }
]);

.- เวทย์มนต์:

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);
    }
);

.- รับข้อมูลการรวบรวมใหม่:

db.booksReloaded.find().pretty()

.- การตอบสนอง :)

{
    "_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" : [ ]
}

ฉันหวังว่าบรรทัดนี้จะช่วยคุณได้


2
ฉันสงสัยว่ารหัสเดียวกันนี้สามารถวิ่งโดยใช้หลักคำสอน mongodb หรือไม่
abbood

4
จะเกิดอะไรขึ้นเมื่อวัตถุอ้างอิงหนึ่งรายการได้รับการอัปเดต การอัปเดตนั้นสะท้อนถึงวัตถุหนังสือโดยอัตโนมัติหรือไม่ หรือว่าลูปนั้นจำเป็นต้องทำงานอีกครั้ง?
balupton

14
นี่เป็นเรื่องปกติตราบใดที่ข้อมูลของคุณมีขนาดเล็ก มันจะนำเนื้อหาหนังสือแต่ละเล่มไปให้ลูกค้าของคุณแล้วดึงข้อมูลแต่ละหมวดหมู่การให้ยืมและผู้แต่งทีละเล่ม ช่วงเวลาที่หนังสือของคุณอยู่ในหลักพันนี่จะช้าไปจริงๆ เทคนิคที่ดีกว่าน่าจะเป็นการใช้ขั้นตอนการรวมตัวและส่งออกข้อมูลที่ผสานไปยังการรวบรวมแยก ให้ฉันกลับไปอีกครั้ง ฉันจะเพิ่มคำตอบนั้น
Sandeep Giri

คุณสามารถปรับอัลกอริทึมของคุณให้เป็นตัวอย่างอื่นได้หรือไม่? stackoverflow.com/q/32718079/287948
Peter Krauss

1
@SandeepGiri ฉันจะทำไปป์ไลน์รวมเนื่องจากฉันมีข้อมูลที่เข้มข้นจริง ๆ ในคอลเลกชันแยกต้องเข้าร่วมได้อย่างไร
Yassine Abdul-Rahman

38

คุณต้องทำตามที่อธิบายไว้ MongoDB เป็นฐานข้อมูลที่ไม่เกี่ยวข้องและไม่รองรับการเชื่อมต่อ


4
ดูเหมือนว่าประสิทธิภาพผิดปกติฉลาดมาจากพื้นหลังเซิร์ฟเวอร์ sql แต่อาจจะไม่ดีกับเอกสาร db?
terjetyl

3
จากพื้นหลังของเซิร์ฟเวอร์ sql เช่นกันฉันขอขอบคุณ MongoDB ที่ใช้ 'ชุดผลลัพธ์' (ที่มีเขตข้อมูลที่เลือก) เป็นข้อมูลป้อนเข้าสำหรับแบบสอบถามใหม่ในครั้งเดียวเหมือนกับแบบสอบถามที่ซ้อนกันใน SQL
Stijn Sanders

1
@terjetyl คุณต้องวางแผนจริงๆ คุณจะนำเสนอฟิลด์ใดในส่วนหน้าหากจำนวน จำกัด ในมุมมองส่วนบุคคลคุณจะต้องใช้ฟิลด์เหล่านั้นเป็นเอกสารฝังตัว กุญแจสำคัญคือไม่จำเป็นต้องเข้าร่วม ถ้าคุณต้องการทำการวิเคราะห์ที่ลึกคุณทำตามข้อเท็จจริงในฐานข้อมูลอื่น รันงานที่แปลงข้อมูลเป็น OLAP cube เพื่อประสิทธิภาพที่ดีที่สุด
Snowburnt

4
รองรับ mongo 3.2 จากซ้ายเข้าร่วมได้รับการสนับสนุน
Somnath Muluk

18

ดังที่คนอื่น ๆ ชี้ให้เห็นว่าคุณกำลังพยายามสร้างฐานข้อมูลเชิงสัมพันธ์จากฐานข้อมูลเชิงสัมพันธ์ที่คุณไม่ต้องการทำนอกจากนี้ถ้าคุณมีกรณีที่คุณต้องทำสิ่งนี้นี่เป็นวิธีแก้ปัญหาที่คุณสามารถใช้ได้ ก่อนอื่นเราทำการค้นหาในคอลเล็กชัน A (หรือในกรณีผู้ใช้ของคุณ) จากนั้นเราได้แต่ละรายการเป็นวัตถุจากนั้นเราจะใช้คุณสมบัติของวัตถุ (ในกรณีของคุณ uid) เพื่อค้นหาในคอลเล็กชันที่สองของเรา (ในกรณีของคุณ) สามารถค้นหาได้จากนั้นเรามีการแข่งขันและเราสามารถพิมพ์หรือทำอะไรกับมัน หวังว่านี่จะช่วยคุณและโชคดี :)

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
    }
});

สิ่งนี้จะไม่พบสินค้าที่เรากำลังวนซ้ำหรือไม่?
Skarlinski

18

ด้วยการรวมกันอย่างเหมาะสมของ$ lookup , $ projectและ$ matchคุณสามารถเข้าร่วมตาราง mutiple ในหลายพารามิเตอร์ นี่เป็นเพราะพวกเขาสามารถถูกผูกมัดหลายครั้ง

สมมติว่าเราต้องการทำตาม ( อ้างอิง )

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

ขั้นตอนที่ 1: เชื่อมโยงตารางทั้งหมด

คุณสามารถค้นหา $ หลาย ๆ ตารางตามที่คุณต้องการ

$ ค้นหา - หนึ่งสำหรับแต่ละตารางในแบบสอบถาม

$ คลาย - เนื่องจากข้อมูลมีความผิดปกติอย่างถูกต้องห่ออื่นในอาร์เรย์

รหัสหลาม ..

db.LeftTable.aggregate([
                        # connect all tables

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

                        ])

ขั้นตอนที่ 2: กำหนดเงื่อนไขทั้งหมด

$ project : กำหนดข้อความตามเงื่อนไขทั้งหมดที่นี่รวมถึงตัวแปรทั้งหมดที่คุณต้องการเลือก

รหัสหลาม ..

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
                        }}
                        ])

ขั้นตอนที่ 3: เข้าร่วมเงื่อนไขทั้งหมด

$ match - เข้าร่วมเงื่อนไขทั้งหมดโดยใช้ OR หรือ AND และอื่น ๆ สามารถมีหลายรายการได้

$ project : ยกเลิกการกำหนดเงื่อนไขทั้งหมด

รหัสหลาม ..

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
                        }}

                        ])

การรวมกันของตารางเงื่อนไขและการรวมสามารถทำได้ในลักษณะนี้


17

นี่คือตัวอย่างของ"เข้าร่วม" * คอลเลกชันนักแสดงและภาพยนตร์ :

https://github.com/mongodb/cookbook/blob/master/content/patterns/pivot.txt

มันใช้.mapReduce()วิธีการ

* เข้าร่วม - ทางเลือกในการเข้าร่วมในฐานข้อมูลเชิงเอกสาร


19
-1, นี่ไม่ได้เข้าร่วมข้อมูลจากสองกลุ่ม มันใช้ข้อมูลจากคอลเลกชันเดียว (นักแสดง) ข้อมูลหมุนรอบ เพื่อให้สิ่งที่มีคีย์นี้มีค่าและค่านิยมในขณะนี้เป็นกุญแจ ... มากที่แตกต่างกันกว่า JOIN
Evan Teran

12
ตรงนี้สิ่งที่คุณต้องทำ MongoDB ไม่เชิงสัมพันธ์ แต่มุ่งเน้นเอกสาร MapReduce ช่วยให้สามารถเล่นกับข้อมูลที่มีประสิทธิภาพสูง (คุณสามารถใช้คลัสเตอร์ ฯลฯ .... ) แต่สำหรับกรณีง่าย ๆ มันมีประโยชน์มาก!
โทมัส Decaux

14

คุณสามารถเข้าร่วมสองคอลเลกชันใน Mongo โดยใช้การค้นหาซึ่งมีให้ใน 3.2 เวอร์ชั่น ในกรณีของคุณแบบสอบถามจะเป็น

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

หรือคุณสามารถเข้าร่วมด้วยความเคารพต่อผู้ใช้จากนั้นจะมีการเปลี่ยนแปลงเล็กน้อยตามที่ระบุด้านล่าง

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

มันจะทำงานเหมือนกับการเข้าร่วมทางซ้ายและขวาใน SQL


11

ขึ้นอยู่กับสิ่งที่คุณพยายามจะทำ

ขณะนี้คุณได้ตั้งค่าเป็นฐานข้อมูลปกติซึ่งใช้ได้และวิธีที่คุณทำอยู่มีความเหมาะสม

อย่างไรก็ตามมีวิธีอื่นในการทำเช่นนี้

คุณสามารถมีคอลเลกชันโพสต์ที่มีความคิดเห็นฝังสำหรับแต่ละโพสต์ที่มีการอ้างอิงถึงผู้ใช้ที่คุณสามารถสอบถามซ้ำได้ คุณสามารถจัดเก็บชื่อผู้ใช้ด้วยความคิดเห็นคุณสามารถเก็บไว้ในเอกสารเดียว

สิ่งที่มี NoSQL มันถูกออกแบบมาสำหรับ schema ที่มีความยืดหยุ่นและการอ่านและการเขียนที่รวดเร็วมาก ในฟาร์มข้อมูลขนาดใหญ่โดยทั่วไปฐานข้อมูลเป็นคอขวดที่ใหญ่ที่สุดคุณมีเอ็นจิ้นฐานข้อมูลน้อยกว่าที่คุณทำแอพพลิเคชั่นและเซิร์ฟเวอร์ส่วนหน้า ... พวกมันมีราคาแพงกว่า แต่ทรงพลังกว่า การทำให้เป็นมาตรฐานมาจากแนวคิดของการพยายามประหยัดพื้นที่ แต่มันมาพร้อมกับค่าใช้จ่ายในการทำให้ฐานข้อมูลของคุณมีความซับซ้อนในการเข้าร่วมและตรวจสอบความสมบูรณ์ของความสัมพันธ์ ทั้งหมดนี้ช่วยให้ผู้พัฒนาประหยัดอาการปวดหัวหากพวกเขาออกแบบฐานข้อมูลอย่างเหมาะสม

ด้วย NoSQL ถ้าคุณยอมรับว่าความซ้ำซ้อนและพื้นที่เก็บข้อมูลไม่ใช่ปัญหาเนื่องจากค่าใช้จ่าย (ทั้งในเวลาประมวลผลที่ต้องใช้ในการอัปเดตและค่าใช้จ่ายของฮาร์ดไดรฟ์ในการจัดเก็บข้อมูลเพิ่มเติม) การ denormalizing ไม่ใช่ปัญหา รายการนับแสนรายการอาจเป็นปัญหาด้านประสิทธิภาพ แต่ส่วนใหญ่ไม่ใช่ปัญหา) นอกจากนี้คุณจะมีแอพพลิเคชั่นและเซิร์ฟเวอร์ส่วนหน้าสำหรับทุกกลุ่มฐานข้อมูล ให้พวกเขายกการเชื่อมอย่างหนักและให้เซิร์ฟเวอร์ฐานข้อมูลติดอ่านและเขียน

TL: DR: สิ่งที่คุณกำลังทำอยู่นั้นดีและมีวิธีอื่นในการทำเช่นนั้น ตรวจสอบรูปแบบข้อมูลของเอกสารประกอบ mongodb สำหรับตัวอย่างที่ยอดเยี่ยม http://docs.mongodb.org/manual/data-modeling/


8
"การทำให้เป็นมาตรฐานมาจากแนวคิดของการพยายามประหยัดพื้นที่" ฉันตั้งคำถามนี้ การปรับสภาพ IMHO มาจากแนวคิดของการหลีกเลี่ยงความซ้ำซ้อน สมมติว่าคุณเก็บชื่อของผู้ใช้พร้อมกับโพสต์บล็อก เกิดอะไรขึ้นถ้าเธอแต่งงาน ในรูปแบบที่ไม่ได้ทำให้เป็นมาตรฐานคุณจะต้องลุยผ่านโพสต์ทั้งหมดและเปลี่ยนชื่อ ในรูปแบบปกติคุณมักจะเปลี่ยนหนึ่งบันทึก
DanielKhan

@DanielKhan การป้องกันความซ้ำซ้อนและประหยัดพื้นที่เป็นแนวคิดที่คล้ายกัน แต่ในการวิเคราะห์ซ้ำฉันยอมรับความซ้ำซ้อนเป็นสาเหตุหลักของการออกแบบนี้ ฉันจะพูดใหม่ ขอบคุณสำหรับการบันทึก
Snowburnt

11

มีสเปคที่ไดรเวอร์จำนวนมากรองรับที่เรียกว่า DBRef

DBRef เป็นข้อกำหนดที่เป็นทางการมากขึ้นสำหรับการสร้างการอ้างอิงระหว่างเอกสาร DBRefs (โดยทั่วไป) รวมชื่อคอลเลกชันเช่นเดียวกับรหัสวัตถุ นักพัฒนาส่วนใหญ่ใช้ DBRefs เท่านั้นหากคอลเลกชันสามารถเปลี่ยนจากเอกสารหนึ่งไปเป็นเอกสารถัดไป หากคอลเล็กชันอ้างอิงของคุณจะเหมือนกันเสมอการอ้างอิงด้วยตนเองที่ระบุไว้ข้างต้นจะมีประสิทธิภาพมากกว่า

นำมาจากเอกสาร MongoDB: ตัวแบบข้อมูล> การอ้างอิงตัวแบบข้อมูล> การอ้างอิง ฐานข้อมูล


11

$ ค้นหา (รวม)

ดำเนินการรวมด้านนอกซ้ายไปยังคอลเลกชันที่ไม่ใช้ร่วมกันในฐานข้อมูลเดียวกันเพื่อกรองเอกสารจากคอลเลกชัน“ เข้าร่วม” สำหรับการประมวลผล สำหรับเอกสารอินพุตแต่ละรายการระยะการค้นหา $ เพิ่มฟิลด์อาร์เรย์ใหม่ซึ่งองค์ประกอบคือเอกสารที่ตรงกันจากคอลเลกชัน“ เข้าร่วม” ขั้นตอนการค้นหา $ จะส่งเอกสารที่ปรับเปลี่ยนใหม่เหล่านี้ไปยังขั้นตอนถัดไป $ lookup stage มีไวยากรณ์ต่อไปนี้:

การแข่งขันที่เท่าเทียมกัน

ในการดำเนินการจับคู่ที่เท่าเทียมกันระหว่างเขตข้อมูลจากเอกสารป้อนเข้าด้วยเขตข้อมูลจากเอกสารของคอลเลกชัน“ เข้าร่วม” เวทีการค้นหา $ จะมีไวยากรณ์ต่อไปนี้:

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

การดำเนินการจะสอดคล้องกับคำสั่งหลอก SQL ต่อไปนี้:

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

Mongo URL


แบบสอบถามย่อยแตกต่างจากการเข้าร่วมโดยสิ้นเชิงหากตารางด้านซ้ายของคุณมีขนาดใหญ่แบบสอบถามย่อยหมายความว่าแต่ละแถวต้องทำแบบสอบถามเอง มันจะช้ามาก เข้าร่วมเร็วมากใน sql
yww325

8

ก่อน3.2.6 Mongodb ไม่สนับสนุนการเข้าร่วมแบบสอบถามเช่น mysql โซลูชันด้านล่างที่เหมาะกับคุณ

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


3

MongoDB ไม่อนุญาตการรวม แต่คุณสามารถใช้ปลั๊กอินเพื่อจัดการสิ่งนั้นได้ ตรวจสอบปลั๊กอินการเข้าร่วม mongo มันดีที่สุดและฉันใช้ไปแล้ว คุณสามารถติดตั้งได้โดยใช้ NPM npm install mongo-joinโดยตรงเช่นนี้ คุณสามารถตรวจสอบเอกสารเต็มรูปแบบด้วยตัวอย่าง

(++) เครื่องมือที่มีประโยชน์จริง ๆ เมื่อเราต้องการเข้าร่วม (N) คอลเลกชัน

(-) เราสามารถใช้เงื่อนไขเฉพาะที่ระดับบนสุดของแบบสอบถาม

ตัวอย่าง

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

คุณสามารถทำได้โดยใช้ไปป์ไลน์การรวม แต่มันเป็นความเจ็บปวดที่จะเขียนด้วยตัวคุณเอง

คุณสามารถใช้mongo-join-queryเพื่อสร้างขั้นตอนการรวมโดยอัตโนมัติจากการสืบค้นของคุณ

นี่คือลักษณะของแบบสอบถามของคุณ:

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))
);

ผลลัพธ์ของคุณจะมีวัตถุผู้ใช้ในuidฟิลด์และคุณสามารถเชื่อมโยงระดับลึกมากเท่าที่คุณต้องการ คุณสามารถเติมการอ้างอิงถึงผู้ใช้ซึ่งทำให้การอ้างอิงถึงทีมซึ่งทำให้การอ้างอิงถึงสิ่งอื่น ฯลฯ

คำเตือน : ฉันเขียนmongo-join-queryเพื่อจัดการปัญหาที่แน่นอนนี้


0

playORM สามารถทำเพื่อคุณโดยใช้ S-SQL (Scalable SQL) ซึ่งเพิ่งเพิ่มการแบ่งพาร์ติชันเพื่อให้คุณสามารถรวมภายในพาร์ติชันได้


-2

ไม่ดูเหมือนคุณจะทำผิด MongoDB joins คือ "ฝั่งไคลเอ็นต์" สวยมากเหมือนที่คุณพูด:

ในขณะนี้ฉันได้รับความคิดเห็นแรกซึ่งตรงกับเกณฑ์ของฉันจากนั้นหา uid ทั้งหมดในชุดผลลัพธ์นั้นรับวัตถุผู้ใช้และรวมเข้ากับผลลัพธ์ของความคิดเห็น ดูเหมือนว่าฉันกำลังทำผิด

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.

มันไม่ใช่การเข้าร่วมแบบ "ของจริง" แต่จริงๆแล้วมันมีประโยชน์มากกว่าการเข้าร่วม SQL เพราะคุณไม่ต้องจัดการกับแถวที่ซ้ำกันสำหรับการเชื่อมต่อด้าน "หลาย" แทนการตกแต่งของคุณในชุดที่เลือกไว้เดิม

มีจำนวนมากไร้สาระและ FUD ในหน้านี้ ปรากฎว่า 5 ปีต่อมา MongoDB ยังคงเป็นเรื่อง


'คุณไม่ต้องจัดการกับแถวที่ซ้ำกันสำหรับการรวมด้าน "จำนวนมาก" - ไม่รู้ว่าคุณหมายถึงอะไร คุณช่วยอธิบายได้ไหม
Mark Amery

1
@ MarkAmery แน่นอน ใน SQL ความสัมพันธ์ nn จะส่งคืนแถวที่ซ้ำกัน เช่นเพื่อน หากบ๊อบเป็นเพื่อนกับแมรี่และเจนคุณจะได้รับ 2 แถวสำหรับบ็อบ: บ็อบแมรี่และบ๊อบเจน 2 Bobs เป็นเรื่องโกหกมีเพียง Bob เดียว ด้วยการเข้าร่วมฝั่งไคลเอ็นต์คุณสามารถเริ่มต้นด้วย Bob และตกแต่งตามที่คุณต้องการ: Bob, "Mary and Jane" SQL ลองทำกับเคียวรีย่อย, แต่มันทำงานบนเซิร์ฟเวอร์ db ที่สามารถทำได้บนไคลเอนต์
Michael Cole

-3

ฉันคิดว่าถ้าคุณต้องการตารางข้อมูลปกติ - คุณต้องลองโซลูชันฐานข้อมูลอื่น ๆ

แต่ฉัน Foun ลำพังว่าสำหรับ Mongo ในGit โดยวิธีการในรหัสแทรก - มันมีชื่อของภาพยนตร์, แต่ ID

ปัญหา

คุณมีคอลเลคชั่นของนักแสดงพร้อมกับภาพยนตร์ที่พวกเขาทำ

คุณต้องการสร้างคอลเลกชันภาพยนตร์ที่มีอาร์เรย์ของนักแสดงในแต่ละรายการ

ตัวอย่างข้อมูลบางส่วน

 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'] });

สารละลาย

เราจำเป็นต้องวนซ้ำภาพยนตร์แต่ละเรื่องในเอกสารนักแสดงและฉายภาพยนตร์แต่ละเรื่องแยกกัน

จับที่นี่อยู่ในขั้นตอนการลด เราไม่สามารถปล่อยอาร์เรย์จากช่วงลดดังนั้นเราต้องสร้างอาร์เรย์ Actors ภายในเอกสาร "value" ที่ส่งคืน

รหัส
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;
}

โปรดสังเกตว่าจริง ๆ แล้ว actor_list เป็นวัตถุจาวาสคริปต์ที่มีอาร์เรย์ นอกจากนี้โปรดสังเกตว่าแผนที่ส่งเสียงโครงสร้างเดียวกัน

ดำเนินการดังต่อไปนี้เพื่อดำเนินการแมป / ลดส่งออกไปยังคอลเลกชัน "pivot" และพิมพ์ผลลัพธ์:

printjson (db.actors.mapReduce (แผนที่, ลด, "pivot")); db.pivot.find () forEach (printjson).

นี่คือตัวอย่างผลลัพธ์โปรดทราบว่า "Pretty Woman" และ "Runaway Bride" มีทั้ง "Richard Gere" และ "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" ] } }


โปรดทราบว่าเนื้อหาส่วนใหญ่ของคำตอบนี้ (เช่นบิตที่เป็นภาษาอังกฤษที่เข้าใจได้) จะถูกคัดลอกจากตำรา MongoDB ที่ลิงก์ GitHub ที่ผู้ตอบคำถามให้ไว้
Mark Amery

-4

เราสามารถรวมสองคอลเลกชันโดยใช้แบบสอบถามย่อย mongoDB นี่คือตัวอย่างความคิดเห็น -

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

Userss--

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

แบบสอบถามย่อย MongoDB สำหรับ JOIN -

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

รับผลจากการรวบรวมใหม่ -

db.newCommentUsers.find().pretty()

ผลลัพธ์--

`{
    "_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"
        }
    ]
}`

หวังว่านี่จะช่วยได้


7
ทำไมคุณถึงคัดลอกคำตอบนี้อายุเกือบหนึ่งปีโดยทั่วไป stackoverflow.com/a/22739813/4186945
hackel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.