Trying to insert entry with additional ObjectId fails
kamilsi opened this issue · 6 comments
In e.g. MongoDB Compass you can insert a document that contains additional ObjectId. For example this JSON:
{"second_id": {"$oid": null}}
Will be translated to a document with two ObjectIds generated on the DB side, e.g:
{
"_id": {
"$oid": "623b324877c4f70061f4b44a"
},
"second_id": {
"$oid": "623b324877c4f70061f4b449"
}
}
This is handy if you are updating a document and want to have a unique identifier that changes after the update.
Unfortunately, you cannot reproduce this with mongolite
:
mongo$insert('{"second_id": {"$oid": null}}')
Error: Invalid read of null in state IN_BSON_TYPE
@kevinAlbs is there a way to create such a query with mongo-c-driver?
@kevinAlbs is there a way to create such a query with mongo-c-driver?
Not directly. {"second_id": {"$oid": null}}
is not valid Extended JSON so I doubt any drivers support parsing that JSON directly.
The only way insert an autogenerated ObjectID other than the _id
in the C driver is to initialize one explicitly:
bson_t *to_insert = bson_new ();
bson_oid_t oid;
bson_oid_init (&oid, NULL /* context */);
bson_append_oid (to_insert, "second_id", -1, &oid);
if (!mongoc_collection_insert_one (coll, to_insert, NULL /* opts */, NULL /* reply */, &error)) {
MONGOC_ERROR ("error in mongoc_collection_insert_one: %s", error.message);
return EXIT_FAILURE;
}
MONGOC_DEBUG ("insert OK");
bson_destroy (to_insert);
Here is a runnable example.
@kamilsi it looks like this is difficult to do with the R bindings. You will have to manually set the second_id
field to match the one from _id
. For example I tested this works in R:
col <- mongo()
col$drop()
col$insert(iris)
iter <- col$iterate(query = '{}', fields = '{"_id":1}')
while(length(rec <- iter$one())){
selector <- sprintf('{"_id":{"$oid":"%s"}}', rec[['_id']])
second <- sprintf('{"$set":{"second_id": {"$oid": "%s"}}}', rec[['_id']])
col$update(selector, update = second)
}
out <- col$find(fields = '{}')
View(out)
Converting string _id's into ObjectId()'s in a dplyr data frame.
- imported_excel_tbl is a dplyr data frame that contains a reference to another table inside the clientcontract_id.
Inserting a dplyr table with mongolite$insert, I used the following code to finally got it working (took me months... but this works).
tbl <- imported_excel_tbl %>%
mutate(
clientcontract_id = list(list("$oid" = clientcontract_id))
)
coll$insert(tbl, auto_unbox = TRUE)
So key elements are using the double list like `list(list("$oid" = clientcontract_id))' and the auto_unbox = TRUE with the insert else it won't work...
Hope this helps others as well to insert an ObjectId() with a dataframe or a dplyr frame :-)
JWR
@Wesseldr I tried your code, but it inserted the field as string for me
If you want to create a new document in a collection that has a custom objectID field – you have to:
- first insert said new document
- use @jeroen hack to update "myObjectID" field to be of actual objectID type.
No news on that?
..at least it works, I guess!
finding myself here again while searching for a way to generate object id, i came up with this function which works for me and successfully inserts objectid into the table:
library(digest)
generate_object_id <- function() {
timestamp <- as.integer(Sys.time())
machine_id <- substr(digest::digest(runif(1)), 1, 6)
process_id <- sprintf("%04x", as.integer(Sys.getpid()) %% 65536)
counter <- sprintf("%06x", as.integer(runif(1, 0, 16777216)))
object_id <- paste0(
sprintf("%08x", timestamp),
machine_id,
process_id,
counter
)
return(object_id)
}
object_id <- generate_object_id()
print(object_id)