mirror of https://gitee.com/bigwinds/arangodb
added selectivity estimates for some index types
This commit is contained in:
parent
436ac1adfc
commit
39603ebaf4
20
CHANGELOG
20
CHANGELOG
|
@ -1,6 +1,26 @@
|
||||||
v2.5.0 (XXXX-XX-XX)
|
v2.5.0 (XXXX-XX-XX)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
* added selectivity estimates for primary, edge, and hash indexes.
|
||||||
|
|
||||||
|
The selectivity estimates are returned by the `GET /_api/index` REST API method
|
||||||
|
in a sub-attribute `selectivityEstimate` for each index that supports it. This
|
||||||
|
attribute will be omitted for indexes that do not provide selectivity estimates.
|
||||||
|
If provided, the selectivity estimate will be a numeric value between 0 and 1.
|
||||||
|
|
||||||
|
Selectivity estimates will also be reported in the result of `collection.getIndexes()`
|
||||||
|
for all indexes that support this. If no selectivity estimate can be determined for
|
||||||
|
an index, the attribute `selectivityEstimate` will be omitted here, too.
|
||||||
|
|
||||||
|
The web interface also shows selectivity estimates for each index that supports this.
|
||||||
|
|
||||||
|
Currently the following index types can provide selectivity estimates:
|
||||||
|
- primary index
|
||||||
|
- edge index
|
||||||
|
- hash index (unique and non-unique)
|
||||||
|
|
||||||
|
No selectivity estimates will be provided when running in cluster mode.
|
||||||
|
|
||||||
* fixed issue #1226: arangod log issues
|
* fixed issue #1226: arangod log issues
|
||||||
|
|
||||||
* added additional logger if arangod is started in foreground mode on a tty
|
* added additional logger if arangod is started in foreground mode on a tty
|
||||||
|
|
|
@ -4,7 +4,6 @@ require 'rspec'
|
||||||
require 'arangodb.rb'
|
require 'arangodb.rb'
|
||||||
|
|
||||||
describe ArangoDB do
|
describe ArangoDB do
|
||||||
api = "/_api/index"
|
|
||||||
prefix = "api-index-hash"
|
prefix = "api-index-hash"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
|
@ -457,6 +457,7 @@ SHELL_COMMON = \
|
||||||
@top_srcdir@/js/common/tests/shell-cap-constraint-timecritical.js \
|
@top_srcdir@/js/common/tests/shell-cap-constraint-timecritical.js \
|
||||||
@top_srcdir@/js/common/tests/shell-unique-constraint.js \
|
@top_srcdir@/js/common/tests/shell-unique-constraint.js \
|
||||||
@top_srcdir@/js/common/tests/shell-hash-index.js \
|
@top_srcdir@/js/common/tests/shell-hash-index.js \
|
||||||
|
@top_srcdir@/js/common/tests/shell-hash-index-noncluster.js \
|
||||||
@top_srcdir@/js/common/tests/shell-fulltext.js \
|
@top_srcdir@/js/common/tests/shell-fulltext.js \
|
||||||
@top_srcdir@/js/common/tests/shell-graph.js
|
@top_srcdir@/js/common/tests/shell-graph.js
|
||||||
|
|
||||||
|
|
|
@ -115,25 +115,23 @@ namespace triagens {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasSelectivityEstimate () const {
|
bool hasSelectivityEstimate () const {
|
||||||
if (type == TRI_IDX_TYPE_PRIMARY_INDEX ||
|
if (! hasInternals()) {
|
||||||
type == TRI_IDX_TYPE_HASH_INDEX) {
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return getInternals()->_hasSelectivityEstimate;
|
||||||
}
|
}
|
||||||
|
|
||||||
double selectivityEstimate () const {
|
double selectivityEstimate () const {
|
||||||
TRI_ASSERT_EXPENSIVE(hasSelectivityEstimate());
|
TRI_index_t* internals = getInternals();
|
||||||
|
|
||||||
if (type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
TRI_ASSERT(internals->_hasSelectivityEstimate);
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
if (type == TRI_IDX_TYPE_HASH_INDEX) {
|
|
||||||
return TRI_SelectivityHashIndex(getInternals());
|
|
||||||
}
|
|
||||||
|
|
||||||
TRI_ASSERT(false);
|
return internals->selectivityEstimate(internals);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool hasInternals () const {
|
||||||
|
return (internals != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TRI_index_t* getInternals () const {
|
TRI_index_t* getInternals () const {
|
||||||
|
|
|
@ -761,7 +761,7 @@ double TRI_SelectivityHashArrayMulti (TRI_hash_array_multi_t* array) {
|
||||||
size_t numTotal = array->_nrUsed + array->_nrOverflowUsed;
|
size_t numTotal = array->_nrUsed + array->_nrOverflowUsed;
|
||||||
|
|
||||||
if (numTotal == 0) {
|
if (numTotal == 0) {
|
||||||
return -1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
return static_cast<double>(array->_nrUsed) / static_cast<double>(numTotal);
|
return static_cast<double>(array->_nrUsed) / static_cast<double>(numTotal);
|
||||||
}
|
}
|
||||||
|
|
|
@ -593,6 +593,20 @@ static int SizeHintHashIndex (TRI_index_t* idx,
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief the hash index can provide a selectivity estimate
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static double SelectivityEstimateHashIndex (TRI_index_t const* idx) {
|
||||||
|
TRI_hash_index_t* hashIndex = (TRI_hash_index_t*) idx;
|
||||||
|
|
||||||
|
if (hashIndex->base._unique) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRI_SelectivityHashArrayMulti(&hashIndex->_hashArrayMulti);
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- constructors and destructors
|
// --SECTION-- constructors and destructors
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -615,11 +629,13 @@ TRI_index_t* TRI_CreateHashIndex (TRI_document_collection_t* document,
|
||||||
|
|
||||||
TRI_InitIndex(idx, iid, TRI_IDX_TYPE_HASH_INDEX, document, unique, false);
|
TRI_InitIndex(idx, iid, TRI_IDX_TYPE_HASH_INDEX, document, unique, false);
|
||||||
|
|
||||||
idx->memory = MemoryHashIndex;
|
idx->_hasSelectivityEstimate = true;
|
||||||
idx->json = JsonHashIndex;
|
idx->selectivityEstimate = SelectivityEstimateHashIndex;
|
||||||
idx->insert = InsertHashIndex;
|
idx->memory = MemoryHashIndex;
|
||||||
idx->remove = RemoveHashIndex;
|
idx->json = JsonHashIndex;
|
||||||
idx->sizeHint = SizeHintHashIndex;
|
idx->insert = InsertHashIndex;
|
||||||
|
idx->remove = RemoveHashIndex;
|
||||||
|
idx->sizeHint = SizeHintHashIndex;
|
||||||
|
|
||||||
// ...........................................................................
|
// ...........................................................................
|
||||||
// Copy the contents of the path list vector into a new vector and store this
|
// Copy the contents of the path list vector into a new vector and store this
|
||||||
|
|
|
@ -1299,7 +1299,7 @@ static void DropIndexCoordinator (const v8::FunctionCallbackInfo<v8::Value>& arg
|
||||||
/// [
|
/// [
|
||||||
/// { "id" : "example/0", "type" : "primary", "fields" : ["_id"] },
|
/// { "id" : "example/0", "type" : "primary", "fields" : ["_id"] },
|
||||||
/// { "id" : "example/991154", "unique" : false, "type" : "skiplist", "fields" : ["a", "b"] }
|
/// { "id" : "example/991154", "unique" : false, "type" : "skiplist", "fields" : ["a", "b"] }
|
||||||
/// ]
|
/// ]
|
||||||
///
|
///
|
||||||
/// arango> db.example.dropIndex(i[0])
|
/// arango> db.example.dropIndex(i[0])
|
||||||
/// false
|
/// false
|
||||||
|
@ -1308,7 +1308,9 @@ static void DropIndexCoordinator (const v8::FunctionCallbackInfo<v8::Value>& arg
|
||||||
/// true
|
/// true
|
||||||
///
|
///
|
||||||
/// arango> i = db.example.getIndexes();
|
/// arango> i = db.example.getIndexes();
|
||||||
/// [{ "id" : "example/0", "type" : "primary", "fields" : ["_id"] }]
|
/// [
|
||||||
|
/// { "id" : "example/0", "type" : "primary", "fields" : ["_id"] }
|
||||||
|
/// ]
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// @endDocuBlock
|
/// @endDocuBlock
|
||||||
|
@ -1422,7 +1424,7 @@ static void GetIndexesCoordinator (const v8::FunctionCallbackInfo<v8::Value>& ar
|
||||||
/// @startDocuBlock collectionGetIndexes
|
/// @startDocuBlock collectionGetIndexes
|
||||||
/// `getIndexes()`
|
/// `getIndexes()`
|
||||||
///
|
///
|
||||||
/// Returns a list of all indexes defined for the collection.
|
/// Returns an array of all indexes defined for the collection.
|
||||||
///
|
///
|
||||||
/// @EXAMPLES
|
/// @EXAMPLES
|
||||||
///
|
///
|
||||||
|
|
|
@ -90,13 +90,15 @@ void TRI_InitIndex (TRI_index_t* idx,
|
||||||
idx->_collection = document;
|
idx->_collection = document;
|
||||||
idx->_unique = unique;
|
idx->_unique = unique;
|
||||||
idx->_sparse = sparse;
|
idx->_sparse = sparse;
|
||||||
|
idx->_hasSelectivityEstimate = false;
|
||||||
|
|
||||||
// init common functions
|
// init common functions
|
||||||
idx->memory = nullptr;
|
idx->selectivityEstimate = nullptr;
|
||||||
idx->removeIndex = nullptr;
|
idx->memory = nullptr;
|
||||||
idx->cleanup = nullptr;
|
idx->removeIndex = nullptr;
|
||||||
idx->sizeHint = nullptr;
|
idx->cleanup = nullptr;
|
||||||
idx->postInsert = nullptr;
|
idx->sizeHint = nullptr;
|
||||||
|
idx->postInsert = nullptr;
|
||||||
|
|
||||||
LOG_TRACE("initialising index of type %s", TRI_TypeNameIndex(idx->_type));
|
LOG_TRACE("initialising index of type %s", TRI_TypeNameIndex(idx->_type));
|
||||||
}
|
}
|
||||||
|
@ -433,6 +435,9 @@ TRI_json_t* TRI_JsonIndex (TRI_memory_zone_t* zone,
|
||||||
TRI_Insert3ObjectJson(zone, json, "id", TRI_CreateStringCopyJson(zone, number, strlen(number)));
|
TRI_Insert3ObjectJson(zone, json, "id", TRI_CreateStringCopyJson(zone, number, strlen(number)));
|
||||||
TRI_Insert3ObjectJson(zone, json, "type", TRI_CreateStringCopyJson(zone, TRI_TypeNameIndex(idx->_type), strlen(TRI_TypeNameIndex(idx->_type))));
|
TRI_Insert3ObjectJson(zone, json, "type", TRI_CreateStringCopyJson(zone, TRI_TypeNameIndex(idx->_type), strlen(TRI_TypeNameIndex(idx->_type))));
|
||||||
TRI_Insert3ObjectJson(zone, json, "unique", TRI_CreateBooleanJson(zone, idx->_unique));
|
TRI_Insert3ObjectJson(zone, json, "unique", TRI_CreateBooleanJson(zone, idx->_unique));
|
||||||
|
if (idx->_hasSelectivityEstimate) {
|
||||||
|
TRI_Insert3ObjectJson(zone, json, "selectivityEstimate", TRI_CreateNumberJson(zone, idx->selectivityEstimate(idx)));
|
||||||
|
}
|
||||||
|
|
||||||
TRI_FreeString(TRI_CORE_MEM_ZONE, number);
|
TRI_FreeString(TRI_CORE_MEM_ZONE, number);
|
||||||
}
|
}
|
||||||
|
@ -525,6 +530,14 @@ static size_t MemoryPrimary (TRI_index_t const* idx) {
|
||||||
return idx->_collection->_primaryIndex._nrAlloc * sizeof(void*);
|
return idx->_collection->_primaryIndex._nrAlloc * sizeof(void*);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return the selectivity estimate for the index
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static double SelectivityEstimatePrimary (TRI_index_t const* idx) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief JSON description of a primary index
|
/// @brief JSON description of a primary index
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -565,10 +578,12 @@ TRI_index_t* TRI_CreatePrimaryIndex (TRI_document_collection_t* document) {
|
||||||
|
|
||||||
TRI_InitIndex(idx, 0, TRI_IDX_TYPE_PRIMARY_INDEX, document, true, false);
|
TRI_InitIndex(idx, 0, TRI_IDX_TYPE_PRIMARY_INDEX, document, true, false);
|
||||||
|
|
||||||
idx->memory = MemoryPrimary;
|
idx->_hasSelectivityEstimate = true;
|
||||||
idx->json = JsonPrimary;
|
idx->selectivityEstimate = &SelectivityEstimatePrimary;
|
||||||
idx->insert = InsertPrimary;
|
idx->memory = MemoryPrimary;
|
||||||
idx->remove = RemovePrimary;
|
idx->json = JsonPrimary;
|
||||||
|
idx->insert = InsertPrimary;
|
||||||
|
idx->remove = RemovePrimary;
|
||||||
|
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
@ -920,6 +935,16 @@ static size_t MemoryEdge (TRI_index_t const* idx) {
|
||||||
TRI_MemoryUsageMultiPointer(&(((TRI_edge_index_t*) idx)->_edges_to));
|
TRI_MemoryUsageMultiPointer(&(((TRI_edge_index_t*) idx)->_edges_to));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a selectivity esimtate for the index
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static double SelectivityEstimateEdge (TRI_index_t const* idx) {
|
||||||
|
// return average selectivity of the two index parts
|
||||||
|
return (TRI_SelectivityEstimateMultiPointer(&(((TRI_edge_index_t*) idx)->_edges_from)) +
|
||||||
|
TRI_SelectivityEstimateMultiPointer(&(((TRI_edge_index_t*) idx)->_edges_to))) * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief JSON description of edge index
|
/// @brief JSON description of edge index
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1028,10 +1053,12 @@ TRI_index_t* TRI_CreateEdgeIndex (TRI_document_collection_t* document,
|
||||||
|
|
||||||
TRI_InitIndex(idx, iid, TRI_IDX_TYPE_EDGE_INDEX, document, false, false);
|
TRI_InitIndex(idx, iid, TRI_IDX_TYPE_EDGE_INDEX, document, false, false);
|
||||||
|
|
||||||
idx->memory = MemoryEdge;
|
idx->_hasSelectivityEstimate = true;
|
||||||
idx->json = JsonEdge;
|
idx->selectivityEstimate = SelectivityEstimateEdge;
|
||||||
idx->insert = InsertEdge;
|
idx->memory = MemoryEdge;
|
||||||
idx->remove = RemoveEdge;
|
idx->json = JsonEdge;
|
||||||
|
idx->insert = InsertEdge;
|
||||||
|
idx->remove = RemoveEdge;
|
||||||
|
|
||||||
idx->sizeHint = SizeHintEdge;
|
idx->sizeHint = SizeHintEdge;
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,9 @@ typedef struct TRI_index_s {
|
||||||
bool _unique;
|
bool _unique;
|
||||||
bool _ignoreNull;
|
bool _ignoreNull;
|
||||||
bool _sparse;
|
bool _sparse;
|
||||||
|
bool _hasSelectivityEstimate;
|
||||||
|
|
||||||
|
double (*selectivityEstimate) (struct TRI_index_s const*);
|
||||||
size_t (*memory) (struct TRI_index_s const*);
|
size_t (*memory) (struct TRI_index_s const*);
|
||||||
TRI_json_t* (*json) (struct TRI_index_s const*);
|
TRI_json_t* (*json) (struct TRI_index_s const*);
|
||||||
void (*removeIndex) (struct TRI_index_s*, struct TRI_document_collection_t*);
|
void (*removeIndex) (struct TRI_index_s*, struct TRI_document_collection_t*);
|
||||||
|
|
|
@ -230,12 +230,13 @@
|
||||||
<th class="collectionInfoTh">ID</th>
|
<th class="collectionInfoTh">ID</th>
|
||||||
<th class="collectionInfoTh">Type</th>
|
<th class="collectionInfoTh">Type</th>
|
||||||
<th class="collectionInfoTh">Unique</th>
|
<th class="collectionInfoTh">Unique</th>
|
||||||
|
<th class="collectionInfoTh">Selectivity</th>
|
||||||
<th class="collectionInfoTh">Fields</th>
|
<th class="collectionInfoTh">Fields</th>
|
||||||
</tr>
|
</tr>
|
||||||
<%
|
<%
|
||||||
if (index){
|
if (index){
|
||||||
var fieldString = '';
|
var fieldString = '';
|
||||||
$.each(index.indexes, function(k,v) {
|
$.each(index.indexes, function(k, v) {
|
||||||
if (v.fields !== undefined) {
|
if (v.fields !== undefined) {
|
||||||
fieldString = v.fields.join(", ");
|
fieldString = v.fields.join(", ");
|
||||||
}
|
}
|
||||||
|
@ -243,11 +244,17 @@
|
||||||
//cut index id
|
//cut index id
|
||||||
var position = v.id.indexOf('/');
|
var position = v.id.indexOf('/');
|
||||||
var indexId = v.id.substr(position+1, v.id.length);
|
var indexId = v.id.substr(position+1, v.id.length);
|
||||||
|
var selectivity = (
|
||||||
|
v.hasOwnProperty("selectivityEstimate") ?
|
||||||
|
(v.selectivityEstimate * 100).toFixed(1) + "%" :
|
||||||
|
"n/a"
|
||||||
|
);
|
||||||
%>
|
%>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="collectionInfoTh modal-text"><%=indexId%></th>
|
<th class="collectionInfoTh modal-text"><%=indexId%></th>
|
||||||
<th class="collectionInfoTh modal-text"><%=v.type%></th>
|
<th class="collectionInfoTh modal-text"><%=v.type%></th>
|
||||||
<th class="collectionInfoTh modal-text"><%=v.unique%></th>
|
<th class="collectionInfoTh modal-text"><%=v.unique%></th>
|
||||||
|
<th class="collectionInfoTh modal-text"><%=selectivity%></th>
|
||||||
<th class="collectionInfoTh modal-text"><%=fieldString%></th>
|
<th class="collectionInfoTh modal-text"><%=fieldString%></th>
|
||||||
</tr>
|
</tr>
|
||||||
<%
|
<%
|
||||||
|
|
|
@ -222,10 +222,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(collectionIsLoaded) {
|
if (collectionIsLoaded) {
|
||||||
// prevent "unexpected sync method error"
|
// prevent "unexpected sync method error"
|
||||||
var wfs = this.model.getProperties().waitForSync;
|
var wfs = this.model.getProperties().waitForSync;
|
||||||
tableContent.push(
|
tableContent.push(
|
||||||
window.modalView.createSelectEntry(
|
window.modalView.createSelectEntry(
|
||||||
"change-collection-sync",
|
"change-collection-sync",
|
||||||
"Wait for sync",
|
"Wait for sync",
|
||||||
|
@ -263,7 +263,7 @@
|
||||||
this.truncateCollection.bind(this)
|
this.truncateCollection.bind(this)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if(collectionIsLoaded) {
|
if (collectionIsLoaded) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
window.modalView.createNotificationButton(
|
window.modalView.createNotificationButton(
|
||||||
"Unload",
|
"Unload",
|
||||||
|
|
|
@ -1095,7 +1095,7 @@
|
||||||
var fieldString = '';
|
var fieldString = '';
|
||||||
var actionString = '';
|
var actionString = '';
|
||||||
|
|
||||||
$.each(this.index.indexes, function(k,v) {
|
$.each(this.index.indexes, function(k, v) {
|
||||||
if (v.type === 'primary' || v.type === 'edge') {
|
if (v.type === 'primary' || v.type === 'edge') {
|
||||||
actionString = '<span class="icon_arangodb_locked" ' +
|
actionString = '<span class="icon_arangodb_locked" ' +
|
||||||
'data-original-title="No action"></span>';
|
'data-original-title="No action"></span>';
|
||||||
|
@ -1111,13 +1111,19 @@
|
||||||
|
|
||||||
//cut index id
|
//cut index id
|
||||||
var position = v.id.indexOf('/');
|
var position = v.id.indexOf('/');
|
||||||
var indexId = v.id.substr(position+1, v.id.length);
|
var indexId = v.id.substr(position + 1, v.id.length);
|
||||||
|
var selectivity = (
|
||||||
|
v.hasOwnProperty("selectivityEstimate") ?
|
||||||
|
(v.selectivityEstimate * 100).toFixed(1) + "%" :
|
||||||
|
"n/a"
|
||||||
|
);
|
||||||
|
|
||||||
$('#collectionEditIndexTable').append(
|
$('#collectionEditIndexTable').append(
|
||||||
'<tr>'+
|
'<tr>'+
|
||||||
'<th class=' + JSON.stringify(cssClass) + '>' + indexId + '</th>'+
|
'<th class=' + JSON.stringify(cssClass) + '>' + indexId + '</th>'+
|
||||||
'<th class=' + JSON.stringify(cssClass) + '>' + v.type + '</th>'+
|
'<th class=' + JSON.stringify(cssClass) + '>' + v.type + '</th>'+
|
||||||
'<th class=' + JSON.stringify(cssClass) + '>' + v.unique + '</th>'+
|
'<th class=' + JSON.stringify(cssClass) + '>' + v.unique + '</th>'+
|
||||||
|
'<th class=' + JSON.stringify(cssClass) + '>' + selectivity + '</th>'+
|
||||||
'<th class=' + JSON.stringify(cssClass) + '>' + fieldString + '</th>'+
|
'<th class=' + JSON.stringify(cssClass) + '>' + fieldString + '</th>'+
|
||||||
'<th class=' + JSON.stringify(cssClass) + '>' + actionString + '</th>'+
|
'<th class=' + JSON.stringify(cssClass) + '>' + actionString + '</th>'+
|
||||||
'</tr>'
|
'</tr>'
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*global require, db, assertEqual, assertTrue */
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test the hash index, selectivity estimates
|
||||||
|
///
|
||||||
|
/// @file
|
||||||
|
///
|
||||||
|
/// DISCLAIMER
|
||||||
|
///
|
||||||
|
/// Copyright 2010-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 Jan Steemann
|
||||||
|
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
var jsunity = require("jsunity");
|
||||||
|
var internal = require("internal");
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- basic methods
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test suite: Creation
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function HashIndexSuite() {
|
||||||
|
var cn = "UnitTestsCollectionHash";
|
||||||
|
var collection = null;
|
||||||
|
|
||||||
|
var sorter = function (l, r) {
|
||||||
|
if (l.length != r.length) {
|
||||||
|
return l.length - r.length < 0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// length is equal
|
||||||
|
for (i = 0; i < l.length; ++i) {
|
||||||
|
if (l[i] != r[i]) {
|
||||||
|
return l[i] < r[i] ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief set up
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
setUp : function () {
|
||||||
|
internal.db._drop(cn);
|
||||||
|
collection = internal.db._create(cn, { waitForSync : false });
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief tear down
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
tearDown : function () {
|
||||||
|
// try...catch is necessary as some tests delete the collection itself!
|
||||||
|
try {
|
||||||
|
collection.unload();
|
||||||
|
collection.drop();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
}
|
||||||
|
|
||||||
|
collection = null;
|
||||||
|
internal.wait(0.0);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief hash index selectivity
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testSelectivityEstimateUnique : function () {
|
||||||
|
var i;
|
||||||
|
|
||||||
|
var idx = collection.ensureUniqueConstraint("value");
|
||||||
|
for (i = 0; i < 1000; ++i) {
|
||||||
|
collection.save({ _key: "test" + i, value: i });
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = collection.ensureUniqueConstraint("value");
|
||||||
|
assertEqual(1, idx.selectivityEstimate);
|
||||||
|
|
||||||
|
for (i = 0; i < 50; ++i) {
|
||||||
|
collection.remove("test" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = collection.ensureUniqueConstraint("value");
|
||||||
|
assertEqual(1, idx.selectivityEstimate);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief multi hash index selectivity
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testSelectivityEstimateNonUnique : function () {
|
||||||
|
var i;
|
||||||
|
|
||||||
|
var idx = collection.ensureHashIndex("value");
|
||||||
|
for (i = 0; i < 1000; ++i) {
|
||||||
|
collection.save({ value: i });
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = collection.ensureHashIndex("value");
|
||||||
|
assertEqual(1, idx.selectivityEstimate);
|
||||||
|
|
||||||
|
for (i = 0; i < 1000; ++i) {
|
||||||
|
collection.save({ value: i });
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = collection.ensureHashIndex("value");
|
||||||
|
assertTrue(idx.selectivityEstimate >= 0.45 && idx.selectivityEstimate <= 0.55);
|
||||||
|
|
||||||
|
for (i = 0; i < 1000; ++i) {
|
||||||
|
collection.save({ value: i });
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = collection.ensureHashIndex("value");
|
||||||
|
assertTrue(idx.selectivityEstimate >= 0.3 && idx.selectivityEstimate <= 0.36);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief multi hash index selectivity
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testSelectivityEstimateAllIdentical : function () {
|
||||||
|
var i;
|
||||||
|
|
||||||
|
var idx = collection.ensureHashIndex("value");
|
||||||
|
for (i = 0; i < 1000; ++i) {
|
||||||
|
collection.save({ value: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = collection.ensureHashIndex("value");
|
||||||
|
assertTrue(idx.selectivityEstimate <= (1 / 1000 + 0.0001));
|
||||||
|
|
||||||
|
for (i = 0; i < 1000; ++i) {
|
||||||
|
collection.save({ value: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = collection.ensureHashIndex("value");
|
||||||
|
assertTrue(idx.selectivityEstimate <= (2 / 2000 + 0.0001));
|
||||||
|
|
||||||
|
for (i = 0; i < 1000; ++i) {
|
||||||
|
collection.save({ value: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = collection.ensureHashIndex("value");
|
||||||
|
assertTrue(idx.selectivityEstimate <= (2 / 3000 + 0.0001));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- main
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief executes the test suites
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
jsunity.run(HashIndexSuite);
|
||||||
|
|
||||||
|
return jsunity.done();
|
||||||
|
|
||||||
|
// Local Variables:
|
||||||
|
// mode: outline-minor
|
||||||
|
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
|
||||||
|
// End:
|
|
@ -78,6 +78,8 @@ int TRI_InitMultiPointer (TRI_multi_pointer_t* array,
|
||||||
array->_memoryZone = zone;
|
array->_memoryZone = zone;
|
||||||
array->_nrUsed = 0;
|
array->_nrUsed = 0;
|
||||||
array->_nrAlloc = 0;
|
array->_nrAlloc = 0;
|
||||||
|
array->_nrUnique = 0;
|
||||||
|
array->_nrDuplicate = 0;
|
||||||
|
|
||||||
if (nullptr == (array->_table_alloc = static_cast<TRI_multi_pointer_entry_t*>(TRI_Allocate(zone,
|
if (nullptr == (array->_table_alloc = static_cast<TRI_multi_pointer_entry_t*>(TRI_Allocate(zone,
|
||||||
sizeof(TRI_multi_pointer_entry_t) * INITIAL_SIZE + 64, true)))) {
|
sizeof(TRI_multi_pointer_entry_t) * INITIAL_SIZE + 64, true)))) {
|
||||||
|
@ -374,6 +376,19 @@ size_t TRI_MemoryUsageMultiPointer (TRI_multi_pointer_t const* array) {
|
||||||
return (size_t) array->_nrAlloc * sizeof(TRI_multi_pointer_entry_t) + 64;
|
return (size_t) array->_nrAlloc * sizeof(TRI_multi_pointer_entry_t) + 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a selectivity estimate of the index
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
double TRI_SelectivityEstimateMultiPointer (TRI_multi_pointer_t const* array) {
|
||||||
|
size_t numTotal = array->_nrUnique + array->_nrDuplicate;
|
||||||
|
|
||||||
|
if (numTotal == 0) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
return static_cast<double>(array->_nrUnique) / static_cast<double>(numTotal);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief adds a key/element to the array
|
/// @brief adds a key/element to the array
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -416,6 +431,7 @@ void* TRI_InsertElementMultiPointer (TRI_multi_pointer_t* array,
|
||||||
array->_table[i].next = TRI_MULTI_POINTER_INVALID_INDEX;
|
array->_table[i].next = TRI_MULTI_POINTER_INVALID_INDEX;
|
||||||
array->_table[i].prev = TRI_MULTI_POINTER_INVALID_INDEX;
|
array->_table[i].prev = TRI_MULTI_POINTER_INVALID_INDEX;
|
||||||
array->_nrUsed++;
|
array->_nrUsed++;
|
||||||
|
array->_nrUnique++;
|
||||||
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
||||||
TRI_CheckMultiPointerHash(array, true, true);
|
TRI_CheckMultiPointerHash(array, true, true);
|
||||||
#endif
|
#endif
|
||||||
|
@ -442,6 +458,7 @@ void* TRI_InsertElementMultiPointer (TRI_multi_pointer_t* array,
|
||||||
array->_table[i].next = TRI_MULTI_POINTER_INVALID_INDEX;
|
array->_table[i].next = TRI_MULTI_POINTER_INVALID_INDEX;
|
||||||
array->_table[i].prev = TRI_MULTI_POINTER_INVALID_INDEX;
|
array->_table[i].prev = TRI_MULTI_POINTER_INVALID_INDEX;
|
||||||
array->_nrUsed++;
|
array->_nrUsed++;
|
||||||
|
array->_nrUnique++;
|
||||||
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
||||||
TRI_CheckMultiPointerHash(array, true, true);
|
TRI_CheckMultiPointerHash(array, true, true);
|
||||||
#endif
|
#endif
|
||||||
|
@ -489,6 +506,7 @@ void* TRI_InsertElementMultiPointer (TRI_multi_pointer_t* array,
|
||||||
array->_table[array->_table[j].next].prev = j;
|
array->_table[array->_table[j].next].prev = j;
|
||||||
}
|
}
|
||||||
array->_nrUsed++;
|
array->_nrUsed++;
|
||||||
|
array->_nrDuplicate++;
|
||||||
|
|
||||||
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
||||||
TRI_CheckMultiPointerHash(array, true, true);
|
TRI_CheckMultiPointerHash(array, true, true);
|
||||||
|
@ -596,6 +614,7 @@ void* TRI_RemoveElementMultiPointer (TRI_multi_pointer_t* array, void const* ele
|
||||||
TRI_CheckMultiPointerHash(array, false, false);
|
TRI_CheckMultiPointerHash(array, false, false);
|
||||||
#endif
|
#endif
|
||||||
HealHole(array, i);
|
HealHole(array, i);
|
||||||
|
array->_nrUnique--;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// There is at least one successor in position j.
|
// There is at least one successor in position j.
|
||||||
|
@ -605,6 +624,7 @@ void* TRI_RemoveElementMultiPointer (TRI_multi_pointer_t* array, void const* ele
|
||||||
TRI_CheckMultiPointerHash(array, false, false);
|
TRI_CheckMultiPointerHash(array, false, false);
|
||||||
#endif
|
#endif
|
||||||
HealHole(array, j);
|
HealHole(array, j);
|
||||||
|
array->_nrDuplicate--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -621,6 +641,7 @@ void* TRI_RemoveElementMultiPointer (TRI_multi_pointer_t* array, void const* ele
|
||||||
TRI_CheckMultiPointerHash(array, false, false);
|
TRI_CheckMultiPointerHash(array, false, false);
|
||||||
#endif
|
#endif
|
||||||
HealHole(array, i);
|
HealHole(array, i);
|
||||||
|
array->_nrDuplicate--;
|
||||||
}
|
}
|
||||||
array->_nrUsed--;
|
array->_nrUsed--;
|
||||||
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
||||||
|
@ -679,11 +700,11 @@ static int ResizeMultiPointer (TRI_multi_pointer_t* array, size_t size) {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
int TRI_ResizeMultiPointer (TRI_multi_pointer_t* array, size_t size) {
|
int TRI_ResizeMultiPointer (TRI_multi_pointer_t* array, size_t size) {
|
||||||
if (2*size+1 < array->_nrUsed) {
|
if (2 * size + 1 < array->_nrUsed) {
|
||||||
return TRI_ERROR_BAD_PARAMETER;
|
return TRI_ERROR_BAD_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResizeMultiPointer(array, 2*size+1);
|
return ResizeMultiPointer(array, 2 * size + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -112,6 +112,8 @@ typedef struct TRI_multi_pointer_s {
|
||||||
|
|
||||||
uint64_t _nrAlloc; // the size of the table
|
uint64_t _nrAlloc; // the size of the table
|
||||||
uint64_t _nrUsed; // the number of used entries
|
uint64_t _nrUsed; // the number of used entries
|
||||||
|
uint64_t _nrUnique; // number of unique entries
|
||||||
|
uint64_t _nrDuplicate; // number of duplicate entries
|
||||||
|
|
||||||
TRI_multi_pointer_entry_t* _table_alloc; // the table itself
|
TRI_multi_pointer_entry_t* _table_alloc; // the table itself
|
||||||
TRI_multi_pointer_entry_t* _table; // the table itself, 64 aligned
|
TRI_multi_pointer_entry_t* _table; // the table itself, 64 aligned
|
||||||
|
@ -175,6 +177,12 @@ void TRI_FreeMultiPointer (TRI_memory_zone_t*, TRI_multi_pointer_t*);
|
||||||
|
|
||||||
size_t TRI_MemoryUsageMultiPointer (TRI_multi_pointer_t const*);
|
size_t TRI_MemoryUsageMultiPointer (TRI_multi_pointer_t const*);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a selectivity estimate for the index
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
double TRI_SelectivityEstimateMultiPointer (TRI_multi_pointer_t const*);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief lookups an element given a key
|
/// @brief lookups an element given a key
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -211,6 +219,7 @@ void* TRI_RemoveElementMultiPointer (TRI_multi_pointer_t*, void const* element);
|
||||||
|
|
||||||
int TRI_ResizeMultiPointer (TRI_multi_pointer_t*, size_t);
|
int TRI_ResizeMultiPointer (TRI_multi_pointer_t*, size_t);
|
||||||
|
|
||||||
|
#if 0
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- MULTI ASSOCIATIVE POINTERS WITH MULTIPLE KEYS
|
// --SECTION-- MULTI ASSOCIATIVE POINTERS WITH MULTIPLE KEYS
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -393,6 +402,7 @@ void* TRI_RemovePairMultiPair (TRI_multi_pair_t*,
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
int TRI_ResizeMultiPair (TRI_multi_pair_t*, size_t);
|
int TRI_ResizeMultiPair (TRI_multi_pair_t*, size_t);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue