1 // Copyright 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "google/cloud/bigtable/testing/table_integration_test.h"
16 #include "google/cloud/testing_util/assert_ok.h"
17 #include "google/cloud/testing_util/chrono_literals.h"
18
19 namespace google {
20 namespace cloud {
21 namespace bigtable {
22 inline namespace BIGTABLE_CLIENT_NS {
23 namespace {
24
25 using FilterIntegrationTest =
26 ::google::cloud::bigtable::testing::TableIntegrationTest;
27
28 /**
29 * Create some complex rows in @p table.
30 *
31 * Create the following rows in @p table, the magic values for the column
32 * families are defined above.
33 *
34 * | Row Key | Family | Column | Contents |
35 * | :---------------------- | :------ | :----- | :----------- |
36 * | "{prefix}/one-cell" | family1 | c | cell @ 3000 |
37 * | "{prefix}/two-cells" | family1 | c | cell @ 3000 |
38 * | "{prefix}/two-cells" | family1 | c2 | cell @ 3000 |
39 * | "{prefix}/many" | family1 | c | cell @ 0 |
40 * | "{prefix}/many" | family1 | c | cell @ 1000 |
41 * | "{prefix}/many" | family1 | c | cell @ 2000 |
42 * | "{prefix}/many" | family1 | c | cell @ 3000 |
43 * | "{prefix}/many-columns" | family1 | c0 | cell @ 3000 |
44 * | "{prefix}/many-columns" | family1 | c1 | cell @ 3000 |
45 * | "{prefix}/many-columns" | family1 | c2 | cell @ 3000 |
46 * | "{prefix}/many-columns" | family1 | c3 | cell @ 3000 |
47 * | "{prefix}/complex" | family1 | col0 | cell @ 3000, 6000 |
48 * | "{prefix}/complex" | family1 | col1 | cell @ 3000, 6000 |
49 * | "{prefix}/complex" | family1 | ... | cell @ 3000, 6000 |
50 * | "{prefix}/complex" | family1 | col9 | cell @ 3000, 6000 |
51 * | "{prefix}/complex" | family2 | col0 | cell @ 3000, 6000 |
52 * | "{prefix}/complex" | family2 | col1 | cell @ 3000, 6000 |
53 * | "{prefix}/complex" | family2 | ... | cell @ 3000, 6000 |
54 * | "{prefix}/complex" | family2 | col9 | cell @ 3000, 6000 |
55 *
56 */
CreateComplexRows(Table & table,std::string const & prefix)57 void CreateComplexRows(Table& table, std::string const& prefix) {
58 namespace bt = bigtable;
59 using ::google::cloud::testing_util::chrono_literals::operator"" _ms;
60
61 bt::BulkMutation mutation;
62 // Prepare a set of rows, with different numbers of cells, columns, and
63 // column families.
64 mutation.emplace_back(bt::SingleRowMutation(
65 prefix + "/one-cell", {bt::SetCell("family1", "c", 3_ms, "foo")}));
66 mutation.emplace_back(bt::SingleRowMutation(
67 prefix + "/two-cells", {bt::SetCell("family1", "c", 3_ms, "foo"),
68 bt::SetCell("family1", "c2", 3_ms, "foo")}));
69 mutation.emplace_back(bt::SingleRowMutation(
70 prefix + "/many", {bt::SetCell("family1", "c", 0_ms, "foo"),
71 bt::SetCell("family1", "c", 1_ms, "foo"),
72 bt::SetCell("family1", "c", 2_ms, "foo"),
73 bt::SetCell("family1", "c", 3_ms, "foo")}));
74 mutation.emplace_back(bt::SingleRowMutation(
75 prefix + "/many-columns", {bt::SetCell("family1", "c0", 3_ms, "foo"),
76 bt::SetCell("family1", "c1", 3_ms, "foo"),
77 bt::SetCell("family1", "c2", 3_ms, "foo"),
78 bt::SetCell("family1", "c3", 3_ms, "foo")}));
79 // This one is complicated: create a mutation with several families and
80 // columns.
81 bt::SingleRowMutation complex(prefix + "/complex");
82 for (int i = 0; i != 4; ++i) {
83 for (int j = 0; j != 10; ++j) {
84 complex.emplace_back(bt::SetCell("family" + std::to_string(i + 1),
85 "col" + std::to_string(j), 3_ms, "foo"));
86 complex.emplace_back(bt::SetCell("family" + std::to_string(i + 1),
87 "col" + std::to_string(j), 6_ms, "bar"));
88 }
89 }
90 mutation.emplace_back(std::move(complex));
91 auto failures = table.BulkApply(std::move(mutation));
92 ASSERT_TRUE(failures.empty());
93 };
94
TEST_F(FilterIntegrationTest,PassAll)95 TEST_F(FilterIntegrationTest, PassAll) {
96 auto table = GetTable();
97 std::string const row_key = "pass-all-row-key";
98 std::vector<Cell> expected{
99 {row_key, "family1", "c", 0, "v-c-0-0"},
100 {row_key, "family1", "c", 1000, "v-c-0-1"},
101 {row_key, "family1", "c", 2000, "v-c-0-2"},
102 {row_key, "family2", "c0", 0, "v-c0-0-0"},
103 {row_key, "family2", "c1", 1000, "v-c1-0-1"},
104 {row_key, "family2", "c1", 2000, "v-c1-0-2"},
105 };
106 CreateCells(table, expected);
107
108 auto actual = ReadRows(table, Filter::PassAllFilter());
109 CheckEqualUnordered(expected, actual);
110 }
111
TEST_F(FilterIntegrationTest,BlockAll)112 TEST_F(FilterIntegrationTest, BlockAll) {
113 auto table = GetTable();
114 std::string const row_key = "block-all-row-key";
115 std::vector<Cell> created{
116 {row_key, "family1", "c", 0, "v-c-0-0"},
117 {row_key, "family1", "c", 1000, "v-c-0-1"},
118 {row_key, "family1", "c", 2000, "v-c-0-2"},
119 {row_key, "family2", "c0", 0, "v-c0-0-0"},
120 {row_key, "family2", "c1", 1000, "v-c1-0-1"},
121 {row_key, "family2", "c1", 2000, "v-c1-0-2"},
122 };
123 CreateCells(table, created);
124 std::vector<Cell> expected{};
125
126 auto actual = ReadRows(table, Filter::BlockAllFilter());
127 CheckEqualUnordered(expected, actual);
128 }
129
TEST_F(FilterIntegrationTest,Latest)130 TEST_F(FilterIntegrationTest, Latest) {
131 auto table = GetTable();
132 std::string const row_key = "latest-row-key";
133 std::vector<Cell> created{
134 {row_key, "family1", "c", 0, "v-c-0-0"},
135 {row_key, "family1", "c", 1000, "v-c-0-1"},
136 {row_key, "family1", "c", 2000, "v-c-0-2"},
137 {row_key, "family2", "c0", 0, "v-c0-0-0"},
138 {row_key, "family2", "c1", 1000, "v-c1-0-1"},
139 {row_key, "family2", "c1", 2000, "v-c1-0-2"},
140 {row_key, "family2", "c1", 3000, "v-c1-0-3"},
141 };
142 CreateCells(table, created);
143 std::vector<Cell> expected{
144 {row_key, "family1", "c", 1000, "v-c-0-1"},
145 {row_key, "family1", "c", 2000, "v-c-0-2"},
146 {row_key, "family2", "c0", 0, "v-c0-0-0"},
147 {row_key, "family2", "c1", 2000, "v-c1-0-2"},
148 {row_key, "family2", "c1", 3000, "v-c1-0-3"},
149 };
150
151 auto actual = ReadRows(table, Filter::Latest(2));
152 CheckEqualUnordered(expected, actual);
153 }
154
TEST_F(FilterIntegrationTest,FamilyRegex)155 TEST_F(FilterIntegrationTest, FamilyRegex) {
156 auto table = GetTable();
157 std::string const row_key = "family-regex-row-key";
158 std::vector<Cell> created{
159 {row_key, "family1", "c2", 0, "bar"},
160 {row_key, "family1", "c", 0, "bar"},
161 {row_key, "family2", "c", 0, "bar"},
162 {row_key, "family3", "c", 0, "bar"},
163 {row_key, "family3", "c2", 0, "bar"},
164 {row_key, "family4", "c2", 0, "bar"},
165 };
166 CreateCells(table, created);
167 std::vector<Cell> expected{
168 {row_key, "family1", "c2", 0, "bar"},
169 {row_key, "family1", "c", 0, "bar"},
170 {row_key, "family3", "c", 0, "bar"},
171 {row_key, "family3", "c2", 0, "bar"},
172 };
173
174 auto actual = ReadRows(table, Filter::FamilyRegex("family[13]"));
175 CheckEqualUnordered(expected, actual);
176 }
177
TEST_F(FilterIntegrationTest,ColumnRegex)178 TEST_F(FilterIntegrationTest, ColumnRegex) {
179 auto table = GetTable();
180 std::string const row_key = "column-regex-row-key";
181 std::vector<Cell> created{
182 {row_key, "family1", "abc", 0, "bar"},
183 {row_key, "family2", "bcd", 0, "bar"},
184 {row_key, "family3", "abc", 0, "bar"},
185 {row_key, "family4", "def", 0, "bar"},
186 {row_key, "family1", "fgh", 0, "bar"},
187 {row_key, "family2", "hij", 0, "bar"},
188 };
189 CreateCells(table, created);
190 std::vector<Cell> expected{
191 {row_key, "family1", "abc", 0, "bar"},
192 {row_key, "family3", "abc", 0, "bar"},
193 {row_key, "family1", "fgh", 0, "bar"},
194 {row_key, "family2", "hij", 0, "bar"},
195 };
196
197 auto actual = ReadRows(table, Filter::ColumnRegex("(abc|.*h.*)"));
198 CheckEqualUnordered(expected, actual);
199 }
200
TEST_F(FilterIntegrationTest,ColumnRange)201 TEST_F(FilterIntegrationTest, ColumnRange) {
202 auto table = GetTable();
203 std::string const row_key = "column-range-row-key";
204 std::vector<Cell> created{
205 {row_key, "family1", "a00", 0, "bar"},
206 {row_key, "family1", "b00", 0, "bar"},
207 {row_key, "family1", "b01", 0, "bar"},
208 {row_key, "family1", "b02", 0, "bar"},
209 {row_key, "family2", "a00", 0, "bar"},
210 {row_key, "family2", "b01", 0, "bar"},
211 {row_key, "family2", "b00", 0, "bar"},
212 };
213 CreateCells(table, created);
214 std::vector<Cell> expected{
215 {row_key, "family1", "b00", 0, "bar"},
216 {row_key, "family1", "b01", 0, "bar"},
217 };
218
219 auto actual = ReadRows(table, Filter::ColumnRange("family1", "b00", "b02"));
220 CheckEqualUnordered(expected, actual);
221 }
222
TEST_F(FilterIntegrationTest,TimestampRange)223 TEST_F(FilterIntegrationTest, TimestampRange) {
224 auto table = GetTable();
225 std::string const row_key = "timestamp-range-row-key";
226 std::vector<Cell> created{
227 {row_key, "family1", "c0", 1000, "v1000"},
228 {row_key, "family2", "c1", 2000, "v2000"},
229 {row_key, "family3", "c2", 3000, "v3000"},
230 {row_key, "family1", "c3", 4000, "v4000"},
231 {row_key, "family2", "c4", 4000, "v5000"},
232 {row_key, "family3", "c5", 6000, "v6000"},
233 };
234 CreateCells(table, created);
235 std::vector<Cell> expected{
236 {row_key, "family3", "c2", 3000, "v3000"},
237 {row_key, "family1", "c3", 4000, "v4000"},
238 {row_key, "family2", "c4", 4000, "v5000"},
239 };
240
241 auto actual =
242 ReadRows(table, Filter::TimestampRange(std::chrono::milliseconds(3),
243 std::chrono::milliseconds(6)));
244 CheckEqualUnordered(expected, actual);
245 }
246
TEST_F(FilterIntegrationTest,RowKeysRegex)247 TEST_F(FilterIntegrationTest, RowKeysRegex) {
248 auto table = GetTable();
249 std::string const row_key = "row-key-regex-row-key";
250 std::vector<Cell> created{
251 {row_key + "/abc0", "family1", "c0", 1000, "v1000"},
252 {row_key + "/bcd0", "family2", "c1", 2000, "v2000"},
253 {row_key + "/abc1", "family3", "c2", 3000, "v3000"},
254 {row_key + "/fgh0", "family1", "c3", 4000, "v4000"},
255 {row_key + "/hij0", "family2", "c4", 4000, "v5000"},
256 {row_key + "/hij1", "family3", "c5", 6000, "v6000"},
257 };
258 CreateCells(table, created);
259 std::vector<Cell> expected{
260 {row_key + "/bcd0", "family2", "c1", 2000, "v2000"},
261 };
262
263 auto actual = ReadRows(table, Filter::RowKeysRegex(row_key + "/bc.*"));
264 CheckEqualUnordered(expected, actual);
265 }
266
TEST_F(FilterIntegrationTest,ValueRegex)267 TEST_F(FilterIntegrationTest, ValueRegex) {
268 auto table = GetTable();
269 std::string const prefix = "value-regex-prefix";
270 std::vector<Cell> created{
271 {prefix + "/abc0", "family1", "c0", 1000, "v1000"},
272 {prefix + "/bcd0", "family2", "c1", 2000, "v2000"},
273 {prefix + "/abc1", "family3", "c2", 3000, "v3000"},
274 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
275 {prefix + "/hij0", "family2", "c4", 4000, "v5000"},
276 {prefix + "/hij1", "family3", "c5", 6000, "v6000"},
277 };
278 CreateCells(table, created);
279 std::vector<Cell> expected{
280 {prefix + "/abc1", "family3", "c2", 3000, "v3000"},
281 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
282 };
283
284 auto actual = ReadRows(table, Filter::ValueRegex("v[34][0-9].*"));
285 CheckEqualUnordered(expected, actual);
286 }
287
TEST_F(FilterIntegrationTest,ValueRange)288 TEST_F(FilterIntegrationTest, ValueRange) {
289 auto table = GetTable();
290 std::string const prefix = "value-range-prefix";
291 std::vector<Cell> created{
292 {prefix + "/abc0", "family1", "c0", 1000, "v1000"},
293 {prefix + "/bcd0", "family2", "c1", 2000, "v2000"},
294 {prefix + "/abc1", "family3", "c2", 3000, "v3000"},
295 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
296 {prefix + "/hij0", "family2", "c4", 4000, "v5000"},
297 {prefix + "/hij1", "family3", "c5", 6000, "v6000"},
298 };
299 CreateCells(table, created);
300 std::vector<Cell> expected{
301 {prefix + "/bcd0", "family2", "c1", 2000, "v2000"},
302 {prefix + "/abc1", "family3", "c2", 3000, "v3000"},
303 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
304 {prefix + "/hij0", "family2", "c4", 4000, "v5000"},
305 };
306
307 auto actual = ReadRows(table, Filter::ValueRange("v2000", "v6000"));
308 CheckEqualUnordered(expected, actual);
309 }
310
TEST_F(FilterIntegrationTest,CellsRowLimit)311 TEST_F(FilterIntegrationTest, CellsRowLimit) {
312 auto table = GetTable();
313 std::string const prefix = "cell-row-limit-prefix";
314 ASSERT_NO_FATAL_FAILURE(CreateComplexRows(table, prefix));
315
316 auto result = ReadRows(table, Filter::CellsRowLimit(3));
317
318 std::map<RowKeyType, int> actual;
319 for (auto const& cell : result) {
320 auto inserted = actual.emplace(cell.row_key(), 0);
321 inserted.first->second++;
322 }
323 std::map<RowKeyType, int> expected{{RowKeyType(prefix + "/one-cell"), 1},
324 {RowKeyType(prefix + "/two-cells"), 2},
325 {RowKeyType(prefix + "/many"), 3},
326 {RowKeyType(prefix + "/many-columns"), 3},
327 {RowKeyType(prefix + "/complex"), 3}};
328
329 EXPECT_THAT(expected, ::testing::ContainerEq(actual));
330 }
331
TEST_F(FilterIntegrationTest,CellsRowOffset)332 TEST_F(FilterIntegrationTest, CellsRowOffset) {
333 auto table = GetTable();
334 std::string const prefix = "cell-row-offset-prefix";
335 ASSERT_NO_FATAL_FAILURE(CreateComplexRows(table, prefix));
336
337 // Search in the range [row_key_prefix, row_key_prefix + "0"), we used '/' as
338 // the separator and the successor of "/" is "0".
339 auto result = ReadRows(table, Filter::CellsRowOffset(2));
340
341 std::map<RowKeyType, int> actual;
342 for (auto const& cell : result) {
343 auto inserted = actual.emplace(cell.row_key(), 0);
344 inserted.first->second++;
345 }
346 std::map<RowKeyType, int> expected{{RowKeyType(prefix + "/many"), 2},
347 {RowKeyType(prefix + "/many-columns"), 2},
348 {RowKeyType(prefix + "/complex"), 78}};
349
350 EXPECT_THAT(expected, ::testing::ContainerEq(actual));
351 }
352
TEST_F(FilterIntegrationTest,RowSample)353 TEST_F(FilterIntegrationTest, RowSample) {
354 using ::google::cloud::testing_util::chrono_literals::operator"" _ms;
355 // TODO(#151) - remove workarounds for emulator bug(s).
356 if (UsingCloudBigtableEmulator()) {
357 return;
358 }
359
360 auto table = GetTable();
361 std::string const prefix = "row-sample-prefix";
362
363 int constexpr kRowCount = 20000;
364 BulkMutation bulk;
365 for (int row = 0; row != kRowCount; ++row) {
366 std::string row_key = prefix + "/" + std::to_string(row);
367 bulk.emplace_back(
368 SingleRowMutation(row_key, SetCell("family1", "col", 4_ms, "foo")));
369 }
370 auto failures = table.BulkApply(std::move(bulk));
371 ASSERT_TRUE(failures.empty());
372
373 // We want to check that the sampling rate was "more or less" the prescribed
374 // value. We use 5% as the allowed error, this is arbitrary. If we wanted to
375 // get serious about testing the sampling rate, we would do some statistics.
376 // We do not really need to, because we are testing the library, not the
377 // server. But for what it's worth, the outline would be:
378 //
379 // - Model sampling as a binomial process.
380 // - Perform power analysis to decide the size of the sample.
381 // - Perform hypothesis testing: is the actual sampling rate != that the
382 // prescribed rate (and sufficiently different, i.e., the effect is large
383 // enough).
384 //
385 // For what is worth, the sample size is large enough to detect effects of 2%
386 // at the conventional significance and power levels. In R:
387 //
388 // ```R
389 // require(pwr)
390 // pwr.p.test(h = ES.h(p1 = 0.63, p2 = 0.65), sig.level = 0.05,
391 // power=0.80, alternative="two.sided")
392 // ```
393 //
394 // h = 0.04167045
395 // n = 4520.123
396 // sig.level = 0.05
397 // power = 0.8
398 // alternative = two.sided
399 //
400 constexpr double kSampleRate = 0.75;
401 constexpr double kAllowedError = 0.05;
402 auto const min_count = static_cast<std::size_t>(
403 std::floor((kSampleRate - kAllowedError) * kRowCount));
404 auto const max_count = static_cast<std::size_t>(
405 std::ceil((kSampleRate + kAllowedError) * kRowCount));
406
407 // Search in the range [row_key_prefix, row_key_prefix + "0"), we used '/' as
408 // the separator and the successor of "/" is "0".
409 auto result = ReadRows(table, Filter::RowSample(kSampleRate));
410 EXPECT_LE(min_count, result.size());
411 EXPECT_GE(max_count, result.size());
412 }
413
TEST_F(FilterIntegrationTest,StripValueTransformer)414 TEST_F(FilterIntegrationTest, StripValueTransformer) {
415 auto table = GetTable();
416 std::string const prefix = "strip-value-transformer-prefix";
417 std::vector<Cell> created{
418 {prefix + "/abc0", "family1", "c0", 1000, "v1000"},
419 {prefix + "/bcd0", "family2", "c1", 2000, "v2000"},
420 {prefix + "/abc1", "family3", "c2", 3000, "v3000"},
421 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
422 {prefix + "/hij0", "family2", "c4", 4000, "v5000"},
423 {prefix + "/hij1", "family3", "c5", 6000, "v6000"},
424 };
425 CreateCells(table, created);
426 std::vector<Cell> expected{
427 {prefix + "/abc0", "family1", "c0", 1000, ""},
428 {prefix + "/bcd0", "family2", "c1", 2000, ""},
429 {prefix + "/abc1", "family3", "c2", 3000, ""},
430 {prefix + "/fgh0", "family1", "c3", 4000, ""},
431 {prefix + "/hij0", "family2", "c4", 4000, ""},
432 {prefix + "/hij1", "family3", "c5", 6000, ""},
433 };
434
435 auto actual = ReadRows(table, Filter::StripValueTransformer());
436 CheckEqualUnordered(expected, actual);
437 }
438
TEST_F(FilterIntegrationTest,ApplyLabelTransformer)439 TEST_F(FilterIntegrationTest, ApplyLabelTransformer) {
440 // TODO(#151) - remove workarounds for emulator bug(s).
441 if (UsingCloudBigtableEmulator()) {
442 return;
443 }
444
445 auto table = GetTable();
446 std::string const prefix = "apply-label-transformer-prefix";
447 std::vector<Cell> created{
448 {prefix + "/abc0", "family1", "c0", 1000, "v1000"},
449 {prefix + "/bcd0", "family2", "c1", 2000, "v2000"},
450 {prefix + "/abc1", "family3", "c2", 3000, "v3000"},
451 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
452 {prefix + "/hij0", "family2", "c4", 4000, "v5000"},
453 {prefix + "/hij1", "family3", "c5", 6000, "v6000"},
454 };
455 CreateCells(table, created);
456 std::vector<Cell> expected{
457 {prefix + "/abc0", "family1", "c0", 1000, "v1000", {"foo"}},
458 {prefix + "/bcd0", "family2", "c1", 2000, "v2000", {"foo"}},
459 {prefix + "/abc1", "family3", "c2", 3000, "v3000", {"foo"}},
460 {prefix + "/fgh0", "family1", "c3", 4000, "v4000", {"foo"}},
461 {prefix + "/hij0", "family2", "c4", 4000, "v5000", {"foo"}},
462 {prefix + "/hij1", "family3", "c5", 6000, "v6000", {"foo"}},
463 };
464
465 auto actual = ReadRows(table, Filter::ApplyLabelTransformer("foo"));
466 CheckEqualUnordered(expected, actual);
467 }
468
TEST_F(FilterIntegrationTest,Condition)469 TEST_F(FilterIntegrationTest, Condition) {
470 auto table = GetTable();
471 std::string const prefix = "condition-prefix";
472 std::vector<Cell> created{
473 {prefix + "/abc0", "family1", "c0", 1000, "v1000"},
474 {prefix + "/bcd0", "family2", "c1", 2000, "v2000"},
475 {prefix + "/abc1", "family3", "c2", 3000, "v3000"},
476 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
477 {prefix + "/hij0", "family2", "c4", 4000, "v5000"},
478 {prefix + "/hij1", "family3", "c5", 6000, "v6000"},
479 };
480 CreateCells(table, created);
481 std::vector<Cell> expected{
482 {prefix + "/abc0", "family1", "c0", 1000, "v1000"},
483 {prefix + "/bcd0", "family2", "c1", 2000, ""},
484 {prefix + "/abc1", "family3", "c2", 3000, ""},
485 {prefix + "/fgh0", "family1", "c3", 4000, ""},
486 {prefix + "/hij0", "family2", "c4", 4000, "v5000"},
487 };
488
489 using F = Filter;
490 auto actual =
491 ReadRows(table, F::Condition(F::ValueRangeClosed("v2000", "v4000"),
492 F::StripValueTransformer(),
493 F::FamilyRegex("family[12]")));
494 CheckEqualUnordered(expected, actual);
495 }
496
TEST_F(FilterIntegrationTest,Chain)497 TEST_F(FilterIntegrationTest, Chain) {
498 auto table = GetTable();
499 std::string const prefix = "chain-prefix";
500 std::vector<Cell> created{
501 {prefix + "/abc0", "family1", "c0", 1000, "v1000"},
502 {prefix + "/bcd0", "family2", "c1", 2000, "v2000"},
503 {prefix + "/abc1", "family3", "c2", 3000, "v3000"},
504 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
505 {prefix + "/hij0", "family2", "c4", 4000, "v5000"},
506 {prefix + "/hij1", "family3", "c5", 6000, "v6000"},
507 };
508 CreateCells(table, created);
509 std::vector<Cell> expected{
510 {prefix + "/fgh0", "family1", "c3", 4000, ""},
511 };
512
513 using F = Filter;
514 auto actual =
515 ReadRows(table, F::Chain(F::ValueRangeClosed("v2000", "v5000"),
516 F::StripValueTransformer(),
517 F::ColumnRangeClosed("family1", "c2", "c3")));
518 CheckEqualUnordered(expected, actual);
519 }
520
TEST_F(FilterIntegrationTest,ChainFromRange)521 TEST_F(FilterIntegrationTest, ChainFromRange) {
522 auto table = GetTable();
523 std::string const prefix = "chain-prefix";
524 std::vector<Cell> created{
525 {prefix + "/abc0", "family1", "c0", 1000, "v1000"},
526 {prefix + "/bcd0", "family2", "c1", 2000, "v2000"},
527 {prefix + "/abc1", "family3", "c2", 3000, "v3000"},
528 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
529 {prefix + "/hij0", "family2", "c4", 4000, "v5000"},
530 {prefix + "/hij1", "family3", "c5", 6000, "v6000"},
531 };
532 CreateCells(table, created);
533 std::vector<Cell> expected{
534 {prefix + "/fgh0", "family1", "c3", 4000, ""},
535 };
536
537 using F = Filter;
538 auto range = {F::ValueRangeClosed("v2000", "v5000"),
539 F::StripValueTransformer(),
540 F::ColumnRangeClosed("family1", "c2", "c3")};
541 auto actual = ReadRows(table, F::ChainFromRange(range.begin(), range.end()));
542 CheckEqualUnordered(expected, actual);
543 }
544
TEST_F(FilterIntegrationTest,Interleave)545 TEST_F(FilterIntegrationTest, Interleave) {
546 auto table = GetTable();
547 std::string const prefix = "interleave-prefix";
548 std::vector<Cell> created{
549 {prefix + "/abc0", "family1", "c0", 1000, "v1000"},
550 {prefix + "/bcd0", "family2", "c1", 2000, "v2000"},
551 {prefix + "/abc1", "family3", "c2", 3000, "v3000"},
552 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
553 {prefix + "/hij0", "family2", "c4", 4000, "v5000"},
554 {prefix + "/hij1", "family3", "c5", 6000, "v6000"},
555 };
556 CreateCells(table, created);
557 std::vector<Cell> expected{
558 {prefix + "/bcd0", "family2", "c1", 2000, ""},
559 {prefix + "/abc1", "family3", "c2", 3000, ""},
560 {prefix + "/fgh0", "family1", "c3", 4000, ""},
561 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
562 {prefix + "/hij0", "family2", "c4", 4000, ""},
563 };
564
565 using F = Filter;
566 auto actual = ReadRows(
567 table, F::Interleave(F::Chain(F::ValueRangeClosed("v2000", "v5000"),
568 F::StripValueTransformer()),
569 F::ColumnRangeClosed("family1", "c2", "c3")));
570 CheckEqualUnordered(expected, actual);
571 }
572
TEST_F(FilterIntegrationTest,InterleaveFromRange)573 TEST_F(FilterIntegrationTest, InterleaveFromRange) {
574 auto table = GetTable();
575 std::string const prefix = "interleave-prefix";
576 std::vector<Cell> created{
577 {prefix + "/abc0", "family1", "c0", 1000, "v1000"},
578 {prefix + "/bcd0", "family2", "c1", 2000, "v2000"},
579 {prefix + "/abc1", "family3", "c2", 3000, "v3000"},
580 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
581 {prefix + "/hij0", "family2", "c4", 4000, "v5000"},
582 {prefix + "/hij1", "family3", "c5", 6000, "v6000"},
583 };
584 CreateCells(table, created);
585 std::vector<Cell> expected{
586 {prefix + "/bcd0", "family2", "c1", 2000, ""},
587 {prefix + "/abc1", "family3", "c2", 3000, ""},
588 {prefix + "/fgh0", "family1", "c3", 4000, ""},
589 {prefix + "/fgh0", "family1", "c3", 4000, "v4000"},
590 {prefix + "/hij0", "family2", "c4", 4000, ""},
591 };
592
593 using F = Filter;
594 std::vector<F> filter_collection{
595 F::Chain(F::ValueRangeClosed("v2000", "v5000"),
596 F::StripValueTransformer()),
597 F::ColumnRangeClosed("family1", "c2", "c3")};
598 auto actual =
599 ReadRows(table, F::InterleaveFromRange(filter_collection.begin(),
600 filter_collection.end()));
601 CheckEqualUnordered(expected, actual);
602 }
603
604 } // namespace
605 } // namespace BIGTABLE_CLIENT_NS
606 } // namespace bigtable
607 } // namespace cloud
608 } // namespace google
609
main(int argc,char * argv[])610 int main(int argc, char* argv[]) {
611 ::testing::InitGoogleMock(&argc, argv);
612 (void)::testing::AddGlobalTestEnvironment(
613 new ::google::cloud::bigtable::testing::TableTestEnvironment);
614
615 return RUN_ALL_TESTS();
616 }
617