How to apply update using Filtered positional operator with arrayFilters
Asked Answered
O

2

6

I am running on Mongodb 3.6, with mongo driver 3.4.3 and spring data mongo 1.5.10. Below is the structure of my document

{
  "_id": 12345,
  "_class": "com.example.ProductRates",
  "rates": [
    {
      "productId": NumberInt(1234),
      "rate": 100.0,
      "rateCardId": NumberInt(1),
      "month": NumberInt(201801)
    },
    {
      "productId": NumberInt(1234),
      "rate": 200.0,
      "rateCardId": NumberInt(1),
      "month": NumberInt(201802)
    },
    {
      "productId": NumberInt(1234),
      "rate": 400.0,
      "rateCardId": NumberInt(2),
      "month": NumberInt(201803)
    },
    {
      "productId": NumberInt(1235),
      "rate": 500.0,
      "rateCardId": NumberInt(1),
      "month": NumberInt(201801)
    },
    {
      "productId": NumberInt(1235),
      "rate": 234,
      "rateCardId": NumberInt(2),
      "month": NumberInt(201803)
    }
  ]
}

am trying to do bulk update on the data as shown below

db.rates.update(
  { "_id" : 1234 },
  { $set: { "rates.$[item].rate": 200  } },
  { multi: true, 
   arrayFilters: [ { "item.rateCardId": {$in: [ 1, 2]} } ]
  }
)

Now and trying to convert this code to java. Below is the code i was able to achieve for a bulk update case. As expected the below query is updating all the document due to the usage of $[]. Am trying to figure out how to apply array filters here using positional array update operators (like $[one] ).

WriteResult wr = getMongoTemplate().updateMulti(
            new Query(where("rates.rateCardId").is(1234)),
            new Update().set("rates.$[].rate", 200),
            ProductRates.class);

Also I could not find enough tutorials or documentation that suggests how to apply all the complex mongo queries in Java. please suggest if there are any good books or tutorial i can refer to.

Oversweet answered 18/4, 2018 at 18:5 Comment(2)
Try new Update().set("rates.$.rate", 200)Isom
I did try that, but that updates only the first element. In my case have to update all the element that matches the criteriaOversweet
L
9

That should be:

WriteResult wr = getMongoTemplate().updateMulti(
  new Query(where("rates.rateCardId").is(1234)),
  new Update().set("rates.$[item].rate", 200),
  new UpdateOptions()
    .arrayFilters(
      Arrays.asList( Filters.in("item.rateCardId",Arrays.asList(1,2)) )
    ), 
  ProductRates.class
);

You need to make sure the underlying Java Driver is a 3.6.x version or greater in order to have the arrayFilters() and likely even to support the addition of UpdateOptions()

Lanettelaney answered 19/4, 2018 at 3:40 Comment(2)
"Java Driver is a 3.6.x" - None of the Spring data 1.5.x versions support this. So is it mandatory to get upgraded to 2.x to use mongo 3.6 related features?Oversweet
@Oversweet If you want MongoDB 3.6 features like arrayFilters then yes you must have a supporting driver. Spring Mongo versions prior to 2.x use deprecated interfaces that are marked for removal. Some quite possibly by the time MongoDB 4.x ships and driver updates are made again. See Driver CompatibilityLanettelaney
P
2

I found Neil's answer useful in solving this problem, but I wanted to offer a slightly different call than the one in his code snippet.

getMongoTemplate().getCollection("ProductRates").updateMany(
    new Document().append("rates.rateCardId", 1234),
    new Document().append("$set", new Document().append("rates.$[item].rate", 200)),
    new UpdateOptions()
        .arrayFilters(
            Collections.singletonList( Filters.in("item.rateCardId",
                                                 Arrays.asList(1,2)) ))
);
Panama answered 28/2, 2019 at 16:53 Comment(1)
I have a question. Is there a way to work with Objects? You just append Text but I need something like a BasicDBObject. I find your answer much better. Got a like!Maeganmaelstrom

© 2022 - 2024 — McMap. All rights reserved.