Espresso++ is a natural query language that abstracts away the native query language of any data management system.
This distro consists of both a library to be used by any client application that needs to support filtering and a command-line utility to help developers debug filters written in the Espresso++ language.
Further information about Espresso++ is available in the following documents:
The Makefile
that comes with this project supports the following targets:
all
: Runs in sequencecheck
,test
, andinstall
get-tools
: Retrieves and installsgoimports
init
: Createsgo.mod
if missingbuild
: Builds theespressopp
utility in theout
sub-directoryclean
: Cleans the last buildtest
: Runs unit testsinstall
: Installs theespressopp
utility into$GOPATH/bin
uninstall
: Uninstalls theespressopp
utility from$GOPATH/bin
docs
: Runs in sequencedocs-html
anddocs-pdf
docs-html
: Generates HTML documentation in theout/html
sub-directorydocs-pdf
: Generates PDF documentation in theout/pdf
sub-directoryfmt
: Formats source code according to the Go best practicessimplify
: Simplifies source code according to the Go best practicescheck
: Checks whether source code is well formatted
The Espesso++ library gets compiled together with the client application, therefore
this build procedure only applies to espressopp
, a command-line utility that
takes an Espresso++ expression as an input and returns the resulting native query.
To build and install espressopp
into $GOPATH/bin
, issue the following command
– ensure you added GOPATH/bin
to your PATH
:
$ make install
Finally, to generate the HTML and PDF documentation in the out/docs
sub-directory,
issue the following command – ensure asciidoctor
, asciidoctor-pdf
, and
asciidoctor-diagrams
are installed on your system:
$ make docs
To generate HTML only:
$ make docs-html
To generate PDF only:
$ make docs-pdf
Currently Espresso++ suports just SQL, but the way a filter is create for data management systems with other query languages does not change. Below is an example of how to get an Espresso++ expression translated into SQL:
package main
import (
"bytes"
"fmt"
"strings"
"gitlab.com/skeeterhealth/espressopp"
)
func main() {
ageProps := &espressopp.FieldProps{
Filterable: true, // whether "age" can be queried
NativeName: "min_age", // actual column name
}
r := strings.NewReader("age gte 30")
w := new(bytes.Buffer)
interpreter := espressopp.NewEspressoppInterpreter()
codeGenerator := espressopp.NewSqlCodeGenerator()
codeGenerator.RenderingOptions.AddFieldProps("age", ageProps)
codeGenerator.RenderingOptions.EnableNamedParams()
err := interpreter.Accept(codeGenerator, r, w)
if err != nil {
msg := fmt.Errorf("Error generating sql: %v", err)
fmt.Println(msg)
} else {
// generated query contains named parameters instead of actual values
fmt.Println(w.String())
// list named parameter values associated with the generated query
namedParamValues, _ := codeGenerator.RenderingOptions.GetNamedParamValues()
for k, v := range namedParamValues {
fmt.Printf("%s: %s\n", k, v)
}
}
}
If Espresso++ supported MongoDB, the client code would be something like this:
package main
import (
"bytes"
"fmt"
"strings"
"gitlab.com/skeeterhealth/espressopp"
)
func main() {
ageProps := &espressopp.FieldProps{
Filterable: true, // whether "age" can be queried
NativeName: "min_age", // actual column name
}
r := strings.NewReader("age gte 30")
w := new(bytes.Buffer)
interpreter := espressopp.NewEspressoppInterpreter()
codeGenerator := espressopp.NewMongoCodeGenerator()
codeGenerator.RenderingOptions.AddFieldProps("age", ageProps)
err := interpreter.Accept(codeGenerator, r, w)
if err != nil {
msg := fmt.Errorf("Error generating mongo: %v", err)
fmt.Println(msg)
} else {
fmt.Println(w.String())
}
}
Last but not least, developers can debug their Espresso++ expressions with the
espressopp
command-line utility:
$ espressopp --help
Usage: espressopp <command>
A utility that converts input Espresso++ expressions into native queries.
Flags:
--help Show context-sensitive help.
Commands:
generate Generate target native query.
Run "espressopp <command> --help" for more information on a command.
The generate
command gets the target language and an Espresso++ expression as
the input:
$ espressopp generate --help
Usage: espressopp generate <target> <expression> [<fieldmap> ...]
Generate target native query.
Arguments:
<target> Target query language.
<expression> Source expression.
[<fieldmap> ...] Mapping to native column names.
Flags:
--help Show context-sensitive help.
-e, --enable-named-params Enable named parameters.
For example, let's translate the Espresso++ expression age gte 30 and weight lt 80
into SQL:
$ espressopp generate sql "age gte 30 and weight lt 80"
age >= 30 AND weight < 80
And let's suppose the native column names differ from the field names in the Espresso++ expression:
$ espressopp generate sql "age gte 30 and weight lt 80" age=min_age weight=body_weight
min_age >= 30 AND body_weight < 80
Literals in the generated query can be replaced with named parameters to improve security:
$ espressopp generate sql -e "age gte 30 and weight lt 80" age=min_age weight=body_weight
min_age >= :P1 AND body_weigh < :P2
Named Parameters
================
P1: 30
P2: 80
Finally, the same Espresso++ expression translated into MongoDB query language:
$ espressopp generate mongo "age gte 30 and weight lt 80"
{ age: { $gte: 30 }, weight: { $lt: 80 } }
Copyright 2020 Skeeter Health