ydn.db
Version:
Javascript database library for IndexedDB, WebDatabase (WebSQL) and WebStorage (localStorage) storage mechanisms supporting version migration, advanced query and transaction workflow.
197 lines (177 loc) • 5.5 kB
JavaScript
// Copyright 2012 YDN Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Abstract join algorithm.
*
* @author kyawtun@yathit.com (Kyaw Tun)
*/
goog.provide('ydn.db.algo.AbstractSolver');
goog.require('goog.log');
goog.require('ydn.db');
goog.require('ydn.db.Streamer');
/**
* Abstract join algorithm.
* @param {(!Array|!{push: Function}|!ydn.db.Streamer)=} opt_out output
* receiver.
* @param {number=} opt_limit limit.
* to algorithm input and output.
* @constructor
*/
ydn.db.algo.AbstractSolver = function(opt_out, opt_limit) {
if (goog.DEBUG && goog.isDefAndNotNull(opt_out) && !('push' in opt_out)) {
throw new ydn.error.ArgumentException('output receiver object must have ' +
'"push" method.');
}
/**
* @protected
* @type {(!Array|!{push: Function}|!ydn.db.Streamer)|null}
*/
this.out = opt_out || null;
this.limit = opt_limit;
this.match_count = 0;
/**
* @protected
* @type {boolean}
*/
this.is_reverse = false;
/**
* For streamer output receiver, if this set true, output both key and
* reference value.
* @type {boolean}
* @protected
*/
this.is_duplex_output = opt_out instanceof ydn.db.Streamer &&
!!opt_out.getFieldName();
};
/**
* @protected
* @type {goog.log.Logger} logger.
*/
ydn.db.algo.AbstractSolver.prototype.logger =
goog.log.getLogger('ydn.db.algo.AbstractSolver');
/**
* Invoke before beginning of the iteration process.
* @param {ydn.db.base.Transaction} tx transaction used in iteration.
* @param {!Array.<!ydn.db.Iterator>} iterators list of iterators feed to the
* scanner.
* @param {!Function} callback on finish callback function.
* @return {boolean}
*/
ydn.db.algo.AbstractSolver.prototype.begin = function(tx, iterators, callback) {
this.is_reverse = iterators[0].isReversed();
if (goog.DEBUG) {
for (var i = 0; i < iterators.length; i++) {
if (!(iterators[i] instanceof ydn.db.Iterator)) {
throw new ydn.debug.error.TypeError('item at iterators ' + i +
' is not an iterator.');
}
if (i > 0) {
if (this.is_reverse != iterators[i].isReversed()) {
var r = this.is_reverse ? 'be reverse' : 'not be reverse';
throw new ydn.debug.error.TypeError('iterator at ' + i +
' must ' + r);
}
}
}
}
if (this.out instanceof ydn.db.Streamer) {
this.out.setTx(tx);
}
if (this.is_duplex_output) {
var iter_index = iterators[0].getIndexKeyPath();
if (iter_index && iter_index.length > 1) {
if (iter_index[iter_index.length - 1] != this.out.getFieldName()) {
throw new ydn.error.InvalidOperationError('Output streamer ' +
'projection field must be same as postfix field in the iterator');
}
} else {
// todo: add validation
/*
if (goog.DEBUG) {
goog.log.warning(this.logger, 'Unable to check correctness of output streamer.');
}
*/
}
}
var s = '{';
for (var i = 0; i < iterators.length; i++) {
if (i > 0) {
s += ', ';
}
s += iterators.toString();
}
s += '}';
if (this.is_reverse) {
s += ' reverse';
}
goog.log.fine(this.logger, this + ' begin ' + s);
return false;
};
/**
* Push the result if all keys match. Break the limit if the number of results
* reach the limit.
* @param {!Array} advance
* @param {!Array} keys input values.
* @param {!Array} values output values.
* @param {*=} opt_match_key match key.
* @param {*=} opt_match_value match key.
* @return {!Object} cursor advancement array.
* @protected
*/
ydn.db.algo.AbstractSolver.prototype.pusher = function(advance, keys, values,
opt_match_key, opt_match_value) {
var matched = goog.isDefAndNotNull(opt_match_key);
if (!goog.isDef(opt_match_key)) {
opt_match_key = values[0];
matched = goog.isDefAndNotNull(opt_match_key);
for (var i = 1; matched && i < values.length; i++) {
if (!goog.isDefAndNotNull(values[i]) ||
ydn.db.cmp(values[i], opt_match_key) != 0) {
matched = false;
}
}
}
if (matched) {
this.match_count++;
//console.log(['match key', match_key, JSON.stringify(keys)]);
if (this.out) {
if (this.is_duplex_output) {
this.out.push(opt_match_key, opt_match_value);
} else {
this.out.push(opt_match_key);
}
}
if (goog.isDef(this.limit) && this.match_count >= this.limit) {
return [];
}
}
return advance;
};
/**
*
* @param {!Array} input input values.
* @param {!Array} output output values.
* @return {!Array|!Object} next positions.
*/
ydn.db.algo.AbstractSolver.prototype.solver = function(input, output) {
return [];
};
/**
* Invoke at the end of the iteration process.
* @param {!Function} callback on finish callback function.
* @return {boolean} true to wait.
*/
ydn.db.algo.AbstractSolver.prototype.finish = function(callback) {
return false;
};