Skip to content

Commit

Permalink
Add option to JSON entity serializer to serialize query matches
Browse files Browse the repository at this point in the history
  • Loading branch information
SanderMertens committed Aug 25, 2023
1 parent 5242c19 commit 9b80c8c
Show file tree
Hide file tree
Showing 8 changed files with 324 additions and 4 deletions.
50 changes: 50 additions & 0 deletions flecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -33375,6 +33375,7 @@ void flecs_rest_parse_json_ser_entity_params(
flecs_rest_bool_param(req, "values", &desc->serialize_values);
flecs_rest_bool_param(req, "private", &desc->serialize_private);
flecs_rest_bool_param(req, "type_info", &desc->serialize_type_info);
flecs_rest_bool_param(req, "matches", &desc->serialize_matches);
flecs_rest_bool_param(req, "alerts", &desc->serialize_alerts);

char *rel = NULL;
Expand Down Expand Up @@ -51532,6 +51533,46 @@ int flecs_json_serialize_refs(
return 0;
}

static
int flecs_json_serialize_matches(
const ecs_world_t *world,
ecs_strbuf_t *buf,
ecs_entity_t entity)
{
ecs_id_record_t *idr = flecs_id_record_get(world,
ecs_pair_t(EcsPoly, EcsQuery));

if (idr) {
ecs_table_cache_iter_t it;
if (idr && flecs_table_cache_iter((ecs_table_cache_t*)idr, &it)) {
const ecs_table_record_t *tr;
while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) {
ecs_table_t *table = tr->hdr.table;
EcsPoly *queries = ecs_table_get_column(table, tr->column, 0);

int32_t i, count = ecs_table_count(table);
ecs_entity_t *entities = ecs_vec_first(&table->data.entities);
for (i = 0; i < count; i ++) {
ecs_poly_t *q = queries[i].poly;
ecs_iter_t qit;
ecs_iter_poly(world, q, &qit, NULL);
if (!qit.variables) {
ecs_iter_fini(&qit);
continue;
}
ecs_iter_set_var(&qit, 0, entity);
if (ecs_iter_is_true(&qit)) {
flecs_json_next(buf);
flecs_json_path(buf, world, entities[i]);
}
}
}
}
}

return 0;
}

int ecs_entity_to_json_buf(
const ecs_world_t *world,
ecs_entity_t entity,
Expand Down Expand Up @@ -51634,6 +51675,15 @@ int ecs_entity_to_json_buf(
flecs_json_object_pop(buf);
}

if (desc && desc->serialize_matches) {
flecs_json_memberl(buf, "matches");
flecs_json_array_push(buf);
if (flecs_json_serialize_matches(world, buf, entity)) {
goto error;
}
flecs_json_array_pop(buf);
}

flecs_json_object_pop(buf);

return 0;
Expand Down
4 changes: 3 additions & 1 deletion flecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -12510,10 +12510,12 @@ typedef struct ecs_entity_to_json_desc_t {
bool serialize_type_info; /**< Serialize type info (requires serialize_values) */
bool serialize_alerts; /**< Serialize active alerts for entity */
ecs_entity_t serialize_refs; /**< Serialize references (incoming edges) for relationship */
bool serialize_matches; /**< Serialize which queries entity matches with */
} ecs_entity_to_json_desc_t;

#define ECS_ENTITY_TO_JSON_INIT (ecs_entity_to_json_desc_t){true, false,\
false, false, false, true, false, true, false, false, false, false, false, false }
false, false, false, true, false, true, false, false, false, false, false,\
false, false }

/** Serialize entity into JSON string.
* This creates a JSON object with the entity's (path) name, which components
Expand Down
4 changes: 3 additions & 1 deletion include/flecs/addons/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,12 @@ typedef struct ecs_entity_to_json_desc_t {
bool serialize_type_info; /**< Serialize type info (requires serialize_values) */
bool serialize_alerts; /**< Serialize active alerts for entity */
ecs_entity_t serialize_refs; /**< Serialize references (incoming edges) for relationship */
bool serialize_matches; /**< Serialize which queries entity matches with */
} ecs_entity_to_json_desc_t;

#define ECS_ENTITY_TO_JSON_INIT (ecs_entity_to_json_desc_t){true, false,\
false, false, false, true, false, true, false, false, false, false, false, false }
false, false, false, true, false, true, false, false, false, false, false,\
false, false }

