Partial document updates with Morphia and Json

856 views
Skip to first unread message

Sonar

unread,
Nov 16, 2010, 5:27:56 PM11/16/10
to Morphia
Greetings,

I'm wondering if there's a way to accomplish the following workflow
with Morphia.

In a restful web app that takes advantage of Json as a state transfer
protocol and Mongo/Morphia back end I have an Object "Listing"

@Entity
class Listing {
@Id
public String id = ObjectId.get().toString();
public String name;
public String description;
public Date addedOn;
public Double ask;
public Double commission;
}

This model work perfect and allows to manage the state of the
Listings. Here comes the question though. What if I need to be able to
send an incomplete json with just an listing id and an ask price:

{
"id":"28973895345873489",
"ask": "122.3"
}

so that my expectation is that the system will update just one
document property (in this example, ask)? The current behavior of
Morphia is that when it saves the object using DAO.save() it nullifies
all the fields that aren't set explicitly, so I have to pass fully-
loaded Json every time I want to save a change, even if the change is
the tiny single property.

The other problem is when I DO want to nullify some property. The
tricky thing is that in a Listing pojo there's no difference between
the field that has been nullified intentionally and the field that is
null but should be ignored on save.

Should I switch from Pojos to simple maps then? Maps seem to solve all
the problems outlined above:

1) if map has key X with value Foo - update the corresponding document
property X to the value of Foo
2) if map doesn't have key X - ignore the property
3) if map has X->null - we should nullify the property

Any thought on this?

Scott Hernandez

unread,
Nov 16, 2010, 6:04:55 PM11/16/10
to mor...@googlegroups.com
You have a few options.

The first is to do an update; http://code.google.com/p/morphia/wiki/Updating

Recently I also added a merge method on the Datastore for something like this.

Only fields that are not-empty/null are persisted by default. This
means that only the fields you fill in will be sent off to be updated
on a merge.

Why do you want to distinguish between the two?

Sonar

unread,
Nov 16, 2010, 6:24:22 PM11/16/10
to Morphia
> Why do you want to distinguish between the two?

This comes from two requirements

1) To be able to nullify the properties of the document
2) To be able to update document's state with json that is partial,
that is, having just a subset of the document's properties without
nullifying the rest

Scott Hernandez

unread,
Nov 16, 2010, 6:49:07 PM11/16/10
to mor...@googlegroups.com
On Tue, Nov 16, 2010 at 3:24 PM, Sonar <ser...@gmail.com> wrote:
>> Why do you want to distinguish between the two?

The two I was talking about were null versus not-set for a field.

> This comes from two requirements
>
> 1) To be able to nullify the properties of the document

You can do this with an update, but this isn't really suggested. I
think what you want to do is "unset" the field, or remove it from the
stored values. There is a subtle difference when persisting to/from
mongodb.

> 2) To be able to update document's state with json that is partial,
> that is, having just a subset of the document's properties without
> nullifying the rest

You can update just a few fields as I described. It will leave the rest alone.

This is very different than save, as you know.

Sonar

unread,
Nov 16, 2010, 9:19:52 PM11/16/10
to Morphia
Let me put my issue in slightly different way. Say I have an object
coming from the client web form:

@Entity
class Listing {
@Id
public String id = ObjectId.get().toString();
public String name = "ABC";
public String description = "Lorem Ipsum";
public Date addedOn = new Date();

public Double ask = null;
public Double commission = null;
}

The reason why last two fields are null might be fundamentally
different. 'Ask' might be null because the UI simply doesn't allow
user to view and edit this piece of data (so this null should not be
propagated to db). And 'commission' is null because the user had
intentionally erased this value (so this change is subject to
persist). The main problem is how to persist this object with Morphia
so it unsets the commission and ignores the null value in ask?





On Nov 16, 6:49 pm, Scott Hernandez <scotthernan...@gmail.com> wrote:

Scott Hernandez

