Skip to content

Commit 91ae40f

Browse files
committed
Add markdown table format for Cypher queries
1 parent b8cb809 commit 91ae40f

7 files changed

+237
-8
lines changed

scripts/executeQuery.sh

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,18 @@
1414
# -> "--no_source_reference" to not append the cypher query file name as last CSV column
1515
# -> any following key=value arguments are used as query parameters
1616

17+
# Requires markdown/formatQueryResultAsMarkdownTable.sh
18+
1719
# Fail on any error ("-e" = exit on first error, "-o pipefail" exist on errors within piped commands)
1820
set -o errexit -o pipefail
1921

22+
## Get this "scripts" directory if not already set
23+
# Even if $BASH_SOURCE is made for Bourne-like shells it is also supported by others and therefore here the preferred solution.
24+
# CDPATH reduces the scope of the cd command to potentially prevent unintended directory changes.
25+
# This way non-standard tools like readlink aren't needed.
26+
SCRIPTS_DIR=${SCRIPTS_DIR:-$( CDPATH=. cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P )} # Repository directory containing the shell scripts
27+
#echo "executeQuery: SCRIPTS_DIR=$SCRIPTS_DIR" >&2
28+
2029
# Overrideable Defaults
2130
NEO4J_HTTP_PORT=${NEO4J_HTTP_PORT:-"7474"} # Neo4j HTTP API port for executing queries
2231
NEO4J_HTTP_TRANSACTION_ENDPOINT=${NEO4J_HTTP_TRANSACTION_ENDPOINT:-"db/neo4j/tx/commit"} # Since Neo4j v5: "db/<name>/tx/commit", Neo4j v4: "db/data/transaction/commit"
@@ -35,20 +44,21 @@ fi
3544
cypher_query_file_name=""
3645
no_source_reference=false
3746
omit_query_error_highlighting=false
47+
output_markdown_table=false
3848
query_parameters=""
3949

4050
# Input Arguments: Function to print usage information
4151
print_usage() {
42-
echo "executeQuery Usage: $0 <filename> [--no-source-reference-column] [--omit-query-error-highlighting]" >&2
52+
echo "executeQuery Usage: $0 <filename> [--no-source-reference-column] [--omit-query-error-highlighting] [--output-markdown-table]" >&2
4353
echo "Options:" >&2
4454
echo " --no-source-reference-column: Exclude the source reference column" >&2
4555
echo " --omit-query-error-highlighting: Log query errors in same color as infos" >&2
56+
echo " --output-markdown-table: Output the result as markdown table instead of CSV" >&2
4657
}
4758

