1

埋め込まれたエンティティのリストがあります:

@Embedded
private List<EmbeddedEntity> embedded = new ArrayList<EmbeddedEntity>();

このリストでは、特定の属性(と呼びましょうfoo)を持ち、別の属性()を持たない埋め込みエンティティを検索しますbar。したがって、Javaではnull以外であり、MongoDBでは存在しfooない必要があります。bar

次のコードを試しました(Entityリストを含むUUIDがあります)。

Query<Entity> query = mongoDataStore.find(Entity.class).field("uuid").equal(uuid)
    .field("embedded.foo").exists()
    .field("embedded.bar").doesNotExist();

fooこれは、リストが空であるか、単一のエントリがある場合(値があり、barまだ存在しない場合)に正しく機能します。ただし、属性に値があるとすぐbarに、クエリは間違った結果を返します。だから私はクエリを探しています。それはすべての埋め込まれたエンティティを繰り返し処理し、欠落しているものをすべて起動しますbar。それは可能ですか?

データ例:

// the query does not pick up the entity as it doesn't have a foo -
// that's what I want
{ uuid: "...", [ { embedded: } ] }

// the query picks up the entity as there is a foo but no bar -
// that's what I want
{ uuid: "...", [ { embedded: { foo: date } } ] }

// the query does not pick up the entity - that's not what I want
// as one foo has a value and its bar doesn't
{ uuid: "...", [ { embedded: { foo: date, bar: date } },
                 { embedded: { foo: date } }
               ] }

PS:。でも同じ結果が得られ.field("embedded.bar").hasThisOne(null)ます。

PPS:更新操作にクエリを使用したいので、リスト要素を手動で繰り返すことは実際にはオプションではありません。

PPS:これはMorphiaのバグだと思います-回避策については、以下の私の回答(https://stackoverflow.com/a/9705175/573153)を参照してください

4

2 に答える 2

4

回避策を見つけました。null を照会することはできないようですが、特定の値を照会することはできます。

私の場合、barフィールドは日付です。したがって、エンティティを初期化できますprivate Date bar = new Date(0)-これは私の場合は明らかに無効な日付であり、使用されることはありません。したがって、クエリは次のようになります。

Query<Entity> query = mongoDataStore
    .find(Entity.class)
    .field("uuid").equal(uuid)
    .field("embedded.foo").exists()
    .field("embedded.bar").hasThisOne(new Date(0));

そして、誰かがそれを必要とする場合に備えて、更新操作を次に示します (.$.そうしないとエラーが発生するため、検証を無効にする必要があります)。

UpdateOperations<Entity> update = mongoDataStore
    .createUpdateOperations(Entity.class)
    .disableValidation()
    .set("embedded.$.bar", new Date());

mongoDataStore.update(query, update);
于 2012-03-14T15:43:48.860 に答える
3

スキーマに概念的な問題があると思います。常に最上位のドキュメントに対してクエリを実行することに注意してください。クエリが機能しない理由は、要素の少なくとも 1 つに foo があり、要素の少なくとも1 つに bar 値がないトップ レベルのドキュメントを返すように要求しているためです。両方の条件が同じ配列要素に適用される必要はないことに注意してください。

$elemMatch を使用して、MongoDB で必要なことを行うことができます。

find({embedded:{$elemMatch:{foo:{$exists:true}, bar:{$exists:false}}}})ここに示すように:

> db.test.save({embedded:[]})
> db.test.save({embedded:[{foo:1}]})
> db.test.save({embedded:[{bar:1}]})
> db.test.save({embedded:[{foo:1, bar:1}]})
> db.test.find({embedded:{$elemMatch:{foo:{$exists:true}, bar:{$exists:false}}}})
{ "_id" : ObjectId("4f60c4d56fa40267a11d2f2c"), "embedded" : [ { "foo" : 1 } ] }

「null」が bar の有効な値である場合は、単純に次のように変更できます。

> db.test.save({embedded:[{foo:1, bar:null}]})
> db.test.find({embedded:{$elemMatch:{foo:{$exists:true}, $or:[{bar:{$exists:false}}, {bar:null}]}}})
{ "_id" : ObjectId("4f60c4d56fa40267a11d2f2c"), "embedded" : [ { "foo" : 1 } ] }
{ "_id" : ObjectId("4f60c52a6fa40267a11d2f30"), "embedded" : [ { "foo" : 1, "bar" : null } ] }

現在、Morphia では、$elemMatch は FieldEnd メソッド「hasThisElement」によってラップされています。私は Morphia にはあまり詳しくありません (私は自分のマッパーを作成して使用しています) が、これは上記の句を値として持つ DBObject を取得する必要があり、その結果、実行する必要がある結果が得られるはずです。

ただし、ここでも、これらの条件に一致する要素が埋め込み配列に含まれているトップ レベルのドキュメントが返されます。一致する要素のみを返したい場合は、埋め込まれた構造を最上位のコレクションに変換する必要がある場合があります。ただし、 $ 位置演算子を使用して一致する要素を変更するだけで更新できれば十分です。

db.test.update(
    {embedded:{$elemMatch:{foo:{$exists:true}, $or:[{bar:{$exists:false}}, {bar:null}]}}},
    {$set:{'embedded.$.bar':"yay!"}}
)
于 2012-03-14T16:23:48.280 に答える