0

ドキュメントにある配列要素(サブドキュメントも含めることができます)の1つ以上の一致するフィールドを照会したいと思います。

例えば:

私のコレクションには、以下の文書が含まれています。

{ 
    "_id": 1, 
    "index": 1,
    "elements":[
        {
            "name":"test",
            "date":"Mon Sep 01 01:00:00 EEST 2014" , 
            "tag":1
        }, 
        {
            "name": "test2",
            "date": "Mon Sep 01 01:00:00 EEST 2014",
            "tag": 2
        },
        {
            "name": "test",
            "date": "Mon Sep 01 02:00:00 EEST 2014",
            "tag": 3
        }
    ]
},
{ 
    "_id":2, 
    "index":2, 
    "elements":[ 
        {
            "name":"test",
            "date":"Mon Sep 01 01:00:00 EEST 2014" ,
            "tag":1
        }, 
        {
            "name": "test2",
            "date": "Mon Sep 01 01:00:00 EEST 2014", 
            "tag":2
        },
        {   
            "name":"test",
            "date":"Mon Sep 01 01:10:00 EEST 2014", 
            "tag":3
        }
    ]
},
{ 
    "_id": 3,
    "index": 3,
    "elements": [ 
        {
            "name": "test",
            "date": "Mon Sep 01 01:00:00 EEST 2014", 
            "tag":1
        },
        { 
            "name": "test2",
            "date": "Mon Sep 01 01:00:00 EEST 2014", 
            "tag":2
        },
        {
            "name": "test",
            "date": "Mon Sep 01 01:10:00 EEST 2014", 
            "tag":3
        }
    ]
}

クエリの結果として、次のようなドキュメントが返されるようにします。

{ 
    "_id":1,
    "index": 1,
    "elements":[
        { 
            "name":"test",
            "date":"Mon Sep 01 02:00:00 EEST 2014" ,
            "tag":3
        }
    ]
}

これを提供するために、クエリを書きました

Date dCriteria = new SimpleDateFormat("dd/MM/yy HH:mm:ss").parse("01/09/2014 05:00:00");

Query find = new Query( Criteria.where("index").is(3) );  //To find matching documents
find.fields().elemMatch(
    "elements",
    Criteria.where("name").is("test").and("date").gte(dCriteria)));

mongotemplate.findOne( find, Document.class );

つまり、MongoDB シェル コマンドでは次のようになります。

db.collection.find(
    { "index": 3 },
    { "elements": { 
        "$elemMatch": {
            "name": "test", 
            "date": { 
                "$gte": { "$date":"2014-09-01T02:00:000Z" }
            }
        }
    }
 )          

ただし、次の結果が返されます。

{
    "_id": 0, 
    "index": 0, 
    "elements":[
        {
            "name": "test",
            "date": "Mon Sep 01 01:00:00 EEST 2014",
            "tag":1
        }
    ]
}

_id とインデックス フィールドを省略しても問題ありませんが、条件が一致するため、配列の最初の要素を返します。一致する "name":"test" または "date" は dCriteria と等しいですが、両方の条件に一致する必要があります。同時に。

これを行うには、$elemMatch 演算子を使用して、配列要素の複数のフィールドに同時に正確に一致するクエリを実行する必要があります。しかし、私のプロジェクションでその構文を使用する方法がわかりません。

4

1 に答える 1

1

MongoDB は日付を UTC として保存します。これは、元の起点を特定せずにさまざまなタイムゾーンとの間で変換できるため、一般的に日付を保存するためのベスト プラクティスでもあります。

そのため、ここでの実際の日付が実際に UTC であることは最も確実ですが、別のタイムゾーンから構築された値を渡しています。ドライバーは、エポック タイムスタンプを使用して BSON 要求を実際にシリアル化し、「日付」BSON タイプに注目します。これは、タイムゾーンの違いによって入力が「歪んだ」ことを意味します。この場合、UTC を表すために 3 時間が取られているため、変換された日付は思ったよりも 3 時間早くなります。

特定のタイムゾーンの日付のソース データがある場合、現地時間で値を表示する場合は、入力として、およびユーザー インターフェイスでの表示として、まずこれを変換する必要があります。

したがって、UTC として変換または構築します。

    // org.joda.time as Date constructor is deprecated
    Date dCriteria = new DateTime(2014,9,1,1,10,DateTimeZone.UTC).toDate();

    Query find = new Query( Criteria.where("index").is(3) );  //To find matching documents
    find.fields().elemMatch(
            "elements",
            Criteria.where("name").is("test").and("date").gte(dCriteria)
    );

    System.out.println(find);

結果のクエリがどのように構造化されるかわからない場合は、一般的なデバッグ用に出力してください。本番環境で抑制できるログレベルでこれを行う必要があります。

上記のように、これは参照するシェル クエリと同じですが、UTC ではなく現地時間を使用しているため、現状のコードはそうではありません。

また、ほとんどの場合、プロジェクションではなくクエリで要素のマッチングを行いたいと考えています。取得された「最初の」一致は、ステートメントのクエリ部分に移動すると、位置$演算子を使用して射影できます。$elemMatchこれにより、配列内のどの要素も実際に条件に一致しない場合に、空の配列を取得しないことが保証されます。

于 2014-09-04T00:22:53.903 に答える