0

I have an structure like this:

{
  _id: 123,
  bs: [
    {
      _id: 234,
      cs: [
        {
          _id: 456,
          ds : [
            {
              _id: 678,
              emails[
                "email@gmail.com"
              ]
            }
          ]
        }
      ]
    }
  ]
}

My classes in Morphia seems like this

@Entity
public class A {

  @Id
  private ObjectId id;

  @Embedded
  private List<B> bs;
}

public class B {

  private ObjectId id;

  @Embedded
  private List<C> cs;
}

public class C {

  private ObjectId id;

  @Embedded
  private List<D> ds;
}

public class D {

  private ObjectId id;

  @Embedded
  private List<String> emails;
}

What I am trying to do is insert an email inside embedded array with Morphia without retrieve all element A and use updateFirst.

This is the query I am trying execute

Query<Event> query = this.basicDAO.createQuery();
query.criteria(Mapper.ID_KEY).equal(new ObjectId(aID));
query.criteria("bs.cs.id").equal(new ObjectId(cID));
query.criteria("bs.cs.ds.id").equal(dID);

UpdateOperations<Event> updateOps = this.basicDAO.createUpdateOperations().set("bs.cs.ds.$.emails", email);

this.basicDAO.update(query, updateOps);

I also read about this post Update an item in an array that is in an array with said that

$ operator does not work "with queries that traverse nested arrays".

So I tried something like:

D d = new D(dID);
C c = new C(new ObjectId(cID));

Query<Event> query = this.basicDAO.createQuery();
query.criteria(Mapper.ID_KEY).equal(new ObjectId(aID));
query.field("bs.cs").hasThisElement(c);
query.field("bs.cs.ds").hasThisElement(d);

UpdateOperations<Event> updateOps = this.basicDAO.createUpdateOperations().set("bs.cs.ds.emails", email);

this.basicDAO.update(query, updateOps);

However it still doesn't work. Any idea how solve this? The error message that I receive is cannot use the part ... to transverse the element

Community
  • 1
  • 1
arthurfnsc
  • 915
  • 3
  • 13
  • 30
  • Other thing that I found and could be related with my problem. Morphia does not store empty Java lists. So when I try insert an element in a empty array which I save before, for Morphia is the same as didn't store the list, the response list is null instead of empty list, so set a new value in a null value will result in a error – arthurfnsc Feb 09 '15 at 15:47
  • You can use the positional matching operator `$` to match multiple levels of nested arrays. This is part of the reason you should avoid nested arrays altogether in your schema. What is the use of this schema? What do the different fields mean? – wdberkeley Feb 09 '15 at 18:43
  • I can't give all details of project however let's do this analogy: Imagine that I can create an Event (class A) which can have a bunch of Editions (Class B) each Edition can have a bunch of Themes (class C) and each Theme can have a bunch of Lectures (class D). In the end of an Edition of Event I want that people vote in their prefer lecture. This is not my model, is just an analogy. My problem could be understand as update the votes of an Lecture from a Theme in an Edition of an Event. This is not the perfect analogy however I think is better than A, B, C and D – arthurfnsc Feb 09 '15 at 23:14
  • Apologies - in my comment I meant to say that you *can't* use the positional matching operator `$` to match multiple levels of nested arrays. – wdberkeley Feb 10 '15 at 15:11
  • This information I didn't know. Thx @wdberkeley. What I was doing was retrieve the entire Event by ID change what I wanted with my Java code and update the entire entity. I could split a collection and just reference this in the others to help – arthurfnsc Feb 10 '15 at 16:50

1 Answers1

1

Based on your stand-in use case, I think you should "invert" your schema. Each document will represent a lecture and will be tagged with its theme, edition, and event:

{
    "_id" : ObjectId("54da1ff0a9ce603a239c3075"),
    "event" : "X0004",
    "edition" : "A0002,
    "theme" : "RT0005",
    "votes" : 22
}

event, edition, and theme can be some kind of identifiers, and might be references to event, edition, and theme documents in other collections. To cast a vote for a particular lecture, just update it by _id:

db.test.update({ "_id" : ObjectId("54da1ff0a9ce603a239c3075") }, { "$inc" : { "votes" : 1 } })

While I don't know your full requirements, I think this is a better basic design given your example use case.

wdberkeley
  • 11,531
  • 1
  • 28
  • 23
  • one of my problems is that event edition theme and lectures are just analogies. In my real problem those entities are more complex that this. I need store an mail to future queries instead just increase it value. My problem continue being how cross multiple arrays in a update operation in Morphia. Thxs anyway – arthurfnsc Feb 10 '15 at 16:45
  • I can only help you so much through incomplete analogy with your use case. Some of the ideas in my answer are valid for more complex situations than the analogy - you may have to modify the operations or complicate things a bit. One thing is certain, though, you'll find it extremely difficult to work with a 4-deep nest of arrays. – wdberkeley Feb 10 '15 at 16:55
  • You give me a great idea to how avoid 4-deep nest arrays. I didn't know about that mongo "limitation" so I will change my model. Thx @wdberkeley – arthurfnsc Feb 10 '15 at 18:25