maxmind / libmaxminddb

C library for the MaxMind DB file format
https://maxmind.github.io/libmaxminddb/
Apache License 2.0
912 stars 239 forks source link

Iterating over map or array returned with MMDB_get_value() #255

Open gonzus opened 3 years ago

gonzus commented 3 years ago

I am working with the example data from the library's documentation:

{
    "names": {
        "en": "Germany",
        "de": "Deutschland"
    },
    "cities": [ "Berlin", "Frankfurt" ]
}

Say I call

MMDB_get_value(&result.entry, &entry_data, "names", NULL);

This will produce entry_data.type == 7, which is a MAP, as expected. How can I then retrieve the values for specific keys in this map? Because entry_data is a MMDB_entry_data_s and NOT a MMDB_entry_s, I cannot pass it into MMDB_get_value.

Same question would apply for an array:

MMDB_get_value(&result.entry, &entry_data, "cities", NULL);

This will produce with entry_data.type == 11, which is an ARRAY, as expected. How can I then retrieve the values for specific positions in this array?

oschwald commented 3 years ago

To iterate over collection, you will need to use the offset_to_next field.

See this example for maps:

https://github.com/maxmind/libmaxminddb/blob/ed7a4252c8a2f19885479fe35ea9e8a12106a1ec/src/maxminddb.c#L1255-L1283

And for arrays:

https://github.com/maxmind/libmaxminddb/blob/ed7a4252c8a2f19885479fe35ea9e8a12106a1ec/src/maxminddb.c#L1231-L1239

gonzus commented 3 years ago

Thanks for the quick reply @oschwald. Is there a way to directly retrieve, without iteration, the value for a given key in a map, or the value for a given position in an array?

Also, there seems to be no public API to do any of this; the code you pointed to depends on a couple of macros and static functions, which I would have to replicate in my code. Is this correct? If so, are there any plans to expose such APIs as part of the stable public interface of the library? I would love to have the equivalent of at least map_retrieve(key), map_get_keys(), array_get_size(), array_fetch(position).

oschwald commented 3 years ago

You can look up particular map values or array index values directly with MMDB_get_values(), e.g., from your example above:

MMDB_get_value(&result.entry, &entry_data, "names", "en", NULL);

This would look up the value for the en key of the names map.

Similarly, the following would look up the first city:

MMDB_get_value(&result.entry, &entry_data, "cities", "0", NULL);
gonzus commented 3 years ago

Yes, this is how I am doing it today. But if I know before hand I will access several keys inside a map, I think it would be more efficient to first get the map entry, and then get the value for each key, instead of repeatedly searching for "names" / "en", "names" / "es", "names" / "de", etc.

oschwald commented 3 years ago

I see. I don't think we expose any functions to do this directly, although you likely could just create a new MMDB_entry_s with the offset to the map and then use MMDB_get_value on that.

gonzus commented 3 years ago

Ok, thanks for all the hints. I still think it would be nice to have an official API for this kind of access, but I understand if this is out of the scope for the library -- feel free to close the issue if this is not in the plans.