mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-29 00:02:08 -05:00
metrics: Fix --templateMetricsHints
Also improve non-string comparisons. Fixes #7048
This commit is contained in:
parent
18cb21ff2e
commit
5eadc4c0a8
3 changed files with 85 additions and 10 deletions
|
@ -13,7 +13,11 @@
|
||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import "github.com/spf13/cast"
|
import (
|
||||||
|
"html/template"
|
||||||
|
|
||||||
|
"github.com/spf13/cast"
|
||||||
|
)
|
||||||
|
|
||||||
// ToStringSlicePreserveString converts v to a string slice.
|
// ToStringSlicePreserveString converts v to a string slice.
|
||||||
// If v is a string, it will be wrapped in a string slice.
|
// If v is a string, it will be wrapped in a string slice.
|
||||||
|
@ -26,3 +30,39 @@ func ToStringSlicePreserveString(v interface{}) []string {
|
||||||
}
|
}
|
||||||
return cast.ToStringSlice(v)
|
return cast.ToStringSlice(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TypeToString converts v to a string if it's a valid string type.
|
||||||
|
// Note that this will not try to convert numeric values etc.,
|
||||||
|
// use ToString for that.
|
||||||
|
func TypeToString(v interface{}) (string, bool) {
|
||||||
|
switch s := v.(type) {
|
||||||
|
case string:
|
||||||
|
return s, true
|
||||||
|
case template.HTML:
|
||||||
|
return string(s), true
|
||||||
|
case template.CSS:
|
||||||
|
return string(s), true
|
||||||
|
case template.HTMLAttr:
|
||||||
|
return string(s), true
|
||||||
|
case template.JS:
|
||||||
|
return string(s), true
|
||||||
|
case template.JSStr:
|
||||||
|
return string(s), true
|
||||||
|
case template.URL:
|
||||||
|
return string(s), true
|
||||||
|
case template.Srcset:
|
||||||
|
return string(s), true
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToString converts v to a string.
|
||||||
|
func ToString(v interface{}) string {
|
||||||
|
if s, ok := TypeToString(v); ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return cast.ToString(v)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,12 @@
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/helpers"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/types"
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
@ -25,8 +31,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/compare"
|
"github.com/gohugoio/hugo/compare"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/hreflect"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// The Provider interface defines an interface for measuring metrics.
|
// The Provider interface defines an interface for measuring metrics.
|
||||||
|
@ -51,15 +55,17 @@ type diff struct {
|
||||||
simSum int
|
simSum int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var counter = 0
|
||||||
|
|
||||||
func (d *diff) add(v interface{}) *diff {
|
func (d *diff) add(v interface{}) *diff {
|
||||||
if !hreflect.IsTruthful(v) {
|
if types.IsNil(d.baseline) {
|
||||||
d.baseline = v
|
d.baseline = v
|
||||||
d.count = 1
|
d.count = 1
|
||||||
d.simSum = 100 // If we get only one it is very cache friendly.
|
d.simSum = 100 // If we get only one it is very cache friendly.
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
adder := howSimilar(v, d.baseline)
|
||||||
d.simSum += howSimilar(v, d.baseline)
|
d.simSum += adder
|
||||||
d.count++
|
d.count++
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
@ -113,6 +119,7 @@ func (s *Store) TrackValue(key string, value interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
d.add(value)
|
d.add(value)
|
||||||
|
|
||||||
s.diffmu.Unlock()
|
s.diffmu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +142,7 @@ func (s *Store) WriteMetrics(w io.Writer) {
|
||||||
var max time.Duration
|
var max time.Duration
|
||||||
|
|
||||||
diff, found := s.diffs[k]
|
diff, found := s.diffs[k]
|
||||||
|
|
||||||
cacheFactor := 0
|
cacheFactor := 0
|
||||||
if found {
|
if found {
|
||||||
cacheFactor = int(math.Floor(float64(diff.simSum) / float64(diff.count)))
|
cacheFactor = int(math.Floor(float64(diff.simSum) / float64(diff.count)))
|
||||||
|
@ -196,11 +204,19 @@ func (b bySum) Less(i, j int) bool { return b[i].sum > b[j].sum }
|
||||||
// howSimilar is a naive diff implementation that returns
|
// howSimilar is a naive diff implementation that returns
|
||||||
// a number between 0-100 indicating how similar a and b are.
|
// a number between 0-100 indicating how similar a and b are.
|
||||||
func howSimilar(a, b interface{}) int {
|
func howSimilar(a, b interface{}) int {
|
||||||
// TODO(bep) object equality fast path, but remember that
|
t1, t2 := reflect.TypeOf(a), reflect.TypeOf(b)
|
||||||
// we can get anytning in here.
|
if t1 != t2 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
as, ok1 := a.(string)
|
if t1.Comparable() && t2.Comparable() {
|
||||||
bs, ok2 := b.(string)
|
if a == b {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
as, ok1 := types.TypeToString(a)
|
||||||
|
bs, ok2 := types.TypeToString(b)
|
||||||
|
|
||||||
if ok1 && ok2 {
|
if ok1 && ok2 {
|
||||||
return howSimilarStrings(as, bs)
|
return howSimilarStrings(as, bs)
|
||||||
|
@ -222,13 +238,21 @@ func howSimilar(a, b interface{}) int {
|
||||||
return 90
|
return 90
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1, h2 := helpers.HashString(a), helpers.HashString(b)
|
||||||
|
if h1 == h2 {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// howSimilar is a naive diff implementation that returns
|
// howSimilar is a naive diff implementation that returns
|
||||||
// a number between 0-100 indicating how similar a and b are.
|
// a number between 0-100 indicating how similar a and b are.
|
||||||
// 100 is when all words in a also exists in b.
|
// 100 is when all words in a also exists in b.
|
||||||
func howSimilarStrings(a, b string) int {
|
func howSimilarStrings(a, b string) int {
|
||||||
|
if a == b {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
|
||||||
// Give some weight to the word positions.
|
// Give some weight to the word positions.
|
||||||
const partitionSize = 4
|
const partitionSize = 4
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"html/template"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -39,13 +40,23 @@ func TestSimilarPercentage(t *testing.T) {
|
||||||
c.Assert(howSimilar("The Hugo", "The Hugo Rules"), qt.Equals, 66)
|
c.Assert(howSimilar("The Hugo", "The Hugo Rules"), qt.Equals, 66)
|
||||||
c.Assert(howSimilar("Totally different", "Not Same"), qt.Equals, 0)
|
c.Assert(howSimilar("Totally different", "Not Same"), qt.Equals, 0)
|
||||||
c.Assert(howSimilar(sentence, sentenceReversed), qt.Equals, 14)
|
c.Assert(howSimilar(sentence, sentenceReversed), qt.Equals, 14)
|
||||||
|
c.Assert(howSimilar(template.HTML("Hugo Rules"), template.HTML("Hugo Rules")), qt.Equals, 100)
|
||||||
|
c.Assert(howSimilar(map[string]interface{}{"a": 32, "b": 33}, map[string]interface{}{"a": 32, "b": 33}), qt.Equals, 100)
|
||||||
|
c.Assert(howSimilar(map[string]interface{}{"a": 32, "b": 33}, map[string]interface{}{"a": 32, "b": 34}), qt.Equals, 0)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testStruct struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
func TestSimilarPercentageNonString(t *testing.T) {
|
func TestSimilarPercentageNonString(t *testing.T) {
|
||||||
c := qt.New(t)
|
c := qt.New(t)
|
||||||
c.Assert(howSimilar(page.NopPage, page.NopPage), qt.Equals, 100)
|
c.Assert(howSimilar(page.NopPage, page.NopPage), qt.Equals, 100)
|
||||||
c.Assert(howSimilar(page.Pages{}, page.Pages{}), qt.Equals, 90)
|
c.Assert(howSimilar(page.Pages{}, page.Pages{}), qt.Equals, 90)
|
||||||
|
c.Assert(howSimilar(testStruct{Name: "A"}, testStruct{Name: "B"}), qt.Equals, 0)
|
||||||
|
c.Assert(howSimilar(testStruct{Name: "A"}, testStruct{Name: "A"}), qt.Equals, 100)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkHowSimilar(b *testing.B) {
|
func BenchmarkHowSimilar(b *testing.B) {
|
||||||
|
|
Loading…
Reference in a new issue