ค้นหาเอกสารที่มีอาร์เรย์ที่มีค่าเฉพาะ


499

ถ้าฉันมีสคีมานี้ ...

person = {
    name : String,
    favoriteFoods : Array
}

... โดยที่favoriteFoodsarray นั้นบรรจุด้วยสตริง ฉันจะหาคนที่มี "ซูชิ" เป็นอาหารโปรดของพวกเขาโดยใช้พังพอนได้อย่างไร?

ฉันหวังว่าจะมีบางอย่างตามมา:

PersonModel.find({ favoriteFoods : { $contains : "sushi" }, function(...) {...});

(ฉันรู้ว่าไม่มี$containsmongodb เพียงอธิบายสิ่งที่ฉันคาดหวังว่าจะพบก่อนที่จะรู้วิธีแก้ปัญหา)

คำตอบ:


693

เช่นเดียวfavouriteFoodsกับอาเรย์อย่างง่าย ๆ ของสตริงคุณสามารถสืบค้นฟิลด์นั้นได้โดยตรง:

PersonModel.find({ favouriteFoods: "sushi" }, ...);

แต่ฉันขอแนะนำให้กำหนดให้อาร์เรย์สตริงชัดเจนในสคีมาของคุณ:

person = {
    name : String,
    favouriteFoods : [String]
}

เอกสารที่เกี่ยวข้องสามารถพบได้ที่นี่: https://docs.mongodb.com/manual/tutorial/query-arrays/


19
สิ่งนี้สามารถใช้ได้ถ้าfavouriteFoodsเป็น:favouriteFoods:[{type:Schema.Types.ObjectId, ref:'Food'}]
k88074

12
ในฐานะที่เป็นคนใหม่สำหรับ Mongo ที่มาจาก RDBMS เช่น MySQL เพื่อพบว่าโซลูชั่นดังกล่าวทำงานได้อย่างง่ายดายโดยไม่จำเป็นต้องเข้าร่วมและตารางเพิ่มเติมทำให้ฉันสงสัยว่าทำไมฉันไม่ได้เริ่ม Mongo เร็วกว่านี้ แต่นั่นไม่ได้หมายความว่า DBMS จะดีกว่าอีกอัน - ขึ้นอยู่กับกรณีการใช้งานของคุณ
Irvin Lim

9
อย่าเข้าใจผิด แม้ว่ามันจะเป็นรายการของ dict คุณยังสามารถสืบค้นได้ด้วยวิธีนี้ ตัวอย่าง: PersonModel.find({ favouriteFoods.text: "sushi" }, ...); person = { name : String, favouriteFoods : [{text:String}] }
Aminah Nuraini

3
จะเกิดอะไรขึ้นเมื่อฉันต้องการค้นหาอาเรย์ที่มีสตริงอย่างน้อยสองสตริง
Aero Wang

151

ไม่มี$containsโอเปอเรเตอร์ใน mongodb

คุณสามารถใช้คำตอบจาก JohnnyHK ได้ การเปรียบเทียบที่ใกล้เคียงที่สุดที่จะมี Mongo คือการ$inใช้แบบสอบถามของคุณจะมีลักษณะดังนี้:

PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);

10
ถูกต้องหรือไม่ mongodb ไม่ต้องการรับค่าอาร์เรย์เมื่อใช้ $ in ใช่หรือไม่ เช่น {name: {$ in: ["Paul", "Dave", "Larry", "Adam"]}}?
Ludwig Magnusson

37
ฮะ? สิ่งนี้ไม่จำเป็น $inจะใช้เมื่อคุณมีค่าคิวรีหลายค่าและเอกสารต้องตรงกับค่าใดค่าหนึ่ง สำหรับการย้อนกลับ (ซึ่งเป็นคำถามนี้เกี่ยวกับ) คำตอบของ JohnnyHK นั้นถูกต้อง ฉันกำลังจะลงคะแนน แต่ฉันคิดว่าคำตอบนี้อาจเป็นประโยชน์กับคนอื่น ๆ ที่ท้ายหน้านี้
MalcolmOcean

4
แต่สิ่งนี้ช่วยให้ฉันค้นหาด้วยค่าหลายค่าจริง: D ขอบคุณมาก!
Alexandre Bourlier

