배열 크기가 1보다 큰 문서를 쿼리합니다.


664

다음 형식의 문서가있는 MongoDB 컬렉션이 있습니다.

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

현재 특정 배열 크기와 일치하는 문서를 얻을 수 있습니다.

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

그러면 name배열에 2 개의 요소가있는 문서가 올바르게 반환 됩니다. 그러나 필드의 배열 크기가 2보다 큰 $gt모든 문서를 반환 하는 명령을 수행 할 수 없습니다 name.

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

name1보다 큰 크기 의 배열을 가진 모든 문서를 선택하려면 어떻게해야합니까 (현재 데이터 구조를 수정하지 않아도 됨)?


3
최신 버전의 MongoDB에는 $ size 연산자가 있습니다. @tobia 님의 답변을 확인해야합니다
AlbertEngelB

4
실제 솔루션 : FooArray : {$ gt : {$ size : 'length'}}-> 길이는 숫자가 될 수 있습니다
Sergi Nadal

답변:


489

최신 정보:

mongodb 버전 2.2 이상 에서는 @JohnnyHK 가 다른 대답으로 설명하는보다 효율적인 방법 입니다.


1. $ where 사용

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

그러나...

Javascript는이 페이지에 나열된 기본 연산자보다 느리게 실행되지만 매우 유연합니다. 자세한 내용은 서버 측 처리 페이지를 참조하십시오.

2.Create 추가 필드 NamesArrayLength, 이름 배열 길이를 업데이트 한 다음 쿼리에서 사용

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

더 나은 솔루션이 될 것이고 훨씬 빠르게 작동합니다 (색인을 만들 수 있습니다).


4
정말 고마워요 실제로 이름이없는 일부 문서가 있지만 쿼리를 다음과 같이 수정해야합니다. db.accommodations.find ({$ where : "if (this.name && this.name.length> 1) {return this ;} "});
emson

당신은 환영합니다. 네, 어떤 자바 스크립트라도 사용할 수 있습니다 $where. 매우 유연합니다.
Andrew Orsich

8
@emson 나는 { "name": {$ exists : 1}, $ where : "this.name.lenght> 1"}과 같은 것을하는 것이 더 빠를 것이라고 생각합니다. 느린 자바 스크립트 쿼리에서 부분을 최소화합니다. 나는 그것이 작동하고 $ exists가 더 높은 우선 순위를 가질 것이라고 가정합니다.
nairbv

1
쿼리에 자바 스크립트를 포함시킬 수 있다는 것을 몰랐습니다 .json은 성 가실 수 있습니다. 이러한 쿼리 중 다수는 한 번만 직접 입력하므로 최적화가 필요하지 않습니다. 이 트릭을 자주 사용합니다 +1
pferrel

3
배열에서 요소를 추가 / 제거한 후 "NamesArrayLength"의 수를 업데이트해야합니다. 단일 쿼리로 수행 할 수 있습니까? 아니면 배열을 업데이트하기위한 쿼리와 카운트를 업데이트하기위한 쿼리가 2 개 필요합니까?
WarLord

1328

쿼리 객체 키에서 숫자 형 배열 인덱스를 사용할 수 있으므로 MongoDB 2.2 이상에서 더 효율적인 방법이 있습니다.

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

부분 필터 표현식을 사용하는 인덱스를 사용하여이 쿼리를 지원할 수 있습니다 (3.2 이상 필요).

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

16
누군가 색인을 생성하는 방법을 설명해 주시겠습니까?
Ben

26
나는 이것이 얼마나 효과적이며 또한이 솔루션을 찾으려고 생각한 '출시 즉시'에 깊은 인상을 받았습니다. 이것은 2.6에서도 작동합니다.
earthmeLon

2
3.0에서도 작동합니다. 이것을 찾아 주셔서 감사합니다.
pikanezi

1
@Dims 정말 차이가 없습니다 : {'Name Field.1': {$exists: true}}.
JohnnyHK

9
짤방 백업 봇 적어도 하나의 요소를 name포함 하는 문서를 찾을 수는 있지만 OP는 1 보다 큽니다.
JohnnyHK

128

나는 이것이 해석 된 $where절을 사용하지 않기 때문에 질문에 대답하는 가장 빠른 쿼리라고 생각합니다 .

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

"이름이 없거나 (존재하지 않거나 비어있는 배열) 이름이 하나만있는 문서를 제외한 모든 문서"를 의미합니다.

테스트:

> 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 모르겠습니다. 이것은 확실히 자바 스크립트 솔루션보다 낫지 만 새로운 MongoDB의 경우 다음을 사용해야합니다.{'name.1': {$exists: true}}
Tobia

