From 396c8a621193106d7fff0d2bb3a117bccaee74ef Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Sat, 4 Jan 2014 20:43:32 -0800 Subject: [PATCH] search: Add ValueMatches{Int,Float} to PermanodeConstraint. Adds constraints for int- and float-valued attributes. Change-Id: Ie8354f8ea12f604b45cf2fa6a7fb45170bab7e46 --- pkg/search/query.go | 28 ++++++++++++++++--- pkg/search/query_test.go | 58 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/pkg/search/query.go b/pkg/search/query.go index 504808fa5..46924eef7 100644 --- a/pkg/search/query.go +++ b/pkg/search/query.go @@ -388,7 +388,7 @@ func (c *IntConstraint) intMatches(v int64) bool { return true } -// A FloatConstraint specifies constraints on an integer. +// A FloatConstraint specifies constraints on a float. type FloatConstraint struct { // Min and Max are both optional and inclusive bounds. // Zero means don't check. @@ -574,6 +574,14 @@ type PermanodeConstraint struct { // match the value against. ValueMatches *StringConstraint `json:"valueMatches,omitempty"` + // ValueMatchesInt optionally specifies an IntConstraint to match + // the value against. Non-integer values will not match. + ValueMatchesInt *IntConstraint `json:"valueMatchesInt,omitempty"` + + // ValueMatchesFloat optionally specifies a FloatConstraint to match + // the value against. Non-float values will not match. + ValueMatchesFloat *FloatConstraint `json:"valueMatchesFloat,omitempty"` + // ValueInSet optionally specifies a sub-query which the value // (which must be a blobref) must be a part of. ValueInSet *Constraint `json:"valueInSet,omitempty"` @@ -1008,11 +1016,15 @@ var numPermanodeFields = reflect.TypeOf(PermanodeConstraint{}).NumField() // hasValueConstraint returns true if one or more constraints that check an attribute's value are set. func (c *PermanodeConstraint) hasValueConstraint() bool { // If a field has been added or removed, update this after adding the new field to the return statement if necessary. - const expectedFields = 12 + const expectedFields = 14 if numPermanodeFields != expectedFields { panic(fmt.Sprintf("PermanodeConstraint field count changed (now %v rather than %v)", numPermanodeFields, expectedFields)) } - return c.Value != "" || c.ValueMatches != nil || c.ValueInSet != nil + return c.Value != "" || + c.ValueMatches != nil || + c.ValueMatchesInt != nil || + c.ValueMatchesFloat != nil || + c.ValueInSet != nil } func (c *PermanodeConstraint) blobMatches(s *search, br blob.Ref, bm camtypes.BlobMeta) (ok bool, err error) { @@ -1145,6 +1157,16 @@ func (c *PermanodeConstraint) permanodeMatchesAttrVal(s *search, val string) (bo if c.ValueMatches != nil && !c.ValueMatches.stringMatches(val) { return false, nil } + if c.ValueMatchesInt != nil { + if i, err := strconv.ParseInt(val, 10, 64); err != nil || !c.ValueMatchesInt.intMatches(i) { + return false, nil + } + } + if c.ValueMatchesFloat != nil { + if f, err := strconv.ParseFloat(val, 64); err != nil || !c.ValueMatchesFloat.floatMatches(f) { + return false, nil + } + } if subc := c.ValueInSet; subc != nil { br, ok := blob.Parse(val) // TODO: use corpus's parse, or keep this as blob.Ref in corpus attr if !ok { diff --git a/pkg/search/query_test.go b/pkg/search/query_test.go index a1f87bc37..0ddf39508 100644 --- a/pkg/search/query_test.go +++ b/pkg/search/query_test.go @@ -476,6 +476,64 @@ func TestQueryPermanodeAttrValueInSet(t *testing.T) { }) } +// Tests PermanodeConstraint.ValueMatchesInt. +func TestQueryPermanodeValueMatchesInt(t *testing.T) { + testQuery(t, func(qt *queryTest) { + id := qt.id + + p1 := id.NewPlannedPermanode("1") + p2 := id.NewPlannedPermanode("2") + p3 := id.NewPlannedPermanode("3") + p4 := id.NewPlannedPermanode("4") + p5 := id.NewPlannedPermanode("5") + id.SetAttribute(p1, "x", "-5") + id.SetAttribute(p2, "x", "0") + id.SetAttribute(p3, "x", "2") + id.SetAttribute(p4, "x", "10.0") + id.SetAttribute(p5, "x", "abc") + + sq := &SearchQuery{ + Constraint: &Constraint{ + Permanode: &PermanodeConstraint{ + Attr: "x", + ValueMatchesInt: &IntConstraint{ + Min: -2, + }, + }, + }, + } + qt.wantRes(sq, p2, p3) + }) +} + +// Tests PermanodeConstraint.ValueMatchesFloat. +func TestQueryPermanodeValueMatchesFloat(t *testing.T) { + testQuery(t, func(qt *queryTest) { + id := qt.id + + p1 := id.NewPlannedPermanode("1") + p2 := id.NewPlannedPermanode("2") + p3 := id.NewPlannedPermanode("3") + p4 := id.NewPlannedPermanode("4") + id.SetAttribute(p1, "x", "2.5") + id.SetAttribute(p2, "x", "5.7") + id.SetAttribute(p3, "x", "10") + id.SetAttribute(p4, "x", "abc") + + sq := &SearchQuery{ + Constraint: &Constraint{ + Permanode: &PermanodeConstraint{ + Attr: "x", + ValueMatchesFloat: &FloatConstraint{ + Max: 6.0, + }, + }, + }, + } + qt.wantRes(sq, p1, p2) + }) +} + // find permanodes matching a certain file query func TestQueryFileConstraint(t *testing.T) { testQuery(t, func(qt *queryTest) {