From 750a20b6d808ec4f67cc13d14c9e76733bf5b589 Mon Sep 17 00:00:00 2001 From: ajaaym <34161822+ajaaym@users.noreply.github.com> Date: Fri, 28 Sep 2018 11:52:44 -0400 Subject: [PATCH] Bigtable: Read rows filter (#1253) * read rows filter * fix test case * Filter * add comments * add espace for literal regex * Fix failing test * add snippet test * Fix doc * code review updates * moved builder in to its own namespace * add snippet test * arrange use statement * fix failing test case * Fix documentation --- Bigtable/src/Filter.php | 421 ++++++++++++++++++ Bigtable/src/Filter/Builder/FamilyFilter.php | 83 ++++ Bigtable/src/Filter/Builder/KeyFilter.php | 115 +++++ Bigtable/src/Filter/Builder/LimitFilter.php | 79 ++++ Bigtable/src/Filter/Builder/OffsetFilter.php | 55 +++ .../src/Filter/Builder/QualifierFilter.php | 102 +++++ Bigtable/src/Filter/Builder/Range.php | 207 +++++++++ Bigtable/src/Filter/Builder/RegexTrait.php | 91 ++++ .../src/Filter/Builder/TimestampFilter.php | 49 ++ Bigtable/src/Filter/Builder/ValueFilter.php | 111 +++++ Bigtable/src/Filter/ChainFilter.php | 75 ++++ Bigtable/src/Filter/ConditionFilter.php | 137 ++++++ Bigtable/src/Filter/FilterInterface.php | 36 ++ Bigtable/src/Filter/InterleaveFilter.php | 75 ++++ Bigtable/src/Filter/QualifierRangeFilter.php | 87 ++++ Bigtable/src/Filter/SimpleFilter.php | 57 +++ Bigtable/src/Filter/TimestampRangeFilter.php | 72 +++ Bigtable/src/Filter/ValueRangeFilter.php | 72 +++ .../Filter/Builder/FamilyFilterTest.php | 62 +++ .../Snippet/Filter/Builder/KeyFilterTest.php | 71 +++ .../Filter/Builder/LimitFilterTest.php | 62 +++ .../Filter/Builder/OffsetFilterTest.php | 53 +++ .../Filter/Builder/QualifierFilterTest.php | 77 ++++ .../Filter/Builder/TimestampFilterTest.php | 59 +++ .../Filter/Builder/ValueFilterTest.php | 76 ++++ .../tests/Snippet/Filter/ChainFilterTest.php | 55 +++ .../Snippet/Filter/ConditionFilterTest.php | 81 ++++ .../Snippet/Filter/InterleaveFilterTest.php | 55 +++ .../Filter/QualifierRangeFilterTest.php | 35 ++ .../Filter/TimestampRangeFilterTest.php | 35 ++ .../Snippet/Filter/ValueRangeFilterTest.php | 35 ++ Bigtable/tests/Snippet/FilterTest.php | 224 ++++++++++ .../Unit/Filter/Builder/FamilyFilterTest.php | 55 +++ .../Unit/Filter/Builder/KeyFilterTest.php | 82 ++++ .../Unit/Filter/Builder/LimitFilterTest.php | 53 +++ .../Unit/Filter/Builder/OffsetFilterTest.php | 45 ++ .../Filter/Builder/QualifierFilterTest.php | 64 +++ .../tests/Unit/Filter/Builder/RangeTest.php | 169 +++++++ .../Unit/Filter/Builder/RegexTraitTest.php | 77 ++++ .../Filter/Builder/TimestampFilterTest.php | 35 ++ .../Unit/Filter/Builder/ValueFilterTest.php | 69 +++ .../tests/Unit/Filter/ChainFilterTest.php | 49 ++ .../tests/Unit/Filter/ConditionFilterTest.php | 73 +++ .../Unit/Filter/InterleaveFilterTest.php | 54 +++ .../Unit/Filter/QualifierRangeFilterTest.php | 78 ++++ .../tests/Unit/Filter/SimpleFilterTest.php | 36 ++ .../Unit/Filter/TimestampRangeFilterTest.php | 67 +++ .../Unit/Filter/ValueRangeFilterTest.php | 68 +++ Bigtable/tests/Unit/FilterTest.php | 138 ++++++ 49 files changed, 4116 insertions(+) create mode 100644 Bigtable/src/Filter.php create mode 100644 Bigtable/src/Filter/Builder/FamilyFilter.php create mode 100644 Bigtable/src/Filter/Builder/KeyFilter.php create mode 100644 Bigtable/src/Filter/Builder/LimitFilter.php create mode 100644 Bigtable/src/Filter/Builder/OffsetFilter.php create mode 100644 Bigtable/src/Filter/Builder/QualifierFilter.php create mode 100644 Bigtable/src/Filter/Builder/Range.php create mode 100644 Bigtable/src/Filter/Builder/RegexTrait.php create mode 100644 Bigtable/src/Filter/Builder/TimestampFilter.php create mode 100644 Bigtable/src/Filter/Builder/ValueFilter.php create mode 100644 Bigtable/src/Filter/ChainFilter.php create mode 100644 Bigtable/src/Filter/ConditionFilter.php create mode 100644 Bigtable/src/Filter/FilterInterface.php create mode 100644 Bigtable/src/Filter/InterleaveFilter.php create mode 100644 Bigtable/src/Filter/QualifierRangeFilter.php create mode 100644 Bigtable/src/Filter/SimpleFilter.php create mode 100644 Bigtable/src/Filter/TimestampRangeFilter.php create mode 100644 Bigtable/src/Filter/ValueRangeFilter.php create mode 100644 Bigtable/tests/Snippet/Filter/Builder/FamilyFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/Builder/KeyFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/Builder/LimitFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/Builder/OffsetFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/Builder/QualifierFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/Builder/TimestampFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/Builder/ValueFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/ChainFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/ConditionFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/InterleaveFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/QualifierRangeFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/TimestampRangeFilterTest.php create mode 100644 Bigtable/tests/Snippet/Filter/ValueRangeFilterTest.php create mode 100644 Bigtable/tests/Snippet/FilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/Builder/FamilyFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/Builder/KeyFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/Builder/LimitFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/Builder/OffsetFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/Builder/QualifierFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/Builder/RangeTest.php create mode 100644 Bigtable/tests/Unit/Filter/Builder/RegexTraitTest.php create mode 100644 Bigtable/tests/Unit/Filter/Builder/TimestampFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/Builder/ValueFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/ChainFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/ConditionFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/InterleaveFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/QualifierRangeFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/SimpleFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/TimestampRangeFilterTest.php create mode 100644 Bigtable/tests/Unit/Filter/ValueRangeFilterTest.php create mode 100644 Bigtable/tests/Unit/FilterTest.php diff --git a/Bigtable/src/Filter.php b/Bigtable/src/Filter.php new file mode 100644 index 000000000000..675c745d1d49 --- /dev/null +++ b/Bigtable/src/Filter.php @@ -0,0 +1,421 @@ +addFilter(Filter::qualifier()->regex('prefix.*')) + * ->addFilter(Filter::limit()->cellsPerRow(10)); + * + * $rows = $dataClient->readRows([ + * 'filter' => $rowFilter + * ]); + * + * foreach ($rows as $row) { + * print_r($row) . PHP_EOL; + * } + * ``` + */ +class Filter +{ + /** + * Creates an empty chain filter. + * + * Filters can be added to the chain by invoking + * {@see Google\Cloud\Bigtable\Filter\ChainFilter::addFilter()}. + * + * The filters are applied in sequence, progressively narrowing the results. + * The full chain is executed atomically. + * + * Conceptually, the process looks like the following: + * `in row -> filter0 -> intermediate row -> filter1 -> ... -> filterN -> out row`. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::chain() + * ->addFilter(Filter::qualifier()->regex('prefix.*')) + * ->addFilter(Filter::limit()->cellsPerRow(10)); + * ``` + * + * @return ChainFilter + */ + public static function chain() + { + return new ChainFilter(); + } + + /** + * Creates an empty interleave filter. + * + * Filters can be added to the interleave by invoking + * {@see Google\Cloud\Bigtable\Filter\InterleaveFilter::addFilter()}. + * + * The supplied filters all process a copy of the input row, and the + * results are pooled, sorted, and combined into a single output row. If + * multiple cells are produced with the same column and timestamp, they will + * all appear in the output row in an unspecified mutual order. The full + * chain is executed atomically. + * + * Consider the following example, with three filters: + * ``` + * input row + * | + * ----------------------------------------------------- + * | | | + * filter1 filter2 filter3 + * | | | + * 1: foo,bar,10,x foo,bar,10,z far,bar,7,a + * 2: foo,blah,11,z far,blah,5,x far,blah,5,x + * | | | + * ----------------------------------------------------- + * | + * 1: foo,bar,10,z // could have switched with #2 + * 2: foo,bar,10,x // could have switched with #1 + * 3: foo,blah,11,z + * 4: far,bar,7,a + * 5: far,blah,5,x // identical to #6 + * 6: far,blah,5,x // identical to #5 + * ``` + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::interleave() + * ->addFilter(Filter::key()->regex('prefix.*')) + * ->addFilter(Filter::sink()); + * ``` + * + * @return InterleaveFilter + */ + public static function interleave() + { + return new InterleaveFilter(); + } + + /** + * Creates a condition filter. + * + * If the result of predicate filter outputs any cells the filter configured + * by {@see Google\Cloud\Bigtable\Filter\ConditionFilter::then()} will be + * applied. Conversely, if the predicate results in no cells, the filter + * configured by + * {@see Google\Cloud\Bigtable\Filter\ConditionFilter::otherwise()} will + * then be applied instead. + * + * IMPORTANT NOTE: The predicate filter does not execute atomically with the + * {@see Google\Cloud\Bigtable\Filter\ConditionFilter::then()} + * and {@see Google\Cloud\Bigtable\Filter\ConditionFilter::otherwise()} + * filters, which may lead to inconsistent or unexpected results. + * Additionally, {@see Google\Cloud\Bigtable\Filter\ConditionFilter} may + * have poor performance, especially when filters are set for the + * {@see Google\Cloud\Bigtable\Filter\ConditionFilter::otherwise()}. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::condition(Filter::key()->regex('prefix.*')) + * ->then(Filter::label('hasPrefix')) + * ->otherwise(Filter::value()->strip()); + * ``` + * + * @param FilterInterface $predicateFilter A predicate filter. + * @return ConditionFilter + */ + public static function condition(FilterInterface $predicateFilter) + { + return new ConditionFilter($predicateFilter); + } + + /** + * Returns a builder used to configure row key filters. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::key() + * ->regex('prefix.*'); + * ``` + * + * @return KeyFilter + */ + public static function key() + { + return new KeyFilter(); + } + + /** + * Returns a builder used to configure column family filters. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::family() + * ->regex('prefix.*'); + * ``` + * + * @return FamilyFilter + */ + public static function family() + { + return new FamilyFilter(); + } + + /** + * Returns a builder used to configure column qualifier filters. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::qualifier() + * ->regex('prefix.*'); + * ``` + * + * @return QualifierFilter + */ + public static function qualifier() + { + return new QualifierFilter(); + } + + /** + * Returns a builder used to configure timestamp related filters. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::timestamp() + * ->range() + * ->of(1536766964380000, 1536766964383000); + * ``` + * + * @return TimestampFilter + */ + public static function timestamp() + { + return new TimestampFilter(); + } + + /** + * Returns a builder used to configure value related filters. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::value() + * ->range() + * ->of('a', 'z'); + * ``` + * + * @return ValueFilter + */ + public static function value() + { + return new ValueFilter(); + } + + /** + * Returns a builder used to configure offset related filters. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::offset() + * ->cellsPerRow(1); + * ``` + * + * @return OffsetFilter + */ + public static function offset() + { + return new OffsetFilter(); + } + + /** + * Returns a builder used to configure limit related filters. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::limit() + * ->cellsPerRow(1); + * ``` + * + * @return LimitFilter + */ + public static function limit() + { + return new LimitFilter(); + } + + /** + * Matches all cells, regardless of input. Functionally equivalent to having + * no filter. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::pass(); + * ``` + * + * @return SimpleFilter + */ + public static function pass() + { + $rowFilter = new RowFilter(); + $rowFilter->setPassAllFilter(true); + return new SimpleFilter($rowFilter); + } + + /** + * Does not match any cells, regardless of input. Useful for temporarily + * disabling just part of a filter. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::block(); + * ``` + * + * @return SimpleFilter + */ + public static function block() + { + $rowFilter = new RowFilter(); + $rowFilter->setBlockAllFilter(true); + return new SimpleFilter($rowFilter); + } + + /** + * Outputs all cells directly to the output of the read rather than to any + * parent filter. For advanced usage, + * [see comments in](https://github.com/googleapis/googleapis/blob/master/google/bigtable/v2/data.proto) + * for more detail. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::sink(); + * ``` + * + * @return SimpleFilter + */ + public static function sink() + { + $rowFilter = new RowFilter(); + $rowFilter->setSink(true); + return new SimpleFilter($rowFilter); + } + + /** + * Applies the given label to all cells in the output row. This allows the + * caller to determine which results were produced from which part of the + * filter. + * + * Due to technical limitation, it is not currently possible to apply + * multiple labels to a cell. As a result, a + * {@see Google\Cloud\Bigtable\Filter\ChainFilter} may have no more than one + * sub-filter which contains a label. It is okay for a + * {@see Google\Cloud\Bigtable\Filter\InterleaveFilter} to contain multiple + * labels, as they will be applied to separate copies of the input. This may + * be relaxed in the future. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $rowFilter = Filter::label('my-label'); + * ``` + * + * @param string $value The label to apply. + * @return SimpleFilter + */ + public static function label($value) + { + $rowFilter = new RowFilter(); + $rowFilter->setApplyLabelTransformer($value); + return new SimpleFilter($rowFilter); + } +} diff --git a/Bigtable/src/Filter/Builder/FamilyFilter.php b/Bigtable/src/Filter/Builder/FamilyFilter.php new file mode 100644 index 000000000000..8ddc0d5c7594 --- /dev/null +++ b/Bigtable/src/Filter/Builder/FamilyFilter.php @@ -0,0 +1,83 @@ +regex('prefix.*'); + * ``` + * + * @param string $value A regex value. + * @return SimpleFilter + */ + public function regex($value) + { + return $this->buildRegexFilter($value, self::$regexSetter); + } + + /** + * Matches only cells from columns whose families match the value. + * + * Example: + * ``` + * $familyFilter = $builder->exactMatch('cf1'); + * ``` + * + * @param string $value An exact value to match. + * @return SimpleFilter + * @throws \InvalidArgumentException When the provided value is not an array + * or string. + */ + public function exactMatch($value) + { + return $this->buildRegexFilter( + $this->escapeLiteralValue($value), + self::$regexSetter + ); + } +} diff --git a/Bigtable/src/Filter/Builder/KeyFilter.php b/Bigtable/src/Filter/Builder/KeyFilter.php new file mode 100644 index 000000000000..1686cdcc368e --- /dev/null +++ b/Bigtable/src/Filter/Builder/KeyFilter.php @@ -0,0 +1,115 @@ +regex('prefix.*'); + * ``` + * + * @param string $value A regex value. + * @return SimpleFilter + */ + public function regex($value) + { + return $this->buildRegexFilter($value, self::$regexSetter); + } + + /** + * Matches only cells from rows whose keys equal the value. In other words, + * passes through the entire row when the key matches, and otherwise + * produces an empty row. + * + * Example: + * ``` + * $keyFilter = $builder->exactMatch('r1'); + * ``` + * + * @param string $value An exact value. + * @return SimpleFilter + * @throws \InvalidArgumentException When the provided value is not an array + * or string. + */ + public function exactMatch($value) + { + return $this->buildRegexFilter( + $this->escapeLiteralValue($value), + self::$regexSetter + ); + } + + /** + * Matches all cells from a row with `probability`, and matches no cells + * from the row with probability 1-`probability`. + * + * Example: + * ``` + * $keyFilter = $builder->sample(.7); + * ``` + * + * @param float $probability The probability to filter by. Must be within + * the range [0, 1], end points excluded. + * @return SimpleFilter + * @throws \InvalidArgumentException When the probability does not fall + * within the acceptable range. + */ + public function sample($probability) + { + if ($probability < 0) { + throw new \InvalidArgumentException('Probability must be positive'); + } + if ($probability >= 1.0) { + throw new \InvalidArgumentException('Probability must be less than 1.0'); + } + + return new SimpleFilter( + (new RowFilter)->setRowSampleFilter($probability) + ); + } +} diff --git a/Bigtable/src/Filter/Builder/LimitFilter.php b/Bigtable/src/Filter/Builder/LimitFilter.php new file mode 100644 index 000000000000..2462c2501511 --- /dev/null +++ b/Bigtable/src/Filter/Builder/LimitFilter.php @@ -0,0 +1,79 @@ +cellsPerRow(2); + * ``` + * + * @param int $count The number of cells to limit to. + * @return SimpleFilter + */ + public function cellsPerRow($count) + { + return new SimpleFilter( + (new RowFilter)->setCellsPerRowLimitFilter($count) + ); + } + + /** + * Matches only the most recent N cells within each column. For + * example, if count=2, this filter would match column `foo:bar` at + * timestamps 10 and 9 skip all earlier cells in `foo:bar`, and then begin + * matching again in column `foo:bar2`. If duplicate cells are present, as + * is possible when using an + * {@see Google\Cloud\Bigtable\Filter\InterleaveFilter}, each copy of the + * cell is counted separately. + * + * Example: + * ``` + * $limitFilter = $builder->cellsPerColumn(2); + * ``` + * + * @param int $count The number of cells to limit to. + * @return SimpleFilter + */ + public function cellsPerColumn($count) + { + return new SimpleFilter( + (new RowFilter)->setCellsPerColumnLimitFilter($count) + ); + } +} diff --git a/Bigtable/src/Filter/Builder/OffsetFilter.php b/Bigtable/src/Filter/Builder/OffsetFilter.php new file mode 100644 index 000000000000..4328f1578896 --- /dev/null +++ b/Bigtable/src/Filter/Builder/OffsetFilter.php @@ -0,0 +1,55 @@ +cellsPerRow(2); + * ``` + * + * @param int $count The count to offset by. + * @return SimpleFilter + */ + public function cellsPerRow($count) + { + return new SimpleFilter( + (new RowFilter)->setCellsPerRowOffsetFilter($count) + ); + } +} diff --git a/Bigtable/src/Filter/Builder/QualifierFilter.php b/Bigtable/src/Filter/Builder/QualifierFilter.php new file mode 100644 index 000000000000..745901fa4b6c --- /dev/null +++ b/Bigtable/src/Filter/Builder/QualifierFilter.php @@ -0,0 +1,102 @@ +regex('prefix.*'); + * ``` + * + * @param string $value A regex value. + * @return SimpleFilter + */ + public function regex($value) + { + return $this->buildRegexFilter($value, self::$regexSetter); + } + + /** + * Matches only cells from rows whose keys equal the value. In other words, passes through the + * entire row when the key matches, and otherwise produces an empty row. + * + * Example: + * ``` + * $qualifierFilter = $builder->exactMatch('cq1'); + * ``` + * + * @param string $value An exact value. + * @return SimpleFilter + * @throws \InvalidArgumentException When the provided value is not an array + * or string. + */ + public function exactMatch($value) + { + return $this->buildRegexFilter( + $this->escapeLiteralValue($value), + self::$regexSetter + ); + } + + /** + * Returns a builder used to configure qualifier range filters. + * + * Example: + * ``` + * $qualifierFilter = $builder->rangeWithinFamily('cf1') + * ->of('cq1', 'cq10'); + * ``` + * + * @param string $family The family name to search within. + * @return QualifierRangeFilter + */ + public function rangeWithinFamily($family) + { + return new QualifierRangeFilter($family); + } +} diff --git a/Bigtable/src/Filter/Builder/Range.php b/Bigtable/src/Filter/Builder/Range.php new file mode 100644 index 000000000000..3cbbb1f1011d --- /dev/null +++ b/Bigtable/src/Filter/Builder/Range.php @@ -0,0 +1,207 @@ +startBound = $startBound; + $this->start = $start; + $this->endBound = $endBound; + $this->end = $end; + } + + /** + * @param string|int $startClosed + * @param string|int $endOpen + * @return Range + */ + public function of($startClosed, $endOpen) + { + return $this->startClosed($startClosed)->endOpen($endOpen); + } + + /** + * @return Range + */ + public function startUnbounded() + { + $this->start = null; + $this->startBound = self::BOUND_TYPE_UNBOUNDED; + return $this; + } + + /** + * @param string|int $start + * @return Range + * @throws \InvalidArgumentException + */ + public function startOpen($start) + { + $this->validateStringOrNumeric($start, __FUNCTION__); + $this->start = $start; + $this->startBound = self::BOUND_TYPE_OPEN; + return $this; + } + + /** + * @param string|int $start + * @return Range + * @throws \InvalidArgumentException + */ + public function startClosed($start) + { + $this->validateStringOrNumeric($start, __FUNCTION__); + $this->start = $start; + $this->startBound = self::BOUND_TYPE_CLOSED; + return $this; + } + + /** + * @return Range + */ + public function endUnbounded() + { + $this->end = null; + $this->endBound = self::BOUND_TYPE_UNBOUNDED; + return $this; + } + + /** + * @param string|int $end + * @return Range + * @throws \InvalidArgumentException + */ + public function endOpen($end) + { + $this->validateStringOrNumeric($end, __FUNCTION__); + $this->end = $end; + $this->endBound = self::BOUND_TYPE_OPEN; + return $this; + } + + /** + * @param string|int $end + * @return Range + * @throws \InvalidArgumentException + */ + public function endClosed($end) + { + $this->validateStringOrNumeric($end, __FUNCTION__); + $this->end = $end; + $this->endBound = self::BOUND_TYPE_CLOSED; + return $this; + } + + /** + * @return int + */ + public function getStartBound() + { + return $this->startBound; + } + + /** + * @return string|int|null + */ + public function getStart() + { + if ($this->startBound === self::BOUND_TYPE_UNBOUNDED) { + throw new \RuntimeException('Start is unbounded.'); + } + return $this->start; + } + + /** + * @return int + */ + public function getEndBound() + { + return $this->endBound; + } + + /** + * @return string|int|null + */ + public function getEnd() + { + if ($this->endBound === self::BOUND_TYPE_UNBOUNDED) { + throw new \RuntimeException('End is unbounded.'); + } + return $this->end; + } + + /** + * @param $value + * @param $func + * @return bool + * @throws \InvalidArgumentException + */ + private function validateStringOrNumeric($value, $func) + { + if (is_string($value) || is_numeric($value)) { + return true; + } + + throw new \InvalidArgumentException( + sprintf( + '%s accepts only string or numeric types.', + $func + ) + ); + } +} diff --git a/Bigtable/src/Filter/Builder/RegexTrait.php b/Bigtable/src/Filter/Builder/RegexTrait.php new file mode 100644 index 000000000000..aa844a84fbb8 --- /dev/null +++ b/Bigtable/src/Filter/Builder/RegexTrait.php @@ -0,0 +1,91 @@ +$setter($value) + ); + } + + /** + * @param array|string $value + * @return string + * @throws \InvalidArgumentException When the provided value is not a string + * or array. + */ + private function escapeLiteralValue($value) + { + if ($value === null) { + return; + } + $nullBytes = unpack('C*', '\\x00'); + $byteValue = null; + if (is_array($value)) { + $byteValue = $value; + } elseif (is_string($value)) { + if (preg_match('//u', $value)) { + $byteValue = unpack('C*', $value); + } else { + $byteValue = unpack('C*', utf8_encode($value)); + } + } else { + throw new \InvalidArgumentException( + sprintf( + 'Expected byte array or string, instead got \'%s\'.', + gettype($value) + ) + ); + } + $quotedBytes = []; + foreach ($byteValue as $byte) { + if (($byte < ord('a') || $byte > ord('z')) + && ($byte < ord('A') || $byte > ord('Z')) + && ($byte < ord('0') || $byte > ord('9')) + && $byte != ord('_') + && ($byte & 128) == 0 + ) { + if ($byte == 0) { + $quotedBytes = array_merge($quotedBytes, $nullBytes); + continue; + } + $quotedBytes[] = ord('\\'); + } + $quotedBytes[] = $byte; + } + return implode(array_map('chr', $quotedBytes)); + } +} diff --git a/Bigtable/src/Filter/Builder/TimestampFilter.php b/Bigtable/src/Filter/Builder/TimestampFilter.php new file mode 100644 index 000000000000..f3d5d2b2a724 --- /dev/null +++ b/Bigtable/src/Filter/Builder/TimestampFilter.php @@ -0,0 +1,49 @@ +range() + * ->of(1536766964380000, 1536766964383000); + * ``` + * + * @return TimestampRangeFilter + */ + public function range() + { + return new TimestampRangeFilter(); + } +} diff --git a/Bigtable/src/Filter/Builder/ValueFilter.php b/Bigtable/src/Filter/Builder/ValueFilter.php new file mode 100644 index 000000000000..e45ec9e33851 --- /dev/null +++ b/Bigtable/src/Filter/Builder/ValueFilter.php @@ -0,0 +1,111 @@ +regex('prefix.*'); + * ``` + * + * @param string $value A regex value. + * @return SimpleFilter + */ + public function regex($value) + { + return $this->buildRegexFilter($value, self::$regexSetter); + } + + /** + * Matches only cells with values that match the given value. + * + * Example: + * ``` + * $valueFilter = $builder->exactMatch('value1'); + * ``` + * + * @param string $value An exact value to match. + * @return SimpleFilter + * @throws \InvalidArgumentException When the provided value is not an array + * or string. + */ + public function exactMatch($value) + { + return $this->buildRegexFilter( + $this->escapeLiteralValue($value), + self::$regexSetter + ); + } + + /** + * Returns a builder used to configure value range filters. + * + * Example: + * ``` + * $valueFilter = $builder->range() + * ->of('value1', 'value10'); + * ``` + * + * @return ValueRangeFilter + */ + public function range() + { + return new ValueRangeFilter(); + } + + /** + * Replaces each cell's value with an empty string. + * + * @return SimpleFilter + */ + public function strip() + { + return new SimpleFilter( + (new RowFilter)->setStripValueTransformer(true) + ); + } +} diff --git a/Bigtable/src/Filter/ChainFilter.php b/Bigtable/src/Filter/ChainFilter.php new file mode 100644 index 000000000000..39359b2371d7 --- /dev/null +++ b/Bigtable/src/Filter/ChainFilter.php @@ -0,0 +1,75 @@ +addFilter( + * Filter::qualifier()->regex('prefix.*') + * ); + * ``` + * + * @param FilterInterface $filter A filter to add to the chain. + * @return ChainFilter + */ + public function addFilter(FilterInterface $filter) + { + $this->filters[] = $filter->toProto(); + return $this; + } + + /** + * Get the proto representation of the filter. + * + * @internal + * @access private + * @return RowFilter + */ + public function toProto() + { + $chain = (new Chain) + ->setFilters($this->filters); + return (new RowFilter) + ->setChain($chain); + } +} diff --git a/Bigtable/src/Filter/ConditionFilter.php b/Bigtable/src/Filter/ConditionFilter.php new file mode 100644 index 000000000000..e94c000bfa02 --- /dev/null +++ b/Bigtable/src/Filter/ConditionFilter.php @@ -0,0 +1,137 @@ +regex('prefix.*')); + * ``` + */ +class ConditionFilter implements FilterInterface +{ + /** + * @var RowFilter + */ + private $predicateFilter; + + /** + * @var RowFilter|null + */ + private $trueFilter = null; + + /** + * @var RowFilter|null + */ + private $falseFilter = null; + + /** + * @param FilterInterface $predicateFilter A predicate filter. + */ + public function __construct(FilterInterface $predicateFilter) + { + $this->predicateFilter = $predicateFilter->toProto(); + } + + /** + * Adds a true filter to the condition filter. This filter will be applied + * if the predicate filter returns any results. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $conditionFilter->then( + * Filter::label('hasPrefix') + * ); + * ``` + * + * @param FilterInterface $trueFilter A filter to be evaluted when the + * predicate evalutes to true. + * @return ConditionFilter + */ + public function then(FilterInterface $trueFilter) + { + $this->trueFilter = $trueFilter->toProto(); + return $this; + } + + /** + * Adds a false filter to condition filter. This filter will be applied if + * the predicate filter does not return results. + * + * Example: + * ``` + * use Google\Cloud\Bigtable\Filter; + * + * $conditionFilter->otherwise( + * Filter::value()->strip() + * ); + * ``` + * + * @param FilterInterface $falseFilter A filter to be evaluated when the + * predicate evalutes to false. + * @return ConditionFilter + */ + public function otherwise(FilterInterface $falseFilter) + { + $this->falseFilter = $falseFilter->toProto(); + return $this; + } + + /** + * Get the proto representation of the filter. + * + * @internal + * @access private + * @return RowFilter + * @throws \RuntimeException + */ + public function toProto() + { + if ($this->trueFilter === null && $this->falseFilter === null) { + throw new \RuntimeException( + sprintf( + 'In order to utilize a condition filter you must supply a filter through either %s:%s or %s:%s.', + self::class, + 'then()', + self::class, + 'otherwise()' + ) + ); + } + $condition = (new Condition) + ->setPredicateFilter($this->predicateFilter); + if ($this->trueFilter) { + $condition->setTrueFilter($this->trueFilter); + } + if ($this->falseFilter) { + $condition->setFalseFilter($this->falseFilter); + } + return (new RowFilter) + ->setCondition($condition); + } +} diff --git a/Bigtable/src/Filter/FilterInterface.php b/Bigtable/src/Filter/FilterInterface.php new file mode 100644 index 000000000000..d468fdc93665 --- /dev/null +++ b/Bigtable/src/Filter/FilterInterface.php @@ -0,0 +1,36 @@ +addFilter( + * Filter::key()->regex('prefix.*') + * ); + * ``` + * + * @param FilterInterface $filter A filter to be added. + * @return InterleaveFilter + */ + public function addFilter(FilterInterface $filter) + { + $this->filters[] = $filter->toProto(); + return $this; + } + + /** + * Get the proto representation of the filter. + * + * @internal + * @access private + * @return RowFilter + */ + public function toProto() + { + $interleave = (new Interleave) + ->setFilters($this->filters); + return (new RowFilter) + ->setInterleave($interleave); + } +} diff --git a/Bigtable/src/Filter/QualifierRangeFilter.php b/Bigtable/src/Filter/QualifierRangeFilter.php new file mode 100644 index 000000000000..a9b56b4ad33d --- /dev/null +++ b/Bigtable/src/Filter/QualifierRangeFilter.php @@ -0,0 +1,87 @@ +rangeWithinFamily('cf1'); + * ``` + */ +class QualifierRangeFilter extends Range implements FilterInterface +{ + /** + * @var string + */ + private $family; + + /** + * @param string $family The family name to search within. + */ + public function __construct($family) + { + parent::__construct(); + $this->family = $family; + } + + /** + * Get the proto representation of the filter. + * + * @internal + * @access private + * @return RowFilter + */ + public function toProto() + { + $columnRange = new ColumnRange(); + $columnRange->setFamilyName($this->family); + + switch ($this->getStartBound()) { + case Range::BOUND_TYPE_CLOSED: + $columnRange->setStartQualifierClosed($this->getStart()); + break; + case Range::BOUND_TYPE_OPEN: + $columnRange->setStartQualifierOpen($this->getStart()); + break; + case Range::BOUND_TYPE_UNBOUNDED: + break; + } + switch ($this->getEndBound()) { + case Range::BOUND_TYPE_CLOSED: + $columnRange->setEndQualifierClosed($this->getEnd()); + break; + case Range::BOUND_TYPE_OPEN: + $columnRange->setEndQualifierOpen($this->getEnd()); + break; + case Range::BOUND_TYPE_UNBOUNDED: + break; + } + $rowFilter = new RowFilter(); + $rowFilter->setColumnRangeFilter($columnRange); + return $rowFilter; + } +} diff --git a/Bigtable/src/Filter/SimpleFilter.php b/Bigtable/src/Filter/SimpleFilter.php new file mode 100644 index 000000000000..e9f68303b24b --- /dev/null +++ b/Bigtable/src/Filter/SimpleFilter.php @@ -0,0 +1,57 @@ +proto = $rowFilter; + } + + /** + * Get the proto representation of the filter. + * + * @internal + * @access private + * @return RowFilter + * @throws \RuntimeException + */ + public function toProto() + { + return $this->proto; + } +} diff --git a/Bigtable/src/Filter/TimestampRangeFilter.php b/Bigtable/src/Filter/TimestampRangeFilter.php new file mode 100644 index 000000000000..8f93166d1385 --- /dev/null +++ b/Bigtable/src/Filter/TimestampRangeFilter.php @@ -0,0 +1,72 @@ +range(); + * ``` + */ +class TimestampRangeFilter extends Range implements FilterInterface +{ + /** + * Get the proto representation of the filter. + * + * @internal + * @access private + * @return RowFilter + */ + public function toProto() + { + $timestampRange = new TimestampRange(); + + switch ($this->getStartBound()) { + case Range::BOUND_TYPE_CLOSED: + $timestampRange->setStartTimestampMicros($this->getStart()); + break; + case Range::BOUND_TYPE_OPEN: + $timestampRange->setStartTimestampMicros($this->getStart() + 1); + break; + case Range::BOUND_TYPE_UNBOUNDED: + break; + } + switch ($this->getEndBound()) { + case Range::BOUND_TYPE_CLOSED: + $timestampRange->setEndTimestampMicros($this->getEnd() + 1); + break; + case Range::BOUND_TYPE_OPEN: + $timestampRange->setEndTimestampMicros($this->getEnd()); + break; + case Range::BOUND_TYPE_UNBOUNDED: + break; + } + $rowFilter = new RowFilter(); + $rowFilter->setTimestampRangeFilter($timestampRange); + return $rowFilter; + } +} diff --git a/Bigtable/src/Filter/ValueRangeFilter.php b/Bigtable/src/Filter/ValueRangeFilter.php new file mode 100644 index 000000000000..3e614d11fbb9 --- /dev/null +++ b/Bigtable/src/Filter/ValueRangeFilter.php @@ -0,0 +1,72 @@ +range(); + * ``` + */ +class ValueRangeFilter extends Range implements FilterInterface +{ + /** + * Get the proto representation of the filter. + * + * @internal + * @access private + * @return RowFilter + */ + public function toProto() + { + $valueRange = new ValueRange(); + + switch ($this->getStartBound()) { + case Range::BOUND_TYPE_CLOSED: + $valueRange->setStartValueClosed($this->getStart()); + break; + case Range::BOUND_TYPE_OPEN: + $valueRange->setStartValueOpen($this->getStart()); + break; + case Range::BOUND_TYPE_UNBOUNDED: + break; + } + switch ($this->getEndBound()) { + case Range::BOUND_TYPE_CLOSED: + $valueRange->setEndValueClosed($this->getEnd()); + break; + case Range::BOUND_TYPE_OPEN: + $valueRange->setEndValueOpen($this->getEnd()); + break; + case Range::BOUND_TYPE_UNBOUNDED: + break; + } + $rowFilter = new RowFilter(); + $rowFilter->setValueRangeFilter($valueRange); + return $rowFilter; + } +} diff --git a/Bigtable/tests/Snippet/Filter/Builder/FamilyFilterTest.php b/Bigtable/tests/Snippet/Filter/Builder/FamilyFilterTest.php new file mode 100644 index 000000000000..d6ccee5ea13a --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/Builder/FamilyFilterTest.php @@ -0,0 +1,62 @@ +builder = Filter::family(); + } + + public function testClass() + { + $snippet = $this->snippetFromClass(FamilyFilter::class); + $res = $snippet->invoke('builder'); + $this->assertInstanceOf(FamilyFilter::class, $res->returnVal()); + } + + public function testRegex() + { + $snippet = $this->snippetFromMethod(FamilyFilter::class, 'regex'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('familyFilter'); + $rowFilter = (new RowFilter)->setFamilyNameRegexFilter('prefix.*'); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testExactMatch() + { + $snippet = $this->snippetFromMethod(FamilyFilter::class, 'exactMatch'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('familyFilter'); + $rowFilter = (new RowFilter)->setFamilyNameRegexFilter('cf1'); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/Builder/KeyFilterTest.php b/Bigtable/tests/Snippet/Filter/Builder/KeyFilterTest.php new file mode 100644 index 000000000000..96dd5831b563 --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/Builder/KeyFilterTest.php @@ -0,0 +1,71 @@ +builder = Filter::key(); + } + + public function testClass() + { + $snippet = $this->snippetFromClass(KeyFilter::class); + $res = $snippet->invoke('builder'); + $this->assertInstanceOf(KeyFilter::class, $res->returnVal()); + } + + public function testRegex() + { + $snippet = $this->snippetFromMethod(KeyFilter::class, 'regex'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('keyFilter'); + $rowFilter = (new RowFilter)->setRowKeyRegexFilter('prefix.*'); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testExactMatch() + { + $snippet = $this->snippetFromMethod(KeyFilter::class, 'exactMatch'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('keyFilter'); + $rowFilter = (new RowFilter)->setRowKeyRegexFilter('r1'); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testSample() + { + $snippet = $this->snippetFromMethod(KeyFilter::class, 'sample'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('keyFilter'); + $rowFilter = (new RowFilter)->setRowSampleFilter(.7); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/Builder/LimitFilterTest.php b/Bigtable/tests/Snippet/Filter/Builder/LimitFilterTest.php new file mode 100644 index 000000000000..bbdffa4346e0 --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/Builder/LimitFilterTest.php @@ -0,0 +1,62 @@ +builder = Filter::limit(); + } + + public function testClass() + { + $snippet = $this->snippetFromClass(LimitFilter::class); + $res = $snippet->invoke('builder'); + $this->assertInstanceOf(LimitFilter::class, $res->returnVal()); + } + + public function testCellsPerRow() + { + $snippet = $this->snippetFromMethod(LimitFilter::class, 'cellsPerRow'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('limitFilter'); + $rowFilter = (new RowFilter)->setCellsPerRowLimitFilter(2); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testCellsPerColumn() + { + $snippet = $this->snippetFromMethod(LimitFilter::class, 'cellsPerColumn'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('limitFilter'); + $rowFilter = (new RowFilter)->setCellsPerColumnLimitFilter(2); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/Builder/OffsetFilterTest.php b/Bigtable/tests/Snippet/Filter/Builder/OffsetFilterTest.php new file mode 100644 index 000000000000..95546cb02e15 --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/Builder/OffsetFilterTest.php @@ -0,0 +1,53 @@ +builder = Filter::offset(); + } + + public function testClass() + { + $snippet = $this->snippetFromClass(OffsetFilter::class); + $res = $snippet->invoke('builder'); + $this->assertInstanceOf(OffsetFilter::class, $res->returnVal()); + } + + public function testCellsPerRow() + { + $snippet = $this->snippetFromMethod(OffsetFilter::class, 'cellsPerRow'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('limitFilter'); + $rowFilter = (new RowFilter)->setCellsPerRowOffsetFilter(2); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/Builder/QualifierFilterTest.php b/Bigtable/tests/Snippet/Filter/Builder/QualifierFilterTest.php new file mode 100644 index 000000000000..93091c11faca --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/Builder/QualifierFilterTest.php @@ -0,0 +1,77 @@ +builder = Filter::qualifier(); + } + + public function testClass() + { + $snippet = $this->snippetFromClass(QualifierFilter::class); + $res = $snippet->invoke('builder'); + $this->assertInstanceOf(QualifierFilter::class, $res->returnVal()); + } + + public function testRegex() + { + $snippet = $this->snippetFromMethod(QualifierFilter::class, 'regex'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('qualifierFilter'); + $rowFilter = (new RowFilter)->setColumnQualifierRegexFilter('prefix.*'); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testExactMatch() + { + $snippet = $this->snippetFromMethod(QualifierFilter::class, 'exactMatch'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('qualifierFilter'); + $rowFilter = (new RowFilter)->setColumnQualifierRegexFilter('cq1'); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testRangeWithinFamily() + { + $snippet = $this->snippetFromMethod(QualifierFilter::class, 'rangeWithinFamily'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('qualifierFilter'); + $rowFilter = (new RowFilter)->setColumnRangeFilter( + (new ColumnRange) + ->setFamilyName('cf1') + ->setStartQualifierClosed('cq1') + ->setEndQualifierOpen('cq10') + ); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/Builder/TimestampFilterTest.php b/Bigtable/tests/Snippet/Filter/Builder/TimestampFilterTest.php new file mode 100644 index 000000000000..457a4bd346cd --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/Builder/TimestampFilterTest.php @@ -0,0 +1,59 @@ +builder = Filter::timestamp(); + } + + public function testClass() + { + $snippet = $this->snippetFromClass(TimestampFilter::class); + $res = $snippet->invoke('builder'); + $this->assertInstanceOf(TimestampFilter::class, $res->returnVal()); + } + + public function testRange() + { + $snippet = $this->snippetFromMethod(TimestampFilter::class, 'range'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('timestampRangeFilter'); + $rowFilter = (new RowFilter)->setTimestampRangeFilter( + (new TimestampRange) + ->setStartTimestampMicros(1536766964380000) + ->setEndTimestampMicros(1536766964383000) + ); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/Builder/ValueFilterTest.php b/Bigtable/tests/Snippet/Filter/Builder/ValueFilterTest.php new file mode 100644 index 000000000000..4ef8546d30e8 --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/Builder/ValueFilterTest.php @@ -0,0 +1,76 @@ +builder = Filter::value(); + } + + public function testClass() + { + $snippet = $this->snippetFromClass(ValueFilter::class); + $res = $snippet->invoke('builder'); + $this->assertInstanceOf(ValueFilter::class, $res->returnVal()); + } + + public function testRegex() + { + $snippet = $this->snippetFromMethod(ValueFilter::class, 'regex'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('valueFilter'); + $rowFilter = (new RowFilter)->setValueRegexFilter('prefix.*'); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testExactMatch() + { + $snippet = $this->snippetFromMethod(ValueFilter::class, 'exactMatch'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('valueFilter'); + $rowFilter = (new RowFilter)->setValueRegexFilter('value1'); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testRange() + { + $snippet = $this->snippetFromMethod(ValueFilter::class, 'range'); + $snippet->addLocal('builder', $this->builder); + $res = $snippet->invoke('valueFilter'); + $rowFilter = (new RowFilter)->setValueRangeFilter( + (new ValueRange) + ->setStartValueClosed('value1') + ->setEndValueOpen('value10') + ); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/ChainFilterTest.php b/Bigtable/tests/Snippet/Filter/ChainFilterTest.php new file mode 100644 index 000000000000..26a312c8fc85 --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/ChainFilterTest.php @@ -0,0 +1,55 @@ +chainFilter = Filter::chain(); + } + + public function testClass() + { + $snippet = $this->snippetFromClass(ChainFilter::class); + $res = $snippet->invoke('chainFilter'); + $this->assertInstanceOf(ChainFilter::class, $res->returnVal()); + } + + public function testAddFilter() + { + $snippet = $this->snippetFromMethod(ChainFilter::class, 'addFilter'); + $snippet->addLocal('chainFilter', $this->chainFilter); + $res = $snippet->invoke('chainFilter'); + $rowFilter = (new RowFilter) + ->setChain((new Chain)->setFilters([(new RowFilter)->setColumnQualifierRegexFilter('prefix.*')])); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/ConditionFilterTest.php b/Bigtable/tests/Snippet/Filter/ConditionFilterTest.php new file mode 100644 index 000000000000..1f7d80fb6ac5 --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/ConditionFilterTest.php @@ -0,0 +1,81 @@ +conditionFilter = Filter::condition(Filter::key()->regex('prefix.*')); + } + + public function testClass() + { + $snippet = $this->snippetFromClass(ConditionFilter::class); + $res = $snippet->invoke('conditionFilter'); + $this->assertInstanceOf(ConditionFilter::class, $res->returnVal()); + } + + public function testThen() + { + $snippet = $this->snippetFromMethod(ConditionFilter::class, 'then'); + $snippet->addLocal('conditionFilter', $this->conditionFilter); + $res = $snippet->invoke('conditionFilter'); + $rowFilter = (new RowFilter) + ->setCondition( + (new Condition) + ->setPredicateFilter( + (new RowFilter)->setRowKeyRegexFilter('prefix.*') + ) + ->setTrueFilter( + (new RowFilter)->setApplyLabelTransformer('hasPrefix') + ) + ); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testOtherwise() + { + $snippet = $this->snippetFromMethod(ConditionFilter::class, 'otherwise'); + $snippet->addLocal('conditionFilter', $this->conditionFilter); + $res = $snippet->invoke('conditionFilter'); + $rowFilter = (new RowFilter) + ->setCondition( + (new Condition) + ->setPredicateFilter( + (new RowFilter)->setRowKeyRegexFilter('prefix.*') + ) + ->setFalseFilter( + (new RowFilter)->setStripValueTransformer(true) + ) + ); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/InterleaveFilterTest.php b/Bigtable/tests/Snippet/Filter/InterleaveFilterTest.php new file mode 100644 index 000000000000..3df6c3bba44e --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/InterleaveFilterTest.php @@ -0,0 +1,55 @@ +interleaveFilter = Filter::interleave(); + } + + public function testClass() + { + $snippet = $this->snippetFromClass(InterleaveFilter::class); + $res = $snippet->invoke('interleaveFilter'); + $this->assertInstanceOf(InterleaveFilter::class, $res->returnVal()); + } + + public function testAddFilter() + { + $snippet = $this->snippetFromMethod(InterleaveFilter::class, 'addFilter'); + $snippet->addLocal('interleaveFilter', $this->interleaveFilter); + $res = $snippet->invoke('interleaveFilter'); + $rowFilter = (new RowFilter) + ->setInterleave((new Interleave)->setFilters([(new RowFilter)->setRowKeyRegexFilter('prefix.*')])); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/QualifierRangeFilterTest.php b/Bigtable/tests/Snippet/Filter/QualifierRangeFilterTest.php new file mode 100644 index 000000000000..13be39c7c7e6 --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/QualifierRangeFilterTest.php @@ -0,0 +1,35 @@ +snippetFromClass(QualifierRangeFilter::class); + $res = $snippet->invoke('rangeFilter'); + $this->assertInstanceOf(QualifierRangeFilter::class, $res->returnVal()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/TimestampRangeFilterTest.php b/Bigtable/tests/Snippet/Filter/TimestampRangeFilterTest.php new file mode 100644 index 000000000000..909279d8cbbd --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/TimestampRangeFilterTest.php @@ -0,0 +1,35 @@ +snippetFromClass(TimestampRangeFilter::class); + $res = $snippet->invoke('rangeFilter'); + $this->assertInstanceOf(TimestampRangeFilter::class, $res->returnVal()); + } +} diff --git a/Bigtable/tests/Snippet/Filter/ValueRangeFilterTest.php b/Bigtable/tests/Snippet/Filter/ValueRangeFilterTest.php new file mode 100644 index 000000000000..7b26c375021e --- /dev/null +++ b/Bigtable/tests/Snippet/Filter/ValueRangeFilterTest.php @@ -0,0 +1,35 @@ +snippetFromClass(ValueRangeFilter::class); + $res = $snippet->invoke('rangeFilter'); + $this->assertInstanceOf(ValueRangeFilter::class, $res->returnVal()); + } +} diff --git a/Bigtable/tests/Snippet/FilterTest.php b/Bigtable/tests/Snippet/FilterTest.php new file mode 100644 index 000000000000..141754dc7662 --- /dev/null +++ b/Bigtable/tests/Snippet/FilterTest.php @@ -0,0 +1,224 @@ + [ + 'cq1' => [[ + 'value' => 'value1', + 'labels' => '', + 'timeStamp' => 0 + ]] + ] + ]; + $dataClient = $this->prophesize(DataClient::class); + $filter = Filter::chain() + ->addFilter(Filter::qualifier()->regex('prefix.*')) + ->addFilter(Filter::limit()->cellsPerRow(10)); + $dataClient->readRows(['filter' => $filter]) + ->shouldBeCalled() + ->willReturn([$expectedRows]); + + $snippet = $this->snippetFromClass(Filter::class); + $snippet->replace('$dataClient = new DataClient(\'my-instance\', \'my-table\');', ''); + $snippet->addLocal('dataClient', $dataClient->reveal()); + $res = $snippet->invoke('rows'); + $this->assertEquals( + print_r($expectedRows, true), + $res->output() + ); + } + + public function testChain() + { + $snippet = $this->snippetFromMethod(Filter::class, 'chain'); + $res = $snippet->invoke('rowFilter'); + $rowFilter = (new RowFilter) + ->setChain( + (new Chain)->setFilters([ + (new RowFilter)->setColumnQualifierRegexFilter('prefix.*'), + (new RowFilter)->setCellsPerRowLimitFilter(10) + ]) + ); + $this->assertInstanceOf(ChainFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testInterleave() + { + $snippet = $this->snippetFromMethod(Filter::class, 'interleave'); + $res = $snippet->invoke('rowFilter'); + $rowFilter = (new RowFilter) + ->setInterleave( + (new Interleave)->setFilters([ + (new RowFilter)->setRowKeyRegexFilter('prefix.*'), + (new RowFilter)->setSink(true) + ]) + ); + $this->assertInstanceOf(InterleaveFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testCondition() + { + $snippet = $this->snippetFromMethod(Filter::class, 'condition'); + $res = $snippet->invoke('rowFilter'); + $condition = (new Condition) + ->setPredicateFilter( + (new RowFilter)->setRowKeyRegexFilter('prefix.*') + ) + ->setTrueFilter( + (new RowFilter)->setApplyLabelTransformer('hasPrefix') + ) + ->setFalseFilter( + (new RowFilter)->setStripValueTransformer(true) + ); + $rowFilter = (new RowFilter)->setCondition($condition); + $this->assertInstanceOf(ConditionFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testKey() + { + $snippet = $this->snippetFromMethod(Filter::class, 'key'); + $res = $snippet->invoke('rowFilter'); + $rowFilter = (new RowFilter)->setRowKeyRegexFilter('prefix.*'); + $this->assertInstanceOf(SimpleFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testFamily() + { + $snippet = $this->snippetFromMethod(Filter::class, 'family'); + $res = $snippet->invoke('rowFilter'); + $rowFilter = (new RowFilter)->setFamilyNameRegexFilter('prefix.*'); + $this->assertInstanceOf(SimpleFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testQualifier() + { + $snippet = $this->snippetFromMethod(Filter::class, 'qualifier'); + $res = $snippet->invoke('rowFilter'); + $rowFilter = (new RowFilter)->setColumnQualifierRegexFilter('prefix.*'); + $this->assertInstanceOf(SimpleFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testTimestamp() + { + $snippet = $this->snippetFromMethod(Filter::class, 'timestamp'); + $res = $snippet->invoke('rowFilter'); + $timestampRange = (new TimestampRange) + ->setStartTimestampMicros(1536766964380000) + ->setEndTimestampMicros(1536766964383000); + $rowFilter = (new RowFilter)->setTimestampRangeFilter($timestampRange); + $this->assertInstanceOf(TimestampRangeFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testValue() + { + $snippet = $this->snippetFromMethod(Filter::class, 'value'); + $res = $snippet->invoke('rowFilter'); + $valueRange = (new ValueRange) + ->setStartValueClosed('a') + ->setEndValueOpen('z'); + $rowFilter = (new RowFilter)->setValueRangeFilter($valueRange); + $this->assertInstanceOf(ValueRangeFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testOffset() + { + $snippet = $this->snippetFromMethod(Filter::class, 'offset'); + $res = $snippet->invoke('rowFilter'); + $rowFilter = (new RowFilter)->setCellsPerRowOffsetFilter(1); + $this->assertInstanceOf(SimpleFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testLimit() + { + $snippet = $this->snippetFromMethod(Filter::class, 'limit'); + $res = $snippet->invoke('rowFilter'); + $rowFilter = (new RowFilter)->setCellsPerRowLimitFilter(1); + $this->assertInstanceOf(SimpleFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testPass() + { + $snippet = $this->snippetFromMethod(Filter::class, 'pass'); + $res = $snippet->invoke('rowFilter'); + $rowFilter = (new RowFilter)->setPassAllFilter(true); + $this->assertInstanceOf(SimpleFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testBlock() + { + $snippet = $this->snippetFromMethod(Filter::class, 'block'); + $res = $snippet->invoke('rowFilter'); + $rowFilter = (new RowFilter)->setBlockAllFilter(true); + $this->assertInstanceOf(SimpleFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testSink() + { + $snippet = $this->snippetFromMethod(Filter::class, 'sink'); + $res = $snippet->invoke('rowFilter'); + $rowFilter = (new RowFilter)->setSink(true); + $this->assertInstanceOf(SimpleFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } + + public function testLabel() + { + $snippet = $this->snippetFromMethod(Filter::class, 'label'); + $res = $snippet->invoke('rowFilter'); + $rowFilter = (new RowFilter)->setApplyLabelTransformer('my-label'); + $this->assertInstanceOf(SimpleFilter::class, $res->returnVal()); + $this->assertEquals($rowFilter, $res->returnVal()->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/Builder/FamilyFilterTest.php b/Bigtable/tests/Unit/Filter/Builder/FamilyFilterTest.php new file mode 100644 index 000000000000..123aa7aa9bdb --- /dev/null +++ b/Bigtable/tests/Unit/Filter/Builder/FamilyFilterTest.php @@ -0,0 +1,55 @@ +familyFilter = new FamilyFilter; + } + + public function testRegex() + { + $filter = $this->familyFilter->regex('v1'); + $this->assertInstanceOf(SimpleFilter::class, $filter); + $rowFilter = new RowFilter(); + $rowFilter->setFamilyNameRegexFilter('v1'); + $this->assertEquals($rowFilter, $filter->toProto()); + } + + public function testExactMatch() + { + $filter = $this->familyFilter->exactMatch('v1'); + $this->assertInstanceOf(SimpleFilter::class, $filter); + $rowFilter = new RowFilter(); + $rowFilter->setFamilyNameRegexFilter('v1'); + $this->assertEquals($rowFilter, $filter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/Builder/KeyFilterTest.php b/Bigtable/tests/Unit/Filter/Builder/KeyFilterTest.php new file mode 100644 index 000000000000..c7c5080394ee --- /dev/null +++ b/Bigtable/tests/Unit/Filter/Builder/KeyFilterTest.php @@ -0,0 +1,82 @@ +keyFilter = new KeyFilter; + } + + public function testRegex() + { + $filter = $this->keyFilter->regex('v1'); + $this->assertInstanceOf(SimpleFilter::class, $filter); + $rowFilter = new RowFilter(); + $rowFilter->setRowKeyRegexFilter('v1'); + $this->assertEquals($rowFilter, $filter->toProto()); + } + + public function testExactMatch() + { + $filter = $this->keyFilter->exactMatch('v1'); + $this->assertInstanceOf(SimpleFilter::class, $filter); + $rowFilter = new RowFilter(); + $rowFilter->setRowKeyRegexFilter('v1'); + $this->assertEquals($rowFilter, $filter->toProto()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Probability must be positive + */ + public function testSampleShouldThrowOnLessThanZero() + { + $this->keyFilter->sample(-1); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Probability must be less than 1.0 + */ + public function testSampleShouldThrowOnGreaterThanOne() + { + $this->keyFilter->sample(1.1); + } + + public function testSample() + { + $filter = $this->keyFilter->sample(0.9); + $this->assertInstanceOf(SimpleFilter::class, $filter); + $rowFilter = new RowFilter(); + $rowFilter->setRowSampleFilter(0.9); + $this->assertEquals($rowFilter, $filter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/Builder/LimitFilterTest.php b/Bigtable/tests/Unit/Filter/Builder/LimitFilterTest.php new file mode 100644 index 000000000000..e2fef4e762cf --- /dev/null +++ b/Bigtable/tests/Unit/Filter/Builder/LimitFilterTest.php @@ -0,0 +1,53 @@ +limitFilter = Filter::limit(); + } + + public function testCellsPerRow() + { + $filter = $this->limitFilter->cellsPerRow(5); + $rowFilter = new RowFilter(); + $rowFilter->setCellsPerRowLimitFilter(5); + $this->assertEquals($rowFilter, $filter->toProto()); + } + + public function testCellsPerColumn() + { + $filter = $this->limitFilter->cellsPerColumn(5); + $rowFilter = new RowFilter(); + $rowFilter->setCellsPerColumnLimitFilter(5); + $this->assertEquals($rowFilter, $filter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/Builder/OffsetFilterTest.php b/Bigtable/tests/Unit/Filter/Builder/OffsetFilterTest.php new file mode 100644 index 000000000000..f4279ba9969a --- /dev/null +++ b/Bigtable/tests/Unit/Filter/Builder/OffsetFilterTest.php @@ -0,0 +1,45 @@ +offsetFilter = Filter::offset(); + } + + public function testCellsPerRow() + { + $filter = $this->offsetFilter->cellsPerRow(5); + $rowFilter = new RowFilter(); + $rowFilter->setCellsPerRowOffsetFilter(5); + $this->assertEquals($rowFilter, $filter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/Builder/QualifierFilterTest.php b/Bigtable/tests/Unit/Filter/Builder/QualifierFilterTest.php new file mode 100644 index 000000000000..8d1b6f0a34c2 --- /dev/null +++ b/Bigtable/tests/Unit/Filter/Builder/QualifierFilterTest.php @@ -0,0 +1,64 @@ +qualifierFilter = new QualifierFilter; + } + + public function testRegex() + { + $filter = $this->qualifierFilter->regex('v1'); + $this->assertInstanceOf(SimpleFilter::class, $filter); + $rowFilter = new RowFilter(); + $rowFilter->setColumnQualifierRegexFilter('v1'); + $this->assertEquals($rowFilter, $filter->toProto()); + } + + public function testExactMatch() + { + $filter = $this->qualifierFilter->exactMatch('v1'); + $this->assertInstanceOf(SimpleFilter::class, $filter); + $rowFilter = new RowFilter(); + $rowFilter->setColumnQualifierRegexFilter('v1'); + $this->assertEquals($rowFilter, $filter->toProto()); + } + + public function testRangeWithInFamily() + { + $this->assertInstanceOf( + QualifierRangeFilter::class, + $this->qualifierFilter->rangeWithInFamily('cf1') + ); + } +} diff --git a/Bigtable/tests/Unit/Filter/Builder/RangeTest.php b/Bigtable/tests/Unit/Filter/Builder/RangeTest.php new file mode 100644 index 000000000000..11b8eb68974d --- /dev/null +++ b/Bigtable/tests/Unit/Filter/Builder/RangeTest.php @@ -0,0 +1,169 @@ +range = TestHelpers::stub(Range::class); + } + + public function testClass() + { + $this->assertInstanceOf(Range::class, $this->range); + $this->assertEquals(Range::BOUND_TYPE_UNBOUNDED, $this->range->getStartBound()); + $this->assertEquals(Range::BOUND_TYPE_UNBOUNDED, $this->range->getEndBound()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Start is unbounded + */ + public function testGetStartShouldThrow() + { + $this->range->getStart(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage End is unbounded + */ + public function testGetEndShouldThrow() + { + $this->range->getEnd(); + } + + public function testStartUnbounded() + { + $range = $this->range->startUnbounded(); + $this->assertEquals($this->range, $range); + $this->assertEquals(Range::BOUND_TYPE_UNBOUNDED, $this->range->getStartBound()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Start is unbounded + */ + public function testStartUnboundedShouldThrowOnGetStart() + { + $this->range->startUnbounded()->getStart(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage startOpen accepts only string or numeric types. + */ + public function testStartOpenShouldThrow() + { + $this->range->startOpen(null); + } + + public function testStartOpen() + { + $range = $this->range->startOpen('so1'); + $this->assertEquals($this->range, $range); + $this->assertEquals(Range::BOUND_TYPE_OPEN, $range->getStartBound()); + $this->assertEquals('so1', $range->getStart()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage startClosed accepts only string or numeric types. + */ + public function testStartClosedShouldThrow() + { + $this->range->startClosed(null); + } + + public function testStartClosed() + { + $range = $this->range->startClosed('sc1'); + $this->assertEquals($this->range, $range); + $this->assertEquals(Range::BOUND_TYPE_CLOSED, $range->getStartBound()); + $this->assertEquals('sc1', $range->getStart()); + } + + public function testEndUnbounded() + { + $range = $this->range->endUnbounded(); + $this->assertEquals($this->range, $range); + $this->assertEquals(Range::BOUND_TYPE_UNBOUNDED, $range->getEndBound()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage End is unbounded + */ + public function testEndUnboundedShouldThrow() + { + $this->range->endUnbounded()->getEnd(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage endOpen accepts only string or numeric types. + */ + public function testEndOpenShouldThrow() + { + $this->range->endOpen(null); + } + + public function testEndOpen() + { + $range = $this->range->endOpen('eo1'); + $this->assertEquals($this->range, $range); + $this->assertEquals(Range::BOUND_TYPE_OPEN, $range->getEndBound()); + $this->assertEquals('eo1', $range->getEnd()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage endClosed accepts only string or numeric types. + */ + public function testEndClosedShouldThrow() + { + $this->range->endClosed(null); + } + + public function testEndClosed() + { + $range = $this->range->endClosed('ec1'); + $this->assertEquals($this->range, $range); + $this->assertEquals(Range::BOUND_TYPE_CLOSED, $range->getEndBound()); + $this->assertEquals('ec1', $range->getEnd()); + } + + public function testOf() + { + $range = $this->range->of('sc1', 'eo1'); + $this->assertEquals($this->range, $range); + $this->assertEquals(Range::BOUND_TYPE_CLOSED, $range->getStartBound()); + $this->assertEquals(Range::BOUND_TYPE_OPEN, $range->getEndBound()); + } +} diff --git a/Bigtable/tests/Unit/Filter/Builder/RegexTraitTest.php b/Bigtable/tests/Unit/Filter/Builder/RegexTraitTest.php new file mode 100644 index 000000000000..477bf13fbb2b --- /dev/null +++ b/Bigtable/tests/Unit/Filter/Builder/RegexTraitTest.php @@ -0,0 +1,77 @@ +implementation = TestHelpers::impl(RegexTrait::class); + } + + public function testEscapeLiteralValueShouldReturnNullOnNull() + { + $this->assertEquals( + null, + $this->implementation->call('escapeLiteralValue', [null]) + ); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Expected byte array or string, instead got 'object'. + */ + public function testEscapeLiteralValueShouldThrowIfNotByteArrayOrString() + { + $this->implementation->call('escapeLiteralValue', [new \stdClass]); + } + + public function testEscapeLiteralValueAsciiChr() + { + $this->assertEquals( + 'hi', + $this->implementation->call('escapeLiteralValue', ['hi']) + ); + } + + public function testEscapeLiteralValueWithEscapeChr() + { + $this->assertEquals( + 'h\\.\\*i', + $this->implementation->call('escapeLiteralValue', ['h.*i']) + ); + } + + public function testEscapeLiteralValueWithByteArray() + { + $value = [ 0xe2, 0x80, 0xb3]; + $escapedValue = $this->implementation->call('escapeLiteralValue', [$value]); + $expected = implode(array_map('chr', $value)); + $this->assertEquals($expected, $escapedValue); + } +} diff --git a/Bigtable/tests/Unit/Filter/Builder/TimestampFilterTest.php b/Bigtable/tests/Unit/Filter/Builder/TimestampFilterTest.php new file mode 100644 index 000000000000..ef7d4d5853cb --- /dev/null +++ b/Bigtable/tests/Unit/Filter/Builder/TimestampFilterTest.php @@ -0,0 +1,35 @@ +assertInstanceOf(TimestampRangeFilter::class, $timestampFilter->range()); + } +} diff --git a/Bigtable/tests/Unit/Filter/Builder/ValueFilterTest.php b/Bigtable/tests/Unit/Filter/Builder/ValueFilterTest.php new file mode 100644 index 000000000000..c1693e325ed6 --- /dev/null +++ b/Bigtable/tests/Unit/Filter/Builder/ValueFilterTest.php @@ -0,0 +1,69 @@ +valueFilter = new ValueFilter; + } + + public function testRegex() + { + $filter = $this->valueFilter->regex('v1'); + $this->assertInstanceOf(SimpleFilter::class, $filter); + $rowFilter = new RowFilter(); + $rowFilter->setValueRegexFilter('v1'); + $this->assertEquals($rowFilter, $filter->toProto()); + } + + public function testExactMatch() + { + $filter = $this->valueFilter->exactMatch('v1'); + $this->assertInstanceOf(SimpleFilter::class, $filter); + $rowFilter = new RowFilter(); + $rowFilter->setValueRegexFilter('v1'); + $this->assertEquals($rowFilter, $filter->toProto()); + } + + public function testRange() + { + $this->assertInstanceOf(ValueRangeFilter::class, $this->valueFilter->range()); + } + + public function testStrip() + { + $filter = $this->valueFilter->strip(); + $rowFilter = new RowFilter(); + $rowFilter->setStripValueTransformer(true); + $this->assertEquals($rowFilter, $filter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/ChainFilterTest.php b/Bigtable/tests/Unit/Filter/ChainFilterTest.php new file mode 100644 index 000000000000..1a58ac968e55 --- /dev/null +++ b/Bigtable/tests/Unit/Filter/ChainFilterTest.php @@ -0,0 +1,49 @@ +chainFilter = new ChainFilter; + } + + public function testFilter() + { + $chainFilter = $this->chainFilter->addFilter(Filter::pass()); + $this->assertEquals($chainFilter, $this->chainFilter); + $chain = new Chain(); + $chain->setFilters([Filter::pass()->toProto()]); + $rowFilter = new RowFilter(); + $rowFilter->setChain($chain); + $this->assertEquals($rowFilter, $this->chainFilter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/ConditionFilterTest.php b/Bigtable/tests/Unit/Filter/ConditionFilterTest.php new file mode 100644 index 000000000000..77b101390569 --- /dev/null +++ b/Bigtable/tests/Unit/Filter/ConditionFilterTest.php @@ -0,0 +1,73 @@ +conditionFilter = new ConditionFilter(Filter::pass()); + $this->condition = new Condition(); + $this->condition->setPredicateFilter(Filter::pass()->toProto()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage In order to utilize a condition filter you must + * supply a filter through either + * Google\Cloud\Bigtable\Filter\ConditionFilter:then() + * or Google\Cloud\Bigtable\Filter\ConditionFilter:otherwise(). + */ + public function testPredicate() + { + $this->conditionFilter->toProto(); + } + + public function testThen() + { + $conditionFilter = $this->conditionFilter->then(Filter::block()); + $this->assertEquals($this->conditionFilter, $conditionFilter); + $this->condition->setTrueFilter(Filter::block()->toProto()); + $rowFilter = new RowFilter(); + $rowFilter->setCondition($this->condition); + $this->assertEquals($rowFilter, $this->conditionFilter->toProto()); + } + + public function testOtherwise() + { + $conditionFilter = $this->conditionFilter->otherwise(Filter::block()); + $this->assertEquals($this->conditionFilter, $conditionFilter); + $this->condition->setFalseFilter(Filter::block()->toProto()); + $rowFilter = new RowFilter(); + $rowFilter->setCondition($this->condition); + $this->assertEquals($rowFilter, $this->conditionFilter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/InterleaveFilterTest.php b/Bigtable/tests/Unit/Filter/InterleaveFilterTest.php new file mode 100644 index 000000000000..0031c0b58503 --- /dev/null +++ b/Bigtable/tests/Unit/Filter/InterleaveFilterTest.php @@ -0,0 +1,54 @@ +interleaveFilter = new InterleaveFilter; + } + + public function testClass() + { + $this->assertInstanceOf(InterleaveFilter::class, $this->interleaveFilter); + } + + public function testFilter() + { + $interleaveFilter = $this->interleaveFilter->addFilter(Filter::pass()); + $this->assertEquals($this->interleaveFilter, $interleaveFilter); + $interleave = new Interleave(); + $interleave->setFilters([Filter::pass()->toProto()]); + $rowFilter = new RowFilter(); + $rowFilter->setInterleave($interleave); + $this->assertEquals($rowFilter, $this->interleaveFilter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/QualifierRangeFilterTest.php b/Bigtable/tests/Unit/Filter/QualifierRangeFilterTest.php new file mode 100644 index 000000000000..25cce7084e3e --- /dev/null +++ b/Bigtable/tests/Unit/Filter/QualifierRangeFilterTest.php @@ -0,0 +1,78 @@ +qualifierRangeFilter = Filter::qualifier()->rangeWithInFamily(self::FAMILY_NAME); + } + + public function testClass() + { + $this->assertInstanceOf(QualifierRangeFilter::class, $this->qualifierRangeFilter); + } + + public function testToProto() + { + $columnRange = new ColumnRange(); + $columnRange->setFamilyName(self::FAMILY_NAME); + $rowFilter = new RowFilter(); + $rowFilter->setColumnRangeFilter($columnRange); + $this->assertEquals($rowFilter, $this->qualifierRangeFilter->toProto()); + } + + public function testToProtoClosedRange() + { + $this->qualifierRangeFilter->startClosed('sqc1')->endClosed('eqc1'); + $columnRange = new ColumnRange(); + $columnRange->setFamilyName(self::FAMILY_NAME); + $columnRange->setStartQualifierClosed('sqc1'); + $columnRange->setEndQualifierClosed('eqc1'); + $rowFilter = new RowFilter(); + $rowFilter->setColumnRangeFilter($columnRange); + $this->assertEquals($rowFilter, $this->qualifierRangeFilter->toProto()); + } + + public function testToProtoOpenRange() + { + $this->qualifierRangeFilter->startOpen('sqo1')->endOpen('eqo1'); + $columnRange = new ColumnRange(); + $columnRange->setFamilyName(self::FAMILY_NAME); + $columnRange->setStartQualifierOpen('sqo1'); + $columnRange->setEndQualifierOpen('eqo1'); + $rowFilter = new RowFilter(); + $rowFilter->setColumnRangeFilter($columnRange); + $this->assertEquals($rowFilter, $this->qualifierRangeFilter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/SimpleFilterTest.php b/Bigtable/tests/Unit/Filter/SimpleFilterTest.php new file mode 100644 index 000000000000..53fc17de8052 --- /dev/null +++ b/Bigtable/tests/Unit/Filter/SimpleFilterTest.php @@ -0,0 +1,36 @@ +assertEquals($rowFilter, $simpleFilter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/TimestampRangeFilterTest.php b/Bigtable/tests/Unit/Filter/TimestampRangeFilterTest.php new file mode 100644 index 000000000000..7a973e6f7264 --- /dev/null +++ b/Bigtable/tests/Unit/Filter/TimestampRangeFilterTest.php @@ -0,0 +1,67 @@ +timestampRangeFilter = new TimestampRangeFilter(); + } + + public function testToProto() + { + $timestampRange = new TimestampRange(); + $rowFilter = new RowFilter(); + $rowFilter->setTimestampRangeFilter($timestampRange); + $this->assertEquals($rowFilter, $this->timestampRangeFilter->toProto()); + } + + public function testToProtoOpenRange() + { + $this->timestampRangeFilter->startOpen(1)->endOpen(5); + $timestampRange = new TimestampRange(); + $timestampRange->setStartTimestampMicros(2); + $timestampRange->setEndTimestampMicros(5); + $rowFilter = new RowFilter(); + $rowFilter->setTimestampRangeFilter($timestampRange); + $this->assertEquals($rowFilter, $this->timestampRangeFilter->toProto()); + } + + public function testToProtoClosedRange() + { + $this->timestampRangeFilter->startClosed(1)->endClosed(5); + $timestampRange = new TimestampRange(); + $timestampRange->setStartTimestampMicros(1); + $timestampRange->setEndTimestampMicros(6); + $rowFilter = new RowFilter(); + $rowFilter->setTimestampRangeFilter($timestampRange); + $this->assertEquals($rowFilter, $this->timestampRangeFilter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/Filter/ValueRangeFilterTest.php b/Bigtable/tests/Unit/Filter/ValueRangeFilterTest.php new file mode 100644 index 000000000000..d1caab74f0cd --- /dev/null +++ b/Bigtable/tests/Unit/Filter/ValueRangeFilterTest.php @@ -0,0 +1,68 @@ +valueRangeFilter = new ValueRangeFilter(); + } + + public function testToProto() + { + $valueRange = new ValueRange(); + $rowFilter = new RowFilter(); + $rowFilter->setValueRangeFilter($valueRange); + $this->assertEquals($rowFilter, $this->valueRangeFilter->toProto()); + } + + public function testToProtoClosedRange() + { + $this->valueRangeFilter->startClosed('svc1')->endClosed('evc1'); + $valueRange = new ValueRange(); + $valueRange->setStartValueClosed('svc1'); + $valueRange->setEndValueClosed('evc1'); + $rowFilter = new RowFilter(); + $rowFilter->setValueRangeFilter($valueRange); + $this->assertEquals($rowFilter, $this->valueRangeFilter->toProto()); + } + + public function testToProtoOpenRange() + { + $this->valueRangeFilter->startOpen('svo1')->endOpen('evo1'); + $valueRange = new ValueRange(); + $valueRange->setStartValueOpen('svo1'); + $valueRange->setEndValueOpen('evo1'); + $rowFilter = new RowFilter(); + $rowFilter->setValueRangeFilter($valueRange); + $this->assertEquals($rowFilter, $this->valueRangeFilter->toProto()); + } +} diff --git a/Bigtable/tests/Unit/FilterTest.php b/Bigtable/tests/Unit/FilterTest.php new file mode 100644 index 000000000000..2a6e537aea5b --- /dev/null +++ b/Bigtable/tests/Unit/FilterTest.php @@ -0,0 +1,138 @@ +assertInstanceOf(ChainFilter::class, $chainFilter); + } + + public function testInterleave() + { + $interleaveFilter = Filter::interleave(); + $this->assertInstanceOf(InterleaveFilter::class, $interleaveFilter); + } + + public function testCondition() + { + $conditionFilter = Filter::condition(new SimpleFilter(new RowFilter)); + $this->assertInstanceOf(ConditionFilter::class, $conditionFilter); + } + + public function testKey() + { + $keyFilter = Filter::key(); + $this->assertInstanceOf(KeyFilter::class, $keyFilter); + } + + public function testFamily() + { + $familyFilter = Filter::family(); + $this->assertInstanceOf(FamilyFilter::class, $familyFilter); + } + + public function testQualifier() + { + $qualifierFilter = Filter::qualifier(); + $this->assertInstanceOf(QualifierFilter::class, $qualifierFilter); + } + + public function testTimestamp() + { + $timestampFilter = Filter::timestamp(); + $this->assertInstanceOf(TimestampFilter::class, $timestampFilter); + } + + public function testValue() + { + $valueFilter = Filter::value(); + $this->assertInstanceOf(ValueFilter::class, $valueFilter); + } + + public function testOffset() + { + $offsetFilter = Filter::offset(); + $this->assertInstanceOf(OffsetFilter::class, $offsetFilter); + } + + public function testLimit() + { + $limitFilter = Filter::limit(); + $this->assertInstanceOf(LimitFilter::class, $limitFilter); + } + + public function testPass() + { + $passFilter = Filter::pass(); + $this->assertInstanceOf(SimpleFilter::class, $passFilter); + $rowFilter = new RowFilter(); + $rowFilter->setPassAllFilter(true); + $this->assertEquals($rowFilter, $passFilter->toProto()); + } + + public function testBlock() + { + $blockFilter = Filter::block(); + $this->assertInstanceOf(SimpleFilter::class, $blockFilter); + $rowFilter = new RowFilter(); + $rowFilter->setBlockAllFilter(true); + $this->assertEquals($rowFilter, $blockFilter->toProto()); + } + + public function testSink() + { + $sinkFilter = Filter::sink(); + $this->assertInstanceOf(SimpleFilter::class, $sinkFilter); + $rowFilter = new RowFilter(); + $rowFilter->setSink(true); + $this->assertEquals($rowFilter, $sinkFilter->toProto()); + } + + public function testLable() + { + $labelFilter = Filter::label('l1'); + $this->assertInstanceOf(SimpleFilter::class, $labelFilter); + $rowFilter = new RowFilter(); + $rowFilter->setApplyLabelTransformer('l1'); + $this->assertEquals($rowFilter, $labelFilter->toProto()); + } +}