@ Tobia 내 첫 번째 사용은 $ exists이지만 실제로 전체 테이블 스캔을 매우 느리게 사용합니다. db.test.find ({ "name": "abc", "d.5": {$ exists : true}, "d.6": {$ exists : true}}) "nReturned": 46525, "executionTimeMillis ": 167289,"totalKeysExamined ": 10990840,"totalDocsExamined ": 10990840,"inputStage ": {"stage ":"IXSCAN ","keyPattern ": {"name ": 1,"d ": 1},"indexName " : "name_1_d_1", "direction": "forward", "indexBounds": { "name": [ "[\"abc \ ", \"abc \ "]"], "d": [ "[MinKey, MaxKey ] "]}} 표시되면 전체 테이블을 스캔 한 것입니다.

같은 다른 대안 (추천 대답을 업데이트 좋을 것이다 'name.1': {$exists: true}}, 또한이 때문에 "1"에 대한 하드 코딩을하고 임의의 또는 파라 메트릭 최소 배열 길이로 확장되지 않습니다.
댄 Dascalescu

1
N이 작지 않은 목록> N을 찾으면 빠르지 만 빠질 수 있습니다.
Brandon Hill

62

집계도 사용할 수 있습니다.

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

// "size_of_name"을 추가하여 문서를 전송하고이를 사용하여 이름의 크기를 필터링합니다.


이 솔루션은 모든 어레이 크기에 사용할 수 있기 때문에 @JohnnyHK와 함께 가장 일반적입니다.
arun

투영 내에서 "size_of_name"을 사용하려면 어떻게해야합니까 ?? 실제로 값이 $ slice와 같은 투영 내에서 $ slice를 사용하고 싶습니다. [0, "size_of_name"-skip] ??
Sudhanshu Gaur

44

다음과 같이 해보십시오.

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

1은 숫자입니다. 50보다 큰 레코드를 가져 오려면 ArrayName.50 감사를 수행하십시오.


2
같은 해답이 3 년 전에 주어졌다 .
Dan Dascalescu 2018 년

나는 미래에서 왔으며 이것을 이해할 것입니다.이 솔루션은 요소가 해당 위치에 존재하는지 확인하여 작동합니다. 따라서, 수집은 그 수보다 커야합니다.
MarAvFe

쿼리 내에 "ArrayName. <some_num>"과 같은 동적 숫자를 넣을 수 있습니까?
Sahil Mahajan

예, 당신은 어떤 숫자를 사용할 수 있습니다. N보다 큰 레코드를 가져 오려면 n을 전달하십시오.
Aman Goel


26

$ expr을 사용할 수 있습니다 일반 쿼리에서 집계 함수를 사용하는 (3.6 몽고 버전 연산자).

비교 query operatorsaggregation comparison operators.

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

$name하위 문서 인 배열 대신 "개인"레코드에서 passport.stamps어떻게 전달합니까? 나는 다양한 인용 조합을 시도했지만 얻을 수 "The argument to $size must be an array, but was of type: string/missing"있습니다.
Dan Dascalescu

3
@DanDascalescu 모든 문서에 스탬프가없는 것 같습니다. 스탬프가없는 경우 ifNull 을 사용 하여 빈 배열을 출력 할 수 있습니다 . db.col.find({$expr:{$gt:[{$size:{$ifNull:["$passport.stamps", []]}}, 1]}})
Sagar Veeram

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

1
이것은 다른 최소 크기 (예 : 10)로 확장되지 않습니다.
Dan Dascalescu

첫 번째 답변과 동일
arianpress


13

특정 길이보다 큰 배열 필드를 가진 항목을 찾기 위해이 솔루션을 찾았습니다.

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

첫 번째 $ match 집계는 모든 문서에 대해 참인 인수를 사용합니다. 비어 있으면 얻을 것입니다

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

이것은 본질적으로 같은 대답 이 하나 이년 이전 제공.
Dan Dascalescu

1

나는 오래된 질문을 알고 있지만 찾기에 $ gte와 $ size로 이것을 시도하고 있습니다. find ()가 더 빠르다고 생각합니다.

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

-5

위의 모든 작업에 대한 답변이 있지만 원래 시도한 것은 올바른 방법이지만 구문은 거꾸로되어 있습니다 ( "$ size"및 "$ gt"스위치).

옳은:

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

잘못됨 :

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

1
왜 그렇게 많은 다운 보트가 있는지 모르겠습니다. 이것은 저에게 완벽하게 작동합니다!
Jake Stokes

공감하지는 않았지만 작동하지 않습니다 (v4.2).
Evgeni Nabokov

완벽하게 작동, v 4.2.5
jperl
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.