10
ขอบคุณ นี่คือสิ่งที่ฉันกำลังมองหาวิธีการค้นหาค่าหลายค่า:PersonModel.find({favouriteFoods: {"$in": ["sushi", "hotdog"]}})
totymedli

@MalcolmOcean ถูกต้องในการที่ $ ในผู้ประกอบการที่เป็นแบบย้อนกลับที่มีอาร์เรย์เป็นที่คุ้มค่า ฟิลด์เป็นอาร์เรย์คือสิ่งที่เป็นคำถามที่ถามเกี่ยวกับ อย่างไรก็ตามถ้าทั้งฟิลด์และค่าเป็นอาร์เรย์ดังนั้นทั้งคำตอบนี้และของ JohnnyHK นั้นมีความเกี่ยวข้องหมายความว่าคุณต้องใช้ $ in
tscizzle

87

ฉันรู้สึกว่า$allจะเหมาะสมกว่าในสถานการณ์นี้ หากคุณกำลังมองหาบุคคลที่เป็นซูชิคุณต้อง:

PersonModel.find({ favoriteFood : { $all : ["sushi"] }, ...})

ตามที่คุณอาจต้องการกรองการค้นหาของคุณเพิ่มเติมเช่น:

PersonModel.find({ favoriteFood : { $all : ["sushi", "bananas"] }, ...})

$inเป็นเช่นหรือและ$allชอบและ ตรวจสอบสิ่งนี้: https://docs.mongodb.com/manual/reference/operator/query/all/


ขออภัยนี่เป็นคำตอบที่ไม่ถูกต้องสำหรับคำถามของฉัน ฉันไม่ได้กำลังมองหาการจับคู่ที่แน่นอน แต่เพียงสำหรับอาร์เรย์ที่มีอย่างน้อยค่าที่ระบุ
Ludwig Magnusson

17
นี่คือคำตอบที่ถูกต้องสำหรับคำถามของคุณ! สำหรับหนึ่งค่าไม่มีความแตกต่างในการใช้ $ all หรือ $ in หากคุณมีค่าหลายอย่างเช่น "ซูชิ", "กล้วย", $ all กำลังมองหาผู้ที่มี "ซูชิ" และ "กล้วย" ในอาร์เรย์อาหารโปรดของพวกเขาหากใช้ $ ในคุณจะได้รับบุคคลที่มี "ซูชิ" หรือ "กล้วย" "ในอาหารที่พวกเขาโปรดปราน
Jodo

ใช่ไม่มี $ มีอยู่ แต่ $ all เรียงลำดับแล้ว
datdinhquoc

2
คำตอบที่ดีที่สุด
Nikolay Tsenkov

65

ในกรณีที่อาร์เรย์มีวัตถุเช่นถ้าfavouriteFoodsเป็นอาร์เรย์ของวัตถุดังต่อไปนี้:

{
  name: 'Sushi',
  type: 'Japanese'
}

คุณสามารถใช้แบบสอบถามต่อไปนี้:

PersonModel.find({"favouriteFoods.name": "Sushi"});

2
นี่เป็นคำตอบที่ง่ายที่สุด ใช้งานง่ายกว่ามากเมื่อคุณรีบ
Uber Schnoz

นี่ควรเป็นคำตอบที่เลือก หากคุณจัดการกับการสอบถามอาเรย์ของเอกสารที่ซ้อนกันใน MongoDB นี่คือวิธีที่คุณทำ ไม่แน่ใจว่ามันมีประสิทธิภาพมากที่สุด แต่ถ้านั่นคือทั้งหมดที่คุณพยายามทำนี่คือทั้งหมดที่คุณต้องการ
Kyle L.

32

ในกรณีที่คุณต้องการค้นหาเอกสารที่มีองค์ประกอบ NULL อยู่ในอาร์เรย์ของเอกสารย่อยฉันพบว่าแบบสอบถามนี้ใช้งานได้ดี:

db.collection.find({"keyWithArray":{$elemMatch:{"$in":[null], "$exists":true}}})

แบบสอบถามนี้นำมาจากโพสต์นี้: MongoDb แถวสอบถามที่มีค่า Null

