tpl: Add intersect operator to where function

Returns true if a given field value that is a slice / array of strings, integers or floats contains elements in common with the matching value. It follows the same rules as the intersect function.

Closes #1945
This commit is contained in:
Christopher Mancini 2016-04-11 16:58:27 -04:00 committed by Bjørn Erik Pedersen
parent ffbd2b6c67
commit 09038865c2
3 changed files with 112 additions and 11 deletions

View file

@ -79,7 +79,6 @@ e.g. Pass into "foo.html" a map with the keys "important, content"
Important {{.important}}
{{.content}}
or create a map on the fly to pass into
{{partial "foo" (dict "important" "Smiles" "content" "You should do more")}}
@ -313,6 +312,15 @@ Following operators are now available
- `<`, `lt`: True if a given field value is lesser than a matching value
- `in`: True if a given field value is included in a matching value. A matching value must be an array or a slice
- `not in`: True if a given field value isn't included in a matching value. A matching value must be an array or a slice
- `intersect`: True if a given field value that is a slice / array of strings or integers contains elements in common with the matching value. It follows the same rules as the intersect function.
*`intersect` operator, e.g.:*
{{ range where .Site.Pages ".Params.tags" "intersect" .Params.tags }}
{{ if ne .Permalink $.Permalink }}
{{ .Render "summary" }}
{{ end }}
{{ end }}
*`where` and `first` can be stacked, e.g.:*

View file

@ -646,6 +646,7 @@ func checkCondition(v, mv reflect.Value, op string) (bool, error) {
var ivp, imvp *int64
var svp, smvp *string
var slv, slmv interface{}
var ima []int64
var sma []string
if mv.Type() == v.Type() {
@ -668,6 +669,9 @@ func checkCondition(v, mv reflect.Value, op string) (bool, error) {
imv := toTimeUnix(mv)
imvp = &imv
}
case reflect.Array, reflect.Slice:
slv = v.Interface()
slmv = mv.Interface()
}
} else {
if mv.Kind() != reflect.Array && mv.Kind() != reflect.Slice {
@ -765,8 +769,24 @@ func checkCondition(v, mv reflect.Value, op string) (bool, error) {
return !r, nil
}
return r, nil
case "intersect":
r, err := intersect(slv, slmv)
if err != nil {
return false, err
}
if reflect.TypeOf(r).Kind() == reflect.Slice {
s := reflect.ValueOf(r)
if s.Len() > 0 {
return true, nil
}
return false, nil
} else {
return false, errors.New("invalid intersect values")
}
default:
return false, errors.New("no such an operator")
return false, errors.New("no such operator")
}
return false, nil
}

View file

