Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 55 additions & 4 deletions packages/bigtable/src/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,45 @@ Table.formatName_ = function(instanceName, name) {
return instanceName + '/tables/' + name;
};

/**
* Creates a range based off of a key prefix.
*
* @private
*
* @param {string} start - The key prefix/starting bound.
* @return {object} range
*
* @example
* Table.createPrefixRange_('start');
* // => {
* // start: 'start',
* // end: {
* // value: 'staru',
* // inclusive: false
* // }
* // }
*/
Table.createPrefixRange_ = function(start) {
var prefix = start.replace(new RegExp('[\xff]+$'), '');
var endKey = '';

if (prefix) {
var position = prefix.length - 1;
var charCode = prefix.charCodeAt(position);
var nextChar = String.fromCharCode(charCode + 1);

endKey = prefix.substring(0, position) + nextChar;
}

return {
start: start,
end: {
value: endKey,
inclusive: !endKey
}
};
};

/**
* Create a column family.
*
Expand Down Expand Up @@ -321,13 +360,14 @@ Table.prototype.createFamily = function(name, rule, callback) {
* @param {options=} options - Configuration object.
* @param {boolean} options.decode - If set to `false` it will not decode Buffer
* values returned from Bigtable. Default: true.
* @param {string[]} options.keys - A list of row keys.
* @param {string} options.start - Start value for key range.
* @param {string} options.end - End value for key range.
* @param {object[]} options.ranges - A list of key ranges.
* @param {module:bigtable/filter} options.filter - Row filters allow you to
* @param {module:bigtable/filter} options.filter - Row filters allow you to
* both make advanced queries and format how the data is returned.
* @param {string[]} options.keys - A list of row keys.
* @param {number} options.limit - Maximum number of rows to be returned.
* @param {string} options.prefix - Prefix that the row key must match.
* @param {object[]} options.ranges - A list of key ranges.
* @param {string} options.start - Start value for key range.
* @return {stream}
*
* @example
Expand Down Expand Up @@ -361,6 +401,13 @@ Table.prototype.createFamily = function(name, rule, callback) {
* });
*
* //-
* // Scan for row keys that contain a specific prefix.
* //-
* table.createReadStream({
* prefix: 'gwash'
* });
*
* //-
* // Specify a contiguous range of rows to read by supplying `start` and `end`
* // keys.
* //
Expand Down Expand Up @@ -421,6 +468,10 @@ Table.prototype.createReadStream = function(options) {
});
}

if (options.prefix) {
options.ranges.push(Table.createPrefixRange_(options.prefix));
}

if (options.keys || options.ranges.length) {
reqOpts.rows = {};

Expand Down
13 changes: 13 additions & 0 deletions packages/bigtable/system-test/bigtable.js
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,19 @@ describe('Bigtable', function() {
});
});

it('should fetch a range of rows via prefix', function(done) {
var options = {
prefix: 'g'
};

TABLE.getRows(options, function(err, rows) {
assert.ifError(err);
assert.strictEqual(rows.length, 1);
assert.strictEqual(rows[0].id, 'gwashington');
done();
});
});

it('should fetch individual cells of a row', function(done) {
var row = TABLE.row('alincoln');

Expand Down
95 changes: 95 additions & 0 deletions packages/bigtable/test/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,68 @@ describe('Bigtable/Table', function() {
});
});

describe('createPrefixRange_', function() {
it('should create a range from the prefix', function() {
assert.deepEqual(Table.createPrefixRange_('start'), {
start: 'start',
end: {
value: 'staru',
inclusive: false
}
});

assert.deepEqual(Table.createPrefixRange_('X\xff'), {
start: 'X\xff',
end: {
value: 'Y',
inclusive: false
}
});

assert.deepEqual(Table.createPrefixRange_('xoo\xff'), {
start: 'xoo\xff',
end: {
value: 'xop',
inclusive: false
}
});

assert.deepEqual(Table.createPrefixRange_('a\xffb'), {
start: 'a\xffb',
end: {
value: 'a\xffc',
inclusive: false
}
});

assert.deepEqual(Table.createPrefixRange_('com.google.'), {
start: 'com.google.',
end: {
value: 'com.google/',
inclusive: false
}
});
});

it('should create an inclusive bound when the prefix is empty', function() {
assert.deepEqual(Table.createPrefixRange_('\xff'), {
start: '\xff',
end: {
value: '',
inclusive: true
}
});

assert.deepEqual(Table.createPrefixRange_(''), {
start: '',
end: {
value: '',
inclusive: true
}
});
});
});

describe('createFamily', function() {
var COLUMN_ID = 'my-column';

Expand Down Expand Up @@ -454,6 +516,39 @@ describe('Bigtable/Table', function() {

table.createReadStream(options);
});

it('should transform the prefix into a range', function(done) {
var fakeRange = {};
var fakePrefixRange = {
start: 'a',
end: 'b'
};

var fakePrefix = 'abc';

var prefixSpy = Table.createPrefixRange_ = sinon.spy(function() {
return fakePrefixRange;
});

var rangeSpy = FakeFilter.createRange = sinon.spy(function() {
return fakeRange;
});

table.requestStream = function(g, reqOpts) {
assert.strictEqual(prefixSpy.getCall(0).args[0], fakePrefix);
assert.deepEqual(reqOpts.rows.rowRanges, [fakeRange]);

assert.deepEqual(rangeSpy.getCall(0).args, [
fakePrefixRange.start,
fakePrefixRange.end,
'Key'
]);

done();
};

table.createReadStream({ prefix: fakePrefix });
});
});

describe('success', function() {
Expand Down