Code

store_json: Moved special casing of parent objects into the caller.
authorSebastian Harl <sh@tokkee.org>
Mon, 28 Sep 2015 07:54:15 +0000 (09:54 +0200)
committerSebastian Harl <sh@tokkee.org>
Mon, 28 Sep 2015 07:54:15 +0000 (09:54 +0200)
This makes the JSON module more generic by removing a special case of the
memory store. The caller may now emit arbitrary objects and the formatter will
do the right thing as long as parent-child relationships are right.

src/core/store_exec.c
src/core/store_json.c
t/unit/core/store_json_test.c

index ee1f8d046f47ad49768c4aa2a2b89a0bff9bbd80..c377129b825e8c6e42a2a690e6953f3a21749337 100644 (file)
  * private helper functions
  */
 
  * private helper functions
  */
 
+typedef struct {
+       sdb_store_json_formatter_t *f;
+       sdb_store_obj_t *current_host;
+} iter_t;
+
+static int
+maybe_emit_host(iter_t *iter, sdb_store_obj_t *obj)
+{
+       if ((obj->type == SDB_HOST) || (obj->type == SDB_ATTRIBUTE))
+               return 0;
+       if (iter->current_host == obj->parent)
+               return 0;
+       iter->current_host = obj->parent;
+       return sdb_store_json_emit(iter->f, obj->parent);
+} /* maybe_emit_host */
+
 static int
 list_tojson(sdb_store_obj_t *obj,
                sdb_store_matcher_t __attribute__((unused)) *filter,
                void *user_data)
 {
 static int
 list_tojson(sdb_store_obj_t *obj,
                sdb_store_matcher_t __attribute__((unused)) *filter,
                void *user_data)
 {
-       sdb_store_json_formatter_t *f = user_data;
-       return sdb_store_json_emit(f, obj);
+       iter_t *iter = user_data;
+       maybe_emit_host(iter, obj);
+       return sdb_store_json_emit(iter->f, obj);
 } /* list_tojson */
 
 static int
 lookup_tojson(sdb_store_obj_t *obj, sdb_store_matcher_t *filter,
                void *user_data)
 {
 } /* list_tojson */
 
 static int
 lookup_tojson(sdb_store_obj_t *obj, sdb_store_matcher_t *filter,
                void *user_data)
 {
-       sdb_store_json_formatter_t *f = user_data;
-       return sdb_store_json_emit_full(f, obj, filter);
+       iter_t *iter = user_data;
+       maybe_emit_host(iter, obj);
+       return sdb_store_json_emit_full(iter->f, obj, filter);
 } /* lookup_tojson */
 
 /*
 } /* lookup_tojson */
 
 /*
@@ -74,6 +92,7 @@ exec_fetch(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
        sdb_store_obj_t *obj;
 
        sdb_store_json_formatter_t *f;
        sdb_store_obj_t *obj;
 
        sdb_store_json_formatter_t *f;
+       int status = 0;
 
        if ((! name) || ((type == SDB_HOST) && hostname)
                        || ((type != SDB_HOST) && (! hostname))) {
 
        if ((! name) || ((type == SDB_HOST) && hostname)
                        || ((type != SDB_HOST) && (! hostname))) {
@@ -127,7 +146,9 @@ exec_fetch(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
        }
 
        sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
        }
 
        sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
-       if (sdb_store_json_emit_full(f, obj, filter)) {
+       if (type != SDB_HOST)
+               status = sdb_store_json_emit(f, obj->parent);
+       if (status || sdb_store_json_emit_full(f, obj, filter)) {
                sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
                                "%s %s.%s to JSON", SDB_STORE_TYPE_TO_NAME(type),
                                hostname, name);
                sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
                                "%s %s.%s to JSON", SDB_STORE_TYPE_TO_NAME(type),
                                hostname, name);
@@ -149,10 +170,10 @@ exec_list(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
                int type, sdb_store_matcher_t *filter)
 {
        uint32_t res_type = htonl(SDB_CONNECTION_LIST);
                int type, sdb_store_matcher_t *filter)
 {
        uint32_t res_type = htonl(SDB_CONNECTION_LIST);
-       sdb_store_json_formatter_t *f;
+       iter_t iter = { NULL, NULL };
 
 
-       f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
-       if (! f) {
+       iter.f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
+       if (! iter.f) {
                char err[1024];
                sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
                                "JSON formatter to handle LIST command: %s",
                char err[1024];
                sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
                                "JSON formatter to handle LIST command: %s",
@@ -163,16 +184,16 @@ exec_list(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
        }
 
        sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
        }
 
        sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
-       if (sdb_store_scan(store, type, /* m = */ NULL, filter, list_tojson, f)) {
+       if (sdb_store_scan(store, type, /* m = */ NULL, filter, list_tojson, &iter)) {
                sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
                                "store to JSON");
                sdb_strbuf_sprintf(errbuf, "Out of memory");
                sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
                                "store to JSON");
                sdb_strbuf_sprintf(errbuf, "Out of memory");
