1
0
Fork 0

Merge branch 'devel' of https://github.com/triAGENS/ArangoDB into devel

This commit is contained in:
Thomas Richter 2013-05-23 14:27:37 +02:00
commit 7042c03020
77 changed files with 1439 additions and 440 deletions

View File

@ -15,6 +15,8 @@ v1.4
v1.3.1 (2013-XX-XX)
-------------------
* issue #540: suppress return of temporary internal variables in AQL
* issue #530: ReferenceError: ArangoError is not a constructor
* issue #535: Problem with AQL user functions javascript API

View File

@ -295,7 +295,8 @@ man: Doxygen/.setup-directories
################################################################################
CLEANUP += \
Doxygen/*
Doxygen/.setup-directories \
Doxygen/*
## -----------------------------------------------------------------------------
## --SECTION-- EXAMPLES

View File

@ -209,8 +209,7 @@ Server Statistics {#NewFeatures11ServerStatistics}
--------------------------------------------------
ArangoDB 1.1 allows querying the server status via the administration
front-end (see @ref UserManualWebInterfaceStatistics) or via REST API
methods.
front-end or via REST API methods.
The following methods are available:
- `GET /_admin/connection-statistics`: provides connection statistics

View File

@ -7,7 +7,7 @@ ArangoDB's Web-Interface {#UserManualWebInterface}
Accessing the Web-Interface {#UserManualWebInterfaceAccess}
===========================================================
The web interfaced can be access as
The web interface can be accessed via the URL
http://localhost:8529
@ -20,64 +20,55 @@ application instead. In this case use
Collections Tab {#UserManualWebInterfaceCollections}
----------------------------------------------------
The collection tabs shows an overview about the loaded and unloaded
collections of the database.
The *Collections* tab shows an overview of the loaded and unloaded
collections present in ArangoDB. System collections (i.e. collections
whose names start with an underscore) are not shown by default.
@htmlonly <img src="images/fe-collections.png" alt="ArangoDB Front-End">@endhtmlonly
@latexonly\includegraphics[width=12cm]{images/fe-collections.png}@endlatexonly
The list of collections can be restricted using the search bar, or by
using the filtering at the top. The filter can also be used to show or
hide system collections.
You can load, unloaded, delete, or inspect the collections. Please
note that you should not delete or change system collections, i. e.,
collections starting with an underscore.
Clicking on a collection will show the documents contained in it.
Clicking the small icon on a collection's badge will bring up a dialog
that allows loading/unloading, renaming and deleting the collection.
If you click on the magnifying glass, you will get a list of all documents
in the collection.
Please note that you should not change or delete system collections.
@htmlonly <img src="images/fe-documents.png" alt="ArangoDB Front-End">@endhtmlonly
@latexonly\includegraphics[width=12cm]{images/fe-documents.png}@endlatexonly
In the list of documents of a collection, you can click on the *Add document*
line to add a new document to the collection. The document will be created
instantly, with a system-defined key. The key and all other attributes of the
document can be adjusted in the following view.
Using the pencil you can edit the document.
Query Tab {#UserManualWebInterfaceQuery}
----------------------------------------
AQL Editor Tab {#UserManualWebInterfaceQuery}
---------------------------------------------
The query tabs allows you to execute AQL queries.
The *AQL Editor* tab allow to execute AQL queries.
@htmlonly <img src="images/fe-query.png" alt="ArangoDB Front-End">@endhtmlonly
@latexonly\includegraphics[width=12cm]{images/fe-query.png}@endlatexonly
Type in a query in the bottom box and execute it by pressing the *Submit* button.
The query result will be shown in the box at the top.
Type in a query and execute it.
Shell Tab {#UserManualWebInterfaceShell}
----------------------------------------
JS Shell Tab {#UserManualWebInterfaceShell}
-------------------------------------------
The shell tabs give you access to a JavaScript shell connection to the
The *JS Shell* tab provides access to a JavaScript shell connection to the
database server.
@htmlonly <img src="images/fe-shell.png" alt="ArangoDB Front-End">@endhtmlonly
@latexonly\includegraphics[width=12cm]{images/fe-shell.png}@endlatexonly
Any valid JavaScript code can be executed inside the shell. The code will be
executed inside your browser. To contact the ArangoDB server, you can use the
`db` object, for example as follows:
JSH> db._create("mycollection");
JSH> db.mycollection.save({ _key: "test", value: "something" });
Use the OK button or return to execute a command.
Logs Tab {#UserManualWebInterfaceLogs}
--------------------------------------
You can browse the log files.
@htmlonly <img src="images/fe-logs.png" alt="ArangoDB Front-End">@endhtmlonly
@latexonly\includegraphics[width=12cm]{images/fe-logs.png}@endlatexonly
You can use the *Logs* tab to browse the most recent log entries provided by the
ArangoDB database server.
Note that the server only keeps a limited number of log entries. For
real log analyses write the logs to disk using syslog or a similar
mechanism.
Statistics Tab {#UserManualWebInterfaceStatistics}
--------------------------------------------------
Use the statistics tab to display information about the server.
@htmlonly <img src="images/fe-statistics.png" alt="ArangoDB Front-End">@endhtmlonly
@latexonly\includegraphics[width=12cm]{images/fe-statistics.png}@endlatexonly
Initially no statistics will be display. You must use the add button
to configure what type of information should be displayed.
mechanism. ArangoDB provides several startup options for this.

View File

@ -6,4 +6,3 @@ TOC {#UserManualWebInterfaceTOC}
- @ref UserManualWebInterfaceQuery
- @ref UserManualWebInterfaceShell
- @ref UserManualWebInterfaceLogs
- @ref UserManualWebInterfaceStatistics

View File

@ -103,7 +103,7 @@ bool RestActionHandler::isDirect () {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& RestActionHandler::queue () {
string const& RestActionHandler::queue () const {
return _queue;
}

View File

@ -126,7 +126,7 @@ namespace triagens {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& queue ();
string const& queue () const;
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}

View File

@ -437,10 +437,13 @@ TRI_aql_node_t* TRI_CreateNodeVariableAql (TRI_aql_context_t* const context,
ABORT_OOM
}
if (! TRI_AddVariableScopeAql(context, name, definingNode)) {
// duplicate variable name
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_VARIABLE_REDECLARED, name);
return NULL;
// if not a temporary variable
if (*name != '_') {
if (! TRI_AddVariableScopeAql(context, name, definingNode)) {
// duplicate variable name
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_VARIABLE_REDECLARED, name);
return NULL;
}
}
TRI_AQL_NODE_STRING(node) = (char*) name;

View File

@ -668,7 +668,7 @@ static void InitArray (TRI_aql_codegen_js_t* const generator,
static void EnterSymbol (TRI_aql_codegen_js_t* const generator,
const char* const name,
const TRI_aql_codegen_register_t registerIndex) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_codegen_scope_t* scope;
TRI_aql_codegen_variable_t* variable = CreateVariable(name, registerIndex);
if (variable == NULL) {
@ -676,6 +676,17 @@ static void EnterSymbol (TRI_aql_codegen_js_t* const generator,
return;
}
// if not a temporary variable
if (*name != '_') {
scope = CurrentScope(generator);
}
else {
assert(generator->_scopes._length > 0);
// get scope at level 0
scope = (TRI_aql_codegen_scope_t*) TRI_AtVectorPointer(&generator->_scopes, 0);
}
if (TRI_InsertKeyAssociativePointer(&scope->_variables, name, (void*) variable, false)) {
// variable already exists in symbol table. this should never happen
LOG_TRACE("variable already registered: %s", name);

View File

@ -87,7 +87,7 @@ bool RestBatchHandler::isDirect () {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& RestBatchHandler::queue () {
string const& RestBatchHandler::queue () const {
static string const client = "STANDARD";
return client;

View File

@ -153,7 +153,7 @@ namespace triagens {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& queue ();
string const& queue () const;
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}

View File

@ -85,7 +85,7 @@ bool RestDocumentHandler::isDirect () {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& RestDocumentHandler::queue () {
string const& RestDocumentHandler::queue () const {
static string const client = "STANDARD";
return client;

View File

@ -94,7 +94,7 @@ namespace triagens {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& queue ();
string const& queue () const;
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}

View File

@ -83,7 +83,7 @@ bool RestImportHandler::isDirect () {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& RestImportHandler::queue () {
string const& RestImportHandler::queue () const {
static string const client = "STANDARD";
return client;

View File

@ -94,7 +94,7 @@ namespace triagens {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& queue ();
string const& queue () const;
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}

View File

@ -87,7 +87,7 @@ bool RestUploadHandler::isDirect () {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& RestUploadHandler::queue () {
string const& RestUploadHandler::queue () const {
static string const client = "STANDARD";
return client;

View File

@ -105,7 +105,7 @@ namespace triagens {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& queue ();
string const& queue () const;
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}

View File

@ -527,7 +527,7 @@ void ApplicationV8::setupOptions (map<string, basics::ProgramOptionsDescription>
("javascript.gc-frequency", &_gcFrequency, "JavaScript time-based garbage collection frequency (each x seconds)")
("javascript.action-directory", &_actionPath, "path to the JavaScript action directory")
("javascript.app-path", &_appPath, "one directory for applications")
("javascript.dev-app-path", &_devAppPath, "one directory for dev aaplications")
("javascript.dev-app-path", &_devAppPath, "one directory for dev applications")
("javascript.modules-path", &_modulesPath, "one or more directories separated by semi-colons")
("javascript.package-path", &_packagePath, "one or more directories separated by semi-colons")
("javascript.startup-directory", &_startupPath, "path to the directory containing alternate JavaScript startup scripts")

View File

@ -235,18 +235,14 @@ static void MoveHeader (TRI_headers_t* h,
headers->_begin = header;
}
else if (headers->_begin == header) {
if (header->_next != NULL) {
headers->_begin = header->_next;
}
headers->_begin = header->_next;
}
if (old->_next == NULL) {
headers->_end = header;
}
else if (headers->_end == header) {
if (header->_prev != NULL) {
headers->_end = header->_prev;
}
headers->_end = header->_prev;
}
if (header->_prev != NULL) {

View File

@ -215,96 +215,6 @@ static bool EqualKeyCollectionName (TRI_associative_pointer_t* array, void const
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief create a JSON array with collection meta data
///
/// this function is called when a collection is created or dropped
////////////////////////////////////////////////////////////////////////////////
static TRI_json_t* CreateJsonCollectionInfo (TRI_vocbase_col_t const* collection,
const char* situation) {
TRI_json_t* json;
TRI_json_t* details;
char* cidString;
details = TRI_CreateArrayJson(TRI_CORE_MEM_ZONE);
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE, details, "type", TRI_CreateStringCopyJson(TRI_CORE_MEM_ZONE, TRI_TypeNameCollection(collection->_type)));
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE, details, "name", TRI_CreateStringCopyJson(TRI_CORE_MEM_ZONE, collection->_name));
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE, details, "action", TRI_CreateStringCopyJson(TRI_CORE_MEM_ZONE, situation));
cidString = TRI_StringUInt64((uint64_t) collection->_cid);
json = TRI_CreateArrayJson(TRI_CORE_MEM_ZONE);
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE, json, "id", TRI_CreateStringCopyJson(TRI_CORE_MEM_ZONE, cidString));
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE, json, "type", TRI_CreateStringCopyJson(TRI_CORE_MEM_ZONE, "collection"));
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE, json, "details", details);
TRI_FreeString(TRI_CORE_MEM_ZONE, cidString);
return json;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief insert the id of a collection into the "_ids" collection
////////////////////////////////////////////////////////////////////////////////
static int InsertIdCallback (TRI_transaction_collection_t* trxCollection,
void* data) {
TRI_shaped_json_t* shaped;
TRI_primary_collection_t* primary;
TRI_json_t* json;
TRI_doc_mptr_t mptr;
int res;
primary = (TRI_primary_collection_t*) trxCollection->_collection->_collection;
json = data;
shaped = TRI_ShapedJsonJson(primary->_shaper, json);
if (shaped == NULL) {
return TRI_ERROR_OUT_OF_MEMORY;
}
res = primary->insert(trxCollection, NULL, &mptr, TRI_DOC_MARKER_KEY_DOCUMENT, shaped, NULL, false, false);
TRI_FreeShapedJson(primary->_shaper, shaped);
return res;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief save collection info on create or drop
///
/// the info will be stored permanently in the "_ids" collection, so we can
/// later reconstruct ids of collections that are/were dropped
////////////////////////////////////////////////////////////////////////////////
static bool WriteCollectionInfo (TRI_vocbase_t* vocbase,
TRI_vocbase_col_t const* collection,
const char* situation) {
TRI_json_t* json;
int res;
if (collection == NULL) {
return false;
}
json = CreateJsonCollectionInfo(collection, situation);
if (json == NULL) {
return false;
}
res = TRI_ExecuteSingleOperationTransaction(vocbase,
"_ids",
TRI_TRANSACTION_WRITE,
InsertIdCallback,
json);
TRI_FreeJson(TRI_CORE_MEM_ZONE, json);
return (res == TRI_ERROR_NO_ERROR);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the current tick value, without using a lock
////////////////////////////////////////////////////////////////////////////////
@ -456,8 +366,6 @@ static void FreeCollection (TRI_vocbase_t* vocbase, TRI_vocbase_col_t* collectio
////////////////////////////////////////////////////////////////////////////////
static bool UnregisterCollection (TRI_vocbase_t* vocbase, TRI_vocbase_col_t* collection) {
WriteCollectionInfo(vocbase, collection, "drop");
TRI_WRITE_LOCK_COLLECTIONS_VOCBASE(vocbase);
TRI_RemoveKeyAssociativePointer(&vocbase->_collectionsByName, collection->_name);

View File

@ -12,21 +12,36 @@
}
*/
.dbNotVisible {
opacity: 0 !important;
}
#dashboardHeader .btn-group {
margin-left: 10px;
margin-top: 6px;
}
.db-zoom, .db-minimize, .db-hide, .db-info {
.group-close, .group-open {
float:right;
margin-top: 17px !important;
margin-right: 7px !important;
}
.db-zoom, .db-minimize, .db-hide, .db-info, #db-collectionMinimize {
float: right;
margin-top: -4px !important;
margin-right: 4px !important;
}
.db-zoom:hover, .db-minimize:hover, .db-hide, .db-info:hover {
.db-zoom:hover, .db-minimize:hover, .db-hide, .db-info:hover, .group-close:hover, .group-open:hover,
#db-collectionMinimize:hover {
cursor:pointer;
}
.groupHidden li {
display:none;
}
.statGroups {
margin-left: 0px;
float:left;
@ -66,36 +81,49 @@
.statClient {
float: left;
height: 150px;
width: 280px;
height: 120px;
width: 203px;
margin-left: 9px;
margin-bottom: 15px;
margin-bottom: 12px;
border: 1px solid black;
border-radius: 2px 2px 2px 2px;
background-color: #F4F3F3;
box-shadow: 0 0 3px #333333;
}
.statChart {
#detailGraphChart {
margin-left: -10px !important;
}
.statGroups .statChart {
margin-left: -45px !important;
}
.nv-axislabel {
margin-left: 20px;
}
.nv-axisMaxMin > text {
font: 10px sans-serif;
}
.svgCollections {
height: 300px;
width: 300px;
}
.svgClass {
margin-top: 0 !important;
padding-top: 0 !important;
height: 150px;
width: 270px;
height: 140px;
width: 255px;
}
.svgDetailClass {
margin-top: 0 !important;
padding-top: 0 !important;
height: 300px;
width: 877px;
width: 887px;
}
.boxHeader {
@ -110,6 +138,7 @@
}
.statsHeader {
margin-top: 12px;
margin-left: -10px;
width: 940px;
background-color: #686766;
@ -143,3 +172,83 @@
.nv-point {
display: none;
}
/*Dashboard Dropdown*/
.dropdown-menu li > a {
padding: 0px 20px !important;
}
.checkboxLabel {
margin-top: 4px;
padding-left: 0;
}
.svgClass .nv-axisMaxMin text {
visibility: hidden;
}
.svgClass .major text {
visibility: hidden;
}
.svgClass .nv-axislabel {
visibility: hidden;
}
/*Dashboard Checkbox*/
input[type=checkbox].css-checkbox {
display:none;
}
input[type=checkbox].css-checkbox + label.css-label {
padding-left:20px;
margin-top: 0px;
margin-bottom: -0px;
height:15px;
display:inline-block;
line-height:15px;
background-repeat:no-repeat;
background-position: 0 0;
font-size:15px;
vertical-align:middle;
cursor:pointer;
}
input[type=checkbox].css-checkbox:checked + label.css-label {
background-position: 0 -15px;
}
.css-label {
background-image:url(../img/dark-check-green.png);
}
/*Dashboard Radio */
.dropdown-menu .radio {
margin-left: -21px;
}
input[type="radio"] {
display:none;
}
input[type="radio"] + label {
/*color:#f2f2f2;*/
/*font-family:Arial, sans-serif;
font-size:14px;*/
}
input[type="radio"] + label span {
display:inline-block;
width:19px;
height:19px;
margin:-1px 4px 0 0;
vertical-align:middle;
background:url(../img/check_radio_sheet.png) -38px top no-repeat;
cursor:pointer;
}
input[type="radio"]:checked + label span {
background:url(../img/check_radio_sheet.png) -57px top no-repeat;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -110,6 +110,7 @@
<script src="js/graphViewer/graph/edgeShaper.js"></script>
<script src="js/graphViewer/graph/arangoAdapter.js"></script>
<script src="js/graphViewer/graph/zoomManager.js"></script>
<script src="js/graphViewer/graph/nodeReducer.js"></script>
<script type="text/javascript" src="js/graphViewer/ui/modalDialogHelper.js"></script>
<script type="text/javascript" src="js/graphViewer/ui/nodeShaperControls.js"></script>

View File

@ -17,6 +17,9 @@ window.arangoCollections = Backbone.Collection.extend({
},
translateStatus : function (status) {
if (status == 0) {
return 'corrupted';
}
if (status == 1) {
return 'new born collection';
}

View File

@ -79,6 +79,9 @@ function JSONAdapter(jsonPath, nodes, edges, width, height) {
return this.start + Math.random() * this.range;
};
self.loadNode = function(nodeId, callback) {
self.loadNodeFromTreeById(nodeId, callback);
};
self.loadNodeFromTreeById = function(nodeId, callback) {
var json = jsonPath + nodeId + ".json";
@ -159,4 +162,8 @@ function JSONAdapter(jsonPath, nodes, edges, width, height) {
};
self.expandCommunity = function (commNode, callback) {
};
}

View File

@ -53,6 +53,7 @@ function ArangoAdapter(nodes, edges, config) {
api = {},
queries = {},
cachedCommunities = {},
joinedInCommunities = {},
nodeCollection,
edgeCollection,
limit,
@ -60,6 +61,7 @@ function ArangoAdapter(nodes, edges, config) {
arangodb,
width,
height,
direction,
setWidth = function(w) {
initialX.range = w / 2;
@ -93,12 +95,22 @@ function ArangoAdapter(nodes, edges, config) {
if (config.height !== undefined) {
setHeight(config.height);
}
if (config.undirected !== undefined) {
if (config.undirected === true) {
direction = "any";
} else {
direction = "outbound";
}
} else {
direction = "outbound";
}
},
findNode = function(id) {
var res = $.grep(nodes, function(e){
return e._id === id;
});
var intId = joinedInCommunities[id] || id,
res = $.grep(nodes, function(e){
return e._id === intId;
});
if (res.length === 0) {
return false;
}
@ -118,7 +130,7 @@ function ArangoAdapter(nodes, edges, config) {
if (res.length === 1) {
return res[0];
}
throw "Too many nodes with the same ID, should never happen";
throw "Too many edges with the same ID, should never happen";
},
insertNode = function(data) {
@ -145,7 +157,8 @@ function ArangoAdapter(nodes, edges, config) {
_data: data,
_id: data._id
},
e = findEdge(edge._id);
e = findEdge(edge._id),
edgeToPush;
if (e) {
return e;
}
@ -160,8 +173,32 @@ function ArangoAdapter(nodes, edges, config) {
edge.source = source;
edge.target = target;
edges.push(edge);
source._outboundCounter++;
target._inboundCounter++;
if (cachedCommunities[source._id] !== undefined) {
edgeToPush = {};
edgeToPush.type = "s";
edgeToPush.id = edge._id;
edgeToPush.source = $.grep(cachedCommunities[source._id].nodes, function(e){
return e._id === data._from;
})[0];
edgeToPush.source._outboundCounter++;
cachedCommunities[source._id].edges.push(edgeToPush);
} else {
source._outboundCounter++;
}
if (cachedCommunities[target._id] !== undefined) {
edgeToPush = {};
edgeToPush.type = "t";
edgeToPush.id = edge._id;
edgeToPush.target = $.grep(cachedCommunities[target._id].nodes, function(e){
return e._id === data._to;
})[0];
edgeToPush.target._inboundCounter++;
cachedCommunities[target._id].edges.push(edgeToPush);
} else {
target._inboundCounter++;
}
return edge;
},
@ -187,7 +224,7 @@ function ArangoAdapter(nodes, edges, config) {
removeEdgesForNode = function (node) {
var i;
for ( i = 0; i < edges.length; i++ ) {
for (i = 0; i < edges.length; i++ ) {
if (edges[i].source === node) {
node._outboundCounter--;
edges[i].target._inboundCounter--;
@ -202,6 +239,53 @@ function ArangoAdapter(nodes, edges, config) {
}
},
combineCommunityEdges = function (nodes, commNode) {
var i, j, s, t,
cachedCommEdges = cachedCommunities[commNode._id].edges,
edgeToPush;
for (i = 0; i < edges.length; i++ ) {
edgeToPush = {};
// s and t keep old values yay!
s = edges[i].source;
t = edges[i].target;
for (j = 0; j < nodes.length; j++) {
if (s === nodes[j]) {
if (edgeToPush.type !== undefined) {
edges[i].target = edgeToPush.target;
delete edgeToPush.target;
edgeToPush.type = "b";
edgeToPush.edge = edges[i];
edges.splice( i, 1 );
i--;
break;
}
edges[i].source = commNode;
edgeToPush.type = "s";
edgeToPush.id = edges[i]._id;
edgeToPush.source = s;
}
if (t === nodes[j]) {
if (edgeToPush.type !== undefined) {
edges[i].source = edgeToPush.source;
delete edgeToPush.source;
edgeToPush.type = "b";
edgeToPush.edge = edges[i];
edges.splice( i, 1 );
i--;
break;
}
edges[i].target = commNode;
edgeToPush.type = "t";
edgeToPush.id = edges[i]._id;
edgeToPush.target = t;
}
}
if (edgeToPush.type !== undefined) {
cachedCommEdges.push(edgeToPush);
}
}
},
// Helper function to easily remove all outbound edges for one node
removeOutboundEdgesFromNode = function ( node ) {
if (node._outboundCounter > 0) {
@ -228,6 +312,9 @@ function ArangoAdapter(nodes, edges, config) {
if (query !== queries.connectedEdges) {
bindVars["@nodes"] = nodeCollection;
}
if (query !== queries.childrenCentrality) {
bindVars["@dir"] = direction;
}
bindVars["@edges"] = edgeCollection;
var data = {
query: query,
@ -257,24 +344,61 @@ function ArangoAdapter(nodes, edges, config) {
},
collapseCommunity = function (community) {
var commId = "community_1",
var commId = "*community_" + Math.floor(Math.random()* 1000000),
commNode = {
_id: commId,
x: 1,
y: 1
edges: []
},
nodesToRemove = _.map(community, function(id) {
return findNode(id);
});
cachedCommunities[commId] = nodesToRemove;
commNode.x = nodesToRemove[0].x;
commNode.y = nodesToRemove[0].y;
cachedCommunities[commId] = {};
cachedCommunities[commId].nodes = nodesToRemove;
cachedCommunities[commId].edges = [];
combineCommunityEdges(nodesToRemove, commNode);
_.each(nodesToRemove, function(n) {
joinedInCommunities[n._id] = commId;
removeNode(n);
removeEdgesForNode(n);
});
nodes.push(commNode);
},
expandCommunity = function (commNode) {
var commId = commNode._id,
nodesToAdd = cachedCommunities[commId].nodes,
edgesToChange = cachedCommunities[commId].edges,
com;
removeNode(commNode);
if (limit < nodes.length + nodesToAdd.length) {
com = reducer.getCommunity(limit);
collapseCommunity(com);
}
_.each(nodesToAdd, function(n) {
delete joinedInCommunities[n._id];
nodes.push(n);
});
_.each(edgesToChange, function(e) {
var edge;
switch(e.type) {
case "t":
edge = findEdge(e.id);
edge.target = e.target;
break;
case "s":
edge = findEdge(e.id);
edge.source = e.source;
break;
case "b":
edges.push(e.edge);
break;
}
});
delete cachedCommunities[commId];
},
parseResultOfTraversal = function (result, callback) {
result = result[0];
_.each(result, function(visited) {
@ -366,7 +490,7 @@ function ArangoAdapter(nodes, edges, config) {
+ "@@nodes, "
+ "@@edges, "
+ "@id, "
+ "\"outbound\", {"
+ "@dir, {"
+ "strategy: \"depthfirst\","
+ "maxDepth: 1,"
+ "paths: true"
@ -379,7 +503,7 @@ function ArangoAdapter(nodes, edges, config) {
+ "@@nodes, "
+ "@@edges, "
+ "n._id, "
+ "\"outbound\", {"
+ "@dir, {"
+ "strategy: \"depthfirst\","
+ "maxDepth: 1,"
+ "paths: true"
@ -399,7 +523,6 @@ function ArangoAdapter(nodes, edges, config) {
reducer = new NodeReducer(nodes, edges);
self.oldLoadNodeFromTreeById = function(nodeId, callback) {
sendQuery(queries.nodeById, {
id: nodeId
@ -408,6 +531,10 @@ function ArangoAdapter(nodes, edges, config) {
});
};
self.loadNode = function(nodeId, callback) {
self.loadNodeFromTreeById(nodeId, callback);
};
self.loadNodeFromTreeById = function(nodeId, callback) {
sendQuery(queries.traversalById, {
id: nodeId
@ -554,9 +681,16 @@ function ArangoAdapter(nodes, edges, config) {
});
};
self.changeTo = function (nodesCol, edgesCol ) {
self.changeTo = function (nodesCol, edgesCol, dir) {
nodeCollection = nodesCol;
edgeCollection = edgesCol;
if (dir !== undefined) {
if (dir === true) {
direction = "any";
} else {
direction = "outbound";
}
}
api.node = api.base + "document?collection=" + nodeCollection;
api.edge = api.base + "edge?collection=" + edgeCollection;
};
@ -572,4 +706,11 @@ function ArangoAdapter(nodes, edges, config) {
}
};
self.expandCommunity = function (commNode, callback) {
expandCommunity(commNode);
if (callback !== undefined) {
callback();
}
};
}

View File

@ -67,8 +67,8 @@ function EventLibrary() {
if (config.startCallback === undefined) {
throw "A callback to the Start-method has to be defined";
}
if (config.loadNode === undefined) {
throw "A callback to load a node has to be defined";
if (config.adapter === undefined) {
throw "An adapter to load data has to be defined";
}
if (config.reshapeNodes === undefined) {
throw "A callback to reshape nodes has to be defined";
@ -81,7 +81,9 @@ function EventLibrary() {
var edges = config.edges,
nodes = config.nodes,
startCallback = config.startCallback,
loadNode = config.loadNode,
adapter = config.adapter,
loadNode = adapter.loadNode,
expandCom = adapter.expandCommunity,
reshapeNodes = config.reshapeNodes,
removeNode = function (node) {
var i;
@ -126,8 +128,12 @@ function EventLibrary() {
},
expandNode = function(n) {
n._expanded = true;
loadNode(n._id, startCallback);
if (/^\*community/.test(n._id)) {
expandCom(n, startCallback);
} else {
n._expanded = true;
loadNode(n._id, startCallback);
}
};
return function(n) {

View File

@ -148,7 +148,7 @@ function NodeReducer(nodes, edges) {
_.each(nodes, function (n) {
var id = n._id,
c1, c2;
if (id == sID || id == lID) {
if (id === sID || id === lID) {
return null;
}
c1 = getDQValue(dQ, id, sID);
@ -277,6 +277,7 @@ function NodeReducer(nodes, edges) {
res = [],
dist = {},
dist2 = {},
detectSteps = true,
sortByDistance = function (a, b) {
var d1 = dist[_.min(a,minDist(dist))],
d2 = dist[_.min(b,minDist(dist))],
@ -290,7 +291,9 @@ function NodeReducer(nodes, edges) {
throw "Load some nodes first.";
}
populateValues(dQ, a, heap);
while (communityDetectionStep(dQ, a, heap, coms)) {}
while (detectSteps) {
detectSteps = communityDetectionStep(dQ, a, heap, coms);
}
res = _.pluck(_.values(coms), "com");
if (focus !== undefined) {
dist = floatDist(focus._id);

View File

@ -64,6 +64,7 @@ function NodeShaper(parent, flags, idfunc) {
"use strict";
var self = this,
communityRegEx = /^\*community/,
nodes = [],
visibleLabels = true,
noop = function (node) {
@ -91,7 +92,17 @@ function NodeShaper(parent, flags, idfunc) {
addColor = noop,
addShape = noop,
addLabel = noop,
addCommunityShape = function(g) {
g.append("polygon")
.attr("points", "0,-25 -16,20 23,-10 -23,-10 16,20");
},
addCommunityLabel = function(g) {
g.append("text") // Append a label for the node
.attr("text-anchor", "middle") // Define text-anchor
.text(function(d) {
return d._size;
});
},
unbindEvents = function() {
// Hard unbind the dragging
self.parent
@ -120,9 +131,18 @@ function NodeShaper(parent, flags, idfunc) {
},
addQue = function (g) {
addShape(g);
var community = g.filter(function(n) {
return communityRegEx.test(n._id);
}),
normal = g.filter(function(n) {
return !communityRegEx.test(n._id);
});
addCommunityShape(community);
addShape(normal);
if (visibleLabels) {
addLabel(g);
addCommunityLabel(community);
addLabel(normal);
}
addColor(g);
addEvents(g);
@ -158,7 +178,12 @@ function NodeShaper(parent, flags, idfunc) {
// Append the group and class to all new
g.enter()
.append("g")
.attr("class", "node") // node is CSS class that might be edited
.attr("class", function(d) {
if (communityRegEx.test(d._id)) {
return "node communitynode";
}
return "node";
}) // node is CSS class that might be edited
.attr("id", idFunction);
// Remove all old
g.exit().remove();
@ -176,7 +201,8 @@ function NodeShaper(parent, flags, idfunc) {
case NodeShaper.shapes.CIRCLE:
radius = shape.radius || 25;
addShape = function (node) {
node.append("circle") // Display nodes as circles
node
.append("circle") // Display nodes as circles
.attr("r", radius); // Set radius
};
break;

View File

@ -85,7 +85,7 @@ function GraphViewer(svg, width, height, adapterConfig, config) {
},
nodeLimitCallBack = function(limit) {
self.adapter.setNodeLimit(limit);
self.adapter.setNodeLimit(limit, self.start);
},
parseZoomConfig = function(config) {
@ -174,7 +174,7 @@ function GraphViewer(svg, width, height, adapterConfig, config) {
edges: edges,
nodes: nodes,
startCallback: self.start,
loadNode: self.adapter.loadNodeFromTreeById,
adapter: self.adapter,
reshapeNodes: self.nodeShaper.reshapeNodes
},
drag: {

View File

@ -43,7 +43,9 @@ var mocks = mocks || {};
patchNode: function(){},
createEdge: function(){},
deleteEdge: function(){},
patchEdge: function(){}
patchEdge: function(){},
loadNode: function(){},
expandCommunity: function(){}
};
}());

View File

@ -58,6 +58,17 @@
checkCallbackFunction = function() {
callbackCheck = true;
},
getCommunityNodes = function() {
return _.filter(nodes, function(n) {
return n._id.match(/^\*community/);
});
},
getCommunityNodesIds = function() {
return _.pluck(getCommunityNodes(), "_id");
},
nodeWithID = function(id) {
return $.grep(nodes, function(e){
return e._id === id;
@ -295,26 +306,40 @@
height: 40
}
);
traversalQuery = function(id, nods, edgs) {
traversalQuery = function(id, nods, edgs, undirected) {
var dir;
if (undirected === true) {
dir = "any";
} else {
dir = "outbound";
}
return JSON.stringify({
query: "RETURN TRAVERSAL(@@nodes, @@edges, @id, \"outbound\","
query: "RETURN TRAVERSAL(@@nodes, @@edges, @id, @dir,"
+ " {strategy: \"depthfirst\",maxDepth: 1,paths: true})",
bindVars: {
id: id,
"@nodes": nods,
"@dir": dir,
"@edges": edgs
}
});
};
filterQuery = function(v, nods, edgs) {
filterQuery = function(v, nods, edgs, undirected) {
var dir;
if (undirected === true) {
dir = "any";
} else {
dir = "outbound";
}
return JSON.stringify({
query: "FOR n IN @@nodes FILTER n.id == @value"
+ " RETURN TRAVERSAL(@@nodes, @@edges, n._id, \"outbound\","
+ " RETURN TRAVERSAL(@@nodes, @@edges, n._id, @dir,"
+ " {strategy: \"depthfirst\",maxDepth: 1,paths: true})",
bindVars: {
value: v,
"@nodes": nods,
"@edges": edgs
"@dir": dir,
"@edges": edgs
}
});
};
@ -455,6 +480,12 @@
});
});
it('should map loadNode to loadByID', function() {
spyOn(adapter, "loadNodeFromTreeById");
adapter.loadNode("a", "b");
expect(adapter.loadNodeFromTreeById).toHaveBeenCalledWith("a", "b");
});
it('should be able to load a tree node from ArangoDB'
+ ' by internal attribute and value', function() {
@ -638,6 +669,40 @@
});
it('should be able to switch to different collections and change to directed', function() {
runs(function() {
spyOn($, "ajax");
adapter.changeTo(altNodesCollection, altEdgesCollection, false);
adapter.loadNode("42");
expect($.ajax).toHaveBeenCalledWith(
requests.cursor(traversalQuery("42", altNodesCollection, altEdgesCollection, false))
);
});
});
it('should be able to switch to different collections'
+ ' and change to undirected', function() {
runs(function() {
spyOn($, "ajax");
adapter.changeTo(altNodesCollection, altEdgesCollection, true);
adapter.loadNode("42");
expect($.ajax).toHaveBeenCalledWith(
requests.cursor(traversalQuery("42", altNodesCollection, altEdgesCollection, true))
);
});
});
describe('that has already loaded one graph', function() {
var c0, c1, c2, c3, c4, c5, c6, c7,
@ -877,46 +942,58 @@
runs(function() {
adapter.setNodeLimit(6);
spyOn(this, "fakeReducerRequest");
spyOn(this, "fakeReducerRequest").andCallFake(function() {
return [c0];
});
adapter.loadNodeFromTreeById(c1, checkCallbackFunction);
expect(this.fakeReducerRequest).toHaveBeenCalledWith(6, nodeWithID(c1));
});
});
it('should not trigger the reducer if the limit is set large enough', function() {
spyOn(this, "fakeReducerRequest");
adapter.setNodeLimit(10);
expect(this.fakeReducerRequest).not.toHaveBeenCalled();
});
it('should trigger the reducer if the limit is set too small', function() {
spyOn(this, "fakeReducerRequest");
adapter.setNodeLimit(2);
expect(this.fakeReducerRequest).toHaveBeenCalledWith(2);
});
describe('checking community nodes', function() {
it('should create a community node if limit is set too small', function() {
var called = false,
callback = function() {
called = true;
};
it('should not trigger the reducer if the limit is set large enough', function() {
spyOn(this, "fakeReducerRequest").andCallFake(function() {
return [c0, c1, c2];
return [c0];
});
adapter.setNodeLimit(2, callback);
adapter.setNodeLimit(10);
expect(this.fakeReducerRequest).not.toHaveBeenCalled();
});
it('should trigger the reducer if the limit is set too small', function() {
spyOn(this, "fakeReducerRequest").andCallFake(function() {
return [c0];
});
adapter.setNodeLimit(2);
expect(this.fakeReducerRequest).toHaveBeenCalledWith(2);
});
notExistNodes([c0, c1, c2]);
existNode("community_1");
existNodes([c3]);
expect(nodes.length).toEqual(2);
existEdge("community_1", c3);
expect(edges.length).toEqual(1);
expect(called).toBeTruthy();
it('should create a community node if limit is set too small', function() {
var called;
runs(function() {
callbackCheck = false;
spyOn(this, "fakeReducerRequest").andCallFake(function() {
return [c0, c1, c2];
});
adapter.setNodeLimit(2, checkCallbackFunction);
});
waitsFor(function() {
return callbackCheck;
});
runs(function() {
var commId = getCommunityNodesIds()[0];
notExistNodes([c0, c1, c2]);
existNode(commId);
existNodes([c3, c4]);
expect(nodes.length).toEqual(3);
existEdge(commId, c3);
existEdge(commId, c4);
expect(edges.length).toEqual(2);
});
});
it('should create a community node if too many nodes are added', function() {
@ -933,20 +1010,312 @@
});
runs(function() {
var commId = getCommunityNodesIds()[0];
notExistNodes([c0, c1, c2, c3]);
existNode("community_1");
existNode(commId);
existNodes([c4, c5, c6, c7]);
expect(nodes.length).toEqual(5);
existEdge("community_1", c4);
existEdge("community_1", c5);
existEdge("community_1", c6);
existEdge("community_1", c7);
existEdge(commId, c4);
existEdge(commId, c5);
existEdge(commId, c6);
existEdge(commId, c7);
expect(edges.length).toEqual(4);
});
});
describe('expanding after a while', function() {
it('should connect edges of internal nodes accordingly', function() {
var commNode, called, counterCallback,
v0, v1, v2, v3, v4,
e0_1, e0_2, e1_3, e1_4, e2_3, e2_4;
runs(function() {
var v = "vertices",
e = "edges";
nodes.length = 0;
edges.length = 0;
v0 = insertNode(v, 0);
v1 = insertNode(v, 1);
v2 = insertNode(v, 2);
v3 = insertNode(v, 3);
v4 = insertNode(v, 4);
e0_1 = insertEdge(e, v0, v1);
e0_2 = insertEdge(e, v0, v2);
e1_3 = insertEdge(e, v1, v3);
e1_4 = insertEdge(e, v1, v4);
e2_3 = insertEdge(e, v2, v3);
e2_4 = insertEdge(e, v2, v4);
called = 0;
counterCallback = function() {
called++;
};
spyOn(this, "fakeReducerRequest").andCallFake(function() {
return [v1, v3, v4];
});
adapter.setNodeLimit(3);
adapter.changeTo(v, e);
adapter.loadNode(v0, counterCallback);
adapter.loadNode(v1, counterCallback);
});
waitsFor(function() {
return called === 2;
});
runs(function() {
adapter.loadNode(v2, counterCallback);
commNode = getCommunityNodes()[0];
});
waitsFor(function() {
return called === 3;
});
runs(function() {
var commId = commNode._id;
// Check start condition
existNodes([commId, v0, v2]);
expect(nodes.length).toEqual(3);
existEdge(v0, v2);
existEdge(v0, commId);
existEdge(v2, commId);
expect(edges.length).toEqual(4);
adapter.setNodeLimit(20);
adapter.expandCommunity(commNode, counterCallback);
});
waitsFor(function() {
return called === 4;
});
runs(function() {
existNodes([v0, v1, v2, v3, v4]);
expect(nodes.length).toEqual(5);
existEdge(v0, v1);
existEdge(v0, v2);
existEdge(v1, v3);
existEdge(v1, v4);
existEdge(v2, v3);
existEdge(v2, v4);
expect(edges.length).toEqual(6);
});
});
it('set inbound and outboundcounter correctly', function() {
var commNode, called, counterCallback,
v0, v1, v2, v3, v4,
e0_1, e0_2, e1_3, e1_4, e2_3, e2_4;
runs(function() {
var v = "vertices",
e = "edges";
nodes.length = 0;
edges.length = 0;
v0 = insertNode(v, 0);
v1 = insertNode(v, 1);
v2 = insertNode(v, 2);
v3 = insertNode(v, 3);
v4 = insertNode(v, 4);
e0_1 = insertEdge(e, v0, v1);
e0_2 = insertEdge(e, v0, v2);
e1_3 = insertEdge(e, v1, v3);
e1_4 = insertEdge(e, v1, v4);
e2_3 = insertEdge(e, v2, v3);
e2_4 = insertEdge(e, v2, v4);
called = 0;
counterCallback = function() {
called++;
};
spyOn(this, "fakeReducerRequest").andCallFake(function() {
return [v1, v3, v4];
});
adapter.setNodeLimit(3);
adapter.changeTo(v, e);
adapter.loadNode(v0, counterCallback);
adapter.loadNode(v1, counterCallback);
});
waitsFor(function() {
return called === 2;
});
runs(function() {
adapter.loadNode(v2, counterCallback);
commNode = getCommunityNodes()[0];
});
waitsFor(function() {
return called === 3;
});
runs(function() {
adapter.setNodeLimit(20);
adapter.expandCommunity(commNode, counterCallback);
});
waitsFor(function() {
return called === 4;
});
runs(function() {
var checkNodeWithInAndOut = function(id, inbound, outbound) {
var n = nodeWithID(id);
expect(n._outboundCounter).toEqual(outbound);
expect(n._inboundCounter).toEqual(inbound);
};
checkNodeWithInAndOut(v0, 0, 2);
checkNodeWithInAndOut(v1, 1, 2);
checkNodeWithInAndOut(v2, 1, 2);
checkNodeWithInAndOut(v3, 2, 0);
checkNodeWithInAndOut(v4, 2, 0);
});
});
});
describe('that displays a community node already', function() {
var firstCommId,
fakeResult;
beforeEach(function() {
runs(function() {
callbackCheck = false;
adapter.setNodeLimit(7);
fakeResult = [c0, c2];
spyOn(this, "fakeReducerRequest").andCallFake(function() {
return fakeResult;
});
adapter.loadNodeFromTreeById(c1, checkCallbackFunction);
});
waitsFor(function() {
return callbackCheck;
});
runs(function() {
firstCommId = getCommunityNodesIds()[0];
});
});
it('should expand a community if enough space is available', function() {
runs(function() {
adapter.setNodeLimit(10);
callbackCheck = false;
adapter.expandCommunity(nodeWithID(firstCommId), checkCallbackFunction);
});
waitsFor(function() {
return callbackCheck;
});
runs(function() {
expect(getCommunityNodes().length).toEqual(0);
existNodes([c0, c1, c2, c3, c4, c5, c6, c7]);
existEdge(c0, c1);
existEdge(c0, c2);
existEdge(c0, c3);
existEdge(c0, c4);
});
});
it('should expand a community and join another '
+ 'one if not enough space is available', function() {
runs(function() {
fakeResult = [c1, c7];
callbackCheck = false;
adapter.expandCommunity(nodeWithID(firstCommId), checkCallbackFunction);
});
waitsFor(function() {
return callbackCheck;
});
runs(function() {
var newCommId = getCommunityNodesIds()[0];
expect(getCommunityNodes().length).toEqual(1);
existNodes([c0, c2, c3, c4, c5, c6, newCommId]);
notExistNodes([c1, c7]);
existEdge(c0, c2);
existEdge(c0, c3);
existEdge(c0, c4);
existEdge(c0, newCommId);
existEdge(newCommId, c5);
existEdge(newCommId, c6);
});
});
it('should join another community if space is further reduced', function() {
runs(function() {
fakeResult = [c1, c7];
callbackCheck = false;
adapter.setNodeLimit(6, checkCallbackFunction);
});
waitsFor(function() {
return callbackCheck;
});
runs(function() {
expect(getCommunityNodes().length).toEqual(2);
var ids = getCommunityNodesIds(),
newCommId;
if (firstCommId === ids[0]) {
newCommId = ids[1];
} else {
newCommId = ids[0];
}
existNodes([c3, c4, c5, c6, firstCommId, newCommId]);
notExistNodes([c0, c1, c2, c7]);
existEdge(firstCommId, c3);
existEdge(firstCommId, c4);
existEdge(firstCommId, newCommId);
existEdge(newCommId, c5);
existEdge(newCommId, c6);
});
});
it('should connect edges to internal nodes', function() {
runs(function() {
insertEdge(edgesCollection, c3, c0);
adapter.setNodeLimit(20);
callbackCheck = false;
adapter.loadNode(c3, checkCallbackFunction);
});
waitsFor(function() {
return callbackCheck;
});
runs(function() {
existEdge(c3, firstCommId);
});
});
});
});
describe('that has loaded several queries', function() {

View File

@ -56,6 +56,7 @@ var describeInterface = function (testee) {
});
// Add functions to load here:
expect(testee).toHaveFunction("loadNode", 2);
expect(testee).toHaveFunction("loadNodeFromTreeById", 2);
expect(testee).toHaveFunction("requestCentralityChildren", 2);
expect(testee).toHaveFunction("loadNodeFromTreeByAttributeValue", 3);
@ -66,6 +67,7 @@ var describeInterface = function (testee) {
expect(testee).toHaveFunction("deleteNode", 2);
expect(testee).toHaveFunction("patchNode", 3);
expect(testee).toHaveFunction("setNodeLimit", 2);
expect(testee).toHaveFunction("expandCommunity", 2);
});
};

View File

@ -62,6 +62,8 @@
spyOn(adapter, "createEdge");
spyOn(adapter, "patchEdge");
spyOn(adapter, "deleteEdge");
spyOn(adapter, "loadNode");
spyOn(adapter, "expandCommunity");
};
beforeEach(function() {
@ -74,9 +76,6 @@
nodes = [];
edges = [];
this.loadNode = function() {};
spyOn(this, "loadNode");
defaultPosition = {
x: 1,
y: 1,
@ -87,7 +86,7 @@
edges: edges,
nodes: nodes,
startCallback: function() {},
loadNode: this.loadNode,
adapter: adapter,
reshapeNodes: function() {}
};
@ -437,7 +436,7 @@
});
waitsFor(function() {
return this.loadNode.wasCalled;
return adapter.loadNode.wasCalled;
}, 1000, "The loadNode function should have been called.");
runs(function() {

View File

@ -51,6 +51,8 @@
spyOn(adapter, "createEdge");
spyOn(adapter, "patchEdge");
spyOn(adapter, "deleteEdge");
spyOn(adapter, "loadNode");
spyOn(adapter, "expandCommunity");
};
@ -87,15 +89,13 @@
}];
adapter = mocks.adapter;
layouter = mocks.layouter;
this.loadNode = function() {};
spyOn(this, "loadNode");
addSpies();
var expandConfig = {
edges: edges,
nodes: nodes,
startCallback: function() {},
loadNode: this.loadNode,
adapter: adapter,
reshapeNodes: function() {}
},
@ -307,7 +307,7 @@
helper.simulateMouseEvent("click", "1");
expect(this.loadNode).toHaveBeenCalledWith(nodes[0]._id, jasmine.any(Function));
expect(adapter.loadNode).toHaveBeenCalledWith(nodes[0]._id, jasmine.any(Function));
});
});

View File

@ -1,6 +1,6 @@
/*jslint indent: 2, nomen: true, maxlen: 100, white: true plusplus: true */
/*global beforeEach, afterEach */
/*global describe, it, expect */
/*global describe, it, expect, jasmine */
/*global runs, spyOn, waitsFor */
/*global window, eb, loadFixtures, document, $ */
/*global EventLibrary*/
@ -40,13 +40,15 @@
var eventLib,
nodeShaperDummy = {},
edgeShaperDummy = {};
edgeShaperDummy = {},
adapterDummy = {};
beforeEach(function() {
eventLib = new EventLibrary();
nodeShaperDummy.reshapeNodes = function() {};
edgeShaperDummy.reshapeEdges = function() {};
adapterDummy.loadNode = function() {};
adapterDummy.expandCommunity = function() {};
spyOn(nodeShaperDummy, "reshapeNodes");
spyOn(edgeShaperDummy, "reshapeEdges");
});
@ -59,10 +61,6 @@
edges,
loadedNodes,
started,
loadNodeCallback = function(node) {
loaded++;
loadedNodes.push(node);
},
reshapeNodesCallback = function() {
reshaped++;
},
@ -84,7 +82,7 @@
edges: edges,
nodes: nodes,
startCallback: startCallback,
loadNode: loadNodeCallback,
adapter: adapterDummy,
reshapeNodes: reshapeNodesCallback
};
});
@ -96,6 +94,11 @@
_inboundCounter: 0
};
nodes.push(node);
spyOn(adapterDummy, "loadNode").andCallFake(function(node) {
loaded++;
loadedNodes.push(node);
});
//config.adapter = adapterDummy.loadNode;
testee = eventLib.Expand(config);
testee(node);
@ -206,6 +209,124 @@
expect(c2._outboundCounter).toEqual(1);
});
describe('with community nodes', function() {
it('should expand a community node properly', function() {
var comm = {
_id: "*community_1"
};
nodes.push(comm);
spyOn(adapterDummy, "expandCommunity");
testee = eventLib.Expand(config);
testee(comm);
expect(adapterDummy.expandCommunity).toHaveBeenCalledWith(comm, jasmine.any(Function));
});
it('should remove a community if last pointer to it is collapsed', function() {
runs(function() {
var c0 = {
_id: 0,
_outboundCounter: 1,
_inboundCounter: 0
},
c1 = {
_id: 1,
_expanded: true,
_outboundCounter: 1,
_inboundCounter: 1
},
comm = {
_id: "*community_1",
_outboundCounter: 1,
_inboundCounter: 1
},
c2 = {
_id: 1,
_outboundCounter: 0,
_inboundCounter: 1
},
e0 = {
source: c0,
target: c1
},
e1 = {
source: c1,
target: comm
},
e2 = {
source: comm,
target: c2
};
nodes.push(c0);
nodes.push(c1);
nodes.push(comm);
nodes.push(c2);
edges.push(e0);
edges.push(e1);
edges.push(e2);
testee = eventLib.Expand(config);
testee(c1);
expect(nodes).toEqual([c0, c1]);
expect(edges).toEqual([e0]);
});
});
it('should not remove a community if a pointer to it still exists', function() {
runs(function() {
var c0 = {
_id: 0,
_outboundCounter: 2,
_inboundCounter: 0
},
c1 = {
_id: 1,
_expanded: true,
_outboundCounter: 1,
_inboundCounter: 1
},
comm = {
_id: "*community_1",
_outboundCounter: 0,
_inboundCounter: 2
},
e0 = {
source: c0,
target: c1
},
e1 = {
source: c0,
target: comm
},
e2 = {
source: c1,
target: comm
};
nodes.push(c0);
nodes.push(c1);
nodes.push(comm);
edges.push(e0);
edges.push(e1);
edges.push(e2);
testee = eventLib.Expand(config);
testee(c1);
expect(nodes).toEqual([c0, c1, comm]);
expect(edges).toEqual([e0, e1]);
});
});
});
@ -248,14 +369,14 @@
function() {
eventLib.Expand(testConfig);
}
).toThrow("A callback to load a node has to be defined");
).toThrow("An adapter to load data has to be defined");
});
it('should throw an error if reshape node callback is not given', function() {
testConfig.edges = [];
testConfig.nodes = [];
testConfig.startCallback = function(){};
testConfig.loadNode = function(){};
testConfig.adapter = adapterDummy;
expect(
function() {
eventLib.Expand(testConfig);

View File

@ -153,7 +153,7 @@ describe("Graph Viewer", function() {
edges: [],
nodes: [],
startCallback: jasmine.any(Function),
loadNode: jasmine.any(Function),
adapter: jasmine.any(Object),
reshapeNodes: jasmine.any(Function)
},
drag: {
@ -172,7 +172,7 @@ describe("Graph Viewer", function() {
edges: [],
nodes: [],
startCallback: jasmine.any(Function),
loadNode: jasmine.any(Function),
adapter: jasmine.any(Object),
reshapeNodes: jasmine.any(Function)
});
expect(viewer.dispatcherConfig.drag).toEqual({
@ -270,6 +270,15 @@ describe("Graph Viewer", function() {
expect(viewer.adapter.setNodeLimit).wasCalled();
});
it('should trigger the start function if node limit is reduced to far', function() {
spyOn(viewer.adapter, "setNodeLimit").andCallFake(function(l, callback) {
callback();
});
spyOn(viewer, "start");
helper.simulateScrollUpMouseEvent("outersvg");
expect(viewer.start).wasCalled();
});
});

View File

@ -149,7 +149,7 @@
edges.push(helper.createSimpleEdge(nodes, 5, 7));
var com = reducer.getCommunity(6);
expect(com).toContainNodes([0, 1, 2]);
expect(com).toContainNodes([0, 1, 2, 3]);
});
});

View File

@ -988,6 +988,76 @@
expect(n.attr("transform")).toEqual("translate(10,10)scale(1)");
});
});
describe('testing community nodes', function() {
var shaper;
beforeEach(function() {
shaper = new NodeShaper(d3.select("svg"));
});
it('should render community nodes', function() {
var nodes = helper.createSimpleNodes([0, 1, 2]),
commNode = {
_id: "*community_42",
_inboundCounter: 0,
_outboundCounter: 0,
position: {
x: 1,
y: 1,
z: 1
}
};
nodes.push(commNode);
shaper.drawNodes(nodes);
expect($("svg .node").length).toEqual(4);
expect($("svg #\\*community_42")[0]).toBeDefined();
});
it('should render communtiy nodes as stars', function() {
var nodes = helper.createSimpleNodes([0, 1, 2]),
commNode = {
_id: "*community_42",
_size: 4,
_inboundCounter: 0,
_outboundCounter: 0,
position: {
x: 1,
y: 1,
z: 1
}
},
star;
nodes.push(commNode);
shaper.drawNodes(nodes);
expect($("svg .communitynode").length).toEqual(1);
expect($("svg #\\*community_42")[0]).toBeDefined();
star = $("svg #\\*community_42 polygon");
expect(star.length).toEqual(1);
expect(star.attr("points")).toEqual("0,-25 -16,20 23,-10 -23,-10 16,20");
});
it('should print the size of the capsulated community', function() {
var nodes = helper.createSimpleNodes([0, 1, 2]),
commNode = {
_id: "*community_42",
_size: 4,
_inboundCounter: 0,
_outboundCounter: 0,
position: {
x: 1,
y: 1,
z: 1
}
},
text;
nodes.push(commNode);
shaper.drawNodes(nodes);
text = $("svg #\\*community_42 text")[0].textContent;
expect(text).toEqual("4");
});
});
});

View File

@ -479,8 +479,9 @@ nv.models.axis = function() {
.attr('transform', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
.attr('text-anchor', rotateLabels%360 > 0 ? 'start' : 'end');
}
axisLabel.enter().append('text').attr('class', 'nv-axislabel')
axisLabel.enter().append('text').attr('class', 'nv-axislabel nv-x-axislabel')
.attr('text-anchor', 'middle')
.attr('class', 'heikotestclass')
.attr('y', xLabelMargin);
var w = (scale.range().length==2) ? scale.range()[1] : (scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]));
axisLabel
@ -565,7 +566,7 @@ nv.models.axis = function() {
.attr('text-anchor', rotateYLabel ? 'middle' : 'end')
.attr('transform', rotateYLabel ? 'rotate(-90)' : '')
//Edited 25 in next line -> origin was 12
.attr('y', rotateYLabel ? (-Math.max(margin.left,width) + 28) : -10); //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
.attr('y', rotateYLabel ? (-Math.max(margin.left,width) + 40) : -10); //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
axisLabel
.attr('x', rotateYLabel ? (-scale.range()[0] / 2) : -axis.tickPadding());
if (showMaxMin) {

View File

@ -8,17 +8,66 @@
<ul class="dropdown-menu">
<li class="nav-header">Type</li>
<li><a href="#"><label class="checkbox"><input type="checkbox" id="checkSystem">System</label></a></li>
<li><a href="#"><label class="checkbox"><input type="checkbox" id="checkDocument">Document</label></a></li>
<li><a href="#"><label class="checkbox"><input type="checkbox" id="checkEdge">Edge</label></a></li>
<li><a href="#">
<label class="checkbox checkboxLabel">
<input class="css-checkbox" type="checkbox" id="checkSystem">
<label class="css-label"></label>System
</label>
</a></li>
<li><a href="#">
<label class="checkbox checkboxLabel">
<input class="css-checkbox" type="checkbox" id="checkDocument">
<label class="css-label"></label>Document
</label>
</a></li>
<li><a href="#">
<label class="checkbox checkboxLabel">
<input class="css-checkbox" type="checkbox" id="checkEdge">
<label class="css-label"></label>Edge
</label>
</a></li>
<li class="divider"></li>
<li class="nav-header">Status</li>
<li><a href="#"><label class="checkbox"><input type="checkbox" id="checkLoaded">Loaded</label></a></li>
<li><a href="#"><label class="checkbox"><input type="checkbox" id="checkUnloaded">Unloaded</label></a></li>
<li><a href="#">
<label class="checkbox checkboxLabel">
<input class="css-checkbox" type="checkbox" id="checkLoaded">
<label class="css-label"></label>Loaded
</label>
</a></li>
<li><a href="#">
<label class="checkbox checkboxLabel">
<input class="css-checkbox" type="checkbox" id="checkUnloaded">
<label class="css-label"></label>Unloaded
</label>
</a></li>
<li class="nav-header">Sorting</li>
<li><a href="#"><label class="radio"><input type="radio" id="sortName">Sort by name</label></a></li>
<li><a href="#"><label class="radio"><input type="radio" id="sortType">Sort by type</label></a></li>
<li><a href="#"><label class="checkbox"><input type="checkbox" id="sortOrder">Sort descending</label></a></li>
<li><a href="#">
<label class="radio">
<input type="radio" id="sortName">
<label><span></span>Sort by name</label>
</label>
</a></li>
<li><a href="#">
<label class="radio">
<input type="radio" id="sortType">
<label><span></span>Sort by type</label>
</label>
</a></li>
<li><a href="#">
<label class="checkbox checkboxLabel">
<input class="css-checkbox" type="checkbox" id="sortOrder">
<label class="css-label"></label>Sort descending
</label>
</a></li>
</ul>
</div>
<!-- /btn-group -->

View File

@ -22,11 +22,11 @@
<ul class="dropdown-menu interval-dropdown">
<li class="nav-header">Update every:</li>
<li><a><label class="radio"><input type="radio" name="updateInterval" id="every5seconds" value="5">5 seconds</label></a></li>
<li><a><label class="radio"><input type="radio" name="updateInterval" id="every15seconds" value="15">15 seconds</label></a></li>
<li><a><label class="radio"><input type="radio" name="updateInterval" id="every30seconds" value="30">30 seconds</label></a></li>
<li><a><label class="radio"><input type="radio" name="updateInterval" id="every60seconds" value="60">60 seconds</label></a></li>
<li><a><label class="radio"><input type="radio" name="updateInterval" id="every120seconds" value="120">120 seconds</label></a></li>
<li><a><label class="radio"><input type="radio" name="updateInterval" id="every5seconds" value="5"><label><span></span>5 seconds</label></label></a></li>
<li><a><label class="radio"><input type="radio" name="updateInterval" id="every15seconds" value="15"><label><span></span>15 seconds</label></label></a></li>
<li><a><label class="radio"><input type="radio" name="updateInterval" id="every30seconds" value="30"><label><span></span>30 seconds</label></label></a></li>
<li><a><label class="radio"><input type="radio" name="updateInterval" id="every60seconds" value="60"><label><span></span>60 seconds</label></label></a></li>
<li><a><label class="radio"><input type="radio" name="updateInterval" id="every120seconds" value="120"><label><span></span>120 seconds</label></label></a></li>
</ul>
</div>
<!-- /btn-group -->
@ -35,6 +35,16 @@
</ul>
<ul class="thumbnails">
<li id="detailCollections" class="statSingleClient" style="margin-bottom: 13px !important;">
<div class="boxHeader">
<h6 id="detailCollectionsHeader" class="dashboardH6">Collections</h6>
<i id="db-collectionMinimize" class="icon-white icon-minus"></i>
</div>
<div id="detailCollectionsChart" class="statChart">
<svg class="svgCollections"/>
</div>
</li>
<li id="detailGraph" class="statSingleClient">
<div class="boxHeader">
<h6 id="detailGraphHeader" class="dashboardH6">User Time</h6>

View File

@ -1,27 +1,44 @@
var dashboardView = Backbone.View.extend({
el: '#content',
updateInterval: 1000, // 1 second, constant
updateInterval: 500, // 0.5 second, constant
updateFrequency: 5, // the actual update rate (5 s)
updateCounter: 0,
arraySize: 99, // how many values will we keep per figure?
arraySize: 20, // how many values will we keep per figure?
seriesData: {},
charts: {},
units: [],
updateNOW: false,
collectionsStats: {
"corrupted": 0,
"new born collection" : 0,
"unloaded" : 0,
"loaded" : 0,
"in the process of being unloaded" : 0,
"deleted" : 0
},
detailGraph: "userTime",
initialize: function () {
var self = this;
this.initUnits();
self.addCustomCharts();
this.collection.fetch({
success: function() {
self.countCollections();
self.calculateSeries();
self.renderCharts();
window.setInterval(function() {
self.updateCounter++;
if (self.updateNOW === true) {
self.calculateSeries();
self.renderCharts();
self.updateNOW = false;
}
if (self.updateCounter < self.updateFrequency) {
return false;
}
@ -47,20 +64,32 @@ var dashboardView = Backbone.View.extend({
events: {
"click .dashboard-dropdown li" : "checkEnabled",
"click .interval-dropdown li" : "checkInterval",
"click .db-zoom" : "renderDetailChart",
"click .db-minimize" : "checkDetailChart",
"click .db-hide" : "hideChart"
"click .interval-dropdown li" : "checkInterval",
"click .db-zoom" : "renderDetailChart",
"click .db-minimize" : "checkDetailChart",
"click .db-hide" : "hideChart",
"click .group-close" : "hideGroup",
"click .group-open" : "showGroup"
},
template: new EJS({url: 'js/templates/dashboardView.ejs'}),
countCollections: function() {
var self = this;
$.each(window.arangoCollectionsStore.models, function(k,v) {
if ( self.collectionsStats[this.attributes.status] === undefined ) {
self.collectionsStats[this.attributes.status] = 0;
}
self.collectionsStats[this.attributes.status]++;
});
},
render: function() {
var self = this;
$(this.el).html(this.template.text);
//Client calculated charts
self.genCustomCategories();
/*self.genCustomCategories();
self.genCustomChartDescription(
"userTime + systemTime",
"custom",
@ -68,17 +97,21 @@ var dashboardView = Backbone.View.extend({
"Total Time (User+System)",
"accumulated",
"seconds"
);
);*/
var counter = 1;
$.each(this.options.description.models[0].attributes.groups, function () {
$('.thumbnails').append(
'<ul class="statGroups" id="' + this.group + '">' +
'<i class="group-close icon-minus icon-white"></i>' +
'<h4 class="statsHeader">' + this.name + '</h4>' +
'</ul>');
$('#menuGroups').append(
'<li class="nav-header">' + this.name + '</li>' +
'<li class="divider" id="' + this.group + 'Divider"></li>'
);
$('#menuGroups').append('<li class="nav-header">' + this.name + '</li>');
$('#menuGroups').append('<li class="divider" id="' + this.group + 'Divider"></li>');
if (self.options.description.models[0].attributes.groups.length === counter) {
$('#'+this.group+'Divider').addClass('dbNotVisible');
}
counter++;
});
$.each(this.options.description.models[0].attributes.figures, function () {
@ -105,45 +138,45 @@ var dashboardView = Backbone.View.extend({
}
return this;
},
//generate function for all custom categories
genCustomCategories: function () {
this.genCustomCategory("Client calculated charts", "custom", "Customized Charts");
},
//generate a custom category
genCustomCategory: function(description, group, name) {
this.options.description.models[0].attributes.groups.push({
"description":description,
"group":group,
"name":name
});
},
//generate a custom description
genCustomChartDescription: function (description, group, identifier, name, type, units) {
var figure = {
"description" : description,
"group" : group,
"identifier" : identifier,
"name" : name,
"type" : type,
"units" : units
};
this.options.description.models[0].attributes.figures.push(figure);
this.renderFigure(figure);
},
//calculate customized chart value functions here
updateCustomChartValues: function () {
this.totalTime2();
},
//custom chart value calculation for totalTime2
totalTime2: function () {
var val1 = this.collection.models[0].attributes.system.userTime;
var val2 = this.collection.models[0].attributes.system.systemTime;
var totalTime2Value = val1+val2;
this.collection.models[0].attributes["custom"] = {"totalTime2":totalTime2Value};
addCustomCharts: function () {
var self = this;
var figure = {
"description" : "my custom chart",
"group" : "custom",
"identifier" : "custom1",
"name" : "Custom1",
"type" : "accumulated",
"units" : "seconds",
"exec" : function () {
var val1 = self.collection.models[0].attributes.system.userTime;
var val2 = self.collection.models[0].attributes.system.systemTime;
var totalTime2Value = val1+val2;
return totalTime2Value;
}
};
var addGroup = true;
$.each(this.options.description.models[0].attributes.groups, function(k, v) {
if (self.options.description.models[0].attributes.groups[k].group === figure.group) {
addGroup = false;
}
});
if (addGroup == true) {
self.options.description.models[0].attributes.groups.push({
"description" : "custom",
"group" : "custom",
"name" : "custom"
});
}
this.options.description.models[0].attributes.figures.push(figure);
},
checkInterval: function (a) {
var self = this;
this.updateFrequency = a.target.value;
self.calculateSeries();
self.renderCharts();
@ -210,6 +243,20 @@ var dashboardView = Backbone.View.extend({
}
},
hideGroup: function (a) {
var group = $(a.target).parent();
$(a.target).removeClass('icon-minus group-close');
$(a.target).addClass('icon-plus group-open');
$(group).addClass("groupHidden");
},
showGroup: function (a) {
var group = $(a.target).parent();
$(a.target).removeClass('icon-plus group-open');
$(a.target).addClass('icon-minus group-close');
$(group).removeClass("groupHidden");
},
hideChart: function (a) {
var figure = $(a.target).attr("value");
$('#'+figure+'Checkbox').prop('checked', false);
@ -222,20 +269,56 @@ var dashboardView = Backbone.View.extend({
$.each(this.options.description.models[0].attributes.figures, function () {
if(this.identifier === self.detailGraph) {
$('#detailGraphHeader').text(this.name);
self.calculateSeries();
self.renderCharts();
$("html, body").animate({ scrollTop: 0 }, "slow");
$('#detailGraphChart').show();
$('#detailGraph').height(300);
$('#dbHideSwitch').addClass('icon-minus');
$('#dbHideSwitch').removeClass('icon-plus');
self.updateNOW = true;
self.calculateSeries();
self.renderCharts();
}
});
},
renderCollectionsChart: function () {
var self = this;
nv.addGraph(function() {
var chart = nv.models.pieChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value })
.showLabels(true);
d3.select("#detailCollectionsChart svg")
.datum(self.convertCollections())
.transition().duration(1200)
.call(chart);
return chart;
});
},
convertCollections: function () {
var self = this;
var collValues = [];
$.each(self.collectionsStats, function(k,v) {
collValues.push({
"label" : k,
"value" : v,
});
});
return [{
key: "Collections Status",
values: collValues
}];
},
renderCharts: function () {
var self = this;
$('#every'+self.updateFrequency+'seconds').prop('checked',true);
self.renderCollectionsChart();
$.each(self.options.description.models[0].attributes.figures, function () {
var figure = this;
@ -284,22 +367,25 @@ var dashboardView = Backbone.View.extend({
}
if (self.detailGraph === identifier) {
d3.select("#detailGraphChart svg")
.call(chart)
.datum([ { values: self.seriesData[identifier].values, key: identifier, color: "#8AA051" } ])
.transition().duration(500);
}
d3.select("#" + identifier + "Chart svg")
.call(chart)
.datum([ { values: self.seriesData[identifier].values, key: identifier, color: "#8AA051" } ])
.transition().duration(500);
}
else {
}
//disable ticks for small charts
//disable label for small charts
d3.select("#" + identifier + "Chart svg")
.call(chart)
.datum([ { values: self.seriesData[identifier].values, key: identifier, color: "#8AA051" } ])
.transition().duration(500);
});
},
calculateSeries: function (flush) {
var self = this;
self.updateCustomChartValues();
var timeStamp = Math.round(new Date() * 10);
@ -320,7 +406,14 @@ var dashboardView = Backbone.View.extend({
return;
}
var responseValue = self.collection.models[0].attributes[figure.group][identifier];
var responseValue;
if (figure.exec) {
responseValue = figure.exec();
}
else {
responseValue = self.collection.models[0].attributes[figure.group][identifier];
}
if (responseValue !== undefined && responseValue !== null) {
if (responseValue.sum !== undefined) {
@ -381,8 +474,10 @@ var dashboardView = Backbone.View.extend({
);
$('#' + figure.group + 'Divider').before(
'<li><a><label class="checkbox">'+
'<input type="checkbox" id=' + figure.identifier + 'Checkbox checked>' + figure.name + '</label></a></li>'
'<li><a><label class="checkbox checkboxLabel">'+
'<input class="css-checkbox" type="checkbox" id=' + figure.identifier + 'Checkbox checked/>' +
'<label class="css-label"/>' +
figure.name + '</label></a></li>'
);
$('.db-info').tooltip({
placement: "top"

View File

@ -241,7 +241,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -235,7 +235,7 @@ function post_api_collection (req, res) {
actions.resultOk(req, res, actions.HTTP_OK, result, headers);
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
@ -668,7 +668,7 @@ function put_api_collection_load (req, res, collection) {
actions.resultOk(req, res, actions.HTTP_OK, result);
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
@ -720,7 +720,7 @@ function put_api_collection_unload (req, res, collection) {
actions.resultOk(req, res, actions.HTTP_OK, result);
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
@ -750,7 +750,7 @@ function put_api_collection_truncate (req, res, collection) {
actions.resultOk(req, res, actions.HTTP_OK, result);
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
@ -812,7 +812,7 @@ function put_api_collection_properties (req, res, collection) {
actions.resultOk(req, res, actions.HTTP_OK, result);
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
@ -870,7 +870,7 @@ function put_api_collection_rename (req, res, collection) {
actions.resultOk(req, res, actions.HTTP_OK, result);
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
@ -979,7 +979,7 @@ function delete_api_collection (req, res) {
actions.resultOk(req, res, actions.HTTP_OK, result);
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
}
@ -1012,7 +1012,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -235,7 +235,7 @@ function POST_api_cursor(req, res) {
// error occurred
if (cursor instanceof Error) {
actions.resultException(req, res, cursor);
actions.resultException(req, res, cursor, undefined, false);
return;
}
@ -420,7 +420,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -131,7 +131,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -137,7 +137,7 @@ function POST_api_explain (req, res) {
var result = EXPLAIN(json.query, json.bindVars);
if (result instanceof Error) {
actions.resultException(req, res, result);
actions.resultException(req, res, result, undefined, false);
return;
}
@ -169,7 +169,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -1770,7 +1770,7 @@ function post_graph_vertex_edges (req, res, g) {
// error occurred
if (cursor instanceof Error) {
actions.resultException(req, res, cursor);
actions.resultException(req, res, cursor, undefined, false);
return;
}
@ -2039,7 +2039,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -890,7 +890,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -126,7 +126,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -115,7 +115,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -193,7 +193,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -312,7 +312,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -430,7 +430,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -522,7 +522,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -611,7 +611,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -688,7 +688,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -734,7 +734,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -828,7 +828,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -904,7 +904,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -990,7 +990,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -1083,7 +1083,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -141,7 +141,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -362,7 +362,7 @@ actions.defineHttp({
actions.resultOk(req, res, actions.HTTP_OK, result);
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -611,7 +611,7 @@ actions.defineHttp({
actions.resultOk(req, res, actions.HTTP_OK, result);
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -456,7 +456,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -341,7 +341,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});
@ -428,7 +428,7 @@ actions.defineHttp({
}
}
catch (err) {
actions.resultException(req, res, err);
actions.resultException(req, res, err, undefined, false);
}
}
});

View File

@ -29,8 +29,6 @@
"../../../html/admin/js/modules/org/arangodb/arango-collection-common.js",
"../../../html/admin/js/modules/org/arangodb/arango-collection.js",
"../../../html/admin/js/modules/org/arangodb/arango-database.js",
"../../../html/admin/js/modules/org/arangodb/arango-error-common.js",
"../../../html/admin/js/modules/org/arangodb/arango-error.js",
"../../../html/admin/js/modules/org/arangodb/arango-query-cursor.js",
"../../../html/admin/js/modules/org/arangodb/arango-statement-common.js",
"../../../html/admin/js/modules/org/arangodb/arango-statement.js",

View File

@ -1198,7 +1198,7 @@ function resultError (req, res, httpReturnCode, errorNum, errorMessage, headers,
res.body = JSON.stringify(result);
if (headers !== undefined) {
if (headers !== undefined && headers !== null) {
res.headers = headers;
}
}
@ -1806,36 +1806,42 @@ function indexNotFound (req, res, collection, index, headers) {
////////////////////////////////////////////////////////////////////////////////
/// @brief generates an error for an exception
///
/// @FUN{actions.resultException(@FA{req}, @FA{res}, @FA{err}, @FA{headers})}
/// @FUN{actions.resultException(@FA{req}, @FA{res}, @FA{err}, @FA{headers}, @FA{verbose})}
///
/// The function generates an error response.
/// The function generates an error response. If @FA{verbose} is set to
/// @LIT{true} or not specified (the default), then the error stack trace will
/// be included in the error message if available.
////////////////////////////////////////////////////////////////////////////////
function resultException (req, res, err, headers) {
function resultException (req, res, err, headers, verbose) {
'use strict';
var code;
var msg;
var num;
if (verbose || verbose === undefined) {
msg = String(err.stack || err);
}
else {
msg = String(err);
}
if (err instanceof internal.ArangoError) {
num = err.errorNum;
msg = err.errorMessage;
code = exports.HTTP_BAD;
if (num === 0) {
num = arangodb.ERROR_INTERNAL;
}
if (msg === "") {
msg = String(err.stack || err);
if (err.errorMessage !== "" && ! verbose) {
msg = err.errorMessage;
}
else {
msg += ": " + String(err.stack || err);
}
switch (num) {
case arangodb.ERROR_INTERNAL:
case arangodb.ERROR_OUT_OF_MEMORY:
code = exports.HTTP_SERVER_ERROR;
break;
@ -1844,23 +1850,17 @@ function resultException (req, res, err, headers) {
code = exports.HTTP_CONFLICT;
break;
}
resultError(req, res, code, num, msg, headers);
}
else if (err instanceof TypeError) {
num = arangodb.ERROR_TYPE_ERROR;
code = exports.HTTP_BAD;
msg = String(err.stack || err);
resultError(req, res, code, num, msg, headers);
}
else {
resultError(req, res,
exports.HTTP_SERVER_ERROR, arangodb.ERROR_HTTP_SERVER_ERROR,
String(err.stack || err),
headers);
num = arangodb.ERROR_HTTP_SERVER_ERROR;
code = exports.HTTP_SERVER_ERROR;
}
resultError(req, res, code, num, msg, headers);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -343,6 +343,32 @@ function ahuacatlQueryVariablesTestSuite () {
var actual = getQueryResults(query);
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief collect and return (should omit any temporary variables)
////////////////////////////////////////////////////////////////////////////////
testTemporaryVariables : function () {
var data = [ { name: "baz" }, { name: "bar" } ];
var expected = [ { "criteria" : "bar", "g" : [ { "y" : { "test" : "test", "name" : "bar" } } ] }, { "criteria" : "baz", "g" : [ { "y" : { "test" : "test", "name" : "baz" } } ] } ];
var query = "FOR y IN (FOR x IN " + JSON.stringify(data) + " LET object = (FOR a IN [ '1', '2' ] RETURN a) RETURN { test: \"test\", name: x.name }) COLLECT criteria = y.name INTO g RETURN { criteria: criteria, g: g }";
var actual = getQueryResults("LET result = (" + query + ") RETURN result");
assertEqual([ expected ], actual);
actual = getQueryResults(query);
assertEqual(expected, actual);
// omit creating sub-objects
query = "FOR y IN (FOR x IN " + JSON.stringify(data) + " RETURN { test: \"test\", name: x.name }) COLLECT criteria = y.name INTO g RETURN { criteria: criteria, g: g }";
actual = getQueryResults("LET result = (" + query + ") RETURN result");
assertEqual([ expected ], actual);
actual = getQueryResults(query);
assertEqual(expected, actual);
}
};

View File

@ -373,24 +373,6 @@
return true;
});
// set up the collection _ids
addTask("setupIds", "setup _ids collection", function () {
return createSystemCollection("_ids", { waitForSync : false });
});
// create a cap constraint for _ids
addTask("ensureIdsCap", "ensureCapConstraint for _ids collection", function () {
var ids = getCollection("_ids");
if (! ids) {
return false;
}
ids.ensureCapConstraint(50);
return true;
});
// set up the collection _trx
addTask("setupTrx", "setup _trx collection", function () {
return createSystemCollection("_trx", { waitForSync : false });

View File

@ -84,7 +84,7 @@ bool RestVersionHandler::isDirect () {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& RestVersionHandler::queue () {
string const& RestVersionHandler::queue () const {
return _queue;
}

View File

@ -122,7 +122,7 @@ namespace triagens {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& queue ();
string const& queue () const;
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the server version number

View File

@ -182,6 +182,14 @@ namespace triagens {
return TRI_EndStringBuffer(&_buffer);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns pointer to the end of the character buffer
////////////////////////////////////////////////////////////////////////////////
char * end () {
return const_cast<char*>(TRI_EndStringBuffer(&_buffer));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns length of the character buffer
////////////////////////////////////////////////////////////////////////////////
@ -190,6 +198,14 @@ namespace triagens {
return TRI_LengthStringBuffer(&_buffer);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief increases length of the character buffer
////////////////////////////////////////////////////////////////////////////////
void increaseLength (size_t n) {
TRI_IncreaseLengthStringBuffer(&_buffer, n);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns true if buffer is empty
////////////////////////////////////////////////////////////////////////////////
@ -257,7 +273,7 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- STRING AND CHARATCER APPENDERS
// --SECTION-- STRING AND CHARACTER APPENDERS
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -167,6 +167,9 @@ static void RemoveAllLockedFiles (void) {
TRI_RemoveVector(&FileDescriptors, i);
}
TRI_DestroyVectorString(&FileNames);
TRI_DestroyVector(&FileDescriptors);
TRI_WriteUnlockReadWriteLock(&FileNamesLock);
}

View File

@ -1770,6 +1770,8 @@ void TRI_InitialiseLogging (bool threaded) {
////////////////////////////////////////////////////////////////////////////////
bool TRI_ShutdownLogging () {
size_t i, j;
if (! Initialised) {
return ThreadedLogging;
}
@ -1798,6 +1800,20 @@ bool TRI_ShutdownLogging () {
TRI_UnlockSpin(&OutputPrefixLock);
// cleanup output buffers
TRI_LockMutex(&BufferLock);
for (i = 0; i < OUTPUT_LOG_LEVELS; i++) {
for (j = 0; j < OUTPUT_BUFFER_SIZE; j++) {
if (BufferOutput[i][j]._text != NULL) {
TRI_FreeString(TRI_CORE_MEM_ZONE, BufferOutput[i][j]._text);
BufferOutput[i][j]._text = NULL;
}
}
}
TRI_UnlockMutex(&BufferLock);
// cleanup locks
TRI_DestroySpin(&OutputPrefixLock);
TRI_DestroySpin(&AppendersLock);

View File

@ -288,6 +288,15 @@ size_t TRI_LengthStringBuffer (TRI_string_buffer_t const * self) {
return (size_t) (self->_current - self->_buffer);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief increases length of the character buffer
////////////////////////////////////////////////////////////////////////////////
void TRI_IncreaseLengthStringBuffer (TRI_string_buffer_t * self, size_t n)
{
self->_current += n;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns true if buffer is empty
////////////////////////////////////////////////////////////////////////////////

View File

@ -164,6 +164,12 @@ char const * TRI_EndStringBuffer (TRI_string_buffer_t const * self);
size_t TRI_LengthStringBuffer (TRI_string_buffer_t const * self);
////////////////////////////////////////////////////////////////////////////////
/// @brief increases length of the character buffer
////////////////////////////////////////////////////////////////////////////////
void TRI_IncreaseLengthStringBuffer (TRI_string_buffer_t * self, size_t n);
////////////////////////////////////////////////////////////////////////////////
/// @brief returns true if buffer is empty
////////////////////////////////////////////////////////////////////////////////

View File

@ -230,8 +230,8 @@ void ApplicationEndpointServer::setupOptions (map<string, ProgramOptionsDescript
("server.cafile", &_cafile, "file containing the CA certificates of clients")
("server.ssl-protocol", &_sslProtocol, "1 = SSLv2, 2 = SSLv23, 3 = SSLv3, 4 = TLSv1")
("server.ssl-cache", &_sslCache, "use SSL session caching")
("server.ssl-options", &_sslOptions, "ssl options, see OpenSSL documentation")
("server.ssl-cipher-list", &_sslCipherList, "ssl cipher list, see OpenSSL documentation")
("server.ssl-options", &_sslOptions, "SSL options, see OpenSSL documentation")
("server.ssl-cipher-list", &_sslCipherList, "SSL cipher list, see OpenSSL documentation")
;
}

View File

@ -78,7 +78,7 @@ Job::JobType Handler::type () {
/// @brief returns the queue name
////////////////////////////////////////////////////////////////////////////////
string const& Handler::queue () {
string const& Handler::queue () const {
static string standard = "STANDARD";
return standard;
}

View File

@ -144,7 +144,7 @@ namespace triagens {
/// @brief returns the queue name
////////////////////////////////////////////////////////////////////////////////
virtual string const& queue ();
virtual string const& queue () const;
////////////////////////////////////////////////////////////////////////////////
/// @brief sets the thread which currently dealing with the job

View File

@ -552,8 +552,7 @@ void HttpResponse::setCookie (string const& name, string const& value,
}
char const* l = StringUtils::duplicate(buffer->c_str());
buffer->clear();
free(buffer);
delete buffer;
_cookies.push_back(l);
_freeables.push_back(l);

View File

@ -67,7 +67,6 @@ SocketTask::SocketTask (TRI_socket_t socket, double keepAliveTimeout)
ownBuffer(true),
writeLength(0) {
_readBuffer = new StringBuffer(TRI_UNKNOWN_MEM_ZONE);
tmpReadBuffer = new char[READ_BLOCK_SIZE];
ConnectionStatisticsAgent::acquire();
ConnectionStatisticsAgentSetStart(this);
@ -102,8 +101,6 @@ SocketTask::~SocketTask () {
delete _readBuffer;
delete[] tmpReadBuffer;
ConnectionStatisticsAgentSetEnd(this);
ConnectionStatisticsAgent::release();
}
@ -151,12 +148,19 @@ void SocketTask::setKeepAliveTimeout (double timeout) {
bool SocketTask::fillReadBuffer (bool& closed) {
closed = false;
// reserve some memory for reading
if (_readBuffer->reserve(READ_BLOCK_SIZE) == TRI_ERROR_OUT_OF_MEMORY) {
// out of memory
LOGGER_TRACE("out of memory");
int nr = TRI_READ_SOCKET(_commSocket, tmpReadBuffer, READ_BLOCK_SIZE, 0);
return false;
}
int nr = TRI_READ_SOCKET(_commSocket, _readBuffer->end(), READ_BLOCK_SIZE, 0);
if (nr > 0) {
_readBuffer->appendText(tmpReadBuffer, nr);
_readBuffer->increaseLength(nr);
return true;
}
else if (nr == 0) {

View File

@ -374,12 +374,6 @@ namespace triagens {
TRI_tid_t tid;
////////////////////////////////////////////////////////////////////////////////
/// @brief temporary static buffer for read requests
////////////////////////////////////////////////////////////////////////////////
char * tmpReadBuffer;
};
}
}

View File

@ -231,9 +231,13 @@ bool ClientConnection::readClientConnection (StringBuffer& stringBuffer) {
assert(_socket.fileHandle > 0);
do {
char buffer[READBUFFER_SIZE];
// reserve some memory for reading
if (stringBuffer.reserve(READBUFFER_SIZE) == TRI_ERROR_OUT_OF_MEMORY) {
// out of memory
return false;
}
int lenRead = TRI_READ_SOCKET(_socket, buffer, READBUFFER_SIZE - 1, 0);
int lenRead = TRI_READ_SOCKET(_socket, stringBuffer.end(), READBUFFER_SIZE - 1, 0);
if (lenRead == -1) {
// error occurred
@ -245,7 +249,7 @@ bool ClientConnection::readClientConnection (StringBuffer& stringBuffer) {
break;
}
stringBuffer.appendText(buffer, lenRead);
stringBuffer.increaseLength(lenRead);
}
while (readable());

View File

@ -227,7 +227,7 @@ bool SslClientConnection::writeClientConnection (void* buffer, size_t length, si
case SSL_ERROR_WANT_CONNECT:
case SSL_ERROR_SYSCALL:
default: {
/* fallthrough */
/* fall through */
}
}
@ -244,13 +244,17 @@ bool SslClientConnection::readClientConnection (StringBuffer& stringBuffer) {
}
do {
char buffer[READBUFFER_SIZE];
// reserve some memory for reading
if (stringBuffer.reserve(READBUFFER_SIZE) == TRI_ERROR_OUT_OF_MEMORY) {
// out of memory
return false;
}
int lenRead = SSL_read(_ssl, buffer, READBUFFER_SIZE - 1);
int lenRead = SSL_read(_ssl, stringBuffer.end(), READBUFFER_SIZE - 1);
switch (SSL_get_error(_ssl, lenRead)) {
case SSL_ERROR_NONE:
stringBuffer.appendText(buffer, lenRead);
stringBuffer.increaseLength(lenRead);
break;
case SSL_ERROR_ZERO_RETURN:

View File

@ -21,6 +21,11 @@
#ifndef _ZLIBIOAPI64_H
#define _ZLIBIOAPI64_H
#ifdef _Z_OF
#undef OF
#define OF _Z_OF
#endif
#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
// Linux needs this to support file operation on files larger then 4+GB