web-dev-qa-db-ja.com

MongoDbアップサート例外の無効なBSONフィールド

この例外:

_Exception in thread "Thread-1" Java.lang.IllegalArgumentException: Invalid BSON field name id
    at org.bson.AbstractBsonWriter.writeName(AbstractBsonWriter.Java:516)
    at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.Java:188)
    at org.bson.codecs.DocumentCodec.encode(DocumentCodec.Java:131)
    at org.bson.codecs.DocumentCodec.encode(DocumentCodec.Java:45)
    at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.Java:63)
    at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.Java:29)
    at com.mongodb.connection.UpdateCommandMessage.writeTheWrites(UpdateCommandMessage.Java:85)
    at com.mongodb.connection.UpdateCommandMessage.writeTheWrites(UpdateCommandMessage.Java:43)
    at com.mongodb.connection.BaseWriteCommandMessage.encodeMessageBodyWithMetadata(BaseWriteCommandMessage.Java:129)
    at com.mongodb.connection.RequestMessage.encodeWithMetadata(RequestMessage.Java:160)
    at com.mongodb.connection.WriteCommandProtocol.sendMessage(WriteCommandProtocol.Java:220)
    at com.mongodb.connection.WriteCommandProtocol.execute(WriteCommandProtocol.Java:101)
    at com.mongodb.connection.UpdateCommandProtocol.execute(UpdateCommandProtocol.Java:64)
    at com.mongodb.connection.UpdateCommandProtocol.execute(UpdateCommandProtocol.Java:37)
    at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.Java:168)
    at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.Java:289)
    at com.mongodb.connection.DefaultServerConnection.updateCommand(DefaultServerConnection.Java:143)
    at com.mongodb.operation.MixedBulkWriteOperation$Run$3.executeWriteCommandProtocol(MixedBulkWriteOperation.Java:490)
    at com.mongodb.operation.MixedBulkWriteOperation$Run$RunExecutor.execute(MixedBulkWriteOperation.Java:656)
    at com.mongodb.operation.MixedBulkWriteOperation$Run.execute(MixedBulkWriteOperation.Java:409)
    at com.mongodb.operation.MixedBulkWriteOperation$1.call(MixedBulkWriteOperation.Java:177)
    at com.mongodb.operation.MixedBulkWriteOperation$1.call(MixedBulkWriteOperation.Java:168)
    at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.Java:422)
    at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.Java:413)
    at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.Java:168)
    at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.Java:74)
    at com.mongodb.Mongo.execute(Mongo.Java:845)
    at com.mongodb.Mongo$2.execute(Mongo.Java:828)
    at com.mongodb.MongoCollectionImpl.executeSingleWriteRequest(MongoCollectionImpl.Java:550)
    at com.mongodb.MongoCollectionImpl.update(MongoCollectionImpl.Java:542)
    at com.mongodb.MongoCollectionImpl.updateOne(MongoCollectionImpl.Java:381)
    at org.hpms.gis.MongoDbTest.insert(MongoDbTest.Java:63)
    at Java.lang.Thread.run(Thread.Java:748)
_

次のコードによってスローされます。

_     final UUID     id        = UUID.randomUUID();
     final double   frequency = 8_000.0 + ( 10_000.0 * Math.random() );
     final double   startSec  = 100*i;
     final double   startNano =  10*i;
     final double   endSec    = startSec  + ( 100*i );
     final double   endNano   = startNano + ( 10*i );
     final double   latitude  = ( 180.0*Math.random() ) -  90.0;
     final double   longitude = ( 360.0*Math.random() ) - 180.0;
     final Document trackID   = new Document(
        "id", new Document(
           "msb", id.getMostSignificantBits ()) .append(
           "lsb", id.getLeastSignificantBits()));
     final Document track = new Document(
        "id", new Document(
           "msb", id.getMostSignificantBits ()) .append(
           "lsb", id.getLeastSignificantBits())).append(
        "frequency", frequency    ) .append(
        "start", new Document(
           "seconds", startSec    ) .append(
           "nanoSec", startNano   )).append(
        "end", new Document(
           "seconds", endSec      ) .append(
           "nanoSec", endNano     )).append(
        "position", new Document(
           "latitude" , latitude  ) .append(
           "longitude", longitude )).append(
        "padding", new byte[1000-8-8-8-4-4-4-4-8-8] );
     //_collection.insertOne( track );
     _collection.updateOne(
        trackID,
        track,
        new UpdateOptions().upsert( true ));
_

コメントされたコード_collection.insertOne( track );は正常に実行されます。

「upsert」がそうでないときに挿入がうまくいくのはなぜですか

6
Aubin

updateOneは、更新演算子を使用してドキュメントフィールドを更新します。置換ドキュメントを受け取るreplaceOneが必要です。

_collection.replaceOne(
        trackID,
        track,
        new UpdateOptions().upsert( true ));

詳細はこちら

更新演算子: https://docs.mongodb.com/manual/reference/operator/update-field/

アップデート1: https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/

置換1つ: https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/

23
user2683814

MongoDBのドキュメントに示されているように、別のオプションはsetOnInsertです。

https://docs.mongodb.com/manual/reference/operator/update/setOnInsert/

この操作は、upserttrueの場合にのみ機能します。

あなたのケースでは、変更されないフィールドをドキュメントに、更新されるフィールドを別のドキュメントに、そして3番目のドキュメントにそれぞれ$setOnInsert$setをキーとして追加する必要があります。

$setOnInsertの大きな利点は、挿入時に$setOnInsertand$set partを実行するが、更新すると、$setのみが実行されます。

たとえば、挿入/更新するドキュメントには、nameagegendercreateAtupdateAtの5つのフィールドがあります。

  • 「名前」フィールドでクエリを実行して一致するものが見つからない場合、5つのフィールドを持つドキュメントを挿入し、createAtおよびupdateAtに現在の日時を入力します。
  • 「名前」でクエリを実行して一致するものを見つけたら、nameupdateAtを現在の日時で更新したいです。

私がやることは:

query = Filters.eq("name", nameToSearch);
Document upsert = new Document();
Date now = new Date();

//only fields not mentioned in "$set" is needed here
Document toInsert = new Document()
        .append("age", newAge)
        .append("gender", genderString)
        .append("createAt", now);
//the fields to update here, whether on insert or on update.
Document toUpdate = new Document().append("name", nameToSearch)
        .append("updateAt", now);

//will: 
// - insert 5 fields if query returns no match
// - updates 2 fields if query returns match
upsert.append("$setOnInsert", toInsert)
        .append("$set", toUpdate);

UpdateResult result = collection.updateOne(query, toUpdate, 
    new UpdateOptions().upsert(true));
2
WesternGun