Dizi boyutunun 1'den büyük olduğu belgeler için sorgu


664

Aşağıdaki biçimde belgeler içeren bir MongoDB koleksiyonum var:

{
  "_id" : ObjectId("4e8ae86d08101908e1000001"),
  "name" : ["Name"],
  "zipcode" : ["2223"]
}
{
  "_id" : ObjectId("4e8ae86d08101908e1000002"),
  "name" : ["Another ", "Name"],
  "zipcode" : ["2224"]
}

Şu anda belirli bir dizi boyutu ile eşleşen belgeler alabilirsiniz:

db.accommodations.find({ name : { $size : 2 }})

Bu, namedizide 2 öğeye sahip belgeleri doğru şekilde döndürür . Ancak, alan 2'den büyük bir dizi boyutu $gtolduğu tüm belgeleri döndürmek için bir komut yapamam name:

db.accommodations.find({ name : { $size: { $gt : 1 } }})

Bir namediziden büyük boyutlu tüm belgeleri (tercihen geçerli veri yapısını değiştirmek zorunda kalmadan) nasıl seçebilirim ?


3
MongoDB'nin yeni sürümleri $ size operatörüne sahiptir; @ tobia adlı kullanıcının yanıtını kontrol etmelisin
AlbertEngelB

4
Gerçek çözüm: FooArray: {$ gt: {$ size: 'length'}} -> uzunluk herhangi bir sayı olabilir
Sergi Nadal

Yanıtlar:


489

Güncelleme:

Mongodb versiyonlar için 2.2+ yapmak için daha verimli bir şekilde bu tarif @JohnnyHK başka cevap .


1. $ kullanarak

db.accommodations.find( { $where: "this.name.length > 1" } );

Fakat...

Javascript, bu sayfada listelenen yerel operatörlerden daha yavaş çalışır, ancak çok esnektir. Daha fazla bilgi için sunucu tarafı işleme sayfasına bakın.

2. ekstra alan oluşturun NamesArrayLength, isimleri dizi uzunluğu ile güncelleyin ve sonra sorgularda kullanın:

db.accommodations.find({"NamesArrayLength": {$gt: 1} });

Daha iyi bir çözüm olacak ve çok daha hızlı çalışacaktır (üzerinde dizin oluşturabilirsiniz).


4
Harika, bu mükemmel oldu teşekkür ederim. Aslında adı olmayan bazı belgelerim olmasına rağmen sorguyu şu şekilde değiştirmek zorunda kaldım: db.aclontions.find ({$ where: "if (this.name && this.name.length> 1) ;} "});
emson

bekliyoruz, evet herhangi bir javascript kullanabilirsiniz $where, çok esnektir.
Andrew Orsich

8
@emson {"name": {$ exist: 1}, $ where: "this.name.lenght> 1"} ... gibi bir şey yapmanın daha hızlı olacağını düşünürüm. İşlerin ve $ varlığının daha yüksek önceliğe sahip olacağını varsayıyorum.
nairbv

1
Javascript'i sorguya ekleyebileceğine dair hiçbir fikrim yoktu, json hantal olabilir. Bu sorguların birçoğu yalnızca elle girilir, bu nedenle optimizasyon gerekmez. Bu numarayı sık sık +1 kullanacağım
Mart'ta pferrel

3
Array öğesinden öğeleri ekledikten / kaldırdıktan sonra, "NamesArrayLength" sayısını güncellememiz gerekiyor. Bu tek bir sorguda yapılabilir mi? Ya da biri diziyi güncellemek için diğeri sayıyı güncellemek için 2 sorgu gerektirir?
WarLord

1328

Sorgu nesnesi tuşlarında sayısal dizi dizinlerini kullanabilmeniz için MongoDB 2.2 ve sonraki sürümlerde bunu yapmanın daha etkili bir yolu vardır.

// Find all docs that have at least two name array elements.
db.accommodations.find({'name.1': {$exists: true}})

Bu sorguyu, kısmi filtre ifadesi kullanan bir dizinle destekleyebilirsiniz (3.2+ gerektirir):

