symisc / unqlite

An Embedded NoSQL, Transactional Database Engine
https://unqlite.symisc.net
Other
2.11k stars 164 forks source link

unqlite_array_fetch numeric string keys not works on JSON array #100

Closed jlab13 closed 4 years ago

jlab13 commented 4 years ago

The documentation states that unqlite_array_fetch function can pass numeric string keys. I try, end that is not works.

void test_array_fetch() {
    const char *srcName = "src";
    const char *dstName = "dst";
    const char *script = "$dst = $src;";

    unqlite *pDb;
    unqlite_vm *pVm;

    unqlite_open(&pDb, ":mem:", UNQLITE_OPEN_IN_MEMORY);
    unqlite_compile(pDb, script, -1, &pVm);

    unqlite_value *pArray = unqlite_vm_new_array(pVm);
    for (int i = 0x1; i < 0x10; i++) {
        unqlite_value *pVal = unqlite_vm_new_scalar(pVm);
        unqlite_value_int(pVal, i);
        unqlite_array_add_elem(pArray, NULL, pVal);
        unqlite_vm_release_value(pVm, pVal);
    }

    unqlite_vm_config(pVm, UNQLITE_VM_CONFIG_CREATE_VAR, srcName, pArray);
    unqlite_vm_release_value(pVm, pArray);
    unqlite_vm_exec(pVm);

    pArray = unqlite_vm_extract_variable(pVm, dstName);
    if (unqlite_value_is_json_array(pArray)) {
        printf("array_count: %d\n", unqlite_array_count(pArray));
    }

    unqlite_value *pVar = unqlite_array_fetch(pArray, "1", -1);
    printf("array_fetch: %p\n", pVar);

    if (pVar) {
        printf("value_is_int: %d\n", unqlite_value_is_int(pVar));
        printf("value_to_int: %d\n", unqlite_value_to_int(pVar));
    } else {
        printf("WARNING unqlite_array_fetch return null\n");
    }

    unqlite_vm_release_value(pVm, pArray);
    unqlite_vm_release(pVm);
    unqlite_close(pDb);
}

Result:

Version: 1.1.9 (1001009)
------------------------
array_count: 15
array_fetch: 0x0
WARNING unqlite_array_fetch return null
symisc commented 4 years ago

This is more a Jx9 issue rather than UnQLite. If the indexed array is fully numeric (i.e. sequence numbers only and does not contains string keys), then you cannot perform hash lookup. Only array traversal via unqlite_array_walk(). A simple workaround is to insert some dummy string key at the end of the iteration and perform your key lookup later.

jlab13 commented 4 years ago

That is, to get access by index, i need get all values use unqlite_array_walk? It is overhead. That how this works in jx9 script?

#define JX9_SCRIPT \
"$arr = $src;\n" \
"$dst = $src[1];\n"

void test_array_fetch() {
    const char *srcName = "src";
    const char *dstName = "dst";
    const char *script = JX9_SCRIPT;

    unqlite *pDb;
    unqlite_vm *pVm;

    unqlite_open(&pDb, ":mem:", UNQLITE_OPEN_IN_MEMORY);
    unqlite_compile(pDb, script, -1, &pVm);

    unqlite_value *pArray = unqlite_vm_new_array(pVm);
    for (int i = 13; i < 15; i++) {
        unqlite_value *pVal = unqlite_vm_new_scalar(pVm);
        unqlite_value_int(pVal, i);
        unqlite_array_add_elem(pArray, NULL, pVal);
        unqlite_vm_release_value(pVm, pVal);
    }
    unqlite_vm_config(pVm, UNQLITE_VM_CONFIG_CREATE_VAR, srcName, pArray);
    unqlite_vm_release_value(pVm, pArray);

    unqlite_vm_exec(pVm);

    unqlite_value *pVal = unqlite_vm_extract_variable(pVm, dstName);
    if (pVal && unqlite_value_is_int(pVal)) {
        printf("%s: %d\n", dstName, unqlite_value_to_int(pVal));
    } else {
        printf("WARNING %s value is not int\n", dstName);
    }
    unqlite_vm_release_value(pVm, pVal);

    unqlite_vm_release(pVm);
    unqlite_close(pDb);
}

Result:

Version: 1.1.9 (1001009)
------------------------
dst: 14
symisc commented 4 years ago

When exiting the insertion loop (for (int i = 0x1; i < 0x10; i++) {}), insert some dummy sentinel string key (do not pass null). After that, you can perform an index lookup instead (in your C/C++ code since Jx9 support indexing without issue) of array traversal.

jlab13 commented 4 years ago

Add redundant element to array? Or do not pass NULL in body of loop? In any case, what if the array is initialized in a Jx9 script?

void test_array_fetch() {
    const char *script = "$array = [1,2,3,4,5];";
    unqlite *pDb;
    unqlite_vm *pVm;

    unqlite_open(&pDb, ":mem:", UNQLITE_OPEN_IN_MEMORY);
    unqlite_compile(pDb, script, -1, &pVm);
    unqlite_vm_exec(pVm);

    unqlite_value *pArray = unqlite_vm_extract_variable(pVm, "array");
    if (unqlite_value_is_json_array(pArray)) {
        printf("array_count: %d\n", unqlite_array_count(pArray));
    }

    unqlite_value *pVar = unqlite_array_fetch(pArray, "3", -1);
    printf("array_fetch: %p\n", pVar);

    if (pVar) {
        printf("value_is_int: %d\n", unqlite_value_is_int(pVar));
        printf("value_to_int: %d\n", unqlite_value_to_int(pVar));
    } else {
        printf("WARNING unqlite_array_fetch return null\n");
    }

    unqlite_vm_release_value(pVm, pArray);
    unqlite_vm_release(pVm);
    unqlite_close(pDb);
}

Result:

Version: 1.1.9 (1001009)
------------------------
array_count: 5
array_fetch: 0x0
WARNING unqlite_array_fetch return null

Maybe it is possible to add a function to library for access array item by index?

symisc commented 4 years ago

Simply add an entry to the array in question from you C code with a dummy string key. The issue could easily be resolved if you populate the array from your Jx9 code instead of C.

jlab13 commented 4 years ago

Sorry, I didn't quite understand. Can you give an example please?

symisc commented 4 years ago

Just add a dummy string entry via unqlite_array_add_strkey_elem() when you exit the loop as follow:

unqlite_array_add_strkey_elem(pArray, "dummy", pVal);

Or create and populate the array from Jx9 first and extract the data later from C.