-               sdb_object_deref(SDB_OBJ(f));
+               sdb_object_deref(SDB_OBJ(iter.f));
                return -1;
        }
 
                return -1;
        }
 
-       sdb_store_json_finish(f);
-       sdb_object_deref(SDB_OBJ(f));
+       sdb_store_json_finish(iter.f);
+       sdb_object_deref(SDB_OBJ(iter.f));
 
        return SDB_CONNECTION_DATA;
 } /* exec_list */
 
        return SDB_CONNECTION_DATA;
 } /* exec_list */
@@ -182,10 +203,10 @@ exec_lookup(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
                int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
 {
        uint32_t res_type = htonl(SDB_CONNECTION_LOOKUP);
                int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
 {
        uint32_t res_type = htonl(SDB_CONNECTION_LOOKUP);
-       sdb_store_json_formatter_t *f;
+       iter_t iter = { NULL, NULL };
 
 
-       f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
-       if (! f) {
+       iter.f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
+       if (! iter.f) {
                char err[1024];
                sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
                                "JSON formatter to handle LOOKUP command: %s",
                char err[1024];
                sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
                                "JSON formatter to handle LOOKUP command: %s",
@@ -197,17 +218,17 @@ exec_lookup(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
 
        sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
 
 
        sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
 
-       if (sdb_store_scan(store, type, m, filter, lookup_tojson, f)) {
+       if (sdb_store_scan(store, type, m, filter, lookup_tojson, &iter)) {
                sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup %ss",
                                SDB_STORE_TYPE_TO_NAME(type));
                sdb_strbuf_sprintf(errbuf, "Failed to lookup %ss",
                                SDB_STORE_TYPE_TO_NAME(type));
                sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup %ss",
                                SDB_STORE_TYPE_TO_NAME(type));
                sdb_strbuf_sprintf(errbuf, "Failed to lookup %ss",
                                SDB_STORE_TYPE_TO_NAME(type));
-               sdb_object_deref(SDB_OBJ(f));
+               sdb_object_deref(SDB_OBJ(iter.f));
                return -1;
        }
 
                return -1;
        }
 
-       sdb_store_json_finish(f);
-       sdb_object_deref(SDB_OBJ(f));
+       sdb_store_json_finish(iter.f);
+       sdb_object_deref(SDB_OBJ(iter.f));
 
        return SDB_CONNECTION_DATA;
 } /* exec_lookup */
 
        return SDB_CONNECTION_DATA;
 } /* exec_lookup */
index 878cc4fe265886653c754a60a83ef23889af1668..639c9534aa8485fc9afce4e5e25ecf33058057eb 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * SysDB - src/core/store_json.c
 /*
  * SysDB - src/core/store_json.c
- * Copyright (C) 2013-2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * Copyright (C) 2013-2015 Sebastian 'tokkee' Harl <sh@tokkee.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -58,9 +58,6 @@ struct sdb_store_json_formatter {
        int context[8];
        size_t current;
 
        int context[8];
        size_t current;
 
-       /* The current host context when processing non-host objects */
-       sdb_store_obj_t *current_host;
-
        int type;
        int flags;
 };
        int type;
        int flags;
 };
