Skip to content

Commit ccdd08d

Browse files
committed
tpl/collections: Add Pages support to Intersect and Union
This enables `AND` (`intersect`) and `OR` (`union`) filters when combined with `where`. Example: ```go {{ $pages := where .Site.RegularPages "Type" "not in" (slice "page" "about") }} {{ $pages := $pages | union (where .Site.RegularPages "Params.pinned" true) }} {{ $pages := $pages | intersect (where .Site.RegularPages "Params.images" "!=" nil) }} ``` The above fetches regular pages not of `page` or `about` type unless they are pinned. And finally, we exclude all pages with no `images` set in Page params. Fixes #3174
1 parent d12cf5a commit ccdd08d

File tree

5 files changed

+227
-146
lines changed

5 files changed

+227
-146
lines changed

‎tpl/collections/apply.go‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,15 @@ func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
148148
}
149149
return v, false
150150
}
151+
152+
func indirectInterface(v reflect.Value) (rv reflect.Value, isNil bool) {
153+
for ; v.Kind() == reflect.Interface; v = v.Elem() {
154+
if v.IsNil() {
155+
return v, true
156+
}
157+
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
158+
break
159+
}
160+
}
161+
return v, false
162+
}

‎tpl/collections/collections.go‎

Lines changed: 68 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,9 @@ func (ns *Namespace) In(l interface{}, v interface{}) bool {
256256
}
257257
default:
258258
if isNumber(vv.Kind()) && isNumber(lvv.Kind()) {
259-
if numberToFloat(vv) == numberToFloat(lvv) {
259+
f1, err1 := numberToFloat(vv)
260+
f2, err2 := numberToFloat(lvv)
261+
if err1 == nil && err2 == nil && f1 == f2 {
260262
return true
261263
}
262264
}
@@ -277,69 +279,24 @@ func (ns *Namespace) Intersect(l1, l2 interface{}) (interface{}, error) {
277279
return make([]interface{}, 0), nil
278280
}
279281

282+
var ins *intersector
283+
280284
l1v := reflect.ValueOf(l1)
281285
l2v := reflect.ValueOf(l2)
282286

283287
switch l1v.Kind() {
284288
case reflect.Array, reflect.Slice:
289+
ins = &intersector{r: reflect.MakeSlice(l1v.Type(), 0, 0), seen: make(map[interface{}]bool)}
285290
switch l2v.Kind() {
286291
case reflect.Array, reflect.Slice:
287-
r := reflect.MakeSlice(l1v.Type(), 0, 0)
288292
for i := 0; i < l1v.Len(); i++ {
289293
l1vv := l1v.Index(i)
290294
for j := 0; j < l2v.Len(); j++ {
291295
l2vv := l2v.Index(j)
292-
switch l1vv.Kind() {
293-
case reflect.String:
294-
l2t, err := toString(l2vv)
295-
if err == nil && l1vv.String() == l2t && !ns.In(r.Interface(), l1vv.Interface()) {
296-
r = reflect.Append(r, l1vv)
297-
}
298-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
299-
l2t, err := toInt(l2vv)
300-
if err == nil && l1vv.Int() == l2t && !ns.In(r.Interface(), l1vv.Interface()) {
301-
r = reflect.Append(r, l1vv)
302-
}
303-
case reflect.Float32, reflect.Float64:
304-
l2t, err := toFloat(l2vv)
305-
if err == nil && l1vv.Float() == l2t && !ns.In(r.Interface(), l1vv.Interface()) {
306-
r = reflect.Append(r, l1vv)
307-
}
308-
case reflect.Interface:
309-
switch l1vvActual := l1vv.Interface().(type) {
310-
case string:
311-
switch l2vvActual := l2vv.Interface().(type) {
312-
case string:
313-
if l1vvActual == l2vvActual && !ns.In(r.Interface(), l1vvActual) {
314-
r = reflect.Append(r, l1vv)
315-
}
316-
}
317-
case int, int8, int16, int32, int64:
318-
switch l2vvActual := l2vv.Interface().(type) {
319-
case int, int8, int16, int32, int64:
320-
if l1vvActual == l2vvActual && !ns.In(r.Interface(), l1vvActual) {
321-
r = reflect.Append(r, l1vv)
322-
}
323-
}
324-
case uint, uint8, uint16, uint32, uint64:
325-
switch l2vvActual := l2vv.Interface().(type) {
326-
case uint, uint8, uint16, uint32, uint64:
327-
if l1vvActual == l2vvActual && !ns.In(r.Interface(), l1vvActual) {
328-
r = reflect.Append(r, l1vv)
329-
}
330-
}
331-
case float32, float64:
332-
switch l2vvActual := l2vv.Interface().(type) {
333-
case float32, float64:
334-
if l1vvActual == l2vvActual && !ns.In(r.Interface(), l1vvActual) {
335-
r = reflect.Append(r, l1vv)
336-
}
337-
}
338-
}
339-
}
296+
ins.handleValuePair(l1vv, l2vv)
340297
}
341298
}
342-
return r.Interface(), nil
299+
return ins.r.Interface(), nil
343300
default:
344301
return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String())
345302
}
@@ -531,6 +488,41 @@ func (ns *Namespace) Slice(args ...interface{}) []interface{} {
531488
return args
532489
}
533490

491+
type intersector struct {
492+
r reflect.Value
493+
seen map[interface{}]bool
494+
}
495+
496+
func (i *intersector) appendIfNotSeen(v reflect.Value) {
497+
vi := v.Interface()
498+
if !i.seen[vi] {
499+
i.r = reflect.Append(i.r, v)
500+
i.seen[vi] = true
501+
}
502+
}
503+
504+
func (ins *intersector) handleValuePair(l1vv, l2vv reflect.Value) {
505+
switch kind := l1vv.Kind(); {
506+
case kind == reflect.String:
507+
l2t, err := toString(l2vv)
508+
if err == nil && l1vv.String() == l2t {
509+
ins.appendIfNotSeen(l1vv)
510+
}
511+
case isNumber(kind):
512+
f1, err1 := numberToFloat(l1vv)
513+
f2, err2 := numberToFloat(l2vv)
514+
if err1 == nil && err2 == nil && f1 == f2 {
515+
ins.appendIfNotSeen(l1vv)
516+
}
517+
case kind == reflect.Ptr, kind == reflect.Struct:
518+
if l1vv.Interface() == l2vv.Interface() {
519+
ins.appendIfNotSeen(l1vv)
520+
}
521+
case kind == reflect.Interface:
522+
ins.handleValuePair(reflect.ValueOf(l1vv.Interface()), l2vv)
523+
}
524+
}
525+
534526
// Union returns the union of the given sets, l1 and l2. l1 and
535527
// l2 must be of the same type and may be either arrays or slices.
536528
// If l1 and l2 aren't of the same type then l1 will be returned.
@@ -547,105 +539,54 @@ func (ns *Namespace) Union(l1, l2 interface{}) (interface{}, error) {
547539
l1v := reflect.ValueOf(l1)
548540
l2v := reflect.ValueOf(l2)
549541

542+
var ins *intersector
543+
550544
switch l1v.Kind() {
551545
case reflect.Array, reflect.Slice:
552546
switch l2v.Kind() {
553547
case reflect.Array, reflect.Slice:
554-
r := reflect.MakeSlice(l1v.Type(), 0, 0)
548+
ins = &intersector{r: reflect.MakeSlice(l1v.Type(), 0, 0), seen: make(map[interface{}]bool)}
555549

556550
if l1v.Type() != l2v.Type() &&
557551
l1v.Type().Elem().Kind() != reflect.Interface &&
558552
l2v.Type().Elem().Kind() != reflect.Interface {
559-
return r.Interface(), nil
553+
return ins.r.Interface(), nil
560554
}
561555

562-
var l1vv reflect.Value
556+
var (
557+
l1vv reflect.Value
558+
isNil bool
559+
)
560+
563561
for i := 0; i < l1v.Len(); i++ {
564-
l1vv = l1v.Index(i)
565-
if !ns.In(r.Interface(), l1vv.Interface()) {
566-
r = reflect.Append(r, l1vv)
562+
l1vv, isNil = indirectInterface(l1v.Index(i))
563+
if !isNil {
564+
ins.appendIfNotSeen(l1vv)
567565
}
568566
}
569567

570568
for j := 0; j < l2v.Len(); j++ {
571569
l2vv := l2v.Index(j)
572570

573-
switch l1vv.Kind() {
574-
case reflect.String:
571+
switch kind := l1vv.Kind(); {
572+
case kind == reflect.String:
575573
l2t, err := toString(l2vv)
576-
if err == nil && !ns.In(r.Interface(), l2t) {
577-
r = reflect.Append(r, reflect.ValueOf(l2t))
578-
}
579-
case reflect.Int:
580-
l2t, err := toInt(l2vv)
581-
if err == nil && !ns.In(r.Interface(), l2t) {
582-
r = reflect.Append(r, reflect.ValueOf(int(l2t)))
583-
}
584-
case reflect.Int8:
585-
l2t, err := toInt(l2vv)
586-
if err == nil && !ns.In(r.Interface(), l2t) {
587-
r = reflect.Append(r, reflect.ValueOf(int8(l2t)))
588-
}
589-
case reflect.Int16:
590-
l2t, err := toInt(l2vv)
591-
if err == nil && !ns.In(r.Interface(), l2t) {
592-
r = reflect.Append(r, reflect.ValueOf(int16(l2t)))
574+
if err == nil {
575+
ins.appendIfNotSeen(reflect.ValueOf(l2t))
593576
}
594-
case reflect.Int32:
595-
l2t, err := toInt(l2vv)
596-
if err == nil && !ns.In(r.Interface(), l2t) {
597-
r = reflect.Append(r, reflect.ValueOf(int32(l2t)))
598-
}
599-
case reflect.Int64:
600-
l2t, err := toInt(l2vv)
601-
if err == nil && !ns.In(r.Interface(), l2t) {
602-
r = reflect.Append(r, reflect.ValueOf(l2t))
603-
}
604-
case reflect.Float32:
605-
l2t, err := toFloat(l2vv)
606-
if err == nil && !ns.In(r.Interface(), float32(l2t)) {
607-
r = reflect.Append(r, reflect.ValueOf(float32(l2t)))
608-
}
609-
case reflect.Float64:
610-
l2t, err := toFloat(l2vv)
611-
if err == nil && !ns.In(r.Interface(), l2t) {
612-
r = reflect.Append(r, reflect.ValueOf(l2t))
613-
}
614-
case reflect.Interface:
615-
switch l1vv.Interface().(type) {
616-
case string:
617-
switch l2vvActual := l2vv.Interface().(type) {
618-
case string:
619-
if !ns.In(r.Interface(), l2vvActual) {
620-
r = reflect.Append(r, l2vv)
621-
}
622-
}
623-
case int, int8, int16, int32, int64:
624-
switch l2vvActual := l2vv.Interface().(type) {
625-
case int, int8, int16, int32, int64:
626-
if !ns.In(r.Interface(), l2vvActual) {
627-
r = reflect.Append(r, l2vv)
628-
}
629-
}
630-
case uint, uint8, uint16, uint32, uint64:
631-
switch l2vvActual := l2vv.Interface().(type) {
632-
case uint, uint8, uint16, uint32, uint64:
633-
if !ns.In(r.Interface(), l2vvActual) {
634-
r = reflect.Append(r, l2vv)
635-
}
636-
}
637-
case float32, float64:
638-
switch l2vvActual := l2vv.Interface().(type) {
639-
case float32, float64:
640-
if !ns.In(r.Interface(), l2vvActual) {
641-
r = reflect.Append(r, l2vv)
642-
}
643-
}
577+
case isNumber(kind):
578+
var err error
579+
l2vv, err = convertNumber(l2vv, kind)
580+
if err == nil {
581+
ins.appendIfNotSeen(l2vv)
644582
}
583+
case kind == reflect.Interface, kind == reflect.Struct, kind == reflect.Ptr:
584+
ins.appendIfNotSeen(l2vv)
585+
645586
}
646587
}
647588

648-
return r.Interface(), nil
589+
return ins.r.Interface(), nil
649590
default:
650591
return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String())
651592
}

0 commit comments

Comments
 (0)