unread,
Nov 16, 2010, 9:32:42 PM11/16/10
to mor...@googlegroups.com
On Tue, Nov 16, 2010 at 6:19 PM, Sonar <ser...@gmail.com> wrote:
> Let me put my issue in slightly different way. Say I have an object
> coming from the client web form:
>
> @Entity
> class Listing {
>   @Id
>   public String id = ObjectId.get().toString();
>   public String name = "ABC";
>   public String description = "Lorem Ipsum";
>   public Date addedOn = new Date();
>
>   public Double ask = null;
>   public Double commission = null;
> }
>
> The reason why last two fields are null might be fundamentally
> different. 'Ask' might be null because the UI simply doesn't allow
> user to view and edit this piece of data (so this null should not be
> propagated to db). And 'commission' is null because the user had
> intentionally erased this value (so this change is subject to
> persist). The main problem is how to persist this object with Morphia
> so it unsets the commission and ignores the null value in ask?

You will have to manually create the updateOperations if you want to
both set fields, and remove some, in one operation.
http://code.google.com/p/morphia/wiki/Updating#set/unset

If you just want to set them you can use the new merge method on
Datastore. Then in a second operation you could unset the commission.

The DAO class/sample is just a thin wrapper around the Datastore
interface for doing these things; it is just a convenience if you
follow the that data access pattern.

Sonar

unread,
Nov 16, 2010, 9:48:02 PM11/16/10
to Morphia
That actually sounds pretty cool. I have no problems manually creating
the updateOperations. But again, I cannot distinguish between the null
values I should bypass when doing set() and the null values that I
need to persist with unset(). From Java perspective there is no
difference here, it's just a null value...

That's why in the original post I have layed out the idea of maps
instead of properties. I'm not quite happy with this workaround, but
at least it offers a way to differentiate between these two types of
nulls. Can you comment on this?


On Nov 16, 9:32 pm, Scott Hernandez <scotthernan...@gmail.com> wrote:
> On Tue, Nov 16, 2010 at 6:19 PM, Sonar <serp...@gmail.com> wrote:
> > Let me put my issue in slightly different way. Say I have an object
> > coming from the client web form:
>
> > @Entity
> > class Listing {
> >   @Id
> >   public String id = ObjectId.get().toString();
> >   public String name = "ABC";
> >   public String description = "Lorem Ipsum";
> >   public Date addedOn = new Date();
>
> >   public Double ask = null;
> >   public Double commission = null;
> > }
>
> > The reason why last two fields are null might be fundamentally
> > different. 'Ask' might be null because the UI simply doesn't allow
> > user to view and edit this piece of data (so this null should not be
> > propagated to db). And 'commission' is null because the user had
> > intentionally erased this value (so this change is subject to
> > persist). The main problem is how to persist this object with Morphia
> > so it unsets the commission and ignores the null value in ask?
>
> You will have to manually create the updateOperations if you want to
> both set fields, and remove some, in one operation.http://code.google.com/p/morphia/wiki/Updating#set/unset

Scott Hernandez

unread,
Nov 16, 2010, 9:56:02 PM11/16/10
to mor...@googlegroups.com
On Tue, Nov 16, 2010 at 6:48 PM, Sonar <ser...@gmail.com> wrote:
> That actually sounds pretty cool. I have no problems manually creating
> the updateOperations. But again, I cannot distinguish between the null
> values I should bypass when doing set() and the null values that I
> need to persist with unset(). From Java perspective there is no
> difference here, it's just a null value...
>
> That's why in the original post I have layed out the idea of maps
> instead of properties. I'm not quite happy with this workaround, but
> at least it offers a way to differentiate between these two types of
> nulls. Can you comment on this?

This seems like more an issue for your web framework/dto than morphia.
I'd suggest using a sentinel value (something you can filter on to
decide if you want to unset or ignore but isn't a valid value). I have
also used javascript to send an additional value (for each field)
indicating which fields have been altered, and then only updated
those.

Reply all
Reply to author
Forward
0 new messages