ใน mongoDb คุณจะลบองค์ประกอบอาร์เรย์ด้วยดัชนีได้อย่างไร?


97

ในตัวอย่างต่อไปนี้สมมติว่าเอกสารอยู่ในคอลเลกชันdb.people

จะลบองค์ประกอบที่ 3 ของอาร์เรย์ความสนใจด้วยดัชนีได้อย่างไร

{
  "_id" : ObjectId("4d1cb5de451600000000497a"),           
  "name" : "dannie",  
  "interests" : [  
    "guitar",  
    "programming",           
    "gadgets",  
    "reading"  
  ]   
}

นี่คือวิธีแก้ปัญหาปัจจุบันของฉัน:

var interests = db.people.findOne({"name":"dannie"}).interests;  
interests.splice(2,1)  
db.people.update({"name":"dannie"}, {"$set" : {"interests" : interests}});

มีวิธีที่ตรงกว่านี้หรือไม่?


คำตอบ:


139

ไม่มีวิธีดึง / ลบโดยใช้ดัชนีอาร์เรย์ อันที่จริงนี่เป็นปัญหาที่เปิดอยู่http://jira.mongodb.org/browse/SERVER-1014คุณสามารถลงคะแนนได้

วิธีแก้ปัญหาคือใช้ $ unset แล้ว $ pull:

db.lists.update({}, {$unset : {"interests.3" : 1 }}) 
db.lists.update({}, {$pull : {"interests" : null}})

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

  • อ่านเอกสารจากฐานข้อมูล
  • อัปเดตเอกสารและลบรายการในอาร์เรย์
  • แทนที่เอกสารในฐานข้อมูล เพื่อให้แน่ใจว่าเอกสารไม่มีการเปลี่ยนแปลงตั้งแต่ที่เราอ่านเราสามารถใช้การอัปเดตได้หากมีการอธิบายรูปแบบปัจจุบันในเอกสาร Mongo

33
ผ่านมาปีครึ่งแล้ว? นี่ยังคงเป็นสถานะของ mongodb หรือไม่?
Abe

คำตอบต่อไปนั้นตรงกว่าและใช้ได้ผลสำหรับฉัน แม้ว่าจะไม่ได้ลบด้วยดัชนี แต่เป็นการลบตามค่า
vish

1
@Javier - วิธีนี้จะไม่ทำให้คุณเปิดรับปัญหาหรือไม่หากฐานข้อมูลเปลี่ยนไประหว่างเวลาที่คุณนับตำแหน่งในดัชนีและเวลาที่คุณไม่ได้ตั้งค่า
UpTheCreek

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

3
8 ปีและตั๋วนี้ยังไม่ได้ใช้งาน ...
Olly John

19

คุณสามารถใช้$pullตัวแก้ไขupdateการดำเนินการเพื่อลบองค์ประกอบเฉพาะในอาร์เรย์ ในกรณีที่คุณระบุแบบสอบถามจะมีลักษณะดังนี้:

db.people.update({"name":"dannie"}, {'$pull': {"interests": "guitar"}})

นอกจากนี้คุณอาจพิจารณาใช้$pullAllเพื่อลบเหตุการณ์ทั้งหมด เพิ่มเติมเกี่ยวกับเรื่องนี้ในหน้าเอกสารอย่างเป็นทางการ - http://www.mongodb.org/display/DOCS/Updating#Updating-%24pull

สิ่งนี้ไม่ได้ใช้ดัชนีเป็นเกณฑ์ในการลบองค์ประกอบ แต่อาจช่วยได้ในกรณีที่คล้ายกับของคุณ IMO การใช้ดัชนีเพื่อกำหนดแอดเดรสองค์ประกอบภายในอาร์เรย์นั้นไม่น่าเชื่อถือมากนักเนื่องจาก mongodb ไม่สอดคล้องกับลำดับองค์ประกอบอย่างที่ฉันรู้