@@ -83,8 +80,6 @@ formatter_init(sdb_object_t *obj, va_list ap)
 
        f->context[0] = 0;
        f->current = 0;
 
        f->context[0] = 0;
        f->current = 0;
-
-       f->current_host = NULL;
        return 0;
 } /* formatter_init */
 
        return 0;
 } /* formatter_init */
 
@@ -135,17 +130,6 @@ json_emit(sdb_store_json_formatter_t *f, sdb_store_obj_t *obj)
 
        assert(f && obj);
 
 
        assert(f && obj);
 
-       if ((f->type != SDB_HOST) && (f->type == obj->type)) {
-               /* create the host for the current entry first */
-               assert(obj->parent && (obj->parent->type == SDB_HOST));
-               if (f->current_host != obj->parent) {
-                       json_emit(f, obj->parent);
-                       sdb_strbuf_append(f->buf, ", \"%ss\": [",
-                                       SDB_STORE_TYPE_TO_NAME(obj->type));
-                       f->current_host = obj->parent;
-               }
-       }
-
        escape_string(SDB_OBJ(obj)->name, name);
        sdb_strbuf_append(f->buf, "{\"name\": %s, ", name);
        if (obj->type == SDB_ATTRIBUTE) {
        escape_string(SDB_OBJ(obj)->name, name);
        sdb_strbuf_append(f->buf, "{\"name\": %s, ", name);
        if (obj->type == SDB_ATTRIBUTE) {
@@ -211,16 +195,9 @@ sdb_store_json_emit(sdb_store_json_formatter_t *f, sdb_store_obj_t *obj)
        if ((! f) || (! obj))
                return -1;
 
        if ((! f) || (! obj))
                return -1;
 
-       if ((f->type != SDB_HOST) && (obj->type == SDB_HOST)) {
-               sdb_log(SDB_LOG_ERR, "store: Unexpected object of type host "
-                               "during %s JSON serialization",
-                               SDB_STORE_TYPE_TO_NAME(f->type));
-               return -1;
-       }
-
        /* first top-level object */
        if (! f->context[0]) {
        /* first top-level object */
        if (! f->context[0]) {
-               if (obj->type != f->type) {
+               if ((obj->type != f->type) && (obj->type != SDB_HOST)) {
                        sdb_log(SDB_LOG_ERR, "store: Unexpected object of type %s "
                                        "as the first element during %s JSON serialization",
                                        SDB_STORE_TYPE_TO_NAME(obj->type),
                        sdb_log(SDB_LOG_ERR, "store: Unexpected object of type %s "
                                        "as the first element during %s JSON serialization",
                                        SDB_STORE_TYPE_TO_NAME(obj->type),
@@ -230,42 +207,38 @@ sdb_store_json_emit(sdb_store_json_formatter_t *f, sdb_store_obj_t *obj)
                if (f->flags & SDB_WANT_ARRAY)
                        sdb_strbuf_append(f->buf, "[");
                assert(f->current == 0);
                if (f->flags & SDB_WANT_ARRAY)
                        sdb_strbuf_append(f->buf, "[");
                assert(f->current == 0);
-               f->context[0] = obj->type;
-               return json_emit(f, obj);
        }
        }
-
-       if ((f->current >= 1) && (obj->type != SDB_ATTRIBUTE)) {
-               /* new entry of a previous type or a new type on the same level;
-                * rewind to the right state */
-               while (f->current > 0) {
-                       if (f->context[f->current] == obj->type)
-                               break;
-                       sdb_strbuf_append(f->buf, "}]");
-                       --f->current;
+       else {
+               if ((f->current >= 1) && (obj->type != SDB_ATTRIBUTE)) {
+                       /* new entry of a previous type or a new type on the same level;
+                        * rewind to the right state */
+                       while (f->current > 0) {
+                               if (f->context[f->current] == obj->type)
+                                       break;
+                               sdb_strbuf_append(f->buf, "}]");
+                               --f->current;
+                       }
                }
                }
-       }
-       if ((obj->type == f->type) && (f->type != SDB_HOST))
-               if (obj->parent != f->current_host)
-                       sdb_strbuf_append(f->buf, "}]");
 
 
-       if (obj->type == f->context[f->current]) {
-               /* new entry of the same type */
-               sdb_strbuf_append(f->buf, "},");
-       }
-       else if ((f->context[f->current] == SDB_HOST)
-                       || (obj->type == SDB_ATTRIBUTE)) {
-               assert(obj->type != SDB_HOST);
-               /* all object types may be children of a host;
-                * attributes may be children of any type */
-               sdb_strbuf_append(f->buf, ", \"%ss\": [",
-                               SDB_STORE_TYPE_TO_NAME(obj->type));
-               ++f->current;
-       }
-       else {
-               sdb_log(SDB_LOG_ERR, "store: Unexpected object of type %s "
-                               "on level %zu during JSON serialization",
-                               SDB_STORE_TYPE_TO_NAME(obj->type), f->current);
-               return -1;
+               if (obj->type == f->context[f->current]) {
+                       /* new entry of the same type */
+                       sdb_strbuf_append(f->buf, "},");
+               }
+               else if ((f->context[f->current] == SDB_HOST)
+                               || (obj->type == SDB_ATTRIBUTE)) {
+                       assert(obj->type != SDB_HOST);
+                       /* all object types may be children of a host;
+                        * attributes may be children of any type */
+                       sdb_strbuf_append(f->buf, ", \"%ss\": [",
+                                       SDB_STORE_TYPE_TO_NAME(obj->type));
+                       ++f->current;
+               }
+               else {
+                       sdb_log(SDB_LOG_ERR, "store: Unexpected object of type %s "
+                                       "on level %zu during JSON serialization",
+                                       SDB_STORE_TYPE_TO_NAME(obj->type), f->current);
+                       return -1;
+               }
        }
 
        json_emit(f, obj);
        }
 
        json_emit(f, obj);
@@ -339,12 +312,7 @@ sdb_store_json_finish(sdb_store_json_formatter_t *f)
                sdb_strbuf_append(f->buf, "}]");
                --f->current;
        }
                sdb_strbuf_append(f->buf, "}]");
                --f->current;
        }
-       if (f->context[0] != SDB_HOST) {
-               assert(f->type != SDB_HOST);
-               sdb_strbuf_append(f->buf, "}]}");
-       }
-       else
-               sdb_strbuf_append(f->buf, "}");
+       sdb_strbuf_append(f->buf, "}");
 
        if (f->flags & SDB_WANT_ARRAY)
                sdb_strbuf_append(f->buf, "]");
 
        if (f->flags & SDB_WANT_ARRAY)
                sdb_strbuf_append(f->buf, "]");
index f8ebac913633fe025df01344c2e2ecab521874cc..4173831f824833b821e68b03183c024460e0ad2c 100644 (file)
@@ -271,54 +271,42 @@ struct {
        { { NULL, 0, SDB_DATA_INIT },
                SDB_SERVICE, scan_tojson_full,
                "["
        { { NULL, 0, SDB_DATA_INIT },
                SDB_SERVICE, scan_tojson_full,
                "["
-                       "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:03 +0000\", "
+                       "{\"name\": \"s1\", "
+                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
+                               "\"update_interval\": \"0s\", \"backends\": []},"
+                       "{\"name\": \"s2\", "
+                               "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
                                "\"update_interval\": \"0s\", \"backends\": [], "
                                "\"update_interval\": \"0s\", \"backends\": [], "
-                               "\"services\": ["
-                                       "{\"name\": \"s1\", "
-                                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
-                                               "\"update_interval\": \"0s\", \"backends\": []},"
-                                       "{\"name\": \"s2\", "
+                               "\"attributes\": ["
+                                       "{\"name\": \"k1\", \"value\": 123, "
                                                "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
                                                "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
-                                               "\"update_interval\": \"0s\", \"backends\": [], "
-                                               "\"attributes\": ["
-                                                       "{\"name\": \"k1\", \"value\": 123, "
-                                                               "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
-                                                               "\"update_interval\": \"0s\", \"backends\": []},"
-                                                       "{\"name\": \"k2\", \"value\": 4711, "
-                                                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
-                                                               "\"update_interval\": \"0s\", \"backends\": []}"
-                                               "]}"
+                                               "\"update_interval\": \"0s\", \"backends\": []},"
+                                       "{\"name\": \"k2\", \"value\": 4711, "
+                                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
+                                               "\"update_interval\": \"0s\", \"backends\": []}"
                                "]}"
                "]" },
        { { NULL, 0, SDB_DATA_INIT },
                SDB_SERVICE, scan_tojson,
                "["
                                "]}"
                "]" },
        { { NULL, 0, SDB_DATA_INIT },
                SDB_SERVICE, scan_tojson,
                "["
-                       "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:03 +0000\", "
-                               "\"update_interval\": \"0s\", \"backends\": [], "
-                               "\"services\": ["
-                                       "{\"name\": \"s1\", "
-                                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
-                                               "\"update_interval\": \"0s\", \"backends\": []},"
-                                       "{\"name\": \"s2\", "
-                                               "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
-                                               "\"update_interval\": \"0s\", \"backends\": []}"
-                               "]}"
+                       "{\"name\": \"s1\", "
+                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
+                               "\"update_interval\": \"0s\", \"backends\": []},"
+                       "{\"name\": \"s2\", "
+                               "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
+                               "\"update_interval\": \"0s\", \"backends\": []}"
                "]" },
        { { sdb_store_gt_matcher, SDB_FIELD_LAST_UPDATE,
                        { SDB_TYPE_DATETIME, { .datetime = 1 * SDB_INTERVAL_SECOND } } },
                SDB_SERVICE, scan_tojson_full,
                "["
                "]" },
        { { sdb_store_gt_matcher, SDB_FIELD_LAST_UPDATE,
                        { SDB_TYPE_DATETIME, { .datetime = 1 * SDB_INTERVAL_SECOND } } },
                SDB_SERVICE, scan_tojson_full,
                "["
-                       "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:03 +0000\", "
+                       "{\"name\": \"s2\", "
+                               "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
                                "\"update_interval\": \"0s\", \"backends\": [], "
                                "\"update_interval\": \"0s\", \"backends\": [], "
-                               "\"services\": ["
-                                       "{\"name\": \"s2\", "
+                               "\"attributes\": ["
+                                       "{\"name\": \"k1\", \"value\": 123, "
                                                "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
                                                "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
-                                               "\"update_interval\": \"0s\", \"backends\": [], "
-                                               "\"attributes\": ["
-                                                       "{\"name\": \"k1\", \"value\": 123, "
-                                                               "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
-                                                               "\"update_interval\": \"0s\", \"backends\": []}"
-                                               "]}"
+                                               "\"update_interval\": \"0s\", \"backends\": []}"
                                "]}"
                "]" },
        { { sdb_store_lt_matcher, SDB_FIELD_LAST_UPDATE,
                                "]}"
                "]" },
        { { sdb_store_lt_matcher, SDB_FIELD_LAST_UPDATE,
@@ -328,68 +316,48 @@ struct {
        { { NULL, 0, SDB_DATA_INIT },
                SDB_METRIC, scan_tojson_full,
                "["
        { { NULL, 0, SDB_DATA_INIT },
                SDB_METRIC, scan_tojson_full,
                "["
-                       "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:01 +0000\", "
+                       "{\"name\": \"m1\", "
+                               "\"timeseries\": false, "
+                               "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
                                "\"update_interval\": \"0s\", \"backends\": [], "
                                "\"update_interval\": \"0s\", \"backends\": [], "
-                               "\"metrics\": ["
-                                       "{\"name\": \"m1\", "
-                                               "\"timeseries\": false, "
+                               "\"attributes\": ["
+                                       "{\"name\": \"k3\", \"value\": 42, "
                                                "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
                                                "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
-                                               "\"update_interval\": \"0s\", \"backends\": [], "
-                                               "\"attributes\": ["
-                                                       "{\"name\": \"k3\", \"value\": 42, "
-                                                               "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
-                                                               "\"update_interval\": \"0s\", \"backends\": []}"
-                                               "]},"
-                                       "{\"name\": \"m2\", "
-                                               "\"timeseries\": false, "
-                                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
                                                "\"update_interval\": \"0s\", \"backends\": []}"
                                "]},"
                                                "\"update_interval\": \"0s\", \"backends\": []}"
                                "]},"
-                       "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:03 +0000\", "
-                               "\"update_interval\": \"0s\", \"backends\": [], "
-                               "\"metrics\": ["
-                                       "{\"name\": \"m1\", "
-                                               "\"timeseries\": false, "
-                                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
-                                               "\"update_interval\": \"0s\", \"backends\": []}"
-                               "]}"
+                       "{\"name\": \"m2\", "
+                               "\"timeseries\": false, "
+                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
+                               "\"update_interval\": \"0s\", \"backends\": []},"
+                       "{\"name\": \"m1\", "
+                               "\"timeseries\": false, "
+                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
+                               "\"update_interval\": \"0s\", \"backends\": []}"
                "]" },
        { { NULL, 0, SDB_DATA_INIT },
                SDB_METRIC, scan_tojson,
                "["
                "]" },
        { { NULL, 0, SDB_DATA_INIT },
                SDB_METRIC, scan_tojson,
                "["
-                       "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:01 +0000\", "
-                               "\"update_interval\": \"0s\", \"backends\": [], "
-                               "\"metrics\": ["
-                                       "{\"name\": \"m1\", "
-                                               "\"timeseries\": false, "
-                                               "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
-                                               "\"update_interval\": \"0s\", \"backends\": []},"
-                                       "{\"name\": \"m2\", "
-                                               "\"timeseries\": false, "
-                                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
-                                               "\"update_interval\": \"0s\", \"backends\": []}"
-                               "]},"
-                       "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:03 +0000\", "
-                               "\"update_interval\": \"0s\", \"backends\": [], "
-                               "\"metrics\": ["
-                                       "{\"name\": \"m1\", "
-                                               "\"timeseries\": false, "
-                                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
-                                               "\"update_interval\": \"0s\", \"backends\": []}"
-                               "]}"
+                       "{\"name\": \"m1\", "
+                               "\"timeseries\": false, "
+                               "\"last_update\": \"1970-01-01 00:00:02 +0000\", "
+                               "\"update_interval\": \"0s\", \"backends\": []},"
+                       "{\"name\": \"m2\", "
+                               "\"timeseries\": false, "
+                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
+                               "\"update_interval\": \"0s\", \"backends\": []},"
+                       "{\"name\": \"m1\", "
+                               "\"timeseries\": false, "
+                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
+                               "\"update_interval\": \"0s\", \"backends\": []}"
                "]" },
        { { sdb_store_le_matcher, SDB_FIELD_LAST_UPDATE,
                        { SDB_TYPE_DATETIME, { .datetime = 1 * SDB_INTERVAL_SECOND } } },
                SDB_METRIC, scan_tojson_full,
                "["
                "]" },
        { { sdb_store_le_matcher, SDB_FIELD_LAST_UPDATE,
                        { SDB_TYPE_DATETIME, { .datetime = 1 * SDB_INTERVAL_SECOND } } },
                SDB_METRIC, scan_tojson_full,
                "["
-                       "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:01 +0000\", "
-                               "\"update_interval\": \"0s\", \"backends\": [], "
-                               "\"metrics\": ["
-                                       "{\"name\": \"m2\", "
-                                               "\"timeseries\": false, "
-                                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
-                                               "\"update_interval\": \"0s\", \"backends\": []}"
-                               "]}"
+                       "{\"name\": \"m2\", "
+                               "\"timeseries\": false, "
+                               "\"last_update\": \"1970-01-01 00:00:01 +0000\", "
+                               "\"update_interval\": \"0s\", \"backends\": []}"
                "]" },
        { { sdb_store_lt_matcher, SDB_FIELD_LAST_UPDATE,
                        { SDB_TYPE_DATETIME, { .datetime = 0 } } },
                "]" },
        { { sdb_store_lt_matcher, SDB_FIELD_LAST_UPDATE,
                        { SDB_TYPE_DATETIME, { .datetime = 0 } } },