Merge pull request #4436 from influxdb/tag-names-to-keys

WIP tag name --> tag key, field name --> field key
pull/4454/head
Sean Beckett 2015-10-14 16:02:46 -07:00
commit 82f104a8b1
9 changed files with 28 additions and 28 deletions

View File

@ -1,4 +1,4 @@
The top level name is called a measurement. These names can contain any characters. Then there are field names, field values, tag keys and tag values, which can also contain any characters. However, if the measurement, field, or tag contains any character other than [A-Z,a-z,0-9,_], or if it starts with a digit, it must be double-quoted. Therefore anywhere a measurement name, field name, field value, tag name, or tag value appears it should be wrapped in double quotes.
The top level name is called a measurement. These names can contain any characters. Then there are field names, field values, tag keys and tag values, which can also contain any characters. However, if the measurement, field, or tag contains any character other than [A-Z,a-z,0-9,_], or if it starts with a digit, it must be double-quoted. Therefore anywhere a measurement name, field key, or tag key appears it should be wrapped in double quotes.
# Databases & retention policies

View File

@ -111,7 +111,7 @@ type WritePointsRequest struct {
Points []models.Point
}
// AddPoint adds a point to the WritePointRequest with field name 'value'
// AddPoint adds a point to the WritePointRequest with field key 'value'
func (w *WritePointsRequest) AddPoint(name string, value interface{}, timestamp time.Time, tags map[string]string) {
w.Points = append(w.Points, models.NewPoint(
name, tags, map[string]interface{}{"value": value}, timestamp,

View File

@ -141,7 +141,7 @@ func (c *Config) applyEnvOverrides(prefix string, spec reflect.Value) error {
configName := typeOfSpec.Field(i).Tag.Get("toml")
// Replace hyphens with underscores to avoid issues with shells
configName = strings.Replace(configName, "-", "_", -1)
fieldName := typeOfSpec.Field(i).Name
fieldKey := typeOfSpec.Field(i).Name
// Skip any fields that we cannot set
if f.CanSet() || f.Kind() == reflect.Slice {
@ -188,14 +188,14 @@ func (c *Config) applyEnvOverrides(prefix string, spec reflect.Value) error {
if f.Type().Name() == "Duration" {
dur, err := time.ParseDuration(value)
if err != nil {
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldName, f.Type().String(), value)
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldKey, f.Type().String(), value)
}
intValue = dur.Nanoseconds()
} else {
var err error
intValue, err = strconv.ParseInt(value, 0, f.Type().Bits())
if err != nil {
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldName, f.Type().String(), value)
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldKey, f.Type().String(), value)
}
}
@ -203,14 +203,14 @@ func (c *Config) applyEnvOverrides(prefix string, spec reflect.Value) error {
case reflect.Bool:
boolValue, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldName, f.Type().String(), value)
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldKey, f.Type().String(), value)
}
f.SetBool(boolValue)
case reflect.Float32, reflect.Float64:
floatValue, err := strconv.ParseFloat(value, f.Type().Bits())
if err != nil {
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldName, f.Type().String(), value)
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldKey, f.Type().String(), value)
}
f.SetFloat(floatValue)

View File