6
องค์ประกอบของเขาคืออาร์เรย์ในคำถาม Mongo สอดคล้องตามลำดับในกรณีนี้ ในความเป็นจริงเอกสารทั้งหมดได้รับคำสั่งจากคอลเลกชันจริง แต่ไดรเวอร์จำนวนมากไม่ได้จัดการในลักษณะนี้ ทำให้เรื่องนี้ซับซ้อนยิ่งขึ้นหากต้องย้ายเอกสารหลังจากการดำเนินการอัปเดต (เนื่องจากการเติบโตเกินกว่าปัจจัยการขยาย) ลำดับของคีย์จะกลายเป็นตัวอักษร (ภายใต้ฝาครอบฉันคิดว่าพวกเขาเรียงตามค่าไบนารีของพวกเขาความสงสัยนี้คือ จากความรู้ของฉันที่ว่าอาร์เรย์เป็นเอกสารมาตรฐานที่มีคีย์ที่เป็นจำนวนเต็มติดต่อกันแทนที่จะเป็นสตริง)
มี.ค. 75

เพื่อหลีกเลี่ยงปัญหา unset + pull แต่จำเป็นต้องสั่งพจนานุกรมของคุณอย่างเคร่งครัด พจนานุกรมในภาษาส่วนใหญ่ไม่เรียงลำดับดังนั้นจึงอาจเป็นเรื่องยากที่จะทำให้สิ่งนี้เกิดขึ้น
Glenn Maynard

@ marr75: คุณมีข้อมูลอ้างอิงหรือไม่? เอกสาร Mongo ควรรักษาความเป็นระเบียบไว้เสมอและหากเคยจัดเรียงเอกสารย่อยใหม่หลาย ๆ อย่างจะพัง
Glenn Maynard

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

เท่าที่ความสอดคล้องขององค์ประกอบกรณีการใช้งานที่ฉันต้องการคือ ไคลเอนต์ร้องขอ json จาก mongo จากนั้นหากไคลเอนต์เลือกที่จะพูดว่า 'ลบ' รายการจากอาร์เรย์ json มันจะส่งดัชนีอาร์เรย์ไปยังเซิร์ฟเวอร์เพื่อลบ ถ้าตำแหน่งรายการอาร์เรย์ย้ายมันจะแปลก แต่จะอธิบายว่าทำไมพวกเขาไม่สามารถใช้คุณลักษณะนี้ได้
Northstrider

4

แทนที่จะใช้ unset (ตามคำตอบที่ยอมรับ) ฉันแก้ปัญหานี้โดยตั้งค่าฟิลด์เป็นค่าเฉพาะ (เช่นไม่ใช่ NULL) แล้วดึงค่านั้นทันที ปลอดภัยกว่าเล็กน้อยจากมุมมองที่ไม่ตรงกัน นี่คือรหัส:

    var update = {};
    var key = "ToBePulled_"+ new Date().toString();
    update['feedback.'+index] = key;
    Venues.update(venueId, {$set: update});
    return Venues.update(venueId, {$pull: {feedback: key}});

หวังว่า mongo จะแก้ไขปัญหานี้ได้โดยการขยายตัวปรับตำแหน่ง $ เพื่อรองรับ $ pull และ $ push


2

ฉันขอแนะนำให้ใช้ฟิลด์ GUID (ฉันมักจะใช้ ObjectID) หรือฟิลด์ที่เพิ่มขึ้นอัตโนมัติสำหรับเอกสารย่อยแต่ละรายการในอาร์เรย์

ด้วย GUID นี้ทำให้ง่ายต่อการออก $ pull และต้องแน่ใจว่าจะดึงอันที่ถูกต้อง เช่นเดียวกันกับการดำเนินการอาร์เรย์อื่น ๆ


2

เริ่มต้นในMongo 4.4การ$functionดำเนินการรวมตัวช่วยให้การใช้ฟังก์ชั่นจาวาสคริปต์ที่กำหนดเองที่จะใช้พฤติกรรมที่ไม่ได้รับการสนับสนุนโดย MongoDB Query Language

ตัวอย่างเช่นในการอัปเดตอาร์เรย์โดยการลบองค์ประกอบที่ดัชนีที่กำหนด:

// { "name": "dannie", "interests": ["guitar", "programming", "gadgets", "reading"] }
db.collection.update(
  { "name": "dannie" },
  [{ $set:
    { "interests":
      { $function: {
          body: function(interests) { interests.splice(2, 1); return interests; },
          args: ["$interests"],
          lang: "js"
      }}
    }
  }]
)
// { "name": "dannie", "interests": ["guitar", "programming", "reading"] }

