Parenting entities makes _children[] of parent dirty

It is possible to add an entity by indicating parent like:

viewer.entities.add({
   parent: parentEntity
   ...
}

This makes a parent-child relationship, as after calling this code, _children property of the parent is populated automaticaly.

So after calling the code above, parent has _children[1], child has parent

The _children property is not exposed publicly (although it is possible to access it).
Whenever you delete the child entity of a parent, the children is removed but the reference from the parent remains, it still contains _children[1].

I think there is something missing here, either I haven’t found the correct solution that removes the reference also from the parent yet , or the api is missing.

Without accessing _children[], its not possible to alter _children[], which was automatically populated when parent: is called with the entity. This way _children remains dirty.

This is a suggestion or brain-storming issue by the way, trying to understand if I’m missing anything

Sandcastle example is here: Cesium Sandcastle

There are a few threads revolving around this topic (example, maybe another), but one overarching question is what exactly you are trying to accomplish.

One could argue (and should think) quite a bit about which behavior could cause the “least astonishment” here. Imagine that the child was automatically removed from the parent when it is removed from the viewer. We’d almost certainly see threads and issues being opened with the question “Why is my child removed from the parent when I remove the child from the viewer?” :slightly_smiling_face:

However, the parent setter of an entity already does the maintenance work of removing the child from the former parent. So in your case, one solution might be to explicitly call
childEntity.parent = undefined; // Detach child from parent
after removing the child from the viewer.

One has to be very clear about the intentions here, and make sure that this does not break any expectations that other parts of the code may have. But just for illustration, here’s a Sandcastle of that:

Okay, setting parent to undefined is the missing part that I’ve been looking for. “There should have been a way to remove it”, and entity.parent = undefined is the answer. Already tested and works as I expected. Thanks Marco

There are plenty of reasons we could want to track a parent/child association if they are truly associated in that manner.

The example of “why was the child removed from the parent when I removed the child from the viewer” is nonsense. If it was removed from the viewer, there’s not a viewable thing to reference or point to. If someone asked that question, the appropriate response would be, why would you think we’d continue to track something you deleted? If you delete a file off my file system, I don’t expect my OS to keep track of it somehow after.

I currently work on an app where I can select an entity and see info on it or “select parent”. If I had a children list I could easily list children of that entity as well or allow a way to quickly manipulate or control all children. Sure I can go loop all entities and find all that have the current set as parent but your looping an unknown sized list finding something that could just be there. And if you’re tracking parent, why not also allow it to track children? Does it honestly matter what our use case is? If you have users asking for it, it’s a requested feature whether you think it’s valuable or not.

The parent/children of an entity are not intended for modeling domain- or application-specific hierarchies. I even hesitated to suggest setting parent=undefined, and emphasized that one needs to be very careful to make sure that this does not break other parts of the code. All this may be obvious (given that the “children” are not even accessible), but it may be worth mentioning…

We can certainly brainstorm about options for of improving the API here.

Does it honestly matter what our use case is?

Yes. Without knowing the use case, it’s impossible to provide a good (or even just sensible) solution.

Looking at the context here and in the other thread, it looks like the main “feature” that people are looking for is the access to the children of an entity. Of course, one could just make this “public” and call it a day. But… that entails some questions:

  • Imagine an entity (with children) is added to an EntityCollection (like the entities of a viewer). Should all children be added to the entity collection automatically?

  • Imagine one of the children from that entity is removed. Should that child be removed from all entity collections that contained it? Or should that child be removed from all entity collections that contain the parent?

  • Imagine one child is removed from an entity collection. Based on the descriptions here, the expectation would be that this child is also automatically removed from its parent. But this is not going to happen.

    The documentation of EntityCollection#remove currently says

    Removes an entity from the collection.

    Imagine this was now changed to say

    Removes an entity from the collection. If the removed entity is the child of a parent entity, then it will be removed from the ‘children’ of that parent, and its parent will be set to undefined

    That would be a (very subtle but) severe breaking change, meaning that it would literally break many existing applications and cause hard-to-debug errors. And it certainly would not be the desired behavior for all use-cases. Why should “removing an element from a collection” have side-effects that completely alter the structure of that object that is removed, and the structure of other objects that refer to this object?

  • How should the changes in the hierarchy be reflected in other entity collections?
    As an example code snippet:

    const collectionA = new Cesium.EntityCollection();
    const collectionB = new Cesium.EntityCollection();
    
    function createEntity(id, parent) {
      return new Cesium.Entity({
        position: Cesium.Cartesian3.fromDegrees(28.94, 41.07),
        id: id,
        parent: parent,
        billboard: {
          image: "../images/Cesium_Logo_overlay.png"
        } 
      });
    }
    
    const parent = createEntity("parentId", undefined);
    const child = createEntity("childId", parent);
    
    // Initially, the child has a parent
    console.log(child.parent);
    
    collectionA.add(parent);
    collectionA.add(child);
    
    collectionB.add(parent);
    collectionB.add(child);
    
    // Remove the child from one collection:
    collectionA.remove(child);
    
    // Here, child.parent should be undefined, right?
    console.log(child.parent);
    
    // And the `parent.children` should be empty, right?
    console.log(parent._children);
    
    // Should the child still be contained in collectionB?
    console.log(collectionB.values.includes(child));
    

And at least in theory, all this refers to hierarchies, meaning that one has to ensure consistency across multiple collections and multiple levels of these hierarchies.


If I had to model some sort of “entity hierarchy” right now, I’d start with drafting a custom, domain-specific EntityHierarchyNode class, thoroughly think about what behavior I’d expect from that class, and refine that based on the exact use case that this is supposed to address.

I’m trying to understand the limitation here.

I’d like to see a good, valid explanation of why _children isn’t exposed. There have been a few posts of people asking how to access the children of an entity with no good explanation as to why we can’t. If Cesium is tracking it, what’s the rationale for attempting to hide it from us? The only kind of answers I’ve seen so far are “that doesn’t exist” or “you’re doing it wrong”. If the people using Cesium don’t need it, why does Cesium need it? Why is Cesium tracking it?

In response to your questions and theoretical issues, why not just limit parent/child relationships on entities to things that reside within the same collection? I don’t understand why you’d be associating items that reside within other collections in the first place. If they are associated, shouldn’t they be together? The fact that two related entities wouldn’t be co-located sounds like a case of something being done wrong. If they aren’t in the same collection, alert the user/dev that, that’s not allowed. Also, entities in multiple collections… why? What’s the benefit to that? To me, that sounds wrong as well. If I want to remove an entity from the visualization, I have to go find all the datasources/collections it lives in and remove it from multiple? Who does that?

Lastly, maybe I’m reading it wrong but your last answer sounds like, “we cant change it because that’s not what the docs currently says it does”. If that’s what you’re saying, so? Change the docs. It’s a breaking change? So? If the change makes sense, there have been plenty of breaking changes in Cesium. I’ve been reading these docs and changelog for many, many years and breaking changes aren’t something new.

Again, I can give justification for what I’d use a children list for. I’m just looking for justification as to why we aren’t allowed to have it. Your reasons listed above don’t make sense to me. They sound like a “what if”, that the developers (the Cesium team) could prevent with a few conditions and changes to the docs.

I’d like to see a good, valid explanation of why _children isn’t exposed. [… ] If the people using Cesium don’t need it, why does Cesium need it? Why is Cesium tracking it?

It is a part of the API that does not offer a user-facing functionality. It is possible to set entity.show = false; on a parent entity, and that will hide that entity and all the elements that this entity consists of. This parent-child relationship is generated only at construction time (and in fact, only in very few situations). It is not supposed to be modified by clients at runtime.

(The fact that these elements are called “children” may be an important detail here. I think that hardly anyone would ask this to be exposed if it was called _internalSubEntities…)

Lastly, maybe I’m reading it wrong but your last answer sounds like, “we cant change it because that’s not what the docs currently says it does”.

The documentation is a form of specification here. It defines the API. It is a contract. Changing its current behavior would be a whole different category than the “breaking changes” that are listed in the change log. These usually refer to methods/classes being removed or renamed, usually with reasonable deprecation periods, and clear migration paths.

In response to your questions and theoretical issues, why not […]

Again, I can give justification for what I’d use a children list for. I’m just looking for justification as to why we aren’t allowed to have it. Your reasons listed above don’t make sense to me. They sound like a “what if”, that the developers (the Cesium team) could prevent with a few conditions and changes to the docs.

For the “why not…” part, I could go through that list, and give a variety of answers. These would usually boil down to bugs, inconsistencies, or undesired behavior. There are good reasons to avoid changes that cause unpredictable behavior for clients, or to avoid exposing internal implementation details.

So I’ll try a more goal-oriented approach here. And maybe this should be moved to its own thread, because it starts with a very basic question:

What exactly are you trying to accomplish?

(If the answer was “I want to access the children of an entity”, then you could do this, with entity._children, with all the theoretical issues that this might entail. So maybe there’s a higher-level answer as well…)

The two main pieces of information right now are

This does seem to be strongly related to CZML and I’ll frankly admit that I’m not really deeply familiar with the details of CZML, and might have to ask about further details, and/or rely on others to chime in. But from what I’ve gathered from that description, it sounds like it should be possible to maintain an EntityCollection and the entities that it contains, in a way that ensures the desired consistency and offers the desired convenience.

For example: Imagine you had some class, let’s call it EntityManager, that offered a functionality like this

const entityWithChildren = create();

// Adds the given entity and its children to the collection (viewer)
entityManager.add(entityWithChildren);

const children = entityManager.getChildren(entityWithChildren);

// Remove the entity from the collection (viewer), and from its parent:
entityManager.remove(children[2]);

My gut feeling is that it should be possible to implement something like that with reasonable effort (although that’s only a gut feeling for now)