@ -3432,7 +3432,7 @@ func TestServer_Query_WildcardExpansion(t *testing.T) {
exp: `{"results":[{"series":[{"name":"wildcard","columns":["time","c","h","region","value"],"values":[["2000-01-01T00:00:00Z",80,"A","us-east",10],["2000-01-01T00:00:10Z",90,"B","us-east",20],["2000-01-01T00:00:20Z",70,"B","us-west",30],["2000-01-01T00:00:30Z",60,"A","us-east",40]]}]}]}`,
},
&Query{
name: "duplicate tag and field name, always favor field over tag",
name: "duplicate tag and field key, always favor field over tag",
command: `SELECT * FROM dupnames`,
params: url.Values{"db": []string{"db0"}},
exp: `{"results":[{"series":[{"name":"dupnames","columns":["time","day","region","value"],"values":[["2000-01-01T00:00:00Z",3,"us-east",10],["2000-01-01T00:00:10Z",2,"us-east",20],["2000-01-01T00:00:20Z",1,"us-west",30]]}]}]}`,

View File

@ -54,7 +54,7 @@ digit = "0" … "9" .
## Identifiers
Identifiers are tokens which refer to database names, retention policy names, user names, measurement names, tag keys, and field names.
Identifiers are tokens which refer to database names, retention policy names, user names, measurement names, tag keys, and field keys.
The rules:
@ -652,7 +652,7 @@ privilege = "ALL" [ "PRIVILEGES" ] | "READ" | "WRITE" .
series_id = int_lit .
sort_field = field_name [ ASC | DESC ] .
sort_field = field_key [ ASC | DESC ] .
sort_fields = sort_field { "," sort_field } .

View File

@ -248,21 +248,21 @@ func scanKey(buf []byte, i int) (int, []byte, error) {
break
}
// equals is special in the tags section. It must be escaped if part of a tag name or value.
// equals is special in the tags section. It must be escaped if part of a tag key or value.
// It does not need to be escaped if part of the measurement.
if buf[i] == '=' && commas > 0 {
if i-1 < 0 || i-2 < 0 {
return i, buf[start:i], fmt.Errorf("missing tag name")
return i, buf[start:i], fmt.Errorf("missing tag key")
}
// Check for "cpu,=value" but allow "cpu,a\,=value"
if buf[i-1] == ',' && buf[i-2] != '\\' {
return i, buf[start:i], fmt.Errorf("missing tag name")
return i, buf[start:i], fmt.Errorf("missing tag key")
}
// Check for "cpu,\ =value"
if buf[i-1] == ' ' && buf[i-2] != '\\' {
return i, buf[start:i], fmt.Errorf("missing tag name")
return i, buf[start:i], fmt.Errorf("missing tag key")
}
i += 1
@ -459,12 +459,12 @@ func scanFields(buf []byte, i int) (int, []byte, error) {
// check for "... =123" but allow "a\ =123"
if buf[i-1] == ' ' && buf[i-2] != '\\' {
return i, buf[start:i], fmt.Errorf("missing field name")
return i, buf[start:i], fmt.Errorf("missing field key")
}
// check for "...a=123,=456" but allow "a=123,a\,=456"
if buf[i-1] == ',' && buf[i-2] != '\\' {
return i, buf[start:i], fmt.Errorf("missing field name")
return i, buf[start:i], fmt.Errorf("missing field key")
}
// check for "... value="

View File

@ -211,7 +211,7 @@ func TestParsePointMissingQuote(t *testing.T) {
}
}
func TestParsePointMissingTagName(t *testing.T) {
func TestParsePointMissingTagKey(t *testing.T) {
_, err := models.ParsePointsString(`cpu,host=serverA,=us-east value=1i`)
if err == nil {
t.Errorf(`ParsePoints("%s") mismatch. got nil, exp error`, `cpu,host=serverA,=us-east value=1i`)
@ -560,7 +560,7 @@ func TestParsePointUnescape(t *testing.T) {
test(t, `cpu,region\,zone=east value=1.0`,
models.NewPoint("cpu",
models.Tags{
"region,zone": "east", // comma in the tag name
"region,zone": "east", // comma in the tag key
},
models.Fields{
"value": 1.0,
@ -571,7 +571,7 @@ func TestParsePointUnescape(t *testing.T) {
test(t, `cpu,region\ zone=east value=1.0`,
models.NewPoint("cpu",
models.Tags{
"region zone": "east", // comma in the tag name
"region zone": "east", // comma in the tag key
},
models.Fields{
"value": 1.0,
@ -600,25 +600,25 @@ func TestParsePointUnescape(t *testing.T) {
},
time.Unix(0, 0)))
// commas in field names
// commas in field keys
test(t, `cpu,regions=east value\,ms=1.0`,
models.NewPoint("cpu",
models.Tags{
"regions": "east",
},
models.Fields{
"value,ms": 1.0, // comma in the field name
"value,ms": 1.0, // comma in the field keys
},
time.Unix(0, 0)))
// spaces in field names
// spaces in field keys
test(t, `cpu,regions=east value\ ms=1.0`,
models.NewPoint("cpu",
models.Tags{
"regions": "east",
},
models.Fields{
"value ms": 1.0, // comma in the field name
"value ms": 1.0, // comma in the field keys
},
time.Unix(0, 0)))
@ -657,7 +657,7 @@ func TestParsePointUnescape(t *testing.T) {
},
time.Unix(0, 0)))
// field name using escape char.
// field keys using escape char.
test(t, `cpu \a=1i`,
models.NewPoint(
"cpu",

View File

@ -14,7 +14,7 @@ To extract tags from metrics, one or more templates must be configured to parse
## Templates
Templates allow matching parts of a metric name to be used as tag names in the stored metric. They have a similar format to graphite metric names. The values in between the separators are used as the tag name. The location of the tag name that matches the same position as the graphite metric section is used as the value. If there is no value, the graphite portion is skipped.
Templates allow matching parts of a metric name to be used as tag keys in the stored metric. They have a similar format to graphite metric names. The values in between the separators are used as the tag keys. The location of the tag key that matches the same position as the graphite metric section is used as the value. If there is no value, the graphite portion is skipped.
The special value _measurement_ is used to define the measurement name. It can have a trailing `*` to indicate that the remainder of the metric should be used. If a _measurement_ is not specified, the full metric name is used.
@ -50,7 +50,7 @@ Additional tags can be added to a metric that don't exist on the received metric
### Fields
A field name can be specified by using the keyword _field_. By default if no _field_ keyword is specified then the metric will be written to a field named _value_.
A field key can be specified by using the keyword _field_. By default if no _field_ keyword is specified then the metric will be written to a field named _value_.
When using the current default engine _BZ1_, it's recommended to use a single field per value for performance reasons.
@ -158,7 +158,7 @@ If you need to add the same set of tags to all metrics, you can define them glob
# filter + template + extra tag
"stats.* .host.measurement* region=us-west,agent=sensu",
# filter + template with field name
# filter + template with field key
"stats.* .host.measurement.field",
# default template. Ignore the first graphite component "servers"

View File

@ -136,7 +136,7 @@ func (db *DatabaseIndex) measurementsByExpr(expr influxql.Expr) (Measurements, e
case influxql.EQ, influxql.NEQ, influxql.EQREGEX, influxql.NEQREGEX:
tag, ok := e.LHS.(*influxql.VarRef)
if !ok {
return nil, fmt.Errorf("left side of '%s' must be a tag name", e.Op.String())
return nil, fmt.Errorf("left side of '%s' must be a tag key", e.Op.String())
}
tf := &TagFilter{