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