pmndrs / koota

🌎 Performant real-time state management for React and TypeScript
130 stars 5 forks source link

Query: Pass traits into relations to filter target entities by #4

Open krispya opened 1 month ago

krispya commented 1 month ago

When using relations in a query you can query for a specific target:

const results = world.query(Contains(gold))

Or a wildcard, which matches any entity targeted by that relation:

const results = world.query(Contains('*'))

The feature request is to extend this by passing traits which filters by any entity that is a target of the relation and also has the matching traits.

const results = world.query(Contains(IsActive))

Challenges: We are currently using the same API for both adding relations as traits and querying entities. This mixes concerns in a way that feels unsafe. Either strong typing needs to address this or there needs to be some symmetry between adding relations and querying them.

Ctrlmonster commented 1 month ago

My preferred approach would be to go for symmetry and enable bulk operations in all contexts. Examples:

isGold = trait();

// ------------------------------------------------------------------

// adding with `...trait[]` arg
myEntity.add(Contains(isGold));

// translates to
world.query(isGold).forEach(goldEntity => myEntity.add(Contains(goldEntity)))

// ------------------------------------------------------------------

// removing with `...trait[]` arg
myEntity.remove(Contains(isGold));

// translates to
world.query(isGold).forEach(goldEntity => myEntity.remove(Contains(goldEntity)))

// ------------------------------------------------------------------

// querying for a relation with `...trait[]` arg
world.query(A, Contains(isGold))

// translates to (pseudo code implementation)
containsGold = world.query(Contains(isGold))
world.query(A, Contains("*")).filter(e => containsGold.some(e2 => e2 === e.targetFor(Contains)))
krispya commented 1 month ago

This looks good to me. My monkee brain understands what is being expressed in each case.