简体   繁体   English

MongoDb upsert异常无效的BSON字段

[英]MongoDb upsert exception invalid BSON field

This exception: 这个例外:

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)

is thrown by the following code: 由以下代码抛出:

     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 ));

The commented code _collection.insertOne( track ); 注释代码_collection.insertOne( track ); executes fine. 执行得很好。

Why the insert is fine when the "upsert" is not? 为什么插入没有“upsert”?

updateOne for updating document fields using update operators. updateOne用于使用更新运算符更新文档字段。 You need replaceOne which takes the replacement document. 您需要替换文档的replaceOne

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

More here 更多这里

Update Operators: https://docs.mongodb.com/manual/reference/operator/update-field/ 更新运算符: https//docs.mongodb.com/manual/reference/operator/update-field/

Update One: https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/ 更新一: https//docs.mongodb.com/manual/reference/method/db.collection.updateOne/

Replace One: https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/ 替换一个: https//docs.mongodb.com/manual/reference/method/db.collection.replaceOne/

Another option is setOnInsert , as shown in the document of MongoDB: 另一个选项是setOnInsert ,如MongoDB文档中所示:

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

The operation works only when upsert is true . 仅当upserttrue时,该操作才有效。

In your case, you should put fields not to be modified in a document, and fields to be updated in another document, and in the third document, prepend $setOnInsert and $set as key, respectively. 在您的情况下,您应该在文档中放置不要修改的字段,在另一个文档中放置要更新的字段,在第三个文档中,分别$set $setOnInsert$set作为键。

A big advantage of $setOnInsert is that when inserting, it will perform $setOnInsert and $set part, but when updating, only $set will be executed. $setOnInsert一大优点是,在插入时,它将执行$setOnInsert $set part,但是在更新时,只会执行$set

For example, we have a document to insert/update, which has 5 fields: name , age , gender , createAt , updateAt . 例如,我们有一个要插入/更新的文档,它有5个字段: nameagegendercreateAtupdateAt

  • When I do a query by field "name" and find no match, I want to insert the document with 5 fields, and fill createAt and updateAt with current datetime. 当我按字段“name”进行查询并找不到匹配项时,我想插入包含5个字段的文档,并使用当前日期时间填充createAtupdateAt
  • When I do a query by "name" and find a match, I want to update name and updateAt with current datetime. 当我按“名称”进行查询并找到匹配项时,我想用当前日期时更新nameupdateAt

What I do is: 我所做的是:

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));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM