Query embedded collections

2,270 views
Skip to first unread message

greenl

unread,
Aug 31, 2010, 9:00:13 PM8/31/10
to Morphia
Hi group

Suppose the "Post" object have the following structure:

{ "_id": ObjectId("..."), "title" : "some title", "content": "some
content", "tags" : [
{ "name": "tag1"},
{ "name": "tag2"},
...
]}

Now I need to find out all queries with tag named "tag1", in the mongo
sh, I can do:

> db.Post.find({tags: {name: 'tag1'}})

How to do it with Morphia find interface?

thanks,
Green

Scott Hernandez

unread,
Aug 31, 2010, 9:14:09 PM8/31/10
to mor...@googlegroups.com
There are a few options, but these are the basics.

If you have classes like this:

class Post {
@Id ObjectId id;
List<Tag> tags = new ArrayList<Tag>();
...
}

class Tag {
String name;
....
}

You would do something like this.

Tag filterTag = new Tag("tag1");
// {tags: { name: "tag1"}}
Query<Post> query =  ...createQuery(Post.class).filter("tags", filterTag); // or field("tags").eq(filterTag)
//or {tags: {$elemMatch : { name: "tag1"}}} 
Query<Post> query =  ...createQuery(Post.class).field("tags").hasThisElement(filterTag); //or filter("tags elem", filterTag)
//or {"tags.name" : "tag1" }
Query<Post> query =  ...createQuery(Post.class).filter("tags.name", "tag1");

for (Post post : query)
//do something with post

Note, depending on what you do you may want to use dotted-notation, $elemMatch, or just match the whole embedded element (as you did in your example). I assume this is a simplified example so it is hard for me to guess what you want to really do.

I would say though if you Tag is that simple, it is probably better to just store them at a List<String> instead.

green

unread,
Aug 31, 2010, 9:30:49 PM8/31/10
to mor...@googlegroups.com
Solution one works! I have tried Solution three before but end with up an Exception thrown by QueryImpl class: "Can not use dot-notation past  ...". Will try solution two later. What is the differences among these query methods in terms of performance?

BTW, agree with you that Tag should be List<String>. Here is a little background: i m creating a PlayFramework plugin to provide mongo db access service to user's model object. This work is based on Morphia. A draft version almost works and now i am porting the famous (to Play community) YABE(Yet Another Blog E?) sample from JPA to morphia/mongo, that's why I get a Tag{name} class here;). 

Really thanks to your great work!.

BRs,
Green

Scott Hernandez

unread,
Aug 31, 2010, 9:51:43 PM8/31/10
to mor...@googlegroups.com
On Tue, Aug 31, 2010 at 6:30 PM, green <green...@gmail.com> wrote:
Solution one works! I have tried Solution three before but end with up an Exception thrown by QueryImpl class: "Can not use dot-notation past  ...". Will try solution two later. What is the differences among these query methods in terms of performance?

Solution one is pretty much query-by-example where you create a template instance (where you want all the fields) to search for. It is a cool by-product of how the query system (in mongodb) and embedding (in morphia) work. 

There isn't much difference between the $elemMatch and an exact embedded element without indexes. With indexes it depends on a few things. "$elemMatch" gives you more flexibility since it matches a set of fields within a single array element whereas the exact match does just that. If you have an index on the array (tags) and an index on the nested field (tags.name) the performance should be almost identical.

BTW, agree with you that Tag should be List<String>. Here is a little background: i m creating a PlayFramework plugin to provide mongo db access service to user's model object. This work is based on Morphia. A draft version almost works and now i am porting the famous (to Play community) YABE(Yet Another Blog E?) sample from JPA to morphia/mongo, that's why I get a Tag{name} class here;). 

Great. I look forward to seeing it. I came at this from a different direction (from google app engine for my last project). It has been a while since I used JPA/Hibernate...

Johannes

unread,
Oct 18, 2010, 11:24:35 AM10/18/10
to Morphia
Hi,
i have a similar question:
I have a object with a embedded HashMap, a collection of NodeVersion
objects
class History(){
Integer historID;
HashMap<Integer, NodeVersion> versionCollection;
...
}

each NodeVersion object has a userID of type Integer:

class NodeVersion(){
Integer userID;
...
}

