diff --git a/Documentation/DocuBlocks/Rest/Cursors/JSF_post_api_cursor.md b/Documentation/DocuBlocks/Rest/Cursors/JSF_post_api_cursor.md index 7b8a018416..97e5e1921c 100644 --- a/Documentation/DocuBlocks/Rest/Cursors/JSF_post_api_cursor.md +++ b/Documentation/DocuBlocks/Rest/Cursors/JSF_post_api_cursor.md @@ -42,8 +42,8 @@ key/value object with extra options for the query. @RESTSTRUCT{fullCount,JSF_post_api_cursor_opts,boolean,optional,} if set to *true* and the query contains a *LIMIT* clause, then the -result will contain an extra attribute *extra* with a sub-attribute *fullCount*. -This sub-attribute will contain the number of documents in the result before the +result will a sub-attribute *fullCount* in the *extra.stats* sub-attribute. +The *fullCount* sub-attribute will contain the number of documents in the result before the last LIMIT in the query was applied. It can be used to count the number of documents that match certain filter criteria, but only return a subset of them, in one go. It is thus similar to MySQL's *SQL_CALC_FOUND_ROWS* hint. Note that setting the option diff --git a/arangod/Aql/BasicBlocks.cpp b/arangod/Aql/BasicBlocks.cpp index f8e28ae3e9..d57204d5f9 100644 --- a/arangod/Aql/BasicBlocks.cpp +++ b/arangod/Aql/BasicBlocks.cpp @@ -343,11 +343,14 @@ int LimitBlock::getOrSkipSome(size_t atLeast, size_t atMost, bool skipping, if (_engine->_stats.fullCount == -1) { _engine->_stats.fullCount = 0; } - _engine->_stats.fullCount += static_cast(_offset); } if (_offset > 0) { - ExecutionBlock::_dependencies[0]->skip(_offset); + size_t numActuallySkipped = 0; + ExecutionBlock::_dependencies[0]->skip(_offset, numActuallySkipped); + if (_fullCount) { + _engine->_stats.fullCount += static_cast(numActuallySkipped); + } } _state = 1; _count = 0; diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index 272c53db30..1d40278ebf 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -284,7 +284,7 @@ size_t ExecutionBlock::skipSome(size_t atLeast, size_t atMost) { // skip exactly outputs, returns if _done after // skipping, and otherwise . . . -bool ExecutionBlock::skip(size_t number) { +bool ExecutionBlock::skip(size_t number, size_t& numActuallySkipped) { DEBUG_BEGIN_BLOCK(); size_t skipped = skipSome(number, number); size_t nr = skipped; @@ -292,6 +292,7 @@ bool ExecutionBlock::skip(size_t number) { nr = skipSome(number - skipped, number - skipped); skipped += nr; } + numActuallySkipped = skipped; if (nr == 0) { return true; } @@ -345,7 +346,8 @@ int ExecutionBlock::getOrSkipSome(size_t atLeast, size_t atMost, bool skipping, while (skipped < atLeast) { if (_buffer.empty()) { if (skipping) { - _dependencies[0]->skip(atLeast - skipped); + size_t numActuallySkipped = 0; + _dependencies[0]->skip(atLeast - skipped, numActuallySkipped); skipped = atLeast; freeCollector(); return TRI_ERROR_NO_ERROR; diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index 8d74cf2879..7372b2f9a7 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -156,7 +156,7 @@ class ExecutionBlock { // skip exactly outputs, returns if _done after // skipping, and otherwise . . . - bool skip(size_t number); + bool skip(size_t number, size_t& numActuallySkipped); virtual bool hasMore(); diff --git a/arangod/Aql/ExecutionEngine.h b/arangod/Aql/ExecutionEngine.h index d2c292c9ac..276208c46b 100644 --- a/arangod/Aql/ExecutionEngine.h +++ b/arangod/Aql/ExecutionEngine.h @@ -111,7 +111,9 @@ class ExecutionEngine { AqlItemBlock* getOne() { return _root->getSome(1, 1); } /// @brief skip - bool skip(size_t number) { return _root->skip(number); } + bool skip(size_t number, size_t& actuallySkipped) { + return _root->skip(number, actuallySkipped); + } /// @brief hasMore inline bool hasMore() const { return _root->hasMore(); } diff --git a/arangod/Aql/RestAqlHandler.cpp b/arangod/Aql/RestAqlHandler.cpp index f21457d058..a7fcece8f7 100644 --- a/arangod/Aql/RestAqlHandler.cpp +++ b/arangod/Aql/RestAqlHandler.cpp @@ -783,7 +783,8 @@ void RestAqlHandler::handleUseQuery(std::string const& operation, Query* query, try { bool exhausted; if (shardId.empty()) { - exhausted = query->engine()->skip(number); + size_t numActuallySkipped = 0; + exhausted = query->engine()->skip(number, numActuallySkipped); } else { auto block = static_cast(query->engine()->root()); diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphViewer2.js b/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphViewer2.js index 6774f5f091..3b6b2abc21 100644 --- a/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphViewer2.js +++ b/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphViewer2.js @@ -25,6 +25,9 @@ 'click #downloadPNG': 'downloadSVG' }, + cursorX: 0, + cursorY: 0, + initSigma: function () { // init sigma try { @@ -70,7 +73,10 @@ $('#content').append( '
' + '' + - 'Fetching graph data. Please wait ...
' + 'Fetching graph data. Please wait ...


