0

I'm trying determine if there is a way using spring-mongodb (or even using the mongo java API), to upsert a document containing a list such that the elements of the list are always a union of the values upserted.

Suppose I have the following Classes (made up to simplify things):

public class Patron {
   private String name;
   private String address;
   private List<Book> booksRead;
 // assume gets/sets
}

public class Book{
   private String title;
   private String author;
// assume gets/sets
}

Further, let's assume I get the updates on only the latest books read, but I want to keep in the DB the full list of all books read. So what I'd like to do is insert a Patron(with booksRead) if it doesn't exist or update their booksRead if the Patron already does exist.

So, the first upsert 'John Doe' is not in collection so the document is inserted and looks like this:

"_id": ObjectId("553450062ef7b63435ec1f57"),
"name" : "John Doe"
"address" : "123 Oak st, Anytown, NY, 13760"
"booksRead" : [
       {
          "title" : "Grapes of Wrath",
          "author" : "John Steinbeck"
       },
       {
          "title" : "Creatures Great and Small",
          "author" : "James Herriot"
       }
 ]

John re-reads 'Grapes of Wrath' and also reads 'Of Mice and Men'. An insert is attempted passing 2 books as books read, but I'd like to only insert 'Of Mice and Men' to the read list so the document looks like:

"_id": ObjectId("553450062ef7b63435ec1f57"),
"name" : "John Doe"
"address" : "123 Oak st, Anytown, NY, 13760"
"booksRead" : [
       {
          "title" : "Grapes of Wrath",
          "author" : "John Steinbeck"
       },
       {
          "title" : "Creatures Great and Small",
          "author" : "James Herriot"
       },
       {
          "title" : "Of Mice and Men",
          "author" : "John Steinbeck"
       }
 ]

Everything I've tried seems to point to needing to separate calls one for the insert and one for the update. Update.set works for the initial load (insert) but replaces full list on second update. $addToSet works on update, but complains about trying to insert into 'non-array' on initial insert.

UPDATE:

This appears to be an issue with Spring-mongodb. I can achieve the above with mongo java api calls, it just fails when using the spring equivalents. (at least up to spring-data-mongodb 1.6.2)

3 Answers3

0

Easiest way to accomplish would be to remove the old entry and then re-add it with the added books. Ideally, this should be done transactionally.

ControlAltDel
  • 33,923
  • 10
  • 53
  • 80
  • 1
    Yeah, the problem being that mongo doesn't support transactions, you have to manage that yourself. So before I create a whole multi-phase commint scheme with locking, I'm trying to see if I can leverage the upsert ability of mongo – kfb.dev.work Apr 20 '15 at 13:23
0

I think you could do this using the addToSet operator. You can find documentation on MongoDB's web site : http://docs.mongodb.org/manual/reference/operator/update/addToSet/#up._S_addToSet

It is used with either update() or findAndModify() methods.

Snorky35
  • 426
  • 6
  • 15
  • Yes, I was able to use addToSet as long as the document (patron) to update already existed. If it didn't I got the error I mentioned 'can't insert into non-array' or something similar. – kfb.dev.work Apr 20 '15 at 13:29
  • actual error message = 'cannot apply $addToSet modifier to non-array' – kfb.dev.work Apr 20 '15 at 13:35
  • and combining with upsert=true, to create the array if it does not exist ? – Snorky35 Apr 20 '15 at 13:44
  • yes, at least with FindAndModify, haven't tried Collection.Update as I expected the same issue – kfb.dev.work Apr 20 '15 at 13:50
  • I take that back, I did try collection.update, but had a different problem. It wanted to put _class : "org.mine.Book" into each entry. Since the original insert didn't do this, it saw each entry as a brand new one. Was a little leary of collection update as I wasn't sure it was guaranteed atomic like findAndModify – kfb.dev.work Apr 20 '15 at 13:55
0

Issue appears to be at Spring layer. While I get errors upserting using its FindAndModify command, I don't have a issue with $addToSet with the mongo DBCollection.findAndModify and DBCollection.update methods.