// idsForExpr will return a collection of series ids and a filter expression that should // be used to filter points from those series. func (m *Measurement) idsForExpr(n *influxql.BinaryExpr) (SeriesIDs, influxql.Expr, error) { // If this binary expression has another binary expression, then this // is some expression math and we should just pass it to the underlying query. if _, ok := n.LHS.(*influxql.BinaryExpr); ok { return m.seriesIDs, n, nil } else if _, ok := n.RHS.(*influxql.BinaryExpr); ok { return m.seriesIDs, n, nil } // Retrieve the variable reference from the correct side of the expression. name, ok := n.LHS.(*influxql.VarRef) value := n.RHS if !ok { name, ok = n.RHS.(*influxql.VarRef) if !ok { return nil, nil, fmt.Errorf("invalid expression: %s", n.String()) } value = n.LHS } // For time literals, return all series IDs and "true" as the filter. if _, ok := value.(*influxql.TimeLiteral); ok || name.Val == "time" { return m.seriesIDs, &influxql.BooleanLiteral{Val: true}, nil } // For fields, return all series IDs from this measurement and return // the expression passed in, as the filter. if name.Val != "_name" && ((name.Type == influxql.Unknown && m.hasField(name.Val)) || name.Type == influxql.AnyField || (name.Type != influxql.Tag && name.Type != influxql.Unknown)) { return m.seriesIDs, n, nil } else if value, ok := value.(*influxql.VarRef); ok { // Check if the RHS is a variable and if it is a field. if value.Val != "_name" && ((value.Type == influxql.Unknown && m.hasField(value.Val)) || name.Type == influxql.AnyField || (value.Type != influxql.Tag && value.Type != influxql.Unknown)) { return m.seriesIDs, n, nil } } // Retrieve list of series with this tag key. tagVals := m.seriesByTagKeyValue[name.Val] // if we're looking for series with a specific tag value if str, ok := value.(*influxql.StringLiteral); ok { var ids SeriesIDs // Special handling for "_name" to match measurement name. if name.Val == "_name" { if (n.Op == influxql.EQ && str.Val == m.Name) || (n.Op == influxql.NEQ && str.Val != m.Name) { return m.seriesIDs, &influxql.BooleanLiteral{Val: true}, nil } return nil, &influxql.BooleanLiteral{Val: true}, nil } if n.Op == influxql.EQ { if str.Val != "" { // return series that have a tag of specific value. ids = tagVals[str.Val] } else { ids = m.seriesIDs for k := range tagVals { ids = ids.Reject(tagVals[k]) } } } else if n.Op == influxql.NEQ { if str.Val != "" { ids = m.seriesIDs.Reject(tagVals[str.Val]) } else { for k := range tagVals { ids = ids.Union(tagVals[k]) } } } return ids, &influxql.BooleanLiteral{Val: true}, nil } // if we're looking for series with a tag value that matches a regex if re, ok := value.(*influxql.RegexLiteral); ok { var ids SeriesIDs // Special handling for "_name" to match measurement name. if name.Val == "_name" { match := re.Val.MatchString(m.Name) if (n.Op == influxql.EQREGEX && match) || (n.Op == influxql.NEQREGEX && !match) { return m.seriesIDs, &influxql.BooleanLiteral{Val: true}, nil } return nil, &influxql.BooleanLiteral{Val: true}, nil } // Check if we match the empty string to see if we should include series // that are missing the tag. empty := re.Val.MatchString("") // Gather the series that match the regex. If we should include the empty string, // start with the list of all series and reject series that don't match our condition. // If we should not include the empty string, include series that match our condition. if empty && n.Op == influxql.EQREGEX { ids = m.seriesIDs for k := range tagVals { if !re.Val.MatchString(k) { ids = ids.Reject(tagVals[k]) } } } else if empty && n.Op == influxql.NEQREGEX { for k := range tagVals { if !re.Val.MatchString(k) { ids = ids.Union(tagVals[k]) } } } else if !empty && n.Op == influxql.EQREGEX { for k := range tagVals { if re.Val.MatchString(k) { ids = ids.Union(tagVals[k]) } } } else if !empty && n.Op == influxql.NEQREGEX { ids = m.seriesIDs for k := range tagVals { if re.Val.MatchString(k) { ids = ids.Reject(tagVals[k]) } } } return ids, &influxql.BooleanLiteral{Val: true}, nil } // compare tag values if ref, ok := value.(*influxql.VarRef); ok { var ids SeriesIDs if n.Op == influxql.NEQ { ids = m.seriesIDs } rhsTagVals := m.seriesByTagKeyValue[ref.Val] for k := range tagVals { tags := tagVals[k].Intersect(rhsTagVals[k]) if n.Op == influxql.EQ { ids = ids.Union(tags) } else if n.Op == influxql.NEQ { ids = ids.Reject(tags) } } return ids, &influxql.BooleanLiteral{Val: true}, nil } if n.Op == influxql.NEQ || n.Op == influxql.NEQREGEX { return m.seriesIDs, &influxql.BooleanLiteral{Val: true}, nil } return nil, nil, nil }
// idsForExpr will return a collection of series ids and a filter expression that should // be used to filter points from those series. func (m *Measurement) idsForExpr(n *influxql.BinaryExpr) (SeriesIDs, influxql.Expr, error) { name, ok := n.LHS.(*influxql.VarRef) value := n.RHS if !ok { name, ok = n.RHS.(*influxql.VarRef) if !ok { return nil, nil, fmt.Errorf("invalid expression: %s", n.String()) } value = n.LHS } // For time literals, return all series IDs and "true" as the filter. if _, ok := value.(*influxql.TimeLiteral); ok || name.Val == "time" { return m.seriesIDs, &influxql.BooleanLiteral{Val: true}, nil } // For fields, return all series IDs from this measurement and return // the expression passed in, as the filter. if m.HasField(name.Val) { return m.seriesIDs, n, nil } tagVals, ok := m.seriesByTagKeyValue[name.Val] if !ok { return nil, nil, nil } // if we're looking for series with a specific tag value if str, ok := value.(*influxql.StringLiteral); ok { var ids SeriesIDs if n.Op == influxql.EQ { // return series that have a tag of specific value. ids = tagVals[str.Val] } else if n.Op == influxql.NEQ { ids = m.seriesIDs.Reject(tagVals[str.Val]) } return ids, &influxql.BooleanLiteral{Val: true}, nil } // if we're looking for series with a tag value that matches a regex if re, ok := value.(*influxql.RegexLiteral); ok { var ids SeriesIDs // The operation is a NEQREGEX, code must start by assuming all match, even // series without any tags. if n.Op == influxql.NEQREGEX { ids = m.seriesIDs } for k := range tagVals { match := re.Val.MatchString(k) if match && n.Op == influxql.EQREGEX { ids = ids.Union(tagVals[k]) } else if match && n.Op == influxql.NEQREGEX { ids = ids.Reject(tagVals[k]) } } return ids, &influxql.BooleanLiteral{Val: true}, nil } return nil, nil, nil }