github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/functions/utils/group_test.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 utils
    22  
    23  import (
    24  	"testing"
    25  
    26  	"github.com/m3db/m3/src/query/block"
    27  	"github.com/m3db/m3/src/query/models"
    28  	"github.com/m3db/m3/src/query/test/compare"
    29  )
    30  
    31  var collectTest = []struct {
    32  	name                       string
    33  	matching                   []string
    34  	tagLists                   []models.Tags
    35  	withTagsExpectedIndices    [][]int
    36  	withTagsExpectedTags       []models.Tags
    37  	withoutTagsExpectedIndices [][]int
    38  	withoutTagsExpectedTags    []models.Tags
    39  }{
    40  	{
    41  		"noMatching",
    42  		[]string{},
    43  		multiTagsFromMaps([]map[string]string{
    44  			{"a": "1"},
    45  			{"a": "1", "b": "2", "c": "4"},
    46  			{"b": "2"},
    47  			{"a": "1", "b": "2", "c": "3"},
    48  			{"a": "1", "b": "2", "d": "3"},
    49  			{"c": "d"},
    50  		}),
    51  		[][]int{{0, 1, 2, 3, 4, 5}},
    52  		[]models.Tags{models.EmptyTags()},
    53  		[][]int{{0}, {3}, {1}, {4}, {2}, {5}},
    54  		multiTagsFromMaps([]map[string]string{
    55  			{"a": "1"},
    56  			{"a": "1", "b": "2", "c": "3"},
    57  			{"a": "1", "b": "2", "c": "4"},
    58  			{"a": "1", "b": "2", "d": "3"},
    59  			{"b": "2"},
    60  			{"c": "d"},
    61  		}),
    62  	},
    63  	{
    64  		"no equal Matching",
    65  		[]string{"f", "g", "h"},
    66  		multiTagsFromMaps([]map[string]string{
    67  			{"a": "1"},
    68  			{"a": "1", "b": "2", "c": "4"},
    69  			{"b": "2"},
    70  			{"a": "1", "b": "2", "c": "3"},
    71  			{"a": "1", "b": "2", "d": "3"},
    72  			{"c": "d"},
    73  		}),
    74  		[][]int{{0, 1, 2, 3, 4, 5}},
    75  		multiTagsFromMaps([]map[string]string{{}}),
    76  		[][]int{{0}, {3}, {1}, {4}, {2}, {5}},
    77  		multiTagsFromMaps([]map[string]string{
    78  			{"a": "1"},
    79  			{"a": "1", "b": "2", "c": "3"},
    80  			{"a": "1", "b": "2", "c": "4"},
    81  			{"a": "1", "b": "2", "d": "3"},
    82  			{"b": "2"},
    83  			{"c": "d"},
    84  		}),
    85  	},
    86  	{
    87  		"one matching",
    88  		[]string{"a"},
    89  		multiTagsFromMaps([]map[string]string{
    90  			{"a": "1"},
    91  			{"a": "1", "b": "2", "c": "4"},
    92  			{"b": "2"},
    93  			{"a": "1", "b": "2", "c": "3"},
    94  			{"a": "1", "b": "2", "d": "3"},
    95  			{"c": "d"},
    96  		}),
    97  		[][]int{{2, 5}, {0, 1, 3, 4}},
    98  		multiTagsFromMaps([]map[string]string{
    99  			{},
   100  			{"a": "1"},
   101  		}),
   102  		[][]int{{0}, {2}, {3}, {1}, {4}, {5}},
   103  		multiTagsFromMaps([]map[string]string{
   104  			{},
   105  			{"b": "2"},
   106  			{"b": "2", "c": "3"},
   107  			{"b": "2", "c": "4"},
   108  			{"b": "2", "d": "3"},
   109  			{"c": "d"},
   110  		}),
   111  	},
   112  	{
   113  		"same tag matching",
   114  		[]string{"a", "a"},
   115  		multiTagsFromMaps([]map[string]string{
   116  			{"a": "1"},
   117  			{"a": "1", "b": "2", "c": "4"},
   118  			{"b": "2"},
   119  			{"a": "1", "b": "2", "c": "3"},
   120  			{"a": "1", "b": "2", "d": "3"},
   121  			{"c": "d"},
   122  		}),
   123  		[][]int{{2, 5}, {0, 1, 3, 4}},
   124  		multiTagsFromMaps([]map[string]string{
   125  			{},
   126  			{"a": "1"},
   127  		}),
   128  		[][]int{{0}, {2}, {3}, {1}, {4}, {5}},
   129  		multiTagsFromMaps([]map[string]string{
   130  			{},
   131  			{"b": "2"},
   132  			{"b": "2", "c": "3"},
   133  			{"b": "2", "c": "4"},
   134  			{"b": "2", "d": "3"},
   135  			{"c": "d"},
   136  		}),
   137  	},
   138  	{
   139  		"diffMatching",
   140  		[]string{"a"},
   141  		multiTagsFromMaps([]map[string]string{
   142  			{"a": "1"},
   143  			{"a": "2", "b": "2", "c": "4"},
   144  			{"a": "2"},
   145  			{"a": "1", "b": "2", "c": "3"},
   146  			{"a": "1", "b": "2", "d": "3"},
   147  			{"a": "d"},
   148  		}),
   149  		[][]int{{0, 3, 4}, {1, 2}, {5}},
   150  		multiTagsFromMaps([]map[string]string{
   151  			{"a": "1"},
   152  			{"a": "2"},
   153  			{"a": "d"},
   154  		}),
   155  		[][]int{{0, 2, 5}, {3}, {1}, {4}},
   156  		multiTagsFromMaps([]map[string]string{
   157  			{},
   158  			{"b": "2", "c": "3"},
   159  			{"b": "2", "c": "4"},
   160  			{"b": "2", "d": "3"},
   161  		}),
   162  	},
   163  	{
   164  		"someMatching",
   165  		[]string{"a", "b"},
   166  		multiTagsFromMaps([]map[string]string{
   167  			{"a": "1"},
   168  			{"a": "1", "b": "2", "c": "4"},
   169  			{"b": "2"},
   170  			{"a": "1", "b": "2", "c": "3"},
   171  			{"a": "1", "b": "2", "d": "3"},
   172  			{"c": "3"},
   173  		}),
   174  		[][]int{{5}, {0}, {1, 3, 4}, {2}},
   175  		multiTagsFromMaps([]map[string]string{
   176  			{},
   177  			{"a": "1"},
   178  			{"a": "1", "b": "2"},
   179  			{"b": "2"},
   180  		}),
   181  		[][]int{{0, 2}, {3, 5}, {1}, {4}},
   182  		multiTagsFromMaps([]map[string]string{
   183  			{},
   184  			{"c": "3"},
   185  			{"c": "4"},
   186  			{"d": "3"},
   187  		}),
   188  	},
   189  	{
   190  		"functionMatching",
   191  		[]string{"a"},
   192  		multiTagsFromMaps([]map[string]string{
   193  			{"a": "1"},
   194  			{"a": "1"},
   195  			{"a": "1", "b": "2"},
   196  			{"a": "2", "b": "2"},
   197  			{"b": "2"},
   198  			{"c": "3"},
   199  		}),
   200  		[][]int{{4, 5}, {0, 1, 2}, {3}},
   201  		multiTagsFromMaps([]map[string]string{
   202  			{},
   203  			{"a": "1"},
   204  			{"a": "2"},
   205  		}),
   206  		[][]int{{0, 1}, {2, 3, 4}, {5}},
   207  		multiTagsFromMaps([]map[string]string{
   208  			{},
   209  			{"b": "2"},
   210  			{"c": "3"},
   211  		}),
   212  	},
   213  	{
   214  		"different matching",
   215  		[]string{"a", "b"},
   216  		multiTagsFromMaps([]map[string]string{
   217  			{"a": "1", "c": "3", "d": "4"},
   218  			{"b": "1", "c": "3", "d": "5"},
   219  			{"b": "1", "c": "3", "d": "6"},
   220  		}),
   221  		[][]int{{0}, {1, 2}},
   222  		multiTagsFromMaps([]map[string]string{
   223  			{"a": "1"},
   224  			{"b": "1"},
   225  		}),
   226  		[][]int{{0}, {1}, {2}},
   227  		multiTagsFromMaps([]map[string]string{
   228  			{"c": "3", "d": "4"},
   229  			{"c": "3", "d": "5"},
   230  			{"c": "3", "d": "6"},
   231  		}),
   232  	},
   233  	{
   234  		"metrics name",
   235  		[]string{"a"},
   236  		multiTagsFromMaps([]map[string]string{
   237  			{"__name__": "foo", "a": "1"},
   238  			{"__name__": "foo", "a": "1", "b": "2", "c": "4"},
   239  			{"__name__": "foo", "b": "2"},
   240  			{"__name__": "foo", "a": "1", "b": "2", "c": "3"},
   241  			{"__name__": "foo", "a": "1", "b": "2", "d": "3"},
   242  			{"__name__": "foo", "c": "d"},
   243  		}),
   244  		[][]int{{2, 5}, {0, 1, 3, 4}},
   245  		multiTagsFromMaps([]map[string]string{
   246  			{},
   247  			{"a": "1"},
   248  		}),
   249  		[][]int{{0}, {2}, {3}, {1}, {4}, {5}},
   250  		multiTagsFromMaps([]map[string]string{
   251  			{},
   252  			{"b": "2"},
   253  			{"b": "2", "c": "3"},
   254  			{"b": "2", "c": "4"},
   255  			{"b": "2", "d": "3"},
   256  			{"c": "d"},
   257  		}),
   258  	},
   259  }
   260  
   261  func testCollect(t *testing.T, without bool) {
   262  	for _, tt := range collectTest {
   263  		name := tt.name + " with tags"
   264  		if without {
   265  			name = tt.name + " without tags"
   266  		}
   267  
   268  		t.Run(name, func(t *testing.T) {
   269  			metas := make([]block.SeriesMeta, len(tt.tagLists))
   270  			for i, tagList := range tt.tagLists {
   271  				metas[i] = block.SeriesMeta{Tags: tagList}
   272  			}
   273  
   274  			match := make([][]byte, len(tt.matching))
   275  			for i, m := range tt.matching {
   276  				match[i] = []byte(m)
   277  			}
   278  
   279  			bName := []byte(name)
   280  			buckets, collected := GroupSeries(match, without, bName, metas)
   281  			expectedTags := tt.withTagsExpectedTags
   282  			expectedIndicies := tt.withTagsExpectedIndices
   283  			if without {
   284  				expectedTags = tt.withoutTagsExpectedTags
   285  				expectedIndicies = tt.withoutTagsExpectedIndices
   286  			}
   287  
   288  			expectedMetas := make([]block.SeriesMeta, len(expectedTags))
   289  			for i, tags := range expectedTags {
   290  				expectedMetas[i] = block.SeriesMeta{
   291  					Tags: tags,
   292  					Name: bName,
   293  				}
   294  			}
   295  
   296  			compare.CompareListsInOrder(t, collected, expectedMetas, buckets, expectedIndicies)
   297  		})
   298  	}
   299  }
   300  
   301  func TestCollectWithTags(t *testing.T) {
   302  	testCollect(t, false)
   303  }
   304  
   305  func TestCollectWithoutTags(t *testing.T) {
   306  	testCollect(t, true)
   307  }
   308  
   309  func multiTagsFromMaps(tagMaps []map[string]string) []models.Tags {
   310  	tags := make([]models.Tags, len(tagMaps))
   311  	for i, m := range tagMaps {
   312  		tags[i] = models.EmptyTags()
   313  		for n, v := range m {
   314  			tags[i] = tags[i].AddTag(models.Tag{Name: []byte(n), Value: []byte(v)})
   315  		}
   316  	}
   317  
   318  	return tags
   319  }