mongoDB / mongoose: ไม่ซ้ำกันถ้าไม่ใช่ null


103

ผมสงสัยว่าถ้ามีวิธีที่จะบังคับให้เข้าคอลเลกชันที่ไม่ซ้ำกันแต่ถ้าเข้าไม่ได้เป็นโมฆะ e ตัวอย่าง schema:

var UsersSchema = new Schema({
    name  : {type: String, trim: true, index: true, required: true},
    email : {type: String, trim: true, index: true, unique: true}
});

"อีเมล" ในกรณีนี้ไม่จำเป็นต้องใช้ แต่หากบันทึก "อีเมล" ไว้ฉันต้องการให้แน่ใจว่ารายการนี้ไม่ซ้ำกัน (ในระดับฐานข้อมูล)

ดูเหมือนว่ารายการที่ว่างเปล่าจะได้รับค่าเป็น "null" ดังนั้นทุกรายการที่ไม่มีอีเมลล่มด้วยตัวเลือก "ไม่ซ้ำกัน" (หากมีผู้ใช้รายอื่นที่ไม่มีอีเมล)

ตอนนี้ฉันกำลังแก้ปัญหาในระดับแอปพลิเคชัน แต่อยากจะบันทึกแบบสอบถามฐานข้อมูลนั้น

ขอบคุณ

คำตอบ:


169

สำหรับ MongoDB v1.8 + คุณจะได้รับพฤติกรรมที่ต้องการในการรับรองค่าที่ไม่ซ้ำกัน แต่อนุญาตให้มีเอกสารหลายชุดโดยไม่มีฟิลด์โดยตั้งค่าsparseตัวเลือกเป็น true เมื่อกำหนดดัชนี ใน:

email : {type: String, trim: true, index: true, unique: true, sparse: true}

หรือในเปลือก:

db.users.ensureIndex({email: 1}, {unique: true, sparse: true});

โปรดทราบว่าที่ไม่ซ้ำกันดัชนีเบาบางยังคงไม่อนุญาตให้มีเอกสารหลายกับemailข้อมูลที่มีค่าของnullเพียงหลายเอกสารโดยไม่ต้องemailฟิลด์

ดูhttp://docs.mongodb.org/manual/core/index-sparse/


18
สุดยอด! คำตอบที่ดีที่สุดสำหรับมือใหม่อย่างฉันหลังจาก 1.8 แน่นอน! หมายเหตุ: พังพอนจะไม่อัปเดตดัชนีเฉพาะของคุณให้เบาบางหากคุณเพียงแค่เพิ่มแบบเบาบาง: จริงในสคีมาของคุณ คุณต้องปล่อยและเพิ่มดัชนีใหม่ Dunno หากเป็นไปตามที่คาดหวังหรือจุดบกพร่อง
Adam A

8
"หมายเหตุ: หากดัชนีมีอยู่แล้วบนฐานข้อมูลจะไม่ถูกแทนที่" - mongoosejs.com/docs/2.7.x/docs/schematypes.html
ชื้น

ฉันไม่คิดว่าสิ่งนี้จะตอบคำถามได้อย่างถูกต้องเนื่องจากเอกสารสองสามฉบับที่ไม่มีฟิลด์เฉพาะนั้นไม่เหมือนกับเอกสารสองสามฉบับที่มีค่าว่างในฟิลด์นั้น (ซึ่งไม่สามารถจัดทำดัชนีโดยไม่ซ้ำกันได้)
kako-nawao

1
@ kako-nawao นั่นเป็นความจริงมันใช้ได้กับเอกสารที่ไม่มีemailฟิลด์เท่านั้นไม่ใช่ที่ที่มีค่าเป็นnullไฟล์. ดูคำตอบที่อัปเดต
JohnnyHK

2
ใช้ไม่ได้กับฟิลด์ที่ขาดหายไป อาจมีการเปลี่ยนแปลงพฤติกรรมใน mongodb รุ่นหลัง ๆ คำตอบควรได้รับการปรับปรุง
joniba

44

tl; dr

ใช่เป็นไปได้ที่จะมีเอกสารหลายฉบับที่มีการตั้งค่าฟิลด์เป็นnullหรือไม่ได้กำหนดไว้ในขณะที่บังคับใช้ค่า "จริง" ที่ไม่ซ้ำกัน

ข้อกำหนด :

  • MongoDB v3.2 +
  • การรู้ประเภทมูลค่าคอนกรีตของคุณล่วงหน้า (เช่นเสมอstringหรือobjectเมื่อไม่ได้null)

หากคุณไม่สนใจในรายละเอียดโปรดข้ามไปที่implementationส่วนนี้

รุ่นที่ยาวขึ้น

เพื่อเสริมคำตอบของ @ Nolan เริ่มต้นด้วย MongoDB v3.2 คุณสามารถใช้ดัชนีเฉพาะบางส่วนกับนิพจน์ตัวกรอง

นิพจน์ตัวกรองบางส่วนมีข้อ จำกัด สามารถรวมได้เฉพาะสิ่งต่อไปนี้:

  • นิพจน์ความเท่าเทียมกัน (เช่นฟิลด์: ค่าหรือใช้ตัว$eqดำเนินการ)
  • $exists: true นิพจน์
  • $gt, $gte, $lt, $lteสำนวน
  • $type นิพจน์
  • $and ผู้ดำเนินการในระดับบนสุดเท่านั้น

ซึ่งหมายความว่า{"yourField"{$ne: null}}ไม่สามารถใช้นิพจน์เล็กน้อยได้

