github.com/m3db/m3@v1.5.0/src/query/functions/tag/join.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package tag 22 23 import ( 24 "bytes" 25 "fmt" 26 27 "github.com/m3db/m3/src/query/block" 28 "github.com/m3db/m3/src/query/functions/utils" 29 "github.com/m3db/m3/src/query/models" 30 ) 31 32 // TagJoinType joins the values of given tags using a given separator 33 // and adds them to a given destination tag. It can combine any number of tags. 34 // NB: This will override an existing tag if the given tag exists in the tag list. 35 const TagJoinType = "label_join" 36 37 func combineTagsWithSeparator(name []byte, separator []byte, values [][]byte) models.Tag { 38 l := len(values) 39 if l == 1 { 40 return models.Tag{Name: name, Value: values[0]} 41 } 42 43 b := bytes.Join(values, separator) 44 return models.Tag{Name: name, Value: b} 45 } 46 47 // Gets tag values from models. 48 // NB: duplicate tag names giving duplicate values is valid. 49 func tagsInOrder(names [][]byte, tags models.Tags) [][]byte { 50 orderedTags := make([][]byte, 0, len(names)) 51 for _, name := range names { 52 for _, tag := range tags.Tags { 53 if bytes.Equal(name, tag.Name) { 54 orderedTags = append(orderedTags, tag.Value) 55 } 56 } 57 } 58 59 return orderedTags 60 } 61 62 // ident function. 63 func identFunc( 64 m block.Metadata, 65 sm []block.SeriesMeta, 66 ) (block.Metadata, []block.SeriesMeta) { 67 return m, sm 68 } 69 70 func makeTagJoinFunc(params []string) (tagTransformFunc, error) { 71 if len(params) < 2 { 72 return nil, fmt.Errorf("invalid number of args for tag join: %d", len(params)) 73 } 74 75 // return shortcircuting noop function if no joining names are provided. 76 if len(params) == 3 { 77 return identFunc, nil 78 } 79 80 name := []byte(params[0]) 81 sep := []byte(params[1]) 82 tagNames := make([][]byte, len(params)-2) 83 uniqueTagCount := utils.UniqueCount(params[2:]) 84 for i, tag := range params[2:] { 85 tagNames[i] = ([]byte(tag)) 86 } 87 88 return func( 89 meta block.Metadata, 90 seriesMeta []block.SeriesMeta, 91 ) (block.Metadata, []block.SeriesMeta) { 92 matchingCommonTags := meta.Tags.TagsWithKeys(tagNames) 93 lMatching := len(matchingCommonTags.Tags) 94 // Optimization if all joining series are shared by the block, 95 // or if there is only a shared metadata and no single series metas. 96 if lMatching == uniqueTagCount || len(seriesMeta) == 0 { 97 if lMatching > 0 { 98 ordered := tagsInOrder(tagNames, matchingCommonTags) 99 meta.Tags = meta.Tags.AddOrUpdateTag(combineTagsWithSeparator(name, sep, ordered)) 100 } 101 102 return meta, seriesMeta 103 } 104 105 for i, meta := range seriesMeta { 106 seriesTags := meta.Tags.TagsWithKeys(tagNames) 107 seriesTags = seriesTags.Add(matchingCommonTags) 108 if seriesTags.Len() > 0 { 109 ordered := tagsInOrder(tagNames, seriesTags) 110 seriesMeta[i].Tags = seriesMeta[i].Tags. 111 AddOrUpdateTag(combineTagsWithSeparator(name, sep, ordered)) 112 } 113 } 114 115 return meta, seriesMeta 116 }, nil 117 }