มันเป็นการค้นพบที่ยอดเยี่ยมและทำงานได้ดีกว่ารุ่นเริ่มต้นและผิดของฉันเอง(ซึ่งกลับกลายเป็นว่าทำงานได้ดีสำหรับอาร์เรย์ที่มีองค์ประกอบเดียวเท่านั้น):

.find({
    'MyArrayOfSubDocuments': { $not: { $size: 0 } },
    'MyArrayOfSubDocuments._id': { $exists: false }
})

3

แม้ว่าเห็นด้วยกับ find () มีประสิทธิภาพมากที่สุดใน usecase ของคุณ ยังคงมีการจับคู่ $ กรอบการรวมเพื่อความสะดวกในการค้นหาของรายการจำนวนมากและสร้างผลลัพธ์จำนวนต่ำที่เก็บค่าไว้กับคุณโดยเฉพาะอย่างยิ่งสำหรับการจัดกลุ่มและการสร้างไฟล์ใหม่

  PersonModel.aggregate([
            { 
                 "$match": { 
                     $and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ]  }
             },
             { $project : {"_id": 0, "name" : 1} }
            ]);

ไม่ทำงานกับ mongodb 4.2 .. โปรดตอบกลับ
vimmi

คุณได้รับข้อผิดพลาดใดโปรดระบุรายละเอียด
Amitesh

3

กรณีของ lookup_food_array คืออาร์เรย์

match_stage["favoriteFoods"] = {'$elemMatch': {'$in': lookup_food_array}}

กรณีของ lookup_food_array เป็นสตริง

match_stage["favoriteFoods"] = {'$elemMatch': lookup_food_string}

1

สำหรับ Loopback3 ตัวอย่างทั้งหมดที่ให้ไว้ไม่ได้ผลสำหรับฉันหรือเร็วเท่ากับการใช้ REST API แต่มันช่วยให้ฉันค้นหาคำตอบที่ฉันต้องการได้อย่างแม่นยำ

{"where":{"arrayAttribute":{ "all" :[String]}}}


1
คุณเป็นผู้ช่วยชีวิตขอบคุณ! เอกสารนั้นอยู่ที่ไหนและฉันพลาดไปหรือไม่ กรุณาโพสต์ลิงค์ได้ไหม? ขอบคุณ
user2078023

-3

หากคุณต้องการใช้ตัวดำเนินการ "มี" ผ่านทางจาวาสคริปต์คุณสามารถใช้นิพจน์ปกติสำหรับ ...

เช่น. สมมติว่าคุณต้องการดึงลูกค้าที่มีชื่อ "Bartolomew" เป็นชื่อ

async function getBartolomew() {
    const custStartWith_Bart = await Customers.find({name: /^Bart/ }); // Starts with Bart
    const custEndWith_lomew = await Customers.find({name: /lomew$/ }); // Ends with lomew
    const custContains_rtol = await Customers.find({name: /.*rtol.*/ }); // Contains rtol

    console.log(custStartWith_Bart);
    console.log(custEndWith_lomew);
    console.log(custContains_rtol);
}

-26

ฉันรู้ว่าหัวข้อนี้เก่า แต่สำหรับคนในอนาคตที่อาจสงสัยคำถามเดียวกันวิธีแก้ปัญหาที่ไม่มีประสิทธิภาพอย่างไม่น่าเชื่ออย่างอื่นสามารถทำได้:

PersonModel.find({$where : 'this.favouriteFoods.indexOf("sushi") != -1'});

นี่เป็นการหลีกเลี่ยงการปรับให้เหมาะสมทั้งหมดโดย MongoDB ดังนั้นอย่าใช้ในรหัสการผลิต


มีความได้เปรียบในการทำเช่นนี้หรือไม่?
Ludwig Magnusson

5
มันไม่มีประสิทธิภาพอย่างเหลือเชื่อเมื่อเทียบกับคำตอบที่ยอมรับ มันหลีกเลี่ยงการเพิ่มประสิทธิภาพทั้งหมดของ Mongo ที่อยู่เบื้องหลังเพื่อค้นหาตรงตามที่ได้รับการยอมรับ
ไม่ทราบ

1
ตรงนี้เป็นคำตอบที่ฉันต้องการในกรณีของฉัน! ขอขอบคุณ "ผู้ใช้" :)
Vasyl Boroviak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.