From 39603ebaf46748178ac41a7080f54315066f50ae Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Fri, 30 Jan 2015 23:04:46 +0100 Subject: [PATCH] added selectivity estimates for some index types --- CHANGELOG | 20 ++ .../HttpInterface/api-index-hash-spec.rb | 1 - UnitTests/Makefile.unittests | 1 + arangod/Aql/Index.h | 22 +- arangod/HashIndex/hash-array-multi.cpp | 2 +- arangod/HashIndex/hash-index.cpp | 26 ++- arangod/V8Server/v8-vocindex.cpp | 8 +- arangod/VocBase/index.cpp | 53 +++-- arangod/VocBase/index.h | 2 + .../js/templates/modalCollectionInfo.ejs | 9 +- .../frontend/js/views/collectionsItemView.js | 8 +- .../frontend/js/views/documentsView.js | 10 +- .../tests/shell-hash-index-noncluster.js | 189 ++++++++++++++++++ lib/Basics/associative-multi.cpp | 25 ++- lib/Basics/associative-multi.h | 10 + 15 files changed, 342 insertions(+), 44 deletions(-) create mode 100644 js/common/tests/shell-hash-index-noncluster.js diff --git a/CHANGELOG b/CHANGELOG index a0a6b81aa4..e9a307b177 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,26 @@ 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 * added additional logger if arangod is started in foreground mode on a tty diff --git a/UnitTests/HttpInterface/api-index-hash-spec.rb b/UnitTests/HttpInterface/api-index-hash-spec.rb index 11efe4eb4c..30d1a80fdd 100644 --- a/UnitTests/HttpInterface/api-index-hash-spec.rb +++ b/UnitTests/HttpInterface/api-index-hash-spec.rb @@ -4,7 +4,6 @@ require 'rspec' require 'arangodb.rb' describe ArangoDB do - api = "/_api/index" prefix = "api-index-hash" ################################################################################ diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index 05acb0736c..89530de120 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -457,6 +457,7 @@ SHELL_COMMON = \ @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-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-graph.js diff --git a/arangod/Aql/Index.h b/arangod/Aql/Index.h index 9131f9f3f6..c8f4634df7 100644 --- a/arangod/Aql/Index.h +++ b/arangod/Aql/Index.h @@ -115,25 +115,23 @@ namespace triagens { } bool hasSelectivityEstimate () const { - if (type == TRI_IDX_TYPE_PRIMARY_INDEX || - type == TRI_IDX_TYPE_HASH_INDEX) { - return true; + if (! hasInternals()) { + return false; } - return false; + return getInternals()->_hasSelectivityEstimate; } double selectivityEstimate () const { - TRI_ASSERT_EXPENSIVE(hasSelectivityEstimate()); + TRI_index_t* internals = getInternals(); - if (type == TRI_IDX_TYPE_PRIMARY_INDEX) { - return 1.0; - } - if (type == TRI_IDX_TYPE_HASH_INDEX) { - return TRI_SelectivityHashIndex(getInternals()); - } + TRI_ASSERT(internals->_hasSelectivityEstimate); - TRI_ASSERT(false); + return internals->selectivityEstimate(internals); + } + + inline bool hasInternals () const { + return (internals != nullptr); } TRI_index_t* getInternals () const { diff --git a/arangod/HashIndex/hash-array-multi.cpp b/arangod/HashIndex/hash-array-multi.cpp index f30bbb2995..125e9b6767 100644 --- a/arangod/HashIndex/hash-array-multi.cpp +++ b/arangod/HashIndex/hash-array-multi.cpp @@ -761,7 +761,7 @@ double TRI_SelectivityHashArrayMulti (TRI_hash_array_multi_t* array) { size_t numTotal = array->_nrUsed + array->_nrOverflowUsed; if (numTotal == 0) { - return -1.0; + return 1.0; } return static_cast(array->_nrUsed) / static_cast(numTotal); } diff --git a/arangod/HashIndex/hash-index.cpp b/arangod/HashIndex/hash-index.cpp index 1184bce2ad..eed2172ce3 100644 --- a/arangod/HashIndex/hash-index.cpp +++ b/arangod/HashIndex/hash-index.cpp @@ -593,6 +593,20 @@ static int SizeHintHashIndex (TRI_index_t* idx, 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 // ----------------------------------------------------------------------------- @@ -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); - idx->memory = MemoryHashIndex; - idx->json = JsonHashIndex; - idx->insert = InsertHashIndex; - idx->remove = RemoveHashIndex; - idx->sizeHint = SizeHintHashIndex; + idx->_hasSelectivityEstimate = true; + idx->selectivityEstimate = SelectivityEstimateHashIndex; + idx->memory = MemoryHashIndex; + idx->json = JsonHashIndex; + idx->insert = InsertHashIndex; + idx->remove = RemoveHashIndex; + idx->sizeHint = SizeHintHashIndex; // ........................................................................... // Copy the contents of the path list vector into a new vector and store this diff --git a/arangod/V8Server/v8-vocindex.cpp b/arangod/V8Server/v8-vocindex.cpp index b930b9a44f..f4c4e00708 100644 --- a/arangod/V8Server/v8-vocindex.cpp +++ b/arangod/V8Server/v8-vocindex.cpp @@ -1299,7 +1299,7 @@ static void DropIndexCoordinator (const v8::FunctionCallbackInfo& arg /// [ /// { "id" : "example/0", "type" : "primary", "fields" : ["_id"] }, /// { "id" : "example/991154", "unique" : false, "type" : "skiplist", "fields" : ["a", "b"] } -/// ] +/// ] /// /// arango> db.example.dropIndex(i[0]) /// false @@ -1308,7 +1308,9 @@ static void DropIndexCoordinator (const v8::FunctionCallbackInfo& arg /// true /// /// arango> i = db.example.getIndexes(); -/// [{ "id" : "example/0", "type" : "primary", "fields" : ["_id"] }] +/// [ +/// { "id" : "example/0", "type" : "primary", "fields" : ["_id"] } +/// ] /// ``` /// /// @endDocuBlock @@ -1422,7 +1424,7 @@ static void GetIndexesCoordinator (const v8::FunctionCallbackInfo& ar /// @startDocuBlock collectionGetIndexes /// `getIndexes()` /// -/// Returns a list of all indexes defined for the collection. +/// Returns an array of all indexes defined for the collection. /// /// @EXAMPLES /// diff --git a/arangod/VocBase/index.cpp b/arangod/VocBase/index.cpp index b7fd74860a..b6955d65ba 100644 --- a/arangod/VocBase/index.cpp +++ b/arangod/VocBase/index.cpp @@ -90,13 +90,15 @@ void TRI_InitIndex (TRI_index_t* idx, idx->_collection = document; idx->_unique = unique; idx->_sparse = sparse; + idx->_hasSelectivityEstimate = false; // init common functions - idx->memory = nullptr; - idx->removeIndex = nullptr; - idx->cleanup = nullptr; - idx->sizeHint = nullptr; - idx->postInsert = nullptr; + idx->selectivityEstimate = nullptr; + idx->memory = nullptr; + idx->removeIndex = nullptr; + idx->cleanup = nullptr; + idx->sizeHint = nullptr; + idx->postInsert = nullptr; 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, "type", TRI_CreateStringCopyJson(zone, TRI_TypeNameIndex(idx->_type), strlen(TRI_TypeNameIndex(idx->_type)))); 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); } @@ -525,6 +530,14 @@ static size_t MemoryPrimary (TRI_index_t const* idx) { 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 //////////////////////////////////////////////////////////////////////////////// @@ -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); - idx->memory = MemoryPrimary; - idx->json = JsonPrimary; - idx->insert = InsertPrimary; - idx->remove = RemovePrimary; + idx->_hasSelectivityEstimate = true; + idx->selectivityEstimate = &SelectivityEstimatePrimary; + idx->memory = MemoryPrimary; + idx->json = JsonPrimary; + idx->insert = InsertPrimary; + idx->remove = RemovePrimary; return idx; } @@ -920,6 +935,16 @@ static size_t MemoryEdge (TRI_index_t const* idx) { 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 //////////////////////////////////////////////////////////////////////////////// @@ -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); - idx->memory = MemoryEdge; - idx->json = JsonEdge; - idx->insert = InsertEdge; - idx->remove = RemoveEdge; + idx->_hasSelectivityEstimate = true; + idx->selectivityEstimate = SelectivityEstimateEdge; + idx->memory = MemoryEdge; + idx->json = JsonEdge; + idx->insert = InsertEdge; + idx->remove = RemoveEdge; idx->sizeHint = SizeHintEdge; diff --git a/arangod/VocBase/index.h b/arangod/VocBase/index.h index c1c29048f9..fffca18949 100644 --- a/arangod/VocBase/index.h +++ b/arangod/VocBase/index.h @@ -101,7 +101,9 @@ typedef struct TRI_index_s { bool _unique; bool _ignoreNull; bool _sparse; + bool _hasSelectivityEstimate; + double (*selectivityEstimate) (struct TRI_index_s const*); size_t (*memory) (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*); diff --git a/js/apps/system/aardvark/frontend/js/templates/modalCollectionInfo.ejs b/js/apps/system/aardvark/frontend/js/templates/modalCollectionInfo.ejs index 8e8279857f..3a493279e3 100644 --- a/js/apps/system/aardvark/frontend/js/templates/modalCollectionInfo.ejs +++ b/js/apps/system/aardvark/frontend/js/templates/modalCollectionInfo.ejs @@ -230,12 +230,13 @@ ID Type Unique + Selectivity Fields <% if (index){ var fieldString = ''; - $.each(index.indexes, function(k,v) { + $.each(index.indexes, function(k, v) { if (v.fields !== undefined) { fieldString = v.fields.join(", "); } @@ -243,11 +244,17 @@ //cut index id var position = v.id.indexOf('/'); var indexId = v.id.substr(position+1, v.id.length); + var selectivity = ( + v.hasOwnProperty("selectivityEstimate") ? + (v.selectivityEstimate * 100).toFixed(1) + "%" : + "n/a" + ); %> <%=indexId%> <%=v.type%> <%=v.unique%> + <%=selectivity%> <%=fieldString%> <% diff --git a/js/apps/system/aardvark/frontend/js/views/collectionsItemView.js b/js/apps/system/aardvark/frontend/js/views/collectionsItemView.js index de2e59dfbe..8c30e6c12f 100644 --- a/js/apps/system/aardvark/frontend/js/views/collectionsItemView.js +++ b/js/apps/system/aardvark/frontend/js/views/collectionsItemView.js @@ -222,10 +222,10 @@ } - if(collectionIsLoaded) { + if (collectionIsLoaded) { // prevent "unexpected sync method error" - var wfs = this.model.getProperties().waitForSync; - tableContent.push( + var wfs = this.model.getProperties().waitForSync; + tableContent.push( window.modalView.createSelectEntry( "change-collection-sync", "Wait for sync", @@ -263,7 +263,7 @@ this.truncateCollection.bind(this) ) ); - if(collectionIsLoaded) { + if (collectionIsLoaded) { buttons.push( window.modalView.createNotificationButton( "Unload", diff --git a/js/apps/system/aardvark/frontend/js/views/documentsView.js b/js/apps/system/aardvark/frontend/js/views/documentsView.js index a461ccf07f..639e58c7f5 100644 --- a/js/apps/system/aardvark/frontend/js/views/documentsView.js +++ b/js/apps/system/aardvark/frontend/js/views/documentsView.js @@ -1095,7 +1095,7 @@ var fieldString = ''; var actionString = ''; - $.each(this.index.indexes, function(k,v) { + $.each(this.index.indexes, function(k, v) { if (v.type === 'primary' || v.type === 'edge') { actionString = ''; @@ -1111,13 +1111,19 @@ //cut index id 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( ''+ '' + indexId + ''+ '' + v.type + ''+ '' + v.unique + ''+ + '' + selectivity + ''+ '' + fieldString + ''+ '' + actionString + ''+ '' diff --git a/js/common/tests/shell-hash-index-noncluster.js b/js/common/tests/shell-hash-index-noncluster.js new file mode 100644 index 0000000000..3e3c6c38ef --- /dev/null +++ b/js/common/tests/shell-hash-index-noncluster.js @@ -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: diff --git a/lib/Basics/associative-multi.cpp b/lib/Basics/associative-multi.cpp index e7839b97c5..1c1e733ac8 100644 --- a/lib/Basics/associative-multi.cpp +++ b/lib/Basics/associative-multi.cpp @@ -78,6 +78,8 @@ int TRI_InitMultiPointer (TRI_multi_pointer_t* array, array->_memoryZone = zone; array->_nrUsed = 0; array->_nrAlloc = 0; + array->_nrUnique = 0; + array->_nrDuplicate = 0; if (nullptr == (array->_table_alloc = static_cast(TRI_Allocate(zone, 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; } +//////////////////////////////////////////////////////////////////////////////// +/// @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(array->_nrUnique) / static_cast(numTotal); +} + //////////////////////////////////////////////////////////////////////////////// /// @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].prev = TRI_MULTI_POINTER_INVALID_INDEX; array->_nrUsed++; + array->_nrUnique++; #ifdef TRI_CHECK_MULTI_POINTER_HASH TRI_CheckMultiPointerHash(array, true, true); #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].prev = TRI_MULTI_POINTER_INVALID_INDEX; array->_nrUsed++; + array->_nrUnique++; #ifdef TRI_CHECK_MULTI_POINTER_HASH TRI_CheckMultiPointerHash(array, true, true); #endif @@ -489,6 +506,7 @@ void* TRI_InsertElementMultiPointer (TRI_multi_pointer_t* array, array->_table[array->_table[j].next].prev = j; } array->_nrUsed++; + array->_nrDuplicate++; #ifdef TRI_CHECK_MULTI_POINTER_HASH 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); #endif HealHole(array, i); + array->_nrUnique--; } else { // 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); #endif HealHole(array, j); + array->_nrDuplicate--; } } else { @@ -621,6 +641,7 @@ void* TRI_RemoveElementMultiPointer (TRI_multi_pointer_t* array, void const* ele TRI_CheckMultiPointerHash(array, false, false); #endif HealHole(array, i); + array->_nrDuplicate--; } array->_nrUsed--; #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) { - if (2*size+1 < array->_nrUsed) { + if (2 * size + 1 < array->_nrUsed) { return TRI_ERROR_BAD_PARAMETER; } - return ResizeMultiPointer(array, 2*size+1); + return ResizeMultiPointer(array, 2 * size + 1); } // ----------------------------------------------------------------------------- diff --git a/lib/Basics/associative-multi.h b/lib/Basics/associative-multi.h index 88b5b14e7a..c50fd14e38 100644 --- a/lib/Basics/associative-multi.h +++ b/lib/Basics/associative-multi.h @@ -112,6 +112,8 @@ typedef struct TRI_multi_pointer_s { uint64_t _nrAlloc; // the size of the table 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; // 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*); +//////////////////////////////////////////////////////////////////////////////// +/// @brief return a selectivity estimate for the index +//////////////////////////////////////////////////////////////////////////////// + +double TRI_SelectivityEstimateMultiPointer (TRI_multi_pointer_t const*); + //////////////////////////////////////////////////////////////////////////////// /// @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); +#if 0 // ----------------------------------------------------------------------------- // --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); +#endif #endif