$function ใช้ 3 พารามิเตอร์:

  • bodyซึ่งเป็นฟังก์ชันที่จะใช้ซึ่งมีพารามิเตอร์คืออาร์เรย์ที่จะแก้ไข ฟังก์ชั่นที่นี่ประกอบด้วยการใช้ splice เพื่อลบ 1 องค์ประกอบที่ดัชนี 2
  • argsซึ่งมีฟิลด์จากเรกคอร์ดที่bodyฟังก์ชันใช้เป็นพารามิเตอร์ ในกรณีของเรา"$interests"ในกรณีของเรา
  • langซึ่งเป็นภาษาที่ใช้bodyเขียนฟังก์ชัน เพียง แต่jsมีอยู่ในปัจจุบัน

2

ใน Mongodb 4.2 คุณสามารถทำได้:

db.example.update({}, [
     {$set: {field: {
           $concatArrays: [ 
                  {$slice: ["$field", P]}, 
                  {$slice: ["$field", {$add: [1, P]}, {$size: "$field"}]}
           ]
     }}}
]);

P คือดัชนีขององค์ประกอบที่คุณต้องการลบออกจากอาร์เรย์

หากคุณต้องการลบออกจาก P จนจบ:

db.example.update({}, [
     {$set: {field: {$slice: ["$field", P]}}
]);

0

สำหรับผู้ที่กำลังค้นหาคำตอบโดยใช้พังพอนกับ nodejs นี่คือวิธีที่ฉันทำ

exports.deletePregunta = function (req, res) {
let codTest = req.params.tCodigo;
let indexPregunta = req.body.pregunta; // the index that come from frontend
let inPregunta = `tPreguntas.0.pregunta.${indexPregunta}`; // my field in my db
let inOpciones = `tPreguntas.0.opciones.${indexPregunta}`; // my other field in my db
let inTipo = `tPreguntas.0.tipo.${indexPregunta}`; // my  other field in my db

Test.findOneAndUpdate({ tCodigo: codTest },
    {
        '$unset': {
            [inPregunta]: 1, // put the field with [] 
            [inOpciones]: 1,
            [inTipo]: 1
        }
    }).then(()=>{ 
    Test.findOneAndUpdate({ tCodigo: codTest }, {
        '$pull': {
            'tPreguntas.0.pregunta': null,
            'tPreguntas.0.opciones': null,
            'tPreguntas.0.tipo': null
        }
    }).then(testModificado => {
        if (!testModificado) {
            res.status(404).send({ accion: 'deletePregunta', message: 'No se ha podido borrar esa pregunta ' });
        } else {
            res.status(200).send({ accion: 'deletePregunta', message: 'Pregunta borrada correctamente' });
        }
    })}).catch(err => { res.status(500).send({ accion: 'deletePregunta', message: 'error en la base de datos ' + err }); });
 }

ฉันสามารถเขียนคำตอบนี้ใหม่ได้หากไม่เข้าใจเป็นอย่างดี แต่ฉันคิดว่าไม่เป็นไร

หวังว่านี่จะช่วยคุณได้ฉันเสียเวลากับปัญหานี้ไปมาก


-3

แทนที่จะใช้ $ pull เราสามารถใช้ $ pop เพื่อลบองค์ประกอบในอาร์เรย์ด้วยดัชนี แต่คุณควรลบ 1 ออกจากตำแหน่งดัชนีสำหรับการลบตามดัชนี

เช่นถ้าคุณต้องการลบองค์ประกอบในดัชนี 0 คุณควรใช้ -1 สำหรับดัชนี 1 คุณควรใช้ 0 และอื่น ๆ ...

แบบสอบถามเพื่อลบองค์ประกอบที่ 3 (แกดเจ็ต):

db.people.update({"name":"dannie"}, {'$pop': {"interests": 1}})

สำหรับการอ้างอิง: https://docs.mongodb.com/manual/reference/operator/update/pop/


4
ตามเอกสารที่เชื่อมโยง$popสามารถลบองค์ประกอบแรกหรือองค์ประกอบสุดท้ายออกจากอาร์เรย์เท่านั้น แบบสอบถามในคำตอบนี้ไม่ได้ลบองค์ประกอบที่สาม
Travis G.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.