// index for at least two name array elements
db.accommodations.createIndex(
    {'name.1': 1},
    {partialFilterExpression: {'name.1': {$exists: true}}}
);

16
Birisi lütfen bunu nasıl endeksleyeceğini açıklayabilir mi?
Ben

26
Bunun ne kadar etkili olduğu ve aynı zamanda bu çözümü bulmayı düşündüğünüzden gerçekten etkilendim. Bu 2.6 üzerinde de çalışır.
earthmeLon

2
Ayrıca 3.0 üzerinde çalışır. Bunu bulduğunuz için çok teşekkür ederim.
pikanezi

1
Gerçekten fark yok, @Dims: {'Name Field.1': {$exists: true}}.
JohnnyHK

9
@JoseRicardoBustosM. Bu name, en az 1 öğe içeren dokümanları bulur , ancak OP 1'den büyük arıyordu .
JohnnyHK

128

Yorumlanan bir $wherecümle kullanmadığından, sorunuza cevap veren en hızlı sorgu olduğuna inanıyorum :

{$nor: [
    {name: {$exists: false}},
    {name: {$size: 0}},
    {name: {$size: 1}}
]}

"Adı olmayan (boş olmayan veya boş dizi) veya tek bir adı olan belgeler dışındaki tüm belgeler".

Ölçek:

> db.test.save({})
> db.test.save({name: []})
> db.test.save({name: ['George']})
> db.test.save({name: ['George', 'Raymond']})
> db.test.save({name: ['George', 'Raymond', 'Richard']})
> db.test.save({name: ['George', 'Raymond', 'Richard', 'Martin']})
> db.test.find({$nor: [{name: {$exists: false}}, {name: {$size: 0}}, {name: {$size: 1}}]})
{ "_id" : ObjectId("511907e3fb13145a3d2e225b"), "name" : [ "George", "Raymond" ] }
{ "_id" : ObjectId("511907e3fb13145a3d2e225c"), "name" : [ "George", "Raymond", "Richard" ] }
{ "_id" : ObjectId("511907e3fb13145a3d2e225d"), "name" : [ "George", "Raymond", "Richard", "Martin" ] }
>

9
@viren bilmiyorum. Bu kesinlikle Javascript çözümlerinden daha iyiydi, ancak daha yeni MongoDB için muhtemelen kullanmalısınız{'name.1': {$exists: true}}
Tobia

