Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trying to insert entry with additional ObjectId fails #237

Open
kamilsi opened this issue Mar 23, 2022 · 6 comments
Open

Trying to insert entry with additional ObjectId fails #237

kamilsi opened this issue Mar 23, 2022 · 6 comments

Comments

@kamilsi
Copy link

kamilsi commented Mar 23, 2022

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
@jeroen
Copy link
Owner

jeroen commented Mar 23, 2022

@kevinAlbs is there a way to create such a query with mongo-c-driver?

@kevinAlbs
Copy link
Contributor

@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.

@jeroen
Copy link
Owner

jeroen commented Mar 24, 2022

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

@Wesseldr
Copy link

Wesseldr commented Apr 2, 2022

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

@lisovyk
Copy link

lisovyk commented Jul 4, 2022

@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!

@lisovyk
Copy link

lisovyk commented Jul 2, 2024

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants