On Mon, Jan 25, 2010 at 11:52 PM, John Patterson <jdpatter...@gmail.com> wrote:
>
> This is why you configure what type of relationship is used using: @Embed,
> @Entity(PARENT), @Entity(CHILD) or @Entity(INDEPENDENT)
> So you have the flexibility to choose configuration _without_ rewriting your
> code.  Very important difference.

The problem is that it *isn't* as simple as just changing an
annotation.  Not in appengine, at any rate.  It works in the simple
case (good for demos and sample apps), but you start to notice edge
cases:

 * In some representations you can add a Photo to an Album in a
transaction, in some representations you can't.
 * In some representations, multiple queries are required to fetch a
fetch an Album containing a Photo.
 * Each representation has a completely different query syntax.

Twig exposes the datastore Query object, which means the developer
gets the full brunt of this exposure.

I don't want to say that it isn't possible to build a system that
abstracts entities into a sophisticated object graph - clearly, we
have JDO & JPA.  What I'm saying is that the people who created JDO &
JPA are not idiots (although I do think the creators of JDO's
annotations are aesthetically challenged).  The reason JDO has all
that complexity and endless configuration and query languages and
fetch groups and proxies and detaching and whatnot is because that's
what you need to abstract an arbitrary entity graph.

> Currently the first type of representation is not an option.  I do want to
> add this as it makes very large collections that change often much more
> efficient.  When it is added you could reconfigure your data schema by
> changing a single annotation.  Such a change in Objectify would require the
> developer to rewrite their entire data layer

You can never just reconfigure your data schema with a single
annotation, both for the reasons above and because you probably have
real-world data to migrate.  And real-world constraints demand a
particular schema!

Let's actually answer the original poster's question - how do you
model a photo album?  I hope he's still listening :-)

The first question is how you should model it in the datastore?  I'll
use Objectify's syntax  here because it corresponds directly to the
datastore representation.

Actually, let's start by describing how you SHOULDN'T model a photo album.

-----

BAD IDEA #1:

class Album {
    @Id Long id;
    String name;
    List<OKey<Photo>> photos;
}
class Photo {
    @Id Long id;
    String caption;
    String blobStoreKey;    // key to GAE's blobstore
}

Fetching photos in an album (again, Objectify syntax but equivalent to
the datastore operation) is:

List<Photo> fetched = ofy.get(album.photos);

There are two reasons why this is a bad idea:

 1) You now have a hard limit of 5,000 photos per album, established by GAE.

 2) Every time you load an Album, you must load the entire set of
Photo keys.  Want to generate a list of Album names?  You have to load
all that key data, orders of mangitude more data than what you want.

-----

BAD IDEA #2:

class Album {
    @Id Long id;
    String name;
}
class Photo {
    @Id Long id;
    @Parent OKey<Album> album;
    String caption;
    String blobStoreKey;
}

This stores the Photo with the Album embedded in the Photo's Key as an
ancestor, making Photo part of the Album's entity group.  At first
glance, this seems kinda cool and you can now do transactions across
Albums and Photos.

Fetching photos in an album:

OQuery<Photo> query = createQuery(Photo.class).ancestor(albumKey);
List<Photo> fetched = ofy.prepare(query).asList();

The problem is what happens when you want to move a Photo from one
Album to another.  You can't just change the parent.  You must delete
the Photo entity and create a whole new Photo entity with the new
parent Album.  And if the Photo has Comments or other referring
entities?  All those comments need to be repointed at the new Photo.
A mess.

-----

GOOD IDEA:

class Album {
    @Id Long id;
    String name;
}
class Photo {
    @Id Long id;
    OKey<Album> album;
    String caption;
    String blobStoreKey;
}

Fetching photos in an album:

OQuery<Photo> query = createQuery(Photo.class).filter("album", albumKey);
List<Photo> fetched = ofy.prepare(query).asList();

You can now move Photos between Albums easily and you can load/query
Albums efficiently.

-----

GOOD IDEA #2:

I considered writing something about index entities as described here:
http://code.google.com/events/io/2009/sessions/BuildingScalableComplexApps.html

This is what you would probably want to use if a Photo can live in
more than one Album.  But this message is long enough already.

-----

So now you're thinking, that's just the representation in the
datastore, wouldn't you rather have a entities that hide all that and
provide an interface like this:

class Album {
    @Id Long id;
    String name;
    List<Photo> photos;
}
class Photo {
    @Id Long id;
    String caption;
    String blobStoreKey;
}

This is one of those things that looks good in a simple demo but in
the real world breaks down.  Twig doesn't currently support mapping
this to GOOD IDEA.  If Twig did support this, it would also need to
support lazy loading of the collection with a proxy - otherwise you
have a worse problem than BAD IDEA #1!  And now, if this list is a
proxy, serialization becomes an issue - you need some sort of
detaching mechanism.

...and then you have JDO.

I hope this message doesn't sound like "Twig is bad" - it's not, and
it does (and probably always will) do things that Objectify does not.
I just want to discourage the notion that you can easily work on
Appengine without using the Key class.  Judging by the official sample
code, this isn't even easy using JDO!

Jeff

-- 
You received this message because you are subscribed to the Google Groups 
"Google App Engine for Java" group.
To post to this group, send email to google-appengine-j...@googlegroups.com.
To unsubscribe from this group, send email to 
google-appengine-java+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/google-appengine-java?hl=en.

Reply via email to