@ Tobia benim ilk kullanım sadece $ var ama aslında çok yavaş tüm tablo tarama kullanın. db.test.find ({"ad": "abc", "d.5": {$ there: true}, "d.6": {$ exist: true}}) "nReturned": 46525, "executionTimeMillis ": 167289," totalKeysExamined ": 10990840," totalDocsExamined ": 10990840," inputStage ": {" stage ":" IXSCAN "," keyPattern ": {" ad ": 1," d ": 1}," indexName " : "name_1_d_1", "direction": "ileri", "indexBounds": {"name": ["[\" abc \ ", \" abc \ "]"], "d": ["[MinKey, MaxKey ] "]}} Tüm tabloyu taradığını görürseniz.

Diğer alternatifleri önermek için yanıtı güncellemek güzel olurdu ( 'name.1': {$exists: true}}ve bunun yanı sıra "1" için kodlanmış olduğu ve rastgele veya parametrik minimum dizi uzunluğuna ölçeklenmediği için)
Dan Dascalescu

1
Bu hızlı olabilir, ancak N'nin küçük olmadığı> N listelerini arıyorsanız ayrılır.
Brandon Hill

62

Toplamı da kullanabilirsiniz:

db.accommodations.aggregate(
[
     {$project: {_id:1, name:1, zipcode:1, 
                 size_of_name: {$size: "$name"}
                }
     },
     {$match: {"size_of_name": {$gt: 1}}}
])

// belgeyi aktarmaya "size_of_name" ekleyip adın boyutunu filtrelemek için kullanıyorsunuz


Bu çözüm, herhangi bir dizi boyutu için kullanılabildiği için @ JohnnyHK's ile birlikte en genel olanıdır.
arun

projeksiyon içinde "size_of_name" kullanmak istiyorsanız o zaman nasıl yapabilirim ?? Aslında değeri $ dilim eşit olduğu projeksiyon içinde $ dilim kullanmak istiyorum: [0, "size_of_name" - atla] ??
Sudhanshu Gaur

44

Böyle bir şey yapmaya çalışın:

db.getCollection('collectionName').find({'ArrayName.1': {$exists: true}})

1 sayıdır, 50'den büyük bir kayıt almak istiyorsanız ArrayName yapın. 50 Teşekkürler.


2
Aynı cevap üç yıl önce verildi .
Dan Dascalescu

Gelecekten geliyorum ve bunu takdir ederdim: Bu çözüm, söz konusu pozisyonda bir eleman olup olmadığını kontrol ederek çalışır. Bu nedenle, koleksiyon bu sayıdan büyük | eşit olmalıdır.
MarAvFe

sorgunun içine "ArrayName. <some_num>" gibi bir dinamik sayı koyabilir miyiz?
Sahil Mahajan

Evet herhangi bir sayı kullanabilirsiniz. N'den daha büyük bir kayıt almak istiyorsanız n'yi iletin.
Aman Goel


26

$ Expr kullanabilirsinizNormal sorguda toplama işlevlerini (3.6 mongo sürüm operatörü) kullanabilirsiniz.

Karşılaştırma query operatorsvs aggregation comparison operators.

db.accommodations.find({$expr:{$gt:[{$size:"$name"}, 1]}})

$nameAlt belge olan bir dizi yerine, örneğin "kişi" kaydında nasıl geçersiniz passport.stamps? Çeşitli alıntı kombinasyonlarını denedim ama anladım "The argument to $size must be an array, but was of type: string/missing".
Dan Dascalescu

3
@DanDascalescu Tüm belgelerde pul bulunmuyor gibi görünüyor. Pullar olmadığında boş dizinin çıktısını almak için ifNull parametresini kullanabilirsiniz . Gibi bir şeydb.col.find({$expr:{$gt:[{$size:{$ifNull:["$passport.stamps", []]}}, 1]}})
Sagar Veeram

22
db.accommodations.find({"name":{"$exists":true, "$ne":[], "$not":{"$size":1}}})

1
Bu, diğer minimum boyutlara göre iyi ölçeklenmez (örneğin, 10).
Dan Dascalescu

ilk cevapla aynı
arianpress


13

Belirli bir uzunluktan daha büyük bir dizi alanına sahip öğeleri bulmak için bu çözümü buldum

db.allusers.aggregate([
  {$match:{username:{$exists:true}}},
  {$project: { count: { $size:"$locations.lat" }}},
  {$match:{count:{$gt:20}}}
])

İlk $ match toplamı, tüm belgeler için doğru olan bir bağımsız değişken kullanır. Boş olsaydım,

"errmsg" : "exception: The argument to $size must be an Array, but was of type: EOO"

Bu temelde aynı cevabı bu bir 2 yıl önce sağlanan.
Dan Dascalescu

1

Eski sorusunu biliyorum, ama ben bulmak $ gte ve $ boyutu ile bu çalışıyorum. () Bulmanın daha hızlı olduğunu düşünüyorum.

db.getCollection('collectionName').find({ name : { $gte : {  $size : 1 } }})

-5

Yukarıdaki tüm cevaplar işe yarıyor olsa da, Başlangıçta yapmaya çalıştığınız doğru yol oldu, ancak sözdizimi geriye sadece ("$ size" ve "$ gt").

Doğru:

db.collection.find({items: {$gt: {$size: 1}}})

Yanlış:

db.collection.find({items: {$size: {$gt: 1}}})

1
Neden bu kadar çok downvotes görmüyorum - bu benim için mükemmel çalışıyor!
Jake Stokes

Ben inmedim, ama çalışmıyor (v4.2).
Evgeni Nabokov

Mükemmel çalışıyor, v 4.2.5
jperl
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.