@ -18,11 +18,6 @@ import (
"encoding/base64"
"errors"
"fmt"
"github.com/spf13/afero"
"github.com/spf13/cast"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"html/template"
"math/rand"
"path"
@ -32,6 +27,12 @@ import (
"strings"
"testing"
"time"
"github.com/spf13/afero"
"github.com/spf13/cast"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)
type tstNoStringer struct {
@ -1202,6 +1203,78 @@ func TestWhere(t *testing.T) {
{"a": 3, "b": 4},
},
},
{
sequence: []map[string][]string{
{"a": []string{"A", "B", "C"}, "b": []string{"D", "E", "F"}}, {"a": []string{"G", "H", "I"}, "b": []string{"J", "K", "L"}}, {"a": []string{"M", "N", "O"}, "b": []string{"P", "Q", "R"}},
},
key: "b", op: "intersect", match: []string{"D", "P", "Q"},
expect: []map[string][]string{
{"a": []string{"A", "B", "C"}, "b": []string{"D", "E", "F"}}, {"a": []string{"M", "N", "O"}, "b": []string{"P", "Q", "R"}},
},
},
{
sequence: []map[string][]int{
{"a": []int{1, 2, 3}, "b": []int{4, 5, 6}}, {"a": []int{7, 8, 9}, "b": []int{10, 11, 12}}, {"a": []int{13, 14, 15}, "b": []int{16, 17, 18}},
},
key: "b", op: "intersect", match: []int{4, 10, 12},
expect: []map[string][]int{
{"a": []int{1, 2, 3}, "b": []int{4, 5, 6}}, {"a": []int{7, 8, 9}, "b": []int{10, 11, 12}},
},
},
{
sequence: []map[string][]int8{
{"a": []int8{1, 2, 3}, "b": []int8{4, 5, 6}}, {"a": []int8{7, 8, 9}, "b": []int8{10, 11, 12}}, {"a": []int8{13, 14, 15}, "b": []int8{16, 17, 18}},
},
key: "b", op: "intersect", match: []int8{4, 10, 12},
expect: []map[string][]int8{
{"a": []int8{1, 2, 3}, "b": []int8{4, 5, 6}}, {"a": []int8{7, 8, 9}, "b": []int8{10, 11, 12}},
},
},
{
sequence: []map[string][]int16{
{"a": []int16{1, 2, 3}, "b": []int16{4, 5, 6}}, {"a": []int16{7, 8, 9}, "b": []int16{10, 11, 12}}, {"a": []int16{13, 14, 15}, "b": []int16{16, 17, 18}},
},
key: "b", op: "intersect", match: []int16{4, 10, 12},
expect: []map[string][]int16{
{"a": []int16{1, 2, 3}, "b": []int16{4, 5, 6}}, {"a": []int16{7, 8, 9}, "b": []int16{10, 11, 12}},
},
},
{
sequence: []map[string][]int32{
{"a": []int32{1, 2, 3}, "b": []int32{4, 5, 6}}, {"a": []int32{7, 8, 9}, "b": []int32{10, 11, 12}}, {"a": []int32{13, 14, 15}, "b": []int32{16, 17, 18}},
},
key: "b", op: "intersect", match: []int32{4, 10, 12},
expect: []map[string][]int32{
{"a": []int32{1, 2, 3}, "b": []int32{4, 5, 6}}, {"a": []int32{7, 8, 9}, "b": []int32{10, 11, 12}},
},
},
{
sequence: []map[string][]int64{
{"a": []int64{1, 2, 3}, "b": []int64{4, 5, 6}}, {"a": []int64{7, 8, 9}, "b": []int64{10, 11, 12}}, {"a": []int64{13, 14, 15}, "b": []int64{16, 17, 18}},
},
key: "b", op: "intersect", match: []int64{4, 10, 12},
expect: []map[string][]int64{
{"a": []int64{1, 2, 3}, "b": []int64{4, 5, 6}}, {"a": []int64{7, 8, 9}, "b": []int64{10, 11, 12}},
},
},
{
sequence: []map[string][]float32{
{"a": []float32{1.0, 2.0, 3.0}, "b": []float32{4.0, 5.0, 6.0}}, {"a": []float32{7.0, 8.0, 9.0}, "b": []float32{10.0, 11.0, 12.0}}, {"a": []float32{13.0, 14.0, 15.0}, "b": []float32{16.0, 17.0, 18.0}},
},
key: "b", op: "intersect", match: []float32{4, 10, 12},
expect: []map[string][]float32{
{"a": []float32{1.0, 2.0, 3.0}, "b": []float32{4.0, 5.0, 6.0}}, {"a": []float32{7.0, 8.0, 9.0}, "b": []float32{10.0, 11.0, 12.0}},
},
},
{
sequence: []map[string][]float64{
{"a": []float64{1.0, 2.0, 3.0}, "b": []float64{4.0, 5.0, 6.0}}, {"a": []float64{7.0, 8.0, 9.0}, "b": []float64{10.0, 11.0, 12.0}}, {"a": []float64{13.0, 14.0, 15.0}, "b": []float64{16.0, 17.0, 18.0}},
},
key: "b", op: "intersect", match: []float64{4, 10, 12},
expect: []map[string][]float64{
{"a": []float64{1.0, 2.0, 3.0}, "b": []float64{4.0, 5.0, 6.0}}, {"a": []float64{7.0, 8.0, 9.0}, "b": []float64{10.0, 11.0, 12.0}},
},
},
{
sequence: []map[string]int{
{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6},