Skip to content

Commit

Permalink
v.db.select: Add column names and types to JSON (OSGeo#3090)
Browse files Browse the repository at this point in the history
The JSON output now has column names and types under info/columns.

The example in documentation does not include the unclear note about booleans (a discussion for another place).

A command to reproduce the example output is now included.

Documentation and test of the new functionality are included.
  • Loading branch information
wenzeslaus authored and landam committed Oct 25, 2023
1 parent a338233 commit a126b0c
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 11 deletions.
28 changes: 26 additions & 2 deletions vector/v.db.select/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,32 @@ int main(int argc, char **argv)
if (format == JSON) {
if (flags.region->answer)
fprintf(stdout, "{\"extent\":\n");
else
fprintf(stdout, "{\"records\":[\n");
else {
fprintf(stdout, "{\"info\":\n{\"columns\":[\n");
for (col = 0; col < ncols; col++) {
column = db_get_table_column(table, col);
if (col)
fprintf(stdout, "},\n");
fprintf(stdout, "{\"name\":\"%s\",",
db_get_column_name(column));
int sql_type = db_get_column_sqltype(column);
fprintf(stdout, "\"sql_type\":\"%s\",",
db_sqltype_name(sql_type));

int c_type = db_sqltype_to_Ctype(sql_type);
fprintf(stdout, "\"is_number\":");
/* Same rules as for quoting, i.e., number only as
* JSON or Python would see it and not numeric which may
* include, e.g., date. */
if (c_type == DB_C_TYPE_INT || c_type == DB_C_TYPE_DOUBLE)
fprintf(stdout, "true");
else
fprintf(stdout, "false");
}

fprintf(stdout, "}\n]},\n");
fprintf(stdout, "\"records\":[\n");
}
}

/* fetch the data */
Expand Down
14 changes: 13 additions & 1 deletion vector/v.db.select/testsuite/test_v_db_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,19 @@
1290,63600420,109186.835938,1291,1290,Zwe,63600422.4739,109186.832069
"""

out_json = """{"records":[
out_json = """\
{"info":
{"columns":[
{"name":"cat","sql_type":"INTEGER","is_number":true},
{"name":"onemap_pro","sql_type":"DOUBLE PRECISION","is_number":true},
{"name":"PERIMETER","sql_type":"DOUBLE PRECISION","is_number":true},
{"name":"GEOL250_","sql_type":"INTEGER","is_number":true},
{"name":"GEOL250_ID","sql_type":"INTEGER","is_number":true},
{"name":"GEO_NAME","sql_type":"CHARACTER","is_number":false},
{"name":"SHAPE_area","sql_type":"DOUBLE PRECISION","is_number":true},
{"name":"SHAPE_len","sql_type":"DOUBLE PRECISION","is_number":true}
]},
"records":[
{"cat":1,"onemap_pro":963738.75,"PERIMETER":4083.97998,"GEOL250_":2,"GEOL250_ID":1,"GEO_NAME":"Zml","SHAPE_area":963738.608571,"SHAPE_len":4083.979839},
{"cat":2,"onemap_pro":22189124,"PERIMETER":26628.261719,"GEOL250_":3,"GEOL250_ID":2,"GEO_NAME":"Zmf","SHAPE_area":22189123.2296,"SHAPE_len":26628.261112},
{"cat":3,"onemap_pro":579286.875,"PERIMETER":3335.55835,"GEOL250_":4,"GEOL250_ID":3,"GEO_NAME":"Zml","SHAPE_area":579286.829631,"SHAPE_len":3335.557182},
Expand Down
17 changes: 16 additions & 1 deletion vector/v.db.select/testsuite/test_v_db_select_json_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# MODULE: Test of v.db.select
# AUTHOR(S): Vaclav Petras <wenzeslaus gmail com>
# PURPOSE: Test parsing and structure of CSV and JSON outputs
# COPYRIGHT: (C) 2021 by Vaclav Petras the GRASS Development Team
# COPYRIGHT: (C) 2021-2023 by Vaclav Petras the GRASS Development Team
#
# This program is free software under the GNU General Public
# License (>=v2). Read the file COPYING that comes with GRASS
Expand Down Expand Up @@ -173,6 +173,21 @@ def test_json_loads(self):
"""Load JSON with difficult values"""
text = gs.read_command("v.db.select", map=self.vector_points, format="json")
data = json.loads(text)

column_info = data["info"]["columns"]
self.assertEqual(column_info[0]["name"], "cat")
self.assertEqual(column_info[0]["sql_type"], "INTEGER")
self.assertEqual(column_info[0]["is_number"], True)
self.assertEqual(column_info[1]["name"], "x")
self.assertEqual(column_info[1]["sql_type"], "DOUBLE PRECISION")
self.assertEqual(column_info[1]["is_number"], True)
self.assertEqual(column_info[4]["name"], "owner_id")
self.assertEqual(column_info[4]["sql_type"], "INTEGER")
self.assertEqual(column_info[4]["is_number"], True)
self.assertEqual(column_info[5]["name"], "place_name")
self.assertEqual(column_info[5]["sql_type"], "TEXT")
self.assertEqual(column_info[5]["is_number"], False)

data = data["records"]
self.assertIsNone(data[2]["place_name"])
self.assertEqual(data[3]["place_name"], 'The "Great" Place')
Expand Down
46 changes: 39 additions & 7 deletions vector/v.db.select/v.db.select.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,24 +105,56 @@ <h4>JSON</h4>
the columns were defined in the database.

<p>
Example with added indentation (note that booleans are not directly supported;
here, an attribute is a string with value <tt>no</tt>):
The JSON also contains information about columns stored under key
<code>info</code>. Column names and types are under key <code>columns</code>.
Each colum has SQL data type under <code>sql_type</code> in all caps.
A boolean <code>is_number</code> specifies whether the value is a number, i.e.,
integer or floating point number. The <code>is_number</code> value
is aded for convenience and it is recommended to rely on the types derived
from the JSON representation or the SQL types. The definition of
<code>is_number</code> may change in the future.

<p>
Example with added indentation:

<!--
Generated using:
v.db.select roadsmajor format=json where="cat in (1, 2)" \
columns="ROAD_NAME AS road_name, PROPYEAR + 2001 AS year, SHAPE_LEN as length" \
| jq
-->

<div class="code"><pre>
{
"info": {
"columns": [
{
"name": "road_name",
"sql_type": "CHARACTER",
"is_number": false
},
{
"name": "year",
"sql_type": "INTEGER",
"is_number": true
},
{
"name": "length",
"sql_type": "DOUBLE PRECISION",
"is_number": true
}
]
},
"records": [
{
"cat": 1,
"road_name": "NC-50",
"multilane": "no",
"year": 2001,
"length": 4825.369405
},
{
"cat": 2,
"road_name": "NC-50",
"multilane": "no",
"year": 2002,
"year": 2001,
"length": 14392.589058
}
]
Expand Down

0 comments on commit a126b0c

Please sign in to comment.