From b66b762258058ba3e75ce95aa9e48fd64d171d8d Mon Sep 17 00:00:00 2001 From: fengzero Date: Thu, 12 Nov 2026 06:01:52 +0000 Subject: [PATCH] api sort --- include/neuron/json/neu_json_rw.h | 1 + include/neuron/msg.h | 2 + include/neuron/tag.h | 3 + plugins/restful/datatag_handle.c | 22 ++++++- plugins/restful/rw_handle.c | 7 ++ src/adapter/driver/driver.c | 6 +- src/base/group.c | 27 +++++++- src/base/group.h | 3 +- src/base/tag.c | 56 ++++++++++++++++ src/parser/neu_json_rw.c | 8 +++ tests/ut/tag_sort_test.cc | 104 ++++++++++++++++++++++++++++++ 11 files changed, 232 insertions(+), 7 deletions(-) diff --git a/include/neuron/json/neu_json_rw.h b/include/neuron/json/neu_json_rw.h index aea758a96..e41b5fc67 100644 --- a/include/neuron/json/neu_json_rw.h +++ b/include/neuron/json/neu_json_rw.h @@ -168,6 +168,7 @@ typedef struct { char *node; char *name; char *desc; + char *sort_by; bool sync; int current_page; int page_size; diff --git a/include/neuron/msg.h b/include/neuron/msg.h index 95e3248de..06ffab3f2 100644 --- a/include/neuron/msg.h +++ b/include/neuron/msg.h @@ -1075,6 +1075,7 @@ typedef struct neu_req_read_group_paginate { char *group; char *name; char *desc; + char *sort_by; bool sync; int current_page; int page_size; @@ -1088,6 +1089,7 @@ neu_req_read_group_paginate_fini(neu_req_read_group_paginate_t *req) free(req->group); free(req->name); free(req->desc); + free(req->sort_by); } typedef struct neu_req_write_tag { diff --git a/include/neuron/tag.h b/include/neuron/tag.h index 9fb6a0b2e..e06eeff6c 100644 --- a/include/neuron/tag.h +++ b/include/neuron/tag.h @@ -109,6 +109,9 @@ typedef struct neu_tag_meta { UT_icd *neu_tag_get_icd(); +typedef int (*neu_tag_cmp_fn)(const void *, const void *); +neu_tag_cmp_fn neu_tag_get_sort_cmp(const char *sort_by); + void neu_tag_format_str(const neu_datatag_t *tag, char *buf, int len); int neu_format_from_str(const char *format_str, uint8_t *formats); diff --git a/plugins/restful/datatag_handle.c b/plugins/restful/datatag_handle.c index 11aa30191..b687d7426 100644 --- a/plugins/restful/datatag_handle.c +++ b/plugins/restful/datatag_handle.c @@ -545,8 +545,26 @@ void handle_get_tags(nng_aio *aio) void handle_get_tags_resp(nng_aio *aio, neu_resp_get_tag_t *tags) { - neu_json_get_tags_resp_t tags_res = { 0 }; - char * result = NULL; + neu_json_get_tags_resp_t tags_res = { 0 }; + char * result = NULL; + char sort_by[32] = { 0 }; + + ssize_t sort_len = + neu_http_get_param_str(aio, "sort_by", sort_by, sizeof(sort_by)); + if (sort_len != -2) { + neu_tag_cmp_fn cmp = + sort_len >= 0 && (size_t) sort_len < sizeof(sort_by) + ? neu_tag_get_sort_cmp(sort_by) + : NULL; + if (!cmp) { + NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, { + neu_http_response(aio, NEU_ERR_PARAM_IS_WRONG, result_error); + }); + utarray_free(tags->tags); + return; + } + utarray_sort(tags->tags, cmp); + } tags_res.n_tag = utarray_len(tags->tags); tags_res.tags = calloc(tags_res.n_tag, sizeof(neu_json_tag_t)); diff --git a/plugins/restful/rw_handle.c b/plugins/restful/rw_handle.c index f61a1a903..01a26eb6f 100644 --- a/plugins/restful/rw_handle.c +++ b/plugins/restful/rw_handle.c @@ -114,6 +114,13 @@ void handle_read_paginate(nng_aio *aio) goto error; } + if (req->sort_by && !neu_tag_get_sort_cmp(req->sort_by)) { + err_type = NEU_ERR_PARAM_IS_WRONG; + goto error; + } + + cmd.sort_by = req->sort_by; + req->sort_by = NULL; cmd.driver = req->node; cmd.group = req->group; cmd.name = req->name; diff --git a/src/adapter/driver/driver.c b/src/adapter/driver/driver.c index b31dcf994..064008f5d 100644 --- a/src/adapter/driver/driver.c +++ b/src/adapter/driver/driver.c @@ -963,12 +963,16 @@ void neu_adapter_driver_read_group_paginate(neu_adapter_driver_t *driver, neu_group_t * group = g->group; UT_array * tags; + neu_tag_cmp_fn cmp = neu_tag_get_sort_cmp(cmd->sort_by); if (cmd->is_error != true && cmd->current_page > 0 && cmd->page_size > 0) { tags = neu_group_query_read_tag_paginate( group, cmd->name, cmd->desc, cmd->current_page, cmd->page_size, - &resp.total_count); + &resp.total_count, cmp); } else { tags = neu_group_query_read_tag(group, cmd->name, cmd->desc, 0, NULL); + if (cmp) { + utarray_sort(tags, cmp); + } resp.total_count = utarray_len(tags); } diff --git a/src/base/group.c b/src/base/group.c index 671559361..92bb5158f 100644 --- a/src/base/group.c +++ b/src/base/group.c @@ -411,7 +411,8 @@ UT_array *neu_group_query_read_tag(neu_group_t *group, const char *name, UT_array *neu_group_query_read_tag_paginate(neu_group_t *group, const char *name, const char *desc, int current_page, int page_size, - int *total_count) + int * total_count, + neu_tag_cmp_fn cmp) { UT_array * array = NULL; struct query q = { @@ -420,10 +421,30 @@ UT_array *neu_group_query_read_tag_paginate(neu_group_t *group, }; pthread_mutex_lock(&group->mtx); - array = filter_tags_by_page(group->tags, match_query, &q, current_page, - page_size, total_count); + if (cmp) { + array = filter_tags(group->tags, match_query, &q); + } else { + array = filter_tags_by_page(group->tags, match_query, &q, current_page, + page_size, total_count); + } pthread_mutex_unlock(&group->mtx); + if (cmp) { + utarray_sort(array, cmp); + if (total_count) { + *total_count = utarray_len(array); + } + size_t start = (size_t)(current_page - 1) * page_size; + if (start >= utarray_len(array)) { + utarray_clear(array); + } else { + utarray_erase(array, 0, start); + if (utarray_len(array) > (unsigned int) page_size) { + utarray_resize(array, page_size); + } + } + } + return array; } diff --git a/src/base/group.h b/src/base/group.h index 4c56e9d9f..6d0f7e41a 100644 --- a/src/base/group.h +++ b/src/base/group.h @@ -50,7 +50,8 @@ UT_array * neu_group_query_read_tag(neu_group_t *group, const char *name, UT_array * neu_group_query_read_tag_paginate(neu_group_t *group, const char *name, const char *desc, int current_page, int page_size, - int *total_count); + int * total_count, + neu_tag_cmp_fn cmp); uint16_t neu_group_tag_size(const neu_group_t *group); neu_datatag_t *neu_group_find_tag(neu_group_t *group, const char *tag); diff --git a/src/base/tag.c b/src/base/tag.c index 281e8e73a..871aee821 100644 --- a/src/base/tag.c +++ b/src/base/tag.c @@ -26,6 +26,62 @@ config_ **/ #include "errcodes.h" #include "tag.h" +#define TAG_CMP(field, expression) \ + static int tag_cmp_##field(const void *left, const void *right) \ + { \ + const neu_datatag_t *a = left, *b = right; \ + int result = (expression); \ + return result ? result : strcmp(a->name, b->name); \ + } \ + static int tag_cmp_##field##_desc(const void *left, const void *right) \ + { \ + return tag_cmp_##field(right, left); \ + } + +#define TAG_STR_CMP(field) \ + TAG_CMP(field, strcmp(a->field ? a->field : "", b->field ? b->field : "")) +#define TAG_NUM_CMP(field) \ + TAG_CMP(field, (a->field > b->field) - (a->field < b->field)) + +TAG_STR_CMP(name) +TAG_STR_CMP(address) +TAG_STR_CMP(description) +TAG_STR_CMP(unit) +TAG_NUM_CMP(type) +TAG_NUM_CMP(attribute) +TAG_NUM_CMP(precision) +TAG_NUM_CMP(decimal) +TAG_NUM_CMP(bias) + +#undef TAG_NUM_CMP +#undef TAG_STR_CMP +#undef TAG_CMP + +neu_tag_cmp_fn neu_tag_get_sort_cmp(const char *sort_by) +{ + if (!sort_by || (sort_by[0] != '+' && sort_by[0] != '-')) { + return NULL; + } + +#define TAG_SORT_FIELD(field) \ + if (strcmp(sort_by + 1, #field) == 0) { \ + return sort_by[0] == '+' ? tag_cmp_##field : tag_cmp_##field##_desc; \ + } + + TAG_SORT_FIELD(name) + TAG_SORT_FIELD(address) + TAG_SORT_FIELD(description) + TAG_SORT_FIELD(unit) + TAG_SORT_FIELD(type) + TAG_SORT_FIELD(attribute) + TAG_SORT_FIELD(precision) + TAG_SORT_FIELD(decimal) + TAG_SORT_FIELD(bias) + +#undef TAG_SORT_FIELD + return NULL; +} + static void tag_array_copy(void *_dst, const void *_src) { neu_datatag_t *dst = (neu_datatag_t *) _dst; diff --git a/src/parser/neu_json_rw.c b/src/parser/neu_json_rw.c index 765c5d308..bbe6f6e2e 100644 --- a/src/parser/neu_json_rw.c +++ b/src/parser/neu_json_rw.c @@ -953,6 +953,11 @@ int neu_json_decode_read_paginate_req(char * buf, .t = NEU_JSON_BOOL, .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, }, + { + .name = "sort_by", + .t = NEU_JSON_STR, + .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, + }, }; ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems), @@ -979,6 +984,7 @@ int neu_json_decode_read_paginate_req(char * buf, req->current_page = query_elems[2].v.val_int; req->page_size = query_elems[3].v.val_int; req->is_error = query_elems[4].v.val_bool; + req->sort_by = query_elems[5].v.val_str; } *result = req; @@ -986,6 +992,7 @@ int neu_json_decode_read_paginate_req(char * buf, return ret; error: + free(query_elems[5].v.val_str); free(query_elems[0].v.val_str); free(query_elems[1].v.val_str); free(req_elems[0].v.val_str); @@ -1001,6 +1008,7 @@ void neu_json_decode_read_paginate_req_free(neu_json_read_paginate_req_t *req) { free(req->group); free(req->node); + free(req->sort_by); free(req); } diff --git a/tests/ut/tag_sort_test.cc b/tests/ut/tag_sort_test.cc index 7093c9029..b62244f47 100644 --- a/tests/ut/tag_sort_test.cc +++ b/tests/ut/tag_sort_test.cc @@ -5,6 +5,11 @@ #include "tag_sort.h" +extern "C" { +#include "base/group.h" +#include "json/neu_json_rw.h" +} + #include "utils/log.h" zlog_category_t *neuron = NULL; @@ -220,6 +225,105 @@ TEST(TagSortTest, SortDiff) free(tag4); } +TEST(TagSortTest, SortByFields) +{ + neu_datatag_t a = {}, b = {}; + a.name = a.address = a.description = a.unit = (char *) "a"; + b.name = b.address = b.description = b.unit = (char *) "b"; + a.type = NEU_TYPE_INT16; + b.type = NEU_TYPE_INT32; + a.attribute = NEU_ATTRIBUTE_READ; + b.attribute = NEU_ATTRIBUTE_WRITE; + a.precision = 1; + b.precision = 2; + a.decimal = a.bias = -1.5; + b.decimal = b.bias = 2.5; + + for (const char *field : { "name", "address", "description", "unit", "type", + "attribute", "precision", "decimal", "bias" }) { + SCOPED_TRACE(field); + auto asc = neu_tag_get_sort_cmp((std::string("+") + field).c_str()); + auto desc = neu_tag_get_sort_cmp((std::string("-") + field).c_str()); + ASSERT_NE(nullptr, asc); + ASSERT_NE(nullptr, desc); + EXPECT_LT(asc(&a, &b), 0); + EXPECT_GT(desc(&a, &b), 0); + EXPECT_EQ(asc(&a, &a), 0); + } + + a.description = a.unit = nullptr; + EXPECT_LT(neu_tag_get_sort_cmp("+description")(&a, &b), 0); + EXPECT_LT(neu_tag_get_sort_cmp("+unit")(&a, &b), 0); + EXPECT_EQ(nullptr, neu_tag_get_sort_cmp(nullptr)); + for (const char *invalid : { "", "+", "-", "name", " name", "+unknown" }) { + EXPECT_EQ(nullptr, neu_tag_get_sort_cmp(invalid)); + } +} + +TEST(TagSortTest, SortBeforePagination) +{ + neu_group_t *group = neu_group_new("group", 1000); + ASSERT_NE(nullptr, group); + for (const char *name : { "tag-c", "tag-a", "tag-b" }) { + neu_datatag_t tag = {}; + tag.name = (char *) name; + tag.attribute = NEU_ATTRIBUTE_READ; + ASSERT_EQ(0, neu_group_add_tag(group, &tag)); + } + + for (const char *sort_by : { "+name", "-name" }) { + auto cmp = neu_tag_get_sort_cmp(sort_by); + const char *first = sort_by[0] == '+' ? "tag-a" : "tag-c"; + const char *last = sort_by[0] == '+' ? "tag-c" : "tag-a"; + int total = 0; + UT_array * tags = neu_group_query_read_tag_paginate( + group, "tag", nullptr, 1, 2, &total, cmp); + EXPECT_EQ(3, total); + ASSERT_EQ(2, utarray_len(tags)); + EXPECT_STREQ(first, ((neu_datatag_t *) utarray_front(tags))->name); + EXPECT_STREQ("tag-b", ((neu_datatag_t *) utarray_back(tags))->name); + utarray_free(tags); + + tags = neu_group_query_read_tag_paginate(group, "tag", nullptr, 2, 2, + &total, cmp); + ASSERT_EQ(1, utarray_len(tags)); + EXPECT_STREQ(last, ((neu_datatag_t *) utarray_front(tags))->name); + utarray_free(tags); + + tags = neu_group_query_read_tag_paginate(group, "tag", nullptr, 3, 2, + &total, cmp); + EXPECT_EQ(0, utarray_len(tags)); + EXPECT_EQ(3, total); + utarray_free(tags); + } + + int total = 0; + UT_array *tags = neu_group_query_read_tag_paginate(group, nullptr, nullptr, + 1, 2, &total, nullptr); + EXPECT_STREQ("tag-c", ((neu_datatag_t *) utarray_front(tags))->name); + utarray_free(tags); + neu_group_destroy(group); +} + +TEST(TagSortTest, DecodePaginateSortBy) +{ + for (const char *sort_by : { "+name", "-address" }) { + std::string body = std::string("{\"node\":\"node\",\"group\":\"group\"," + "\"query\":{\"sort_by\":\"") + + sort_by + "\"}}"; + neu_json_read_paginate_req_t *req = nullptr; + ASSERT_EQ(0, neu_json_decode_read_paginate_req(&body[0], &req)); + ASSERT_NE(nullptr, req); + EXPECT_STREQ(sort_by, req->sort_by); + neu_json_decode_read_paginate_req_free(req); + } + char body[] = "{\"node\":\"node\",\"group\":\"group\"}"; + neu_json_read_paginate_req_t *req = nullptr; + ASSERT_EQ(0, neu_json_decode_read_paginate_req(body, &req)); + EXPECT_EQ(nullptr, req->sort_by); + neu_json_decode_read_paginate_req_free(req); +} + int main(int argc, char **argv) { zlog_init("./config/dev.conf");