แต่สมมติว่าข้อมูลของคุณมักจะใช้ชนิดเดียวกันคุณสามารถใช้การแสดงออก$type

{ field: { $type: <BSON type number> | <String alias> } }

MongoDB v3.6 เพิ่มการรองรับสำหรับการระบุประเภทที่เป็นไปได้หลายแบบซึ่งสามารถส่งผ่านเป็นอาร์เรย์:

{ field: { $type: [ <BSON type1> , <BSON type2>, ... ] } }

ซึ่งหมายความว่ามันจะช่วยให้ค่าเป็นใด ๆ nullของจำนวนของหลายประเภทเมื่อไม่

ดังนั้นหากเราต้องการอนุญาตให้emailฟิลด์ในตัวอย่างด้านล่างยอมรับstringหรือกล่าวว่าbinary dataค่า$typeนิพจน์ที่เหมาะสมจะเป็น:

{email: {$type: ["string", "binData"]}}

การนำไปใช้งาน

พังพอน

คุณสามารถระบุได้ในสคีมาพังพอน:

const UsersSchema = new Schema({
  name: {type: String, trim: true, index: true, required: true},
  email: {
    type: String, trim: true, index: {
      unique: true,
      partialFilterExpression: {email: {$type: "string"}}
    }
  }
});

หรือเพิ่มลงในคอลเล็กชันโดยตรง (ซึ่งใช้ไดรเวอร์ node.js ดั้งเดิม):

User.collection.createIndex("email", {
  unique: true,
  partialFilterExpression: {
    "email": {
      $type: "string"
    }
  }
});

ไดรเวอร์ mongodb พื้นเมือง

โดยใช้ collection.createIndex

db.collection('users').createIndex({
    "email": 1
  }, {
    unique: true,
    partialFilterExpression: {
      "email": {
        $type: "string"
      }
    }
  },
  function (err, results) {
    // ...
  }
);

เปลือก mongodb

ใช้db.collection.createIndex:

db.users.createIndex({
  "email": 1
}, {
  unique: true, 
  partialFilterExpression: {
    "email": {$type: "string"}
  }
})

วิธีนี้จะช่วยให้สามารถแทรกหลายระเบียนด้วยnullอีเมลหรือไม่มีฟิลด์อีเมลเลย แต่จะไม่สามารถใช้สตริงอีเมลเดียวกันได้


คำตอบที่ยอดเยี่ยม คุณเป็นผู้กอบกู้
r3wt

คำตอบนี้ก็สำหรับฉันเช่นกัน
Emmanuel NK

1
คำตอบที่ได้รับการยอมรับส่วนใหญ่สำหรับคำถามนี้เกี่ยวข้องกับการตรวจสอบว่าคุณไม่ได้กำหนดค่า null ให้กับคีย์ที่จัดทำดัชนีไว้อย่างชัดเจน พวกเขาแทนที่จะถูกส่งต่อโดยไม่ได้กำหนด ฉันทำเช่นนั้นและยังคงได้รับข้อผิดพลาด (ขณะใช้งานuniqueและsparse) ฉันอัปเดตสคีมาของฉันด้วยคำตอบนี้ลดดัชนีที่มีอยู่และมันก็ใช้งานได้ดี
ฟิลิป

โหวตให้อันนี้เพราะให้ความรู้และคำตอบที่เป็นไปได้ตามสถานการณ์ที่พบบ่อยที่สุดคำตอบ SO นี้ตั้งแต่แรก ขอบคุณสำหรับคำตอบโดยละเอียด! : +1:
anothercoder

6

เพียงแค่อัปเดตอย่างรวดเร็วสำหรับผู้ที่กำลังค้นคว้าหัวข้อนี้

คำตอบที่เลือกจะใช้ได้ แต่คุณอาจต้องการพิจารณาใช้ดัชนีบางส่วนแทน

การเปลี่ยนแปลงในเวอร์ชัน 3.2: เริ่มต้นใน MongoDB 3.2 MongoDB มีตัวเลือกในการสร้างดัชนีบางส่วน ดัชนีบางส่วนนำเสนอส่วนเหนือของฟังก์ชันการทำงานของดัชนีแบบกระจัดกระจาย หากคุณใช้ MongoDB 3.2 หรือใหม่กว่าควรเลือกใช้ดัชนีบางส่วนมากกว่าดัชนีแบบกระจัดกระจาย

doco เพิ่มเติมเกี่ยวกับดัชนีบางส่วน: https://docs.mongodb.com/manual/core/index-partial/


2

จริงๆแล้วเฉพาะเอกสารแรกที่ไม่มีฟิลด์ "email" as ที่จะได้รับการบันทึกสำเร็จ การบันทึกในภายหลังเมื่อไม่มี "อีเมล" จะล้มเหลวในขณะที่ให้ข้อผิดพลาด (ดูข้อมูลโค้ดด้านล่าง) สำหรับรูปลักษณ์เหตุผลที่เอกสารที่เป็นทางการ MongoDB ที่เกี่ยวกับดัชนีที่ไม่ซ้ำกันและ Keys หายไปที่นี่ที่http://www.mongodb.org/display/DOCS/Indexes#Indexes-UniqueIndexes

  // NOTE: Code to executed in mongo console.

  db.things.ensureIndex({firstname: 1}, {unique: true});
  db.things.save({lastname: "Smith"});

  // Next operation will fail because of the unique index on firstname.
  db.things.save({lastname: "Jones"});

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

คุณอาจต้องการอ่านhttp://www.mongodb.org/display/DOCS/Querying+and+nullsนี้

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