' + + 'If it`s taking too much time to draw the graph, please go to:
' + + '' + window.location.href + '/settings
and adjust your settings.' + + 'It is possible that the graph is too big to be handled by the browser.
' ); var continueFetchGraph = function () { @@ -158,23 +164,78 @@ }); } + // clear events + var c = document.getElementsByClassName('sigma-mouse')[0]; + c.removeEventListener('mousemove', self.drawLine.bind(this), false); + // clear info div }, + trackCursorPosition: function (e) { + this.cursorX = e.x; + this.cursorY = e.y; + }, + createContextMenu: function (e) { - var x = e.data.captor.clientX; - var y = e.data.captor.clientX; - console.log('Context menu'); - console.log(x); - console.log(y); + var self = this; + var x = self.cursorX - 50; + var y = self.cursorY - 50; + console.log(e); this.clearOldContextMenu(); + + var generateMenu = function (e) { + var hotaru = ['#364C4A', '#497C7F', '#92C5C0', '#858168', '#CCBCA5']; + + var Wheelnav = wheelnav; + + var wheel = new Wheelnav('nodeContextMenu'); + wheel.maxPercent = 1.0; + wheel.wheelRadius = 50; + wheel.clockwise = false; + wheel.colors = hotaru; + wheel.multiSelect = true; + wheel.clickModeRotate = false; + wheel.slicePathFunction = slicePath().DonutSlice; + wheel.createWheel([icon.plus, icon.trash]); + + wheel.navItems[0].selected = false; + wheel.navItems[0].hovered = false; + // add menu events + + // function 0: edit + wheel.navItems[0].navigateFunction = function (e) { + self.clearOldContextMenu(); + }; + + // function 1: delete + wheel.navItems[1].navigateFunction = function (e) { + self.clearOldContextMenu(); + }; + + // deselect active default entry + wheel.navItems[0].selected = false; + wheel.navItems[0].hovered = false; + }; + + $('#nodeContextMenu').css('position', 'fixed'); + $('#nodeContextMenu').css('left', x); + $('#nodeContextMenu').css('top', y); + $('#nodeContextMenu').width(100); + $('#nodeContextMenu').height(100); + + generateMenu(e); }, createNodeContextMenu: function (nodeId, e) { var self = this; - var x = e.data.node['renderer1:x']; - var y = e.data.node['renderer1:y']; + // var x = e.data.node['renderer1:x']; + // var y = e.data.node['renderer1:y']; + // better to use x,y from top, but sometimes values are not correct ... + console.log(e); + var x = e.data.captor.clientX - 52; + var y = e.data.captor.clientY - 52; + console.log(e.data); this.clearOldContextMenu(); @@ -233,8 +294,8 @@ wheel.navItems[0].hovered = false; }; - $('#nodeContextMenu').css('left', x + 115); - $('#nodeContextMenu').css('top', y + 72); + $('#nodeContextMenu').css('left', x); + $('#nodeContextMenu').css('top', y); $('#nodeContextMenu').width(100); $('#nodeContextMenu').height(100); @@ -498,6 +559,11 @@ } console.log(dragListener); + // add listener to keep track of cursor position + var c = document.getElementsByClassName('sigma-mouse')[0]; + c.addEventListener('mousemove', self.trackCursorPosition.bind(this), false); + + // clear up info div $('#calculatingGraph').remove(); } diff --git a/js/server/tests/aql/aql-fullcount.js b/js/server/tests/aql/aql-fullcount.js index 6591355a1b..cbac254376 100644 --- a/js/server/tests/aql/aql-fullcount.js +++ b/js/server/tests/aql/aql-fullcount.js @@ -104,6 +104,94 @@ function optimizerFullcountTestSuite () { assertEqual(2, result.stats.fullCount); assertEqual(1, result.json.length); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test with fullcount +//////////////////////////////////////////////////////////////////////////////// + + testHigherLimit : function () { + var result = AQL_EXECUTE("FOR doc IN UnitTestsCollection LIMIT 100 RETURN doc", null, { fullCount: true }); + + assertEqual(3, result.stats.fullCount); + assertEqual(3, result.json.length); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test with fullcount +//////////////////////////////////////////////////////////////////////////////// + + testHigherLimit2 : function () { + var result = AQL_EXECUTE("FOR doc IN UnitTestsCollection LIMIT 1, 100 RETURN doc", null, { fullCount: true }); + + assertEqual(3, result.stats.fullCount); + assertEqual(2, result.json.length); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test with fullcount +//////////////////////////////////////////////////////////////////////////////// + + testHigherLimit3 : function () { + var result = AQL_EXECUTE("FOR doc IN UnitTestsCollection LIMIT 2, 100 RETURN doc", null, { fullCount: true }); + + assertEqual(3, result.stats.fullCount); + assertEqual(1, result.json.length); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test with fullcount +//////////////////////////////////////////////////////////////////////////////// + + testHigherLimit4 : function () { + var result = AQL_EXECUTE("FOR doc IN UnitTestsCollection LIMIT 3, 100 RETURN doc", null, { fullCount: true }); + + assertEqual(3, result.stats.fullCount); + assertEqual(0, result.json.length); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test with fullcount +//////////////////////////////////////////////////////////////////////////////// + + testHigherLimit5 : function () { + var result = AQL_EXECUTE("FOR doc IN UnitTestsCollection LIMIT 10000, 100 RETURN doc", null, { fullCount: true }); + + assertEqual(3, result.stats.fullCount); + assertEqual(0, result.json.length); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test with fullcount +//////////////////////////////////////////////////////////////////////////////// + + testHigherLimit6 : function () { + var result = AQL_EXECUTE("FOR doc IN UnitTestsCollection FILTER 'bar' IN doc.values LIMIT 1, 10 RETURN doc", null, { fullCount: true }); + + assertEqual(2, result.stats.fullCount); + assertEqual(1, result.json.length); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test with fullcount +//////////////////////////////////////////////////////////////////////////////// + + testHigherLimit7 : function () { + var result = AQL_EXECUTE("FOR doc IN UnitTestsCollection FILTER 'bar' IN doc.values LIMIT 10, 10 RETURN doc", null, { fullCount: true }); + + assertEqual(2, result.stats.fullCount); + assertEqual(0, result.json.length); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test with fullcount +//////////////////////////////////////////////////////////////////////////////// + + testHigherLimit8 : function () { + var result = AQL_EXECUTE("FOR doc IN UnitTestsCollection FILTER 'bar' IN doc.values LIMIT 1000, 1 RETURN doc", null, { fullCount: true }); + + assertEqual(2, result.stats.fullCount); + assertEqual(0, result.json.length); } };