@zzuegg said:
You are right, its not a once per frame thing. In conventional SQL databases this would be a 'x JOIN y JOIN z' opration, but considering a graph database such queries are probably possible in real time.
With the ‘added passed entity’ the cost for such a operation would be ~twice the cost of a simple query. It’s a two pass query. First get a HashSet with all relationships. Then get all Entities with the need components and apply the HashSet.constains(EntityId) filter.
It depends, in the book keeping application i am developing need such queries mostliy in the evaluation part such as ‘Available Stock’, ‘Used/Bought Substances’. Of course there is no need for realtime.
Following the Game example above i would like to implement such queries for different things:
a) Security. In games like D3 or similar ‘duping’ Items is always a problem. The best known Dupe method is to give all your items to a second account and then claim your account was hacked. In general you have get your account restored once. But if you have high end items in diablo 3 the account could be worth a few hundred dollars.
With a full history of an item available, it would be possible to simply undo the transactions, resulting in no dupes.
Adiditonally, duped items due a exploit or something could possible get tracked down.
This can’t really happen with an ES because the object/entity is already unique. As long as the player has no ability to clone an entity then one cannot be created from thin air. And if they do have the ability to clone an entity then that’s the problem and history won’t solve it.
@zzuegg said:
b) Immagine a player fulfills a very epic and hard event. All items the player ever had could get a little stats increas and an engraving like 'This sword was held by the epic pspeed, creator of the Zay-ES'
This is a nice example. And a one time rare query. And as it turns out, not a join because just getting “all of the items a player ever owned” is enough.
For these rare queries, I would not use an EntitySet at all. There is overhead and you have to remember to clear them or you build up garbage. After all, in the background they are managed by the ES to keep them up to date.
Do one EntityId query to get one set of results then do another EntityId query to get the other set of results… then use set.retainAll() to get the intersection of the two. That’s actually what the ES is doing internally to combine the component query results so you are no slower.
@zzuegg said:
c) In a street system with waypoints, all crossings would be waypoints with 2 or more streets, you already have a n:m relationship
This is a conventional query. The relationships are represented by entities and you ask for all entities with head = streetCorner1 OR tail = streetCorner1. You could even write a Guava function to allow you to transform the set into just the other endpoints. (I actually have a bit of pedigree here with graph operations. One of my other projects: http://filament.sourceforge.net/)
In fact, if you make a Guava function as described and make it fstep compatible then you can use fstep to do the traversals for path finding (no A* built in but it’s easy to add). Out of the box, it provides shortest path, priority traversals, etc…
@zzuegg said:
In general, it's not that such query's are essential for a game, (If they where, you probably would already needed them). But if such a 'simple' modification of the system allows a wide bunch of new features (Every n:m operation) it might be worth considering it.
For a data-driven application such queries are a must. All workaround with the current component filter are more a dirty hack then a clean solution.
I would still post filter them as I describe. Or as zarch suggests, shunt history over to another DB.
Note: there is no strong benefit to grabbing an EntitySet just to throw it away. Especially if you will be post filtering it then it’s actually faster to grab the EntityId sets and merge them… you end up with a lot less garbage and work since you don’t create all of the components for entities that just get thrown away.