4859
# Input Arguments: Parse the command-line arguments
4960
while [[ $# -gt 0 ]]; do
5061
arg="$1"
51-
5262
case $arg in
5363
--no-source-reference-column)
5464
no_source_reference=true
@@ -58,6 +68,10 @@ while [[ $# -gt 0 ]]; do
5868
omit_query_error_highlighting=true
5969
shift
6070
;;
71+
--output-markdown-table)
72+
output_markdown_table=true
73+
shift
74+
;;
6175
*)
6276
if [[ -z "${cypher_query_file_name}" ]]; then
6377
# Input Arguments: Read the first unnamed input argument containing the name of the cypher file
@@ -134,11 +148,16 @@ if [[ -n "${error_message}" ]]; then
134148
exit 1
135149
fi
136150

137-
# Output results in CSV format
138-
if [ "${no_source_reference}" = true ] ; then
139-
echo -n "${cypher_query_result}" | jq -r '(.results[0])? | .columns,(.data[].row)? | map(if type == "array" then join(",") else . end) | flatten | @csv'
151+
if [ "${output_markdown_table}" = "true" ] ; then
152+
echo "executeQuery: Will output in Markdown Table Format" >&2
153+
echo -n "${cypher_query_result}" | "${SCRIPTS_DIR}/markdown/formatQueryResultAsMarkdownTable.sh"
140154
else
141-
cypher_query_file_relative_name=${cypher_query_file_name#/**/cypher/}
142-
sourceFileReferenceInfo="Source Cypher File: ${cypher_query_file_relative_name}"
143-
echo -n "${cypher_query_result}" | jq -r --arg sourceReference "${sourceFileReferenceInfo}" '(.results[0])? | .columns + [$sourceReference], (.data[].row)? + [""] | map(if type == "array" then join(",") else . end) | flatten | @csv'
155+
# Output results in CSV format
156+
if [ "${no_source_reference}" = true ] ; then
157+
echo -n "${cypher_query_result}" | jq -r '(.results[0])? | .columns,(.data[].row)? | map(if type == "array" then join(",") else . end) | flatten | @csv'
158+
else
159+
cypher_query_file_relative_name=${cypher_query_file_name#/**/cypher/}
160+
sourceFileReferenceInfo="Source Cypher File: ${cypher_query_file_relative_name}"
161+
echo -n "${cypher_query_result}" | jq -r --arg sourceReference "${sourceFileReferenceInfo}" '(.results[0])? | .columns + [$sourceReference], (.data[].row)? + [""] | map(if type == "array" then join(",") else . end) | flatten | @csv'
162+
fi
144163
fi
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env bash
2+
3+
# Takes the input stream (Cypher query result in JSON format) and formats it as a Markdown table.
4+
5+
# Fail on any error ("-e" = exit on first error, "-o pipefail" exist on errors within piped commands)
6+
set -o errexit -o pipefail
7+
8+
## Get this "scripts" directory if not already set
9+
# Even if $BASH_SOURCE is made for Bourne-like shells it is also supported by others and therefore here the preferred solution.
10+
# CDPATH reduces the scope of the cd command to potentially prevent unintended directory changes.
11+
# This way non-standard tools like readlink aren't needed.
12+
MARKDOWN_SCRIPTS_DIR=${MARKDOWN_SCRIPTS_DIR:-$( CDPATH=. cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P )} # Repository directory containing the shell scripts
13+
#echo "formatQueryResultAsMarkdownTable: MARKDOWN_SCRIPTS_DIR=${MARKDOWN_SCRIPTS_DIR}" >&2
14+
15+
echo "formatQueryResultAsMarkdownTable: Will output in Markdown Table Format" >&2
16+
17+
# Read all input (including multiline) into cypher_query_result
18+
cypher_query_result=$(cat)
19+
20+
echo -n "${cypher_query_result}" | jq -r '
21+
# Take the first query result
22+
.results[0] as $result
23+
24+
# Extract the column names
25+
| $result.columns as $columns
26+
27+
# Build the Markdown header row
28+
| ( "| " + ( $columns | join(" | ") ) + " |" )
29+
30+
# Build the Markdown separator row
31+
, ( "| " + ( $columns | map("---") | join(" | ") ) + " |" )
32+
33+
# Build one row for each data entry
34+
, ( $result.data[].row
35+
| map(tostring)
36+
| "| " + ( join(" | ") ) + " |"
37+
)
38+
'
39+
40+
#echo "formatQueryResultAsMarkdownTable: $(date +'%Y-%m-%dT%H:%M:%S%z') Successfully finished." >&2
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/env bash
2+
3+
# Tests formatting of Cypher query results as Markdown table.
4+
5+
# Requires formatQueryResultAsMarkdownTable.sh
6+
7+
# Fail on any error ("-e" = exit on first error, "-o pipefail" exist on errors within piped commands)
8+
set -o errexit -o pipefail
9+
10+
## Get this "scripts" directory if not already set
11+
# Even if $BASH_SOURCE is made for Bourne-like shells it is also supported by others and therefore here the preferred solution.
12+
# CDPATH reduces the scope of the cd command to potentially prevent unintended directory changes.
13+
# This way non-standard tools like readlink aren't needed.
14+
MARKDOWN_SCRIPTS_DIR=${MARKDOWN_SCRIPTS_DIR:-$( CDPATH=. cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P )} # Repository directory containing the shell scripts
15+
#echo "testFormatQueryResultAsMarkdownTable: MARKDOWN_SCRIPTS_DIR=${MARKDOWN_SCRIPTS_DIR}" >&2
16+
17+
tearDown() {
18+
# echo "testFormatQueryResultAsMarkdownTable: Tear down tests...."
19+
rm -rf "${temporaryTestDirectory}"
20+
}
21+
22+
successful() {
23+
local COLOR_SUCCESSFUL="\033[0;32m" # green
24+
local COLOR_DEFAULT='\033[0m'
25+
26+
echo -e "testFormatQueryResultAsMarkdownTable: ${COLOR_SUCCESSFUL}Tests finished successfully.${COLOR_DEFAULT}"
27+
28+
tearDown
29+
}
30+
31+
fail() {
32+
local COLOR_ERROR='\033[0;31m' # red
33+
local COLOR_DEFAULT='\033[0m'
34+
35+
local errorMessage="${1}"
36+
37+
echo -e "testFormatQueryResultAsMarkdownTable: ${COLOR_ERROR}${errorMessage}${COLOR_DEFAULT}"
38+
tearDown
39+
return 1
40+
}
41+
42+
echo "testFormatQueryResultAsMarkdownTable: Starting tests...."
43+
44+
# Create testing resources
45+
temporaryTestDirectory=$(mktemp -d 2>/dev/null || mktemp -d -t 'temporaryTestDirectory')
46+
47+
# ------------------------------------------------------------
48+
# Test case --
49+
# ------------------------------------------------------------
50+
echo "testFormatQueryResultAsMarkdownTable: 1.) Convert a simple query result to a Markdown table."
51+
52+
# Read expected result from test_data_cypher_query_result_simple_expected
53+
expected_result=$(<"${MARKDOWN_SCRIPTS_DIR}/test_data_cypher_query_result_simple_expected.md")
54+
55+
# - Execute script under test
56+
embeddedContent=$(cd "${temporaryTestDirectory}"; cat "${MARKDOWN_SCRIPTS_DIR}/test_data_cypher_query_result_simple.json" | "${MARKDOWN_SCRIPTS_DIR}/formatQueryResultAsMarkdownTable.sh")
57+
58+
# - Verify results
59+
if [ "${embeddedContent}" != "${expected_result}" ]; then
60+
fail "1.) Test failed: Expected Markdown table to be \n${expected_result}, but got:\n${embeddedContent}"
61+
fi
62+
63+
64+
echo "testFormatQueryResultAsMarkdownTable: 2.) Convert an array query result to a Markdown table."
65+
66+
# Read expected result from test_data_cypher_query_result_simple_expected
67+
expected_result=$(<"${MARKDOWN_SCRIPTS_DIR}/test_data_cypher_query_result_with_array_expected.md")
68+
69+
# - Execute script under test
70+
embeddedContent=$(cd "${temporaryTestDirectory}"; cat "${MARKDOWN_SCRIPTS_DIR}/test_data_cypher_query_result_with_array.json" | "${MARKDOWN_SCRIPTS_DIR}/formatQueryResultAsMarkdownTable.sh")
71+
72+
# - Verify results
73+
if [ "${embeddedContent}" != "${expected_result}" ]; then
74+
fail "2.) Test failed: Expected Markdown table to be \n${expected_result}, but got:\n${embeddedContent}"
75+
fi
76+
77+
successful
78+
return 0
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"results": [
3+
{
4+
"columns": [
5+
"nodeLabel",
6+
"nodesWithThatLabel",
7+
"nodesWithThatLabelPercent"
8+
],
9+
"data": [
10+
{
11+
"row": [
12+
"Git",
13+
234151,
14+
77.73524646765112
15+
]
16+
},
17+
{
18+
"row": [
19+
"Change",
20+
213807,
21+
70.98128917454584
22+
]
23+
},
24+
{
25+
"row": [
26+
"Update",
27+
138781,
28+
46.073581748645495
29+
]
30+
}
31+
]
32+
}
33+
],
34+
"errors": []
35+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| nodeLabel | nodesWithThatLabel | nodesWithThatLabelPercent |
2+
| --- | --- | --- |
3+
| Git | 234151 | 77.73524646765112 |
4+
| Change | 213807 | 70.98128917454584 |
5+
| Update | 138781 | 46.073581748645495 |
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"results": [
3+
{
4+
"columns": [
5+
"nodeLabel",
6+
"nodesWithThatLabel",
7+
"nodesWithThatLabelPercent",
8+
"keys"
9+
],
10+
"data": [
11+
{
12+
"row": [
13+
"Git",
14+
234151,
15+
77.73524646765112,
16+
[
17+
"name",
18+
"fileName",
19+
"modificationKind"
20+
]
21+
]
22+
},
23+
{
24+
"row": [
25+
"Change",
26+
213807,
27+
70.98128917454584,
28+
[
29+
"modificationKind"
30+
]
31+
]
32+
},
33+
{
34+
"row": [
35+
"Update",
36+
138781,
37+
46.073581748645495,
38+
[
39+
"modificationKind"
40+
]
41+
]
42+
}
43+
]
44+
}
45+
],
46+
"errors": []
47+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| nodeLabel | nodesWithThatLabel | nodesWithThatLabelPercent | keys |
2+
| --- | --- | --- | --- |
3+
| Git | 234151 | 77.73524646765112 | ["name","fileName","modificationKind"] |
4+
| Change | 213807 | 70.98128917454584 | ["modificationKind"] |
5+
| Update | 138781 | 46.073581748645495 | ["modificationKind"] |

0 commit comments

Comments
 (0)