vwrobel
vwrobel

Reputation: 1736

How to update an array of subdocuments with R mongolite?

I am using R mongolite to read and write in a mongo collection with the following structure :

[{_id: 1, name: a, sites: [
  {ref: site_a1},
  {ref: site_a2}
]},
{_id: 2, name: b, sites: [
  {ref: site_b1},
  {ref: site_b2}
]},
{_id: 3, name: c, sites: [
  {ref: site_c1},
  {ref: site_c2},
  {ref: site_c3},
  {ref: site_c4}
]}]

For a given document of my collection, I want to add some attributes to each site. For example, I would like to update the first document like this:

{_id: 1, name: a, sites: [
  {ref: site_a1, lat: 10, lng: 20},
  {ref: site_a2, lat: 5, lng: 40}
]}

In mongolite, I am able to get the sites as a dataframe and calculate the wanted attributes:

sites <- db$find(query = '{"name": 1}',
        fields='{"_id": 0, "name": 0}')$sites[[1]]
loc <- getLatLng(sites)
# loc is a dataframe with lat and lng for each site

But I cannot find a way to update my db. I have tried:

db$update(query = '{"name": 1}',
           update = paste0('{"$push":{"sites": {"$each":',jsonlite::toJSON(loc),'}}}'),
           upsert = FALSE, multiple = FALSE)

which unsurprisingly gives me:

{_id: 1, name: a, sites: [
  {ref: site_a1},
  {ref: site_a2},
  {lat: 10, lng: 20},
  {lat: 5, lng: 40}
]}

Is there a way to update the sites field by adding the new attributes to existing elements of the arry or should I replace the whole sites field with a $set? Thanks for your help.

I guess that my issue is linked to this question so I might have to loop over each element of my array...

Upvotes: 3

Views: 1185

Answers (1)

vwrobel
vwrobel

Reputation: 1736

The following seems to do the trick:

sites <- db$find(query = '{"name": 1}',
        fields='{"_id": 0, "name": 0}')$sites[[1]]
loc <- getLatLng(sites)

for (i in 1:nrow(sites)){
  db$update(query = paste0('{"name": 1, "sites.ref":', sites$ref[i],'}'),
            update = paste0('{"$set":{"sites.$.lat":',loc[i, "lat"],'},
                             "$set":{"sites.$.lon":',loc[i, "lng"],'}}'))
}

Still interested if there exists a better way...

Upvotes: 2

Related Questions