mirror of https://gitee.com/bigwinds/arangodb
688 lines
20 KiB
JavaScript
688 lines
20 KiB
JavaScript
/* jshint strict: false */
|
|
/* global ArangoClusterComm */
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief Arango Simple Query Language
|
|
// /
|
|
// / @file
|
|
// /
|
|
// / DISCLAIMER
|
|
// /
|
|
// / Copyright 2012 triagens GmbH, Cologne, Germany
|
|
// /
|
|
// / 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.
|
|
// /
|
|
// / Copyright holder is triAGENS GmbH, Cologne, Germany
|
|
// /
|
|
// / @author Dr. Frank Celler
|
|
// / @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
var internal = require('internal');
|
|
|
|
var ArangoError = require('@arangodb').ArangoError;
|
|
|
|
var sq = require('@arangodb/simple-query-common');
|
|
|
|
var GeneralArrayCursor = sq.GeneralArrayCursor;
|
|
var SimpleQueryAll = sq.SimpleQueryAll;
|
|
var SimpleQueryArray = sq.SimpleQueryArray;
|
|
var SimpleQueryByExample = sq.SimpleQueryByExample;
|
|
var SimpleQueryFulltext = sq.SimpleQueryFulltext;
|
|
var SimpleQueryGeo = sq.SimpleQueryGeo;
|
|
var SimpleQueryNear = sq.SimpleQueryNear;
|
|
var SimpleQueryRange = sq.SimpleQueryRange;
|
|
var SimpleQueryWithin = sq.SimpleQueryWithin;
|
|
var SimpleQueryWithinRectangle = sq.SimpleQueryWithinRectangle;
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief rewrites an index id by stripping the collection name from it
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
var rewriteIndex = function (id) {
|
|
if (id === null || id === undefined) {
|
|
return;
|
|
}
|
|
|
|
if (typeof id === 'string') {
|
|
return id.replace(/^[a-zA-Z0-9_\-]+\//, '');
|
|
}
|
|
if (typeof id === 'object' && id.hasOwnProperty('id')) {
|
|
return id.id.replace(/^[a-zA-Z0-9_\-]+\//, '');
|
|
}
|
|
return id;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief build a limit string
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
var limitString = function (skip, limit) {
|
|
if (skip > 0 || limit > 0) {
|
|
if (limit <= 0) {
|
|
limit = 99999999999;
|
|
}
|
|
return 'LIMIT ' + parseInt(skip, 10) + ', ' + parseInt(limit, 10) + ' ';
|
|
}
|
|
return '';
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief executes an all query
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
SimpleQueryAll.prototype.execute = function () {
|
|
if (this._execution !== null) {
|
|
return;
|
|
}
|
|
|
|
if (this._skip === null) {
|
|
this._skip = 0;
|
|
}
|
|
|
|
var bindVars = { '@collection': this._collection.name() };
|
|
|
|
var query = 'FOR doc IN @@collection ' + limitString(this._skip, this._limit) + ' RETURN doc';
|
|
var documents = require('internal').db._query({ query, bindVars}).toArray();
|
|
|
|
this._execution = new GeneralArrayCursor(documents);
|
|
this._countQuery = documents.length;
|
|
this._countTotal = documents.length;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief executes a query-by-example
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
SimpleQueryByExample.prototype.execute = function () {
|
|
if (this._execution !== null) {
|
|
return;
|
|
}
|
|
|
|
if (this._skip === null || this._skip <= 0) {
|
|
this._skip = 0;
|
|
}
|
|
|
|
if (this._example === null ||
|
|
typeof this._example !== 'object' ||
|
|
Array.isArray(this._example)) {
|
|
// invalid datatype for example
|
|
var err = new ArangoError();
|
|
err.errorNum = internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code;
|
|
err.errorMessage = "invalid document type '" + (typeof this._example) + "'";
|
|
throw err;
|
|
}
|
|
|
|
var bindVars = { '@collection': this._collection.name() };
|
|
var filters = [];
|
|
var self = this;
|
|
Object.keys(this._example).forEach(function (key) {
|
|
var value = self._example[key];
|
|
filters.push('FILTER doc.`' + key.replace(/\`/g, '').split('.').join('`.`') + '` == ' + JSON.stringify(value));
|
|
});
|
|
|
|
var query = 'FOR doc IN @@collection ' + filters.join(' ') + ' ' +
|
|
limitString(this._skip, this._limit) + ' RETURN doc';
|
|
|
|
var documents = require('internal').db._query({ query, bindVars}).toArray();
|
|
|
|
this._execution = new GeneralArrayCursor(documents);
|
|
this._countQuery = documents.length;
|
|
this._countTotal = documents.length;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief executes a range query
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
SimpleQueryRange.prototype.execute = function () {
|
|
if (this._execution !== null) {
|
|
return;
|
|
}
|
|
|
|
var query = 'FOR doc IN @@collection ';
|
|
var bindVars = {
|
|
'@collection': this._collection.name(),
|
|
attribute: this._attribute,
|
|
left: this._left,
|
|
right: this._right
|
|
};
|
|
|
|
if (this._type === 0) {
|
|
query += 'FILTER doc.@attribute >= @left && doc.@attribute < @right ';
|
|
} else if (this._type === 1) {
|
|
query += 'FILTER doc.@attribute >= @left && doc.@attribute <= @right ';
|
|
} else {
|
|
throw 'unknown type';
|
|
}
|
|
|
|
query += limitString(this._skip, this._limit) + ' RETURN doc';
|
|
|
|
var documents = require('internal').db._query({ query, bindVars}).toArray();
|
|
|
|
this._execution = new GeneralArrayCursor(documents);
|
|
this._countQuery = documents.length - this._skip;
|
|
this._countTotal = documents.length;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief executes a near query
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
SimpleQueryNear.prototype.execute = function () {
|
|
if (this._execution !== null) {
|
|
return;
|
|
}
|
|
|
|
if (this._skip === null) {
|
|
this._skip = 0;
|
|
}
|
|
|
|
if (this._skip < 0) {
|
|
var err = new ArangoError();
|
|
err.errorNum = internal.errors.ERROR_BAD_PARAMETER.code;
|
|
err.errorMessage = 'skip must be non-negative';
|
|
throw err;
|
|
}
|
|
|
|
if (this._limit <= 0) {
|
|
this._limit = 100;
|
|
}
|
|
|
|
var bindVars = {
|
|
'@collection': this._collection.name(),
|
|
latitude: this._latitude,
|
|
longitude: this._longitude,
|
|
limit: parseInt((this._skip || 0) + (this._limit || 99999999999), 10)
|
|
};
|
|
|
|
var mustSort = false;
|
|
var documents = [];
|
|
var cluster = require('@arangodb/cluster');
|
|
if (cluster.isCoordinator()) {
|
|
if (this._distance === null || this._distance === undefined) {
|
|
this._distance = '_distance';
|
|
}
|
|
mustSort = true;
|
|
|
|
var dbName = require('internal').db._name();
|
|
var shards = cluster.shardList(dbName, this._collection.name());
|
|
var coord = { coordTransactionID: ArangoClusterComm.getId() };
|
|
var options = { coordTransactionID: coord.coordTransactionID, timeout: 360 };
|
|
|
|
var _limit = 0;
|
|
if (this._limit > 0) {
|
|
_limit = parseInt(this._skip + this._limit, 10);
|
|
}
|
|
|
|
var attribute;
|
|
if (this._distance !== null) {
|
|
attribute = this._distance;
|
|
} else {
|
|
// use a pseudo-attribute for distance (we need this for sorting)
|
|
attribute = '$distance';
|
|
}
|
|
|
|
var self = this;
|
|
shards.forEach(function (shard) {
|
|
ArangoClusterComm.asyncRequest('put',
|
|
'shard:' + shard,
|
|
dbName,
|
|
'/_api/simple/near',
|
|
JSON.stringify({
|
|
collection: shard,
|
|
latitude: self._latitude,
|
|
longitude: self._longitude,
|
|
distance: attribute,
|
|
geo: rewriteIndex(self._index),
|
|
skip: 0,
|
|
limit: _limit || undefined,
|
|
batchSize: 100000000
|
|
}),
|
|
{ },
|
|
options);
|
|
});
|
|
|
|
var result = cluster.wait(coord, shards.length);
|
|
|
|
result.forEach(function (part) {
|
|
var body = JSON.parse(part.body);
|
|
documents = documents.concat(body.result);
|
|
});
|
|
|
|
if (shards.length > 1) {
|
|
documents.sort(function (l, r) {
|
|
if (l[attribute] === r[attribute]) {
|
|
return 0;
|
|
}
|
|
return (l[attribute] < r[attribute] ? -1 : 1);
|
|
});
|
|
}
|
|
|
|
if (_limit > 0) {
|
|
documents = documents.slice(this._skip, _limit);
|
|
} else if (this._skip > 0) {
|
|
documents = documents.slice(this._skip);
|
|
}
|
|
|
|
if (this._distance === null) {
|
|
var n = documents.length;
|
|
for (var i = 0; i < n; ++i) {
|
|
delete documents[i][attribute];
|
|
}
|
|
}
|
|
} else {
|
|
var query;
|
|
if (typeof this._distance === 'string') {
|
|
query = 'FOR doc IN NEAR(@@collection, @latitude, @longitude, @limit, @distance) ';
|
|
bindVars.distance = this._distance;
|
|
} else {
|
|
query = 'FOR doc IN NEAR(@@collection, @latitude, @longitude, @limit) ';
|
|
}
|
|
|
|
if (mustSort) {
|
|
query += 'SORT doc.@distance ';
|
|
}
|
|
|
|
query += limitString(this._skip, this._limit) + ' RETURN doc';
|
|
|
|
documents = require('internal').db._query({ query, bindVars}).toArray();
|
|
}
|
|
|
|
this._execution = new GeneralArrayCursor(documents);
|
|
this._countQuery = documents.length - this._skip;
|
|
this._countTotal = documents.length;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief executes a within query
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
SimpleQueryWithin.prototype.execute = function () {
|
|
if (this._execution !== null) {
|
|
return;
|
|
}
|
|
|
|
if (this._skip === null) {
|
|
this._skip = 0;
|
|
}
|
|
|
|
if (this._skip < 0) {
|
|
var err = new ArangoError();
|
|
err.errorNum = internal.errors.ERROR_BAD_PARAMETER.code;
|
|
err.errorMessage = 'skip must be non-negative';
|
|
throw err;
|
|
}
|
|
|
|
var mustSort = false;
|
|
|
|
var cluster = require('@arangodb/cluster');
|
|
|
|
var documents = [];
|
|
if (cluster.isCoordinator()) {
|
|
if (this._distance === null || this._distance === undefined) {
|
|
this._distance = '_distance';
|
|
}
|
|
mustSort = true;
|
|
|
|
var dbName = require('internal').db._name();
|
|
var shards = cluster.shardList(dbName, this._collection.name());
|
|
var coord = { coordTransactionID: ArangoClusterComm.getId() };
|
|
var options = { coordTransactionID: coord.coordTransactionID, timeout: 360 };
|
|
|
|
var _limit = 0;
|
|
if (this._limit > 0) {
|
|
_limit = parseInt(this._skip + this._limit, 10);
|
|
}
|
|
|
|
var attribute;
|
|
if (this._distance !== null) {
|
|
attribute = this._distance;
|
|
} else {
|
|
// use a pseudo-attribute for distance (we need this for sorting)
|
|
attribute = '$distance';
|
|
}
|
|
|
|
var self = this;
|
|
shards.forEach(function (shard) {
|
|
ArangoClusterComm.asyncRequest('put',
|
|
'shard:' + shard,
|
|
dbName,
|
|
'/_api/simple/within',
|
|
JSON.stringify({
|
|
collection: shard,
|
|
latitude: self._latitude,
|
|
longitude: self._longitude,
|
|
distance: attribute,
|
|
radius: self._radius,
|
|
geo: rewriteIndex(self._index),
|
|
skip: 0,
|
|
limit: _limit || undefined,
|
|
batchSize: 100000000
|
|
}),
|
|
{ },
|
|
options);
|
|
});
|
|
|
|
var result = cluster.wait(coord, shards.length);
|
|
|
|
result.forEach(function (part) {
|
|
var body = JSON.parse(part.body);
|
|
documents = documents.concat(body.result);
|
|
});
|
|
|
|
if (shards.length > 1) {
|
|
documents.sort(function (l, r) {
|
|
if (l[attribute] === r[attribute]) {
|
|
return 0;
|
|
}
|
|
return (l[attribute] < r[attribute] ? -1 : 1);
|
|
});
|
|
}
|
|
|
|
if (_limit > 0) {
|
|
documents = documents.slice(this._skip, _limit);
|
|
} else if (this._skip > 0) {
|
|
documents = documents.slice(this._skip);
|
|
}
|
|
|
|
if (this._distance === null) {
|
|
var n = documents.length;
|
|
for (var i = 0; i < n; ++i) {
|
|
delete documents[i][attribute];
|
|
}
|
|
}
|
|
} else {
|
|
var bindVars = {
|
|
'@collection': this._collection.name(),
|
|
latitude: this._latitude,
|
|
longitude: this._longitude,
|
|
radius: this._radius
|
|
};
|
|
|
|
var query;
|
|
if (typeof this._distance === 'string') {
|
|
query = 'FOR doc IN WITHIN(@@collection, @latitude, @longitude, @radius, @distance) ';
|
|
bindVars.distance = this._distance;
|
|
} else {
|
|
query = 'FOR doc IN WITHIN(@@collection, @latitude, @longitude, @radius) ';
|
|
}
|
|
|
|
if (mustSort) {
|
|
query += 'SORT doc.@distance ';
|
|
}
|
|
|
|
query += limitString(this._skip, this._limit) + ' RETURN doc';
|
|
|
|
documents = require('internal').db._query({ query, bindVars}).toArray();
|
|
}
|
|
|
|
this._execution = new GeneralArrayCursor(documents);
|
|
this._countQuery = documents.length - this._skip;
|
|
this._countTotal = documents.length;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief executes a fulltext query
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
SimpleQueryFulltext.prototype.execute = function () {
|
|
if (this._execution !== null) {
|
|
return;
|
|
}
|
|
|
|
var documents = [];
|
|
|
|
let bindVars = {
|
|
'@collection': this._collection.name(),
|
|
attribute: this._attribute,
|
|
query: this._query
|
|
};
|
|
let query;
|
|
let limit = parseInt((this._skip || 0) + (this._limit || 0), 10);
|
|
if (limit > 0) {
|
|
bindVars.limit = limit;
|
|
query = 'FOR doc IN FULLTEXT(@@collection, @attribute, @query, @limit) ' +
|
|
limitString(this._skip, this._limit) + ' RETURN doc';
|
|
} else {
|
|
query = 'FOR doc IN FULLTEXT(@@collection, @attribute, @query) RETURN doc';
|
|
}
|
|
documents = require('internal').db._query({ query, bindVars}).toArray();
|
|
|
|
this._execution = new GeneralArrayCursor(documents);
|
|
this._countQuery = documents.length - this._skip;
|
|
this._countTotal = documents.length;
|
|
};
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief executes a within-rectangle query
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
SimpleQueryWithinRectangle.prototype.execute = function () {
|
|
var result;
|
|
var documents;
|
|
|
|
if (this._execution !== null) {
|
|
return;
|
|
}
|
|
|
|
if (this._skip === null) {
|
|
this._skip = 0;
|
|
}
|
|
|
|
if (this._skip < 0) {
|
|
var err = new ArangoError();
|
|
err.errorNum = internal.errors.ERROR_BAD_PARAMETER.code;
|
|
err.errorMessage = 'skip must be non-negative';
|
|
throw err;
|
|
}
|
|
|
|
var cluster = require('@arangodb/cluster');
|
|
|
|
if (cluster.isCoordinator()) {
|
|
var dbName = require('internal').db._name();
|
|
var shards = cluster.shardList(dbName, this._collection.name());
|
|
var coord = { coordTransactionID: ArangoClusterComm.getId() };
|
|
var options = { coordTransactionID: coord.coordTransactionID, timeout: 360 };
|
|
var _limit = 0;
|
|
if (this._limit > 0) {
|
|
if (this._skip >= 0) {
|
|
_limit = this._skip + this._limit;
|
|
}
|
|
}
|
|
|
|
var self = this;
|
|
shards.forEach(function (shard) {
|
|
ArangoClusterComm.asyncRequest('put',
|
|
'shard:' + shard,
|
|
dbName,
|
|
'/_api/simple/within-rectangle',
|
|
JSON.stringify({
|
|
collection: shard,
|
|
latitude1: self._latitude1,
|
|
longitude1: self._longitude1,
|
|
latitude2: self._latitude2,
|
|
longitude2: self._longitude2,
|
|
geo: rewriteIndex(self._index),
|
|
skip: 0,
|
|
limit: _limit || undefined,
|
|
batchSize: 100000000
|
|
}),
|
|
{ },
|
|
options);
|
|
});
|
|
|
|
var _documents = [], total = 0;
|
|
result = cluster.wait(coord, shards.length);
|
|
|
|
result.forEach(function (part) {
|
|
var body = JSON.parse(part.body);
|
|
total += body.total;
|
|
|
|
_documents = _documents.concat(body.result);
|
|
});
|
|
|
|
if (this._limit > 0) {
|
|
_documents = _documents.slice(0, this._skip + this._limit);
|
|
}
|
|
|
|
documents = {
|
|
documents: _documents,
|
|
count: _documents.length,
|
|
total: total
|
|
};
|
|
} else {
|
|
var distanceMeters = function (lat1, lon1, lat2, lon2) {
|
|
var deltaLat = (lat2 - lat1) * Math.PI / 180;
|
|
var deltaLon = (lon2 - lon1) * Math.PI / 180;
|
|
var a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
|
|
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
|
|
Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
|
|
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
|
|
return 6378.137 /* radius of earth in kilometers */
|
|
* c
|
|
* 1000; // kilometers to meters
|
|
};
|
|
|
|
var diameter = distanceMeters(this._latitude1, this._longitude1, this._latitude2, this._longitude2);
|
|
var midpoint = [
|
|
this._latitude1 + (this._latitude2 - this._latitude1) * 0.5,
|
|
this._longitude1 + (this._longitude2 - this._longitude1) * 0.5
|
|
];
|
|
|
|
result = this._collection.within(midpoint[0], midpoint[1], diameter).toArray();
|
|
|
|
var idx = this._collection.index(this._index);
|
|
var latLower, latUpper, lonLower, lonUpper;
|
|
|
|
if (this._latitude1 < this._latitude2) {
|
|
latLower = this._latitude1;
|
|
latUpper = this._latitude2;
|
|
} else {
|
|
latLower = this._latitude2;
|
|
latUpper = this._latitude1;
|
|
}
|
|
|
|
if (this._longitude1 < this._longitude2) {
|
|
lonLower = this._longitude1;
|
|
lonUpper = this._longitude2;
|
|
} else {
|
|
lonLower = this._longitude2;
|
|
lonUpper = this._longitude1;
|
|
}
|
|
|
|
var deref = function (doc, parts) {
|
|
if (parts.length === 1) {
|
|
return doc[parts[0]];
|
|
}
|
|
|
|
var i = 0;
|
|
try {
|
|
while (i < parts.length && doc !== null && doc !== undefined) {
|
|
doc = doc[parts[i]];
|
|
++i;
|
|
}
|
|
return doc;
|
|
} catch (err) {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
documents = [];
|
|
if (idx.type === 'geo1') {
|
|
// geo1, we have both coordinates in a list
|
|
var attribute = idx.fields[0];
|
|
var parts = attribute.split('.');
|
|
|
|
if (idx.geoJson) {
|
|
result.forEach(function (document) {
|
|
var doc = deref(document, parts);
|
|
if (!Array.isArray(doc)) {
|
|
return;
|
|
}
|
|
|
|
// check if within bounding rectangle
|
|
// first list value is longitude, then latitude
|
|
if (doc[1] >= latLower && doc[1] <= latUpper &&
|
|
doc[0] >= lonLower && doc[0] <= lonUpper) {
|
|
documents.push(document);
|
|
}
|
|
});
|
|
} else {
|
|
result.forEach(function (document) {
|
|
var doc = deref(document, parts);
|
|
if (!Array.isArray(doc)) {
|
|
return;
|
|
}
|
|
|
|
// check if within bounding rectangle
|
|
// first list value is latitude, then longitude
|
|
if (doc[0] >= latLower && doc[0] <= latUpper &&
|
|
doc[1] >= lonLower && doc[1] <= lonUpper) {
|
|
documents.push(document);
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
// geo2, we have dedicated latitude and longitude attributes
|
|
var latAtt = idx.fields[0], lonAtt = idx.fields[1];
|
|
var latParts = latAtt.split('.');
|
|
var lonParts = lonAtt.split('.');
|
|
|
|
result.forEach(function (document) {
|
|
var latDoc = deref(document, latParts);
|
|
if (latDoc === null || latDoc === undefined) {
|
|
return;
|
|
}
|
|
var lonDoc = deref(document, lonParts);
|
|
if (lonDoc === null || lonDoc === undefined) {
|
|
return;
|
|
}
|
|
|
|
// check if within bounding rectangle
|
|
if (latDoc >= latLower && latDoc <= latUpper &&
|
|
lonDoc >= lonLower && lonDoc <= lonUpper) {
|
|
documents.push(document);
|
|
}
|
|
});
|
|
}
|
|
|
|
documents = {
|
|
documents: documents,
|
|
count: result.length,
|
|
total: result.length
|
|
};
|
|
|
|
if (this._limit > 0) {
|
|
documents.documents = documents.documents.slice(0, this._skip + this._limit);
|
|
documents.count = documents.documents.length;
|
|
}
|
|
}
|
|
|
|
this._execution = new GeneralArrayCursor(documents.documents, this._skip, null);
|
|
this._countQuery = documents.total - this._skip;
|
|
this._countTotal = documents.total;
|
|
};
|
|
|
|
exports.GeneralArrayCursor = GeneralArrayCursor;
|
|
exports.SimpleQueryAll = SimpleQueryAll;
|
|
exports.SimpleQueryArray = SimpleQueryArray;
|
|
exports.SimpleQueryByExample = SimpleQueryByExample;
|
|
exports.SimpleQueryFulltext = SimpleQueryFulltext;
|
|
exports.SimpleQueryGeo = SimpleQueryGeo;
|
|
exports.SimpleQueryNear = SimpleQueryNear;
|
|
exports.SimpleQueryRange = SimpleQueryRange;
|
|
exports.SimpleQueryWithin = SimpleQueryWithin;
|
|
exports.SimpleQueryWithinRectangle = SimpleQueryWithinRectangle;
|