mongoDB / firavun faresi: boş değilse benzersiz


103

Benzersiz bir koleksiyon girişini zorlamanın bir yolu olup olmadığını merak ediyordum, ancak yalnızca giriş boş değilse . e Örnek şema:

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

Bu durumda 'e-posta' gerekli değildir, ancak 'e-posta' kaydedilmişse, bu girişin benzersiz olduğundan emin olmak istiyorum (veritabanı düzeyinde).

Boş girişler 'null' değerini alıyor gibi görünüyor, bu nedenle e-posta içermeyen her giriş 'benzersiz' seçeneğiyle çöküyor (e-postası olmayan farklı bir kullanıcı varsa).

Şu anda bunu bir uygulama düzeyinde çözüyorum, ancak bu db sorgusunu kaydetmek isterim.

Teşekkürler

Yanıtlar:


169

MongoDB v1.8 + sürümünden itibaren sparse, endeksi tanımlarken seçeneği true olarak ayarlayarak, benzersiz değerler sağlamaya ancak alan olmadan birden çok dokümana izin verme gibi istenen davranışı elde edebilirsiniz . De olduğu gibi:

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

Veya kabukta:

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

Benzersiz, seyrek endeks hala çoklu dokümanlar izin vermez Not emaila ile sahaya değeri arasında nullsadece çoklu dokümanlar, olmadan bir emailalanda.

Bkz. Http://docs.mongodb.org/manual/core/index-sparse/


18
Harika! 1.8'den sonra benim gibi yeniler için kesinlikle en iyi cevap! NOT: Mongoose, şemanıza seyrek: true eklerseniz benzersiz dizininizi seyrek olacak şekilde güncellemeyecektir. Dizini bırakıp yeniden eklemelisiniz. Dunno eğer beklenen veya bir hata ise.
Adam A

8
"Not: dizin veritabanında zaten varsa, değiştirilmeyecektir." - mongoosejs.com/docs/2.7.x/docs/schematypes.html
damphat

Bunun soruyu doğru yanıtladığını sanmıyorum, çünkü belirli bir alanı olmayan birkaç belge, bu alanda boş bir değere sahip (benzersiz bir şekilde dizine eklenemeyen) birkaç belge ile aynı değildir.
kako-nawao

1
@ kako-nawao Bu doğru, yalnızca emailalanı olmayan dokümanlar için işe yarıyor , gerçekte bir değeri olduğu yerde değil null. Güncellenen yanıta bakın.
JohnnyHK

2
Eksik alanlarla çalışmaz. Belki de mongodb'un sonraki sürümlerinde davranış değişti. Cevap güncellenmelidir.
joniba

44

tl; dr

Evet, nullbenzersiz "gerçek" değerleri zorunlu kılarken, alanı tanımlanmış veya tanımlanmamış birden çok belgeye sahip olmak mümkündür .

gereksinimleri :

  • MongoDB v3.2 +.
  • Önceden somut değer türlerinizi bilmek (örneğin, her zaman a stringveya objectne zaman null).

Ayrıntılarla ilgilenmiyorsanız, implementationbölüme geçmekten çekinmeyin .

daha uzun versiyon

@ Nolan'ın cevabını tamamlamak için, MongoDB v3.2'den başlayarak, bir filtre ifadesiyle kısmi benzersiz bir dizin kullanabilirsiniz.

Kısmi filtre ifadesinin sınırlamaları vardır. Yalnızca aşağıdakileri içerebilir:

  • eşitlik ifadeleri (yani alan: değer veya $eqoperatörü kullanma ),
  • $exists: true ifade
  • $gt, $gte, $lt, $lteİfadeler,
  • $type ifade,
  • $and yalnızca üst düzeydeki operatör

Bu, önemsiz ifadenin {"yourField"{$ne: null}} kullanılamayacağı .

Bununla birlikte, alanınızın her zaman aynı türü kullandığını varsayarsak, bir $typeifade kullanabilirsiniz .

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

MongoDB v3.6, dizi olarak geçirilebilen birden çok olası türü belirtmek için destek ekledi:

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

bu, değerin, olmadığında birden çok türden herhangi birinde olmasına izin verdiği anlamına gelir null.

Bu nedenle, emailaşağıdaki örnekteki alanın değerlerden birini stringveya örneğin binary datadeğerleri kabul etmesine izin vermek istiyorsak , uygun bir $typeifade şöyle olacaktır:

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

uygulama

firavun faresi

Bunu bir firavun faresi şemasında belirtebilirsiniz:

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

veya doğrudan koleksiyona ekleyin (yerel node.js sürücüsünü kullanır):

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

yerli mongodb sürücüsü

kullanma collection.createIndex

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

mongodb kabuğu

kullanma db.collection.createIndex:

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

Bu, bir nulle-posta ile veya hiç bir e-posta alanı olmadan, ancak aynı e-posta dizesiyle birden çok kayıt eklemeye izin verecektir .


Harika cevap. Sen bir kurtarıcısın.
r3wt

Bu cevap benim için de yaptı.
Emmanuel NK

1
Bu soru için kabul edilen yanıtların çoğu, indekslenmiş anahtarlarınıza açıkça boş değerler ayarlamadığınızdan emin olmayı içerir. Bunun yerine tanımsız geçirilecekler. Bunu yapıyor ve (kullanırken yine de hata başlamıştı uniqueve sparse). Şemamı bu cevapla güncelledim, mevcut indeksimi çıkardım ve bir cazibe gibi çalıştı.
Phil

Bu soruya oy verildi, çünkü kişinin ilk etapta bu SO cevabına ulaşacağı en yaygın senaryolara dayanarak bilgiyi ve olası cevapları sağlıyor. Detaylı cevap için teşekkürler! : +1:
kodlayıcı

6

Bu konuyu araştıranlara hızlı bir güncelleme.

Seçilen cevap işe yarayacaktır, ancak bunun yerine kısmi dizinler kullanmayı düşünebilirsiniz.

Sürüm 3.2'de değiştirildi: MongoDB 3.2'den başlayarak, MongoDB, kısmi dizinler oluşturma seçeneği sunuyor. Kısmi dizinler, seyrek dizinlerin işlevselliğinin bir üst kümesini sunar. MongoDB 3.2 veya sonraki bir sürümünü kullanıyorsanız, seyrek dizinler yerine kısmi dizinler tercih edilmelidir.

Kısmi dizinler hakkında daha fazla doco: https://docs.mongodb.com/manual/core/index-partial/


2

Aslında, yalnızca "e-posta" alanının bulunmadığı ilk belge başarıyla kaydedilecektir. "E-postanın" bulunmadığı sonraki kayıtlar hata verirken başarısız olur (aşağıdaki kod parçacığına bakın). Bu nedenle, http://www.mongodb.org/display/DOCS/Indexes#Indexes-UniqueIndexes adresindeki Unique Indexes ve Missing Keys ile ilgili MongoDB resmi belgelerine bakın .

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

Tanıma göre benzersiz dizin, yalnızca bir değerin yalnızca bir kez depolanmasına izin verebilir. Null değerinin böyle bir değer olduğunu düşünürseniz, yalnızca bir kez eklenebilir! Yaklaşımınızı uygulama düzeyinde sağlayarak ve doğrulayarak, yaklaşımınızda haklısınız. Bu nasıl yapılabilir.

Bu http://www.mongodb.org/display/DOCS/Querying+and+nulls adresini de okumak isteyebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.