/** Serialize entity into JSON string.
* This creates a JSON object with the entity's (path) name, which components
Expand Down
49 changes: 49 additions & 0 deletions src/addons/json/serialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,46 @@ int flecs_json_serialize_refs(
return 0;
}

static
int flecs_json_serialize_matches(
const ecs_world_t *world,
ecs_strbuf_t *buf,
ecs_entity_t entity)
{
ecs_id_record_t *idr = flecs_id_record_get(world,
ecs_pair_t(EcsPoly, EcsQuery));

if (idr) {
ecs_table_cache_iter_t it;
if (idr && flecs_table_cache_iter((ecs_table_cache_t*)idr, &it)) {
const ecs_table_record_t *tr;
while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) {
ecs_table_t *table = tr->hdr.table;
EcsPoly *queries = ecs_table_get_column(table, tr->column, 0);

int32_t i, count = ecs_table_count(table);
ecs_entity_t *entities = ecs_vec_first(&table->data.entities);
for (i = 0; i < count; i ++) {
ecs_poly_t *q = queries[i].poly;
ecs_iter_t qit;
ecs_iter_poly(world, q, &qit, NULL);
if (!qit.variables) {
ecs_iter_fini(&qit);
continue;
}
ecs_iter_set_var(&qit, 0, entity);
if (ecs_iter_is_true(&qit)) {
flecs_json_next(buf);
flecs_json_path(buf, world, entities[i]);
}
}
}
}
}

return 0;
}

int ecs_entity_to_json_buf(
const ecs_world_t *world,
ecs_entity_t entity,
Expand Down Expand Up @@ -1234,6 +1274,15 @@ int ecs_entity_to_json_buf(
flecs_json_object_pop(buf);
}

if (desc && desc->serialize_matches) {
flecs_json_memberl(buf, "matches");
flecs_json_array_push(buf);
if (flecs_json_serialize_matches(world, buf, entity)) {
goto error;
}
flecs_json_array_pop(buf);
}

flecs_json_object_pop(buf);

return 0;
Expand Down
1 change: 1 addition & 0 deletions src/addons/rest.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ void flecs_rest_parse_json_ser_entity_params(
flecs_rest_bool_param(req, "values", &desc->serialize_values);
flecs_rest_bool_param(req, "private", &desc->serialize_private);
flecs_rest_bool_param(req, "type_info", &desc->serialize_type_info);
flecs_rest_bool_param(req, "matches", &desc->serialize_matches);
flecs_rest_bool_param(req, "alerts", &desc->serialize_alerts);

char *rel = NULL;
Expand Down
6 changes: 5 additions & 1 deletion test/meta/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,11 @@
"serialize_refs_childof",
"serialize_refs_custom",
"serialize_refs_wildcard",
"serialize_no_ids"
"serialize_no_ids",
"serialize_matches_filter",
"serialize_matches_query",
"serialize_matches_rule",
"serialize_no_matches"
]
}, {
"id": "SerializeIterToJson",
Expand Down
192 changes: 192 additions & 0 deletions test/meta/src/SerializeEntityToJson.c
Original file line number Diff line number Diff line change
Expand Up @@ -1443,3 +1443,195 @@ void SerializeEntityToJson_serialize_no_ids(void) {

ecs_fini(world);
}

void SerializeEntityToJson_serialize_matches_filter(void) {
ecs_world_t *world = ecs_init();

ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_TAG(world, TagC);

ecs_entity_t e1 = ecs_new_entity(world, "e1");
ecs_add(world, e1, TagA);
ecs_entity_t e2 = ecs_new_entity(world, "e2");
ecs_add(world, e2, TagB);

ecs_filter_t *f_a = ecs_filter(world, {
.entity = ecs_entity(world, { .name = "f_a"} ),
.terms = {{ TagA }}
});
test_assert(f_a != NULL);

ecs_filter_t *f_b = ecs_filter(world, {
.entity = ecs_entity(world, { .name = "f_b"} ),
.terms = {{ TagB }}
});
test_assert(f_b != NULL);

ecs_filter_t *f_c = ecs_filter(world, {
.entity = ecs_entity(world, { .name = "f_c"} ),
.terms = {{ TagC }}
});
test_assert(f_c != NULL);

ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT;
desc.serialize_matches = true;
char *json = ecs_entity_to_json(world, e1, &desc);
test_assert(json != NULL);
test_str(json, "{"
"\"path\":\"e1\", "
"\"ids\":[[\"TagA\"]], "
"\"matches\":["
"\"f_a\""
"]"
"}");
ecs_os_free(json);

ecs_filter_fini(f_a);
ecs_filter_fini(f_b);
ecs_filter_fini(f_c);

ecs_fini(world);
}

void SerializeEntityToJson_serialize_matches_query(void) {
ecs_world_t *world = ecs_init();

ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_TAG(world, TagC);

ecs_entity_t e1 = ecs_new_entity(world, "e1");
ecs_add(world, e1, TagA);
ecs_entity_t e2 = ecs_new_entity(world, "e2");
ecs_add(world, e2, TagB);

ecs_query_t *f_a = ecs_query(world, {
.filter.entity = ecs_entity(world, { .name = "f_a"} ),
.filter.terms = {{ TagA }}
});
test_assert(f_a != NULL);

ecs_query_t *f_b = ecs_query(world, {
.filter.entity = ecs_entity(world, { .name = "f_b"} ),
.filter.terms = {{ TagB }}
});
test_assert(f_b != NULL);

ecs_query_t *f_c = ecs_query(world, {
.filter.entity = ecs_entity(world, { .name = "f_c"} ),
.filter.terms = {{ TagC }}
});
test_assert(f_c != NULL);

ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT;
desc.serialize_matches = true;
char *json = ecs_entity_to_json(world, e1, &desc);
test_assert(json != NULL);
test_str(json, "{"
"\"path\":\"e1\", "
"\"ids\":[[\"TagA\"]], "
"\"matches\":["
"\"f_a\""
"]"
"}");
ecs_os_free(json);

ecs_query_fini(f_a);
ecs_query_fini(f_b);
ecs_query_fini(f_c);

ecs_fini(world);
}

void SerializeEntityToJson_serialize_matches_rule(void) {
ecs_world_t *world = ecs_init();

ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_TAG(world, TagC);

ecs_entity_t e1 = ecs_new_entity(world, "e1");
ecs_add(world, e1, TagA);
ecs_entity_t e2 = ecs_new_entity(world, "e2");
ecs_add(world, e2, TagB);

ecs_rule_t *f_a = ecs_rule(world, {
.entity = ecs_entity(world, { .name = "f_a"} ),
.terms = {{ TagA }}
});
test_assert(f_a != NULL);

ecs_rule_t *f_b = ecs_rule(world, {
.entity = ecs_entity(world, { .name = "f_b"} ),
.terms = {{ TagB }}
});
test_assert(f_b != NULL);

ecs_rule_t *f_c = ecs_rule(world, {
.entity = ecs_entity(world, { .name = "f_c"} ),
.terms = {{ TagC }}
});
test_assert(f_c != NULL);

ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT;
desc.serialize_matches = true;
char *json = ecs_entity_to_json(world, e1, &desc);
test_assert(json != NULL);
test_str(json, "{"
"\"path\":\"e1\", "
"\"ids\":[[\"TagA\"]], "
"\"matches\":["
"\"f_a\""
"]"
"}");
ecs_os_free(json);

ecs_rule_fini(f_a);
ecs_rule_fini(f_b);
ecs_rule_fini(f_c);

ecs_fini(world);
}

void SerializeEntityToJson_serialize_no_matches(void) {
ecs_world_t *world = ecs_init();

ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_TAG(world, TagC);

ecs_entity_t e1 = ecs_new_entity(world, "e1");
ecs_add(world, e1, TagA);
ecs_entity_t e2 = ecs_new_entity(world, "e2");
ecs_add(world, e2, TagB);

ecs_filter_t *f_b = ecs_filter(world, {
.entity = ecs_entity(world, { .name = "f_b"} ),
.terms = {{ TagB }}
});
test_assert(f_b != NULL);

ecs_filter_t *f_c = ecs_filter(world, {
.entity = ecs_entity(world, { .name = "f_c"} ),
.terms = {{ TagC }}
});
test_assert(f_c != NULL);

ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT;
desc.serialize_matches = true;
char *json = ecs_entity_to_json(world, e1, &desc);
test_assert(json != NULL);
test_str(json, "{"
"\"path\":\"e1\", "
"\"ids\":[[\"TagA\"]], "
"\"matches\":["
"]"
"}");
ecs_os_free(json);

ecs_filter_fini(f_b);
ecs_filter_fini(f_c);

ecs_fini(world);
}
Loading

0 comments on commit 9b80c8c

Please sign in to comment.