Now i would like to query all History objects from my mongodb where a
NodeVersion in the embedded collection matches a certain variable
uid=xxx;
I tried
Query<History> q
=nodeds.createQuery(History.class).filter("versionCollection.NodeVersion.userID",uid);
Query<History> q
=nodeds.createQuery(History.class).field("versionCollection.NodeVersion.userID").equal(uid);
Query<History> q
=nodeds.createQuery(History.class).filter("versionCollection.NodeVersion.userID
elem",uid);

Either i get a Exception "com.mongodb.MongoInternalException: couldn't
get next element" or in the later case an empty list.
Is it possible to query an embedded HashMap? Anyone have an idea or
can help me to reveal my mistake?

thanks, johannes


On 1 Sep., 03:51, Scott Hernandez <scotthernan...@gmail.com> wrote:
> On Tue, Aug 31, 2010 at 6:30 PM, green <greenlaw...@gmail.com> wrote:
> > Solution one works! I have tried Solution three before but end with up an
> > Exception thrown by QueryImpl class: "Can not use dot-notation past  ...".
> > Will try solution two later. What is the differences among these query
> > methods in terms of performance?
>
> Solution one is pretty much query-by-example where you create a template
> instance (where you want all the fields) to search for. It is a cool
> by-product of how the query system (in mongodb) and embedding (in morphia)
> work.
>
> There isn't much difference between the $elemMatch and an exact embedded
> element without indexes. With indexes it depends on a few things.
> "$elemMatch" gives you more flexibility since it matches a set of fields
> within a single array element whereas the exact match does just that. If you
> have an index on the array (tags) and an index on the nested field (
> tags.name) the performance should be almost identical.
>
> BTW, agree with you that Tag should be List<String>. Here is a little
>
> > background: i m creating a PlayFramework <http://www.playframework.org>plugin to provide mongo db access service to user's model object. This work
> > is based on Morphia. A draft version almost works and now i am porting the
> > famous (to Play community) YABE(Yet Another Blog E?) sample from JPA to
> > morphia/mongo, that's why I get a Tag{name} class here;).
>
> Great. I look forward to seeing it. I came at this from a different
> direction (from google app engine for my last project). It has been a while
> since I used JPA/Hibernate...
>
> > Really thanks to your great work!.
>
> > BRs,
> > Green
>
> > On Wed, Sep 1, 2010 at 11:14 AM, Scott Hernandez <scotthernan...@gmail.com

Scott Hernandez

unread,
Oct 18, 2010, 12:16:10 PM10/18/10
to mor...@googlegroups.com
On Mon, Oct 18, 2010 at 8:24 AM, Johannes <johanne...@googlemail.com> wrote:
> Hi,
> i have a similar question:
> I have a object with a embedded HashMap, a collection of  NodeVersion
> objects
> class History(){
> Integer historID;
> HashMap<Integer, NodeVersion> versionCollection;
> ...
> }
> each NodeVersion object has a userID of type Integer:
>
> class NodeVersion(){
> Integer userID;
> ...
> }
>
> Now i would like to query all History objects from my mongodb where a
> NodeVersion in the embedded collection matches a certain variable
> uid=xxx;
> I tried
> Query<History> q
> =nodeds.createQuery(History.class).filter("versionCollection.NodeVersion.userID",uid);
> Query<History> q
> =nodeds.createQuery(History.class).field("versionCollection.NodeVersion.userID").equal(uid);
> Query<History> q

What value are you using for NodeVersion? It should be your numeric
value, correct?

"versionCollection.12.userID"

> =nodeds.createQuery(History.class).filter("versionCollection.NodeVersion.userID
> elem",uid);

elem doesn't take a value like this. In this case you would your whole
NodeVersion instance for example.

nodeds.createQuery(History.class).filter("versionCollection.12 elem",
nodeVersion);

http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24elemMatch

It is usually used for collections not maps, usually.

> Either i get a Exception "com.mongodb.MongoInternalException: couldn't
> get next element" or in the later case an empty list.
> Is it possible to query an embedded HashMap? Anyone have an idea or
> can help me to reveal my mistake?

I'm a little unclear about the versionCollection. Is it a map, or a
list/collection?

Maybe it would work better to have a VersionNode include the version
as well as the user and not be a map (but a collection/list/array).

Sriram Varadharajan

unread,
Feb 24, 2013, 10:30:22 PM2/24/13
to mor...@googlegroups.com
What i have is pretty much a ditto scenario of what is described above . I have an Event class that has a collection of Occurrences . Each occurrence instance  has three attributes of which one of it is "timetaken". I want to run a query to get the event that has an occurrence.timeTaken equals "0". My database looks like this 

"_id" : ObjectId("512ad8b64b8541b674139110"), "className" : "com.xxx.log.Event", "eventName" : "portalSecurity filter", "occurrence" : [       {       "id" : BinData(3,"MgkBAAAAAAA5MAAAAAAA
"),   "timetaken" : "0",      "timestamp" : ISODate("2013-02-25T03:21:26.831Z") },    {       "id" : BinData(3,"OTAAAAAAAAClXgEAAAAAAA=="),   "timetaken" : "1",      "timestamp" : ISODate("2013-02
03:21:26.831Z") } ] }


Query<Event> query = datastore.createQuery(Event.class).disableValidation();
query=query.filter("eventName = ","portalSecurity filter").filter("occurrence.timetaken","0") ;
for (Event occ : query){
for(Occurrence occu:occ.getOccurrence()){
System.out.println(occu.getTimetaken());
}
}

Output:
0
1

I was hoping for only 0. I have spent enough time trying to figure this but no success. Please help as we are building some perfomance log analysis engine and are seriously considering Mongo/Morphia for it.
Reply all
Reply to author
Forward
0 new messages