Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions script.go
Original file line number Diff line number Diff line change
Expand Up @@ -712,31 +712,34 @@ func (p *Pipe) Join() *Pipe {
})
}

// JQ executes query on the pipe's contents (presumed to be newline-delimited
// JSON), applying the query to each input value and producing the results. An
// invalid query or value will set the appropriate error on the pipe.

// JQ executes query on the pipe's contents (presumed to be valid JSON or
// [JSONLines] data), applying the query to each newline-delimited input value
// and producing results until the first error is encountered. An invalid query
// or value will set the appropriate error on the pipe.
//
// The exact dialect of JQ supported is that provided by
// [github.com/itchyny/gojq], whose documentation explains the differences
// between it and standard JQ.
//
// [JSONLines]: https://jsonlines.org/
func (p *Pipe) JQ(query string) *Pipe {
parsedQuery, err := gojq.Parse(query)
if err != nil {
return p.WithError(err)
}
code, err := gojq.Compile(parsedQuery)
if err != nil {
return p.WithError(err)
}
return p.Filter(func(r io.Reader, w io.Writer) error {
q, err := gojq.Parse(query)
if err != nil {
return err
}
c, err := gojq.Compile(q)
if err != nil {
return err
}
dec := json.NewDecoder(r)
for dec.More() {
var input interface{}
var input any
err := dec.Decode(&input)
if err != nil {
return err
}
iter := c.Run(input)
iter := code.Run(input)
for {
v, ok := iter.Next()
if !ok {
Expand Down
46 changes: 9 additions & 37 deletions script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -804,23 +804,20 @@ func TestJQHandlesGithubJSONWithRealWorldExampleQuery(t *testing.T) {
}
}

func TestJQWithNewlineDelimitedInputAndFieldQueryProducesSelectedFields(t *testing.T) {
func TestJQCorrectlyQueriesMultilineInputFields(t *testing.T) {
t.Parallel()
input := `{"timestamp": 1649264191, "iss_position": {"longitude": "52.8439", "latitude": "10.8107"}, "message": "success"}` + "\n"
input += input
want := `{"latitude":"10.8107","longitude":"52.8439"}` + "\n"
want += want
got, err := script.Echo(input).JQ(".iss_position").String()
input := `{"a":1}` + "\n" + `{"a":2}`
want := "1\n2\n"
got, err := script.Echo(input).JQ(".a").String()
if err != nil {
t.Fatal(err)
}
if want != got {
t.Error(want, got)
t.Error(cmp.Diff(want, got))
}
}

func TestJQWithNewlineDelimitedInputAndArrayInputAndElementQueryProducesSelectedElements(t *testing.T) {
func TestJQCorrectlyQueriesMultilineInputArrays(t *testing.T) {
t.Parallel()
input := `[1, 2, 3]` + "\n" + `[4, 5, 6]`
want := "1\n4\n"
Expand All @@ -829,30 +826,6 @@ func TestJQWithNewlineDelimitedInputAndArrayInputAndElementQueryProducesSelected
t.Fatal(err)
}
if want != got {
t.Error(want, got)
t.Error(cmp.Diff(want, got))
}
}

func TestJQWithNewlineDelimitedMixedAndPrettyPrintedInputValues(t *testing.T) {
t.Parallel()
input := `
{
"key1": "val1",
"key2": "val2"
}
[
0,
1
]
`
want := `{"key1":"val1","key2":"val2"}` + "\n" + "[0,1]" + "\n"
got, err := script.Echo(input).JQ(".").String()
if err != nil {
t.Fatal(err)
}
if want != got {
t.Error(want, got)
t.Error(cmp.Diff(want, got))
}
}
Expand All @@ -875,16 +848,15 @@ func TestJQErrorsWithInvalidInput(t *testing.T) {
}
}

func TestJQWithNewlineDelimitedInputErrorsAfterFirstInvalidInput(t *testing.T) {
func TestJQProducesValidResultsUntilFirstError(t *testing.T) {
t.Parallel()
input := `[0]` + "\n" + `[1` + "\n" + `[2]` // missing `]` in second line
want := "0\n"
input := "[1]\ninvalid JSON value"
want := "1\n"
got, err := script.Echo(input).JQ(".[0]").String()
if err == nil {
t.Fatal("want error from invalid JSON, got nil")
t.Fatal("want error from invalid JSON input, got nil")
}
if want != got {
t.Error(want, got)
t.Error(cmp.Diff(want, got))
}
}
Expand Down
Loading