mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:arangodb/arangodb into devel
This commit is contained in:
commit
98ef80ab29
|
@ -0,0 +1,7 @@
|
|||
!CHAPTER Working with Persistent Indexes
|
||||
|
||||
If a suitable persistent index exists, then /_api/simple/range and other operations
|
||||
will use this index to execute queries.
|
||||
|
||||
<!-- js/actions/api-index.js -->
|
||||
@startDocuBlock JSF_post_api_index_persistent
|
|
@ -1,6 +1,7 @@
|
|||
!CHAPTER Working with Skiplist Indexes
|
||||
|
||||
If a suitable skip-list index exists, then /_api/simple/range will use this index to execute a range query.
|
||||
If a suitable skip-list index exists, then /_api/simple/range and other operations
|
||||
will use this index to execute queries.
|
||||
|
||||
<!-- js/actions/api-index.js -->
|
||||
@startDocuBlock JSF_post_api_index_skiplist
|
||||
@startDocuBlock JSF_post_api_index_skiplist
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
* [Working with Indexes](Indexes/WorkingWith.md)
|
||||
* [Hash](Indexes/Hash.md)
|
||||
* [Skiplist](Indexes/Skiplist.md)
|
||||
* [Persistent](Indexes/Persistent.md)
|
||||
* [Geo](Indexes/Geo.md)
|
||||
* [Fulltext](Indexes/Fulltext.md)
|
||||
* [Transactions](Transaction/README.md)
|
||||
|
|
|
@ -28,9 +28,9 @@ of the index details. Depending on the index type, a single attribute or
|
|||
multiple attributes can be indexed. In the latter case, an array of
|
||||
strings is expected.
|
||||
|
||||
Indexing system attributes such as *_id*, *_key*, *_from*, and *_to*
|
||||
is not supported for user-defined indexes. Manually creating an index using
|
||||
any of these attributes will fail with an error.
|
||||
Indexing the system attribute *_id* is not supported for user-defined indexes.
|
||||
Manually creating an index using *_id* as an index attribute will fail with
|
||||
an error.
|
||||
|
||||
Some indexes can be created as unique or non-unique variants. Uniqueness
|
||||
can be controlled for most indexes by specifying the *unique* flag in the
|
||||
|
@ -41,12 +41,13 @@ create a non-unique index.
|
|||
**Note**: The following index types do not support uniqueness, and using
|
||||
the *unique* attribute with these types may lead to an error:
|
||||
|
||||
- geo indexes
|
||||
- fulltext indexes
|
||||
|
||||
**Note**: Unique indexes on non-shard keys are not supported in a
|
||||
cluster.
|
||||
|
||||
Hash and skiplist indexes can optionally be created in a sparse
|
||||
Hash, skiplist and persistent indexes can optionally be created in a sparse
|
||||
variant. A sparse index will be created if the *sparse* attribute in
|
||||
the index details is set to *true*. Sparse indexes do not index documents
|
||||
for which any of the index attributes is either not set or is *null*.
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
|
||||
@startDocuBlock JSF_post_api_index_persistent
|
||||
@brief creates a persistent index
|
||||
|
||||
@RESTHEADER{POST /_api/index#persistent, Create a persistent index}
|
||||
|
||||
@RESTQUERYPARAMETERS
|
||||
|
||||
@RESTQUERYPARAM{collection-name,string,required}
|
||||
The collection name.
|
||||
|
||||
@RESTBODYPARAM{type,string,required,string}
|
||||
must be equal to *"persistent"*.
|
||||
|
||||
@RESTBODYPARAM{fields,array,required,string}
|
||||
an array of attribute paths.
|
||||
|
||||
@RESTBODYPARAM{unique,boolean,required,}
|
||||
if *true*, then create a unique index.
|
||||
|
||||
@RESTBODYPARAM{sparse,boolean,required,}
|
||||
if *true*, then create a sparse index.
|
||||
|
||||
@RESTDESCRIPTION
|
||||
|
||||
Creates a persistent index for the collection *collection-name*, if
|
||||
it does not already exist. The call expects an object containing the index
|
||||
details.
|
||||
|
||||
In a sparse index all documents will be excluded from the index that do not
|
||||
contain at least one of the specified index attributes (i.e. *fields*) or that
|
||||
have a value of *null* in any of the specified index attributes. Such documents
|
||||
will not be indexed, and not be taken into account for uniqueness checks if
|
||||
the *unique* flag is set.
|
||||
|
||||
In a non-sparse index, these documents will be indexed (for non-present
|
||||
indexed attributes, a value of *null* will be used) and will be taken into
|
||||
account for uniqueness checks if the *unique* flag is set.
|
||||
|
||||
**Note**: unique indexes on non-shard keys are not supported in a cluster.
|
||||
|
||||
@RESTRETURNCODES
|
||||
|
||||
@RESTRETURNCODE{200}
|
||||
If the index already exists, then a *HTTP 200* is
|
||||
returned.
|
||||
|
||||
@RESTRETURNCODE{201}
|
||||
If the index does not already exist and could be created, then a *HTTP 201*
|
||||
is returned.
|
||||
|
||||
@RESTRETURNCODE{400}
|
||||
If the collection already contains documents and you try to create a unique
|
||||
persistent index in such a way that there are documents violating the
|
||||
uniqueness, then a *HTTP 400* is returned.
|
||||
|
||||
@RESTRETURNCODE{404}
|
||||
If the *collection-name* is unknown, then a *HTTP 404* is returned.
|
||||
|
||||
@EXAMPLES
|
||||
|
||||
Creating a persistent index
|
||||
|
||||
@EXAMPLE_ARANGOSH_RUN{RestIndexCreateNewPersistent}
|
||||
var cn = "products";
|
||||
db._drop(cn);
|
||||
db._create(cn);
|
||||
|
||||
var url = "/_api/index?collection=" + cn;
|
||||
var body = {
|
||||
type: "persistent",
|
||||
unique: false,
|
||||
fields: [ "a", "b" ]
|
||||
};
|
||||
|
||||
var response = logCurlRequest('POST', url, body);
|
||||
|
||||
assert(response.code === 201);
|
||||
|
||||
logJsonResponse(response);
|
||||
~ db._drop(cn);
|
||||
@END_EXAMPLE_ARANGOSH_RUN
|
||||
|
||||
Creating a sparse persistent index
|
||||
|
||||
@EXAMPLE_ARANGOSH_RUN{RestIndexCreateSparsePersistent}
|
||||
var cn = "products";
|
||||
db._drop(cn);
|
||||
db._create(cn);
|
||||
|
||||
var url = "/_api/index?collection=" + cn;
|
||||
var body = {
|
||||
type: "persistent",
|
||||
unique: false,
|
||||
sparse: true,
|
||||
fields: [ "a" ]
|
||||
};
|
||||
|
||||
var response = logCurlRequest('POST', url, body);
|
||||
|
||||
assert(response.code === 201);
|
||||
|
||||
logJsonResponse(response);
|
||||
~ db._drop(cn);
|
||||
@END_EXAMPLE_ARANGOSH_RUN
|
||||
@endDocuBlock
|
||||
|
|
@ -2,14 +2,13 @@
|
|||
@startDocuBlock JSF_post_api_index_skiplist
|
||||
@brief creates a skip-list
|
||||
|
||||
@RESTHEADER{POST /_api/index, Create skip list}
|
||||
@RESTHEADER{POST /_api/index#skiplist, Create skip list}
|
||||
|
||||
@RESTQUERYPARAMETERS
|
||||
|
||||
@RESTQUERYPARAM{collection-name,string,required}
|
||||
The collection name.
|
||||
|
||||
|
||||
@RESTBODYPARAM{type,string,required,string}
|
||||
must be equal to *"skiplist"*.
|
||||
|
||||
|
|
|
@ -0,0 +1,579 @@
|
|||
# coding: utf-8
|
||||
|
||||
require 'rspec'
|
||||
require 'arangodb.rb'
|
||||
|
||||
describe ArangoDB do
|
||||
prefix = "api-index-persistent"
|
||||
|
||||
################################################################################
|
||||
## unique constraints during create
|
||||
################################################################################
|
||||
|
||||
context "creating persistent index:" do
|
||||
context "dealing with unique constraints violation:" do
|
||||
before do
|
||||
@cn = "UnitTestsCollectionIndexes"
|
||||
ArangoDB.drop_collection(@cn)
|
||||
@cid = ArangoDB.create_collection(@cn)
|
||||
end
|
||||
|
||||
after do
|
||||
ArangoDB.drop_collection(@cn)
|
||||
end
|
||||
|
||||
it "does not create the index in case of violation" do
|
||||
# create a document
|
||||
cmd1 = "/_api/document?collection=#{@cn}"
|
||||
body = "{ \"a\" : 1, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
# create another document
|
||||
body = "{ \"a\" : 1, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
# try to create the index
|
||||
cmd = "/_api/index?collection=#{@cn}"
|
||||
body = "{ \"type\" : \"persistent\", \"unique\" : true, \"fields\" : [ \"a\" ] }"
|
||||
doc = ArangoDB.log_post("#{prefix}-fail", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(400)
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
doc.parsed_response['errorNum'].should eq(1210)
|
||||
end
|
||||
|
||||
it "does not create the index in case of violation, null attributes" do
|
||||
# create a document
|
||||
cmd1 = "/_api/document?collection=#{@cn}"
|
||||
body = "{ \"a\" : null, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
# create another document
|
||||
body = "{ \"a\" : null, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
# try to create the index
|
||||
cmd = "/_api/index?collection=#{@cn}"
|
||||
body = "{ \"type\" : \"persistent\", \"unique\" : true, \"fields\" : [ \"a\" ] }"
|
||||
doc = ArangoDB.log_post("#{prefix}-fail", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(400)
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
doc.parsed_response['errorNum'].should eq(1210)
|
||||
end
|
||||
|
||||
it "does not create the index in case of violation, sparse index" do
|
||||
# create a document
|
||||
cmd1 = "/_api/document?collection=#{@cn}"
|
||||
body = "{ \"a\" : 1, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
# create another document
|
||||
body = "{ \"a\" : 1, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
# try to create the index
|
||||
cmd = "/_api/index?collection=#{@cn}"
|
||||
body = "{ \"type\" : \"persistent\", \"unique\" : true, \"fields\" : [ \"a\" ], \"sparse\" : true }"
|
||||
doc = ArangoDB.log_post("#{prefix}-fail", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(400)
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
doc.parsed_response['errorNum'].should eq(1210)
|
||||
end
|
||||
|
||||
it "creates the index in case of null attributes, sparse index" do
|
||||
# create a document
|
||||
cmd1 = "/_api/document?collection=#{@cn}"
|
||||
body = "{ \"a\" : null, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
# create another document
|
||||
body = "{ \"a\" : null, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
# try to create the index
|
||||
cmd = "/_api/index?collection=#{@cn}"
|
||||
body = "{ \"type\" : \"persistent\", \"unique\" : true, \"fields\" : [ \"a\" ], \"sparse\" : true }"
|
||||
doc = ArangoDB.log_post("#{prefix}-fail", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## unique constraints during create
|
||||
################################################################################
|
||||
|
||||
context "creating documents:" do
|
||||
context "dealing with unique constraints:" do
|
||||
before do
|
||||
@cn = "UnitTestsCollectionIndexes"
|
||||
ArangoDB.drop_collection(@cn)
|
||||
@cid = ArangoDB.create_collection(@cn)
|
||||
end
|
||||
|
||||
after do
|
||||
ArangoDB.drop_collection(@cn)
|
||||
end
|
||||
|
||||
it "rolls back in case of violation" do
|
||||
cmd = "/_api/index?collection=#{@cn}"
|
||||
body = "{ \"type\" : \"persistent\", \"unique\" : true, \"fields\" : [ \"a\" ] }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create1", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.parsed_response['type'].should eq("persistent")
|
||||
doc.parsed_response['unique'].should eq(true)
|
||||
|
||||
# create a document
|
||||
cmd1 = "/_api/document?collection=#{@cn}"
|
||||
body = "{ \"a\" : 1, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
id1 = doc.parsed_response['_id']
|
||||
id1.should be_kind_of(String)
|
||||
|
||||
rev1 = doc.parsed_response['_rev']
|
||||
rev1.should be_kind_of(String)
|
||||
|
||||
# check it
|
||||
cmd2 = "/_api/document/#{id1}"
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
|
||||
# create a unique constraint violation
|
||||
body = "{ \"a\" : 1, \"b\" : 2 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create3", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(409)
|
||||
|
||||
# check it again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
|
||||
# third try (make sure the rollback has not destroyed anything)
|
||||
body = "{ \"a\" : 1, \"b\" : 3 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create4", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(409)
|
||||
|
||||
# check it again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
|
||||
# unload collection
|
||||
cmd3 = "/_api/collection/#{@cn}/unload"
|
||||
doc = ArangoDB.log_put("#{prefix}", cmd3)
|
||||
doc.code.should eq(200)
|
||||
|
||||
# flush wal
|
||||
doc = ArangoDB.put("/_admin/wal/flush");
|
||||
doc.code.should eq(200)
|
||||
|
||||
cmd3 = "/_api/collection/#{@cn}"
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd3)
|
||||
doc.code.should eq(200)
|
||||
|
||||
while doc.parsed_response['status'] != 2
|
||||
doc = ArangoDB.get(cmd3)
|
||||
doc.code.should eq(200)
|
||||
sleep 1
|
||||
end
|
||||
|
||||
# check it again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
end
|
||||
|
||||
it "rolls back in case of violation, sparse index" do
|
||||
cmd = "/_api/index?collection=#{@cn}"
|
||||
body = "{ \"type\" : \"persistent\", \"unique\" : true, \"fields\" : [ \"a\" ], \"sparse\" : true }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create1", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.parsed_response['type'].should eq("persistent")
|
||||
doc.parsed_response['unique'].should eq(true)
|
||||
|
||||
# create a document
|
||||
cmd1 = "/_api/document?collection=#{@cn}"
|
||||
body = "{ \"a\" : 1, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
id1 = doc.parsed_response['_id']
|
||||
id1.should be_kind_of(String)
|
||||
|
||||
rev1 = doc.parsed_response['_rev']
|
||||
rev1.should be_kind_of(String)
|
||||
|
||||
# check it
|
||||
cmd2 = "/_api/document/#{id1}"
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
|
||||
# create a unique constraint violation
|
||||
body = "{ \"a\" : 1, \"b\" : 2 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create3", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(409)
|
||||
|
||||
# check it again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
|
||||
# third try (make sure the rollback has not destroyed anything)
|
||||
body = "{ \"a\" : 1, \"b\" : 3 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-create4", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(409)
|
||||
|
||||
# check it again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
|
||||
# unload collection
|
||||
cmd3 = "/_api/collection/#{@cn}/unload"
|
||||
doc = ArangoDB.log_put("#{prefix}", cmd3)
|
||||
doc.code.should eq(200)
|
||||
|
||||
# flush wal
|
||||
doc = ArangoDB.put("/_admin/wal/flush");
|
||||
doc.code.should eq(200)
|
||||
|
||||
cmd3 = "/_api/collection/#{@cn}"
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd3)
|
||||
doc.code.should eq(200)
|
||||
|
||||
while doc.parsed_response['status'] != 2
|
||||
doc = ArangoDB.get(cmd3)
|
||||
doc.code.should eq(200)
|
||||
sleep 1
|
||||
end
|
||||
|
||||
# check it again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## unique constraints during update
|
||||
################################################################################
|
||||
|
||||
context "updating documents:" do
|
||||
context "dealing with unique constraints:" do
|
||||
before do
|
||||
@cn = "UnitTestsCollectionIndexes"
|
||||
ArangoDB.drop_collection(@cn)
|
||||
@cid = ArangoDB.create_collection(@cn)
|
||||
end
|
||||
|
||||
after do
|
||||
ArangoDB.drop_collection(@cn)
|
||||
end
|
||||
|
||||
it "rolls back in case of violation" do
|
||||
cmd = "/_api/index?collection=#{@cn}"
|
||||
body = "{ \"type\" : \"persistent\", \"unique\" : true, \"fields\" : [ \"a\" ] }"
|
||||
doc = ArangoDB.log_post("#{prefix}-update1", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.parsed_response['type'].should eq("persistent")
|
||||
doc.parsed_response['unique'].should eq(true)
|
||||
|
||||
# create a document
|
||||
cmd1 = "/_api/document?collection=#{@cn}"
|
||||
body = "{ \"a\" : 1, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-update2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
id1 = doc.parsed_response['_id']
|
||||
id1.should be_kind_of(String)
|
||||
|
||||
rev1 = doc.parsed_response['_rev']
|
||||
rev1.should be_kind_of(String)
|
||||
|
||||
# check it
|
||||
cmd2 = "/_api/document/#{id1}"
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
|
||||
# create a second document
|
||||
body = "{ \"a\" : 2, \"b\" : 2 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-update3", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
id2 = doc.parsed_response['_id']
|
||||
id2.should be_kind_of(String)
|
||||
|
||||
rev2 = doc.parsed_response['_rev']
|
||||
rev2.should be_kind_of(String)
|
||||
|
||||
# create a unique constraint violation during update
|
||||
body = "{ \"a\" : 2, \"b\" : 3 }"
|
||||
doc = ArangoDB.log_put("#{prefix}", cmd2, :body => body)
|
||||
|
||||
doc.code.should eq(409)
|
||||
|
||||
# check first document again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
|
||||
rev3 = doc.parsed_response['_rev']
|
||||
rev3.should be_kind_of(String)
|
||||
|
||||
# check second document again
|
||||
cmd3 = "/_api/document/#{id2}"
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd3)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(2)
|
||||
doc.parsed_response['b'].should eq(2)
|
||||
doc.parsed_response['_id'].should eq(id2)
|
||||
doc.parsed_response['_rev'].should eq(rev2)
|
||||
|
||||
# third try (make sure the rollback has not destroyed anything)
|
||||
body = "{ \"a\" : 2, \"b\" : 4 }"
|
||||
doc = ArangoDB.log_put("#{prefix}", cmd2, :body => body)
|
||||
|
||||
doc.code.should eq(409)
|
||||
|
||||
# check the first document again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
doc.parsed_response['_rev'].should_not eq(rev2)
|
||||
|
||||
# unload collection
|
||||
cmd4 = "/_api/collection/#{@cn}/unload"
|
||||
doc = ArangoDB.log_put("#{prefix}", cmd4)
|
||||
doc.code.should eq(200)
|
||||
|
||||
# flush wal
|
||||
doc = ArangoDB.put("/_admin/wal/flush");
|
||||
doc.code.should eq(200)
|
||||
|
||||
cmd4 = "/_api/collection/#{@cn}"
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd4)
|
||||
doc.code.should eq(200)
|
||||
|
||||
while doc.parsed_response['status'] != 2
|
||||
doc = ArangoDB.get(cmd4)
|
||||
doc.code.should eq(200)
|
||||
sleep 1
|
||||
end
|
||||
|
||||
# check the first document again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
doc.parsed_response['_rev'].should_not eq(rev2)
|
||||
end
|
||||
|
||||
it "rolls back in case of violation, sparse index" do
|
||||
cmd = "/_api/index?collection=#{@cn}"
|
||||
body = "{ \"type\" : \"persistent\", \"unique\" : true, \"fields\" : [ \"a\" ], \"sparse\" : true }"
|
||||
doc = ArangoDB.log_post("#{prefix}-update1", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.parsed_response['type'].should eq("persistent")
|
||||
doc.parsed_response['unique'].should eq(true)
|
||||
|
||||
# create a document
|
||||
cmd1 = "/_api/document?collection=#{@cn}"
|
||||
body = "{ \"a\" : 1, \"b\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-update2", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
id1 = doc.parsed_response['_id']
|
||||
id1.should be_kind_of(String)
|
||||
|
||||
rev1 = doc.parsed_response['_rev']
|
||||
rev1.should be_kind_of(String)
|
||||
|
||||
# check it
|
||||
cmd2 = "/_api/document/#{id1}"
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
|
||||
# create a second document
|
||||
body = "{ \"a\" : 2, \"b\" : 2 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-update3", cmd1, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
id2 = doc.parsed_response['_id']
|
||||
id2.should be_kind_of(String)
|
||||
|
||||
rev2 = doc.parsed_response['_rev']
|
||||
rev2.should be_kind_of(String)
|
||||
|
||||
# create a unique constraint violation during update
|
||||
body = "{ \"a\" : 2, \"b\" : 3 }"
|
||||
doc = ArangoDB.log_put("#{prefix}", cmd2, :body => body)
|
||||
|
||||
doc.code.should eq(409)
|
||||
|
||||
# check first document again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
|
||||
rev3 = doc.parsed_response['_rev']
|
||||
rev3.should be_kind_of(String)
|
||||
|
||||
# check second document again
|
||||
cmd3 = "/_api/document/#{id2}"
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd3)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(2)
|
||||
doc.parsed_response['b'].should eq(2)
|
||||
doc.parsed_response['_id'].should eq(id2)
|
||||
doc.parsed_response['_rev'].should eq(rev2)
|
||||
|
||||
# third try (make sure the rollback has not destroyed anything)
|
||||
body = "{ \"a\" : 2, \"b\" : 4 }"
|
||||
doc = ArangoDB.log_put("#{prefix}", cmd2, :body => body)
|
||||
|
||||
doc.code.should eq(409)
|
||||
|
||||
# check the first document again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
doc.parsed_response['_rev'].should_not eq(rev2)
|
||||
|
||||
# unload collection
|
||||
cmd4 = "/_api/collection/#{@cn}/unload"
|
||||
doc = ArangoDB.log_put("#{prefix}", cmd4)
|
||||
doc.code.should eq(200)
|
||||
|
||||
# flush wal
|
||||
doc = ArangoDB.put("/_admin/wal/flush");
|
||||
doc.code.should eq(200)
|
||||
|
||||
cmd4 = "/_api/collection/#{@cn}"
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd4)
|
||||
doc.code.should eq(200)
|
||||
|
||||
while doc.parsed_response['status'] != 2
|
||||
doc = ArangoDB.get(cmd4)
|
||||
doc.code.should eq(200)
|
||||
sleep 1
|
||||
end
|
||||
|
||||
# check the first document again
|
||||
doc = ArangoDB.log_get("#{prefix}", cmd2)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['a'].should eq(1)
|
||||
doc.parsed_response['b'].should eq(1)
|
||||
doc.parsed_response['_id'].should eq(id1)
|
||||
doc.parsed_response['_rev'].should eq(rev1)
|
||||
doc.parsed_response['_rev'].should_not eq(rev2)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -363,7 +363,7 @@ void Agent::run() {
|
|||
while (!this->isStopping() && size() > 1) { // need only to run in multi-host
|
||||
|
||||
if (leading())
|
||||
_appendCV.wait(500000); // Only if leading
|
||||
_appendCV.wait(25000); // Only if leading
|
||||
else
|
||||
_appendCV.wait(); // Just sit there doing nothing
|
||||
|
||||
|
|
|
@ -767,7 +767,6 @@ AstNode const* ExecutionPlan::parseTraversalVertexNode(ExecutionNode* previous,
|
|||
// operand is some misc expression
|
||||
auto calc = createTemporaryCalculation(vertex, previous);
|
||||
vertex = _ast->createNodeReference(getOutVariable(calc));
|
||||
previous = calc;
|
||||
}
|
||||
|
||||
return vertex;
|
||||
|
|
|
@ -601,6 +601,10 @@ bool AgencyComm::tryInitializeStructure(std::string const& jwtSecret) {
|
|||
}
|
||||
builder.add("NumberOfCoordinators", VPackSlice::nullSlice());
|
||||
builder.add("NumberOfDBServers", VPackSlice::nullSlice());
|
||||
builder.add(VPackValue("CleanedServers"));
|
||||
{
|
||||
VPackArrayBuilder dd(&builder);
|
||||
}
|
||||
builder.add("Lock", VPackValue("UNLOCKED"));
|
||||
addEmptyVPackObject("MapLocalToID", builder);
|
||||
addEmptyVPackObject("Failed", builder);
|
||||
|
|
|
@ -258,6 +258,7 @@ void RocksDBIndex::toVelocyPackFigures(VPackBuilder& builder) const {
|
|||
|
||||
int RocksDBIndex::insert(arangodb::Transaction* trx, TRI_doc_mptr_t const* doc,
|
||||
bool) {
|
||||
auto comparator = RocksDBFeature::instance()->comparator();
|
||||
std::vector<TRI_index_element_t*> elements;
|
||||
|
||||
int res;
|
||||
|
@ -281,9 +282,18 @@ int RocksDBIndex::insert(arangodb::Transaction* trx, TRI_doc_mptr_t const* doc,
|
|||
}
|
||||
|
||||
VPackSlice const key = Transaction::extractKeyFromDocument(VPackSlice(doc->vpack()));
|
||||
std::string const prefix = buildPrefix(trx->vocbase()->_id, _collection->_info.id(), _iid);
|
||||
|
||||
VPackBuilder builder;
|
||||
std::vector<std::string> values;
|
||||
values.reserve(elements.size());
|
||||
|
||||
// lower and upper bounds, only required if the index is unique
|
||||
std::vector<std::pair<std::string, std::string>> bounds;
|
||||
if (_unique) {
|
||||
bounds.reserve(elements.size());
|
||||
}
|
||||
|
||||
for (auto& it : elements) {
|
||||
builder.clear();
|
||||
builder.openArray();
|
||||
|
@ -296,9 +306,44 @@ int RocksDBIndex::insert(arangodb::Transaction* trx, TRI_doc_mptr_t const* doc,
|
|||
VPackSlice const s = builder.slice();
|
||||
std::string value;
|
||||
value.reserve(keyPrefixSize() + s.byteSize());
|
||||
value += buildPrefix(trx->vocbase()->_id, _collection->_info.id(), _iid);
|
||||
value += prefix;
|
||||
value.append(s.startAs<char const>(), s.byteSize());
|
||||
values.emplace_back(std::move(value));
|
||||
|
||||
if (_unique) {
|
||||
builder.clear();
|
||||
builder.openArray();
|
||||
for (size_t i = 0; i < _fields.size(); ++i) {
|
||||
builder.add(it->subObjects()[i].slice(doc));
|
||||
}
|
||||
builder.add(VPackSlice::minKeySlice());
|
||||
builder.close();
|
||||
|
||||
VPackSlice s = builder.slice();
|
||||
std::string value;
|
||||
value.reserve(keyPrefixSize() + s.byteSize());
|
||||
value += prefix;
|
||||
value.append(s.startAs<char const>(), s.byteSize());
|
||||
|
||||
std::pair<std::string, std::string> p;
|
||||
p.first = value;
|
||||
|
||||
builder.clear();
|
||||
builder.openArray();
|
||||
for (size_t i = 0; i < _fields.size(); ++i) {
|
||||
builder.add(it->subObjects()[i].slice(doc));
|
||||
}
|
||||
builder.add(VPackSlice::maxKeySlice());
|
||||
builder.close();
|
||||
|
||||
s = builder.slice();
|
||||
value.clear();
|
||||
value += prefix;
|
||||
value.append(s.startAs<char const>(), s.byteSize());
|
||||
|
||||
p.second = value;
|
||||
bounds.emplace_back(std::move(p));
|
||||
}
|
||||
}
|
||||
|
||||
auto rocksTransaction = trx->rocksTransaction();
|
||||
|
@ -309,10 +354,28 @@ int RocksDBIndex::insert(arangodb::Transaction* trx, TRI_doc_mptr_t const* doc,
|
|||
size_t const count = elements.size();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (_unique) {
|
||||
std::string existing;
|
||||
auto status = rocksTransaction->Get(readOptions, values[i], &existing);
|
||||
bool uniqueConstraintViolated = false;
|
||||
auto iterator = rocksTransaction->GetIterator(readOptions);
|
||||
|
||||
if (status.ok()) {
|
||||
if (iterator != nullptr) {
|
||||
auto& bound = bounds[i];
|
||||
iterator->Seek(rocksdb::Slice(bound.first.c_str(), bound.first.size()));
|
||||
|
||||
while (iterator->Valid()) {
|
||||
int res = comparator->Compare(iterator->key(), rocksdb::Slice(bound.second.c_str(), bound.second.size()));
|
||||
|
||||
if (res > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
uniqueConstraintViolated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
delete iterator;
|
||||
}
|
||||
|
||||
if (uniqueConstraintViolated) {
|
||||
// duplicate key
|
||||
res = TRI_ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED;
|
||||
if (!_collection->useSecondaryIndexes()) {
|
||||
|
@ -324,6 +387,7 @@ int RocksDBIndex::insert(arangodb::Transaction* trx, TRI_doc_mptr_t const* doc,
|
|||
|
||||
if (res == TRI_ERROR_NO_ERROR) {
|
||||
auto status = rocksTransaction->Put(values[i], std::string());
|
||||
|
||||
if (! status.ok()) {
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ RestCursorHandler::RestCursorHandler(
|
|||
_queryRegistry(queryRegistry),
|
||||
_queryLock(),
|
||||
_query(nullptr),
|
||||
_hasStarted(false),
|
||||
_queryKilled(false) {}
|
||||
|
||||
HttpHandler::status_t RestCursorHandler::execute() {
|
||||
|
@ -235,6 +236,10 @@ void RestCursorHandler::processQuery(VPackSlice const& slice) {
|
|||
|
||||
void RestCursorHandler::registerQuery(arangodb::aql::Query* query) {
|
||||
MUTEX_LOCKER(mutexLocker, _queryLock);
|
||||
|
||||
if (_queryKilled) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_REQUEST_CANCELED);
|
||||
}
|
||||
|
||||
TRI_ASSERT(_query == nullptr);
|
||||
_query = query;
|
||||
|
@ -259,6 +264,10 @@ bool RestCursorHandler::cancelQuery() {
|
|||
|
||||
if (_query != nullptr) {
|
||||
_query->killed(true);
|
||||
_queryKilled = true;
|
||||
_hasStarted = true;
|
||||
return true;
|
||||
} else if (!_hasStarted) {
|
||||
_queryKilled = true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -151,6 +151,12 @@ class RestCursorHandler : public RestVocbaseBaseHandler {
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
arangodb::aql::Query* _query;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not the query has already started executing
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool _hasStarted;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not the query was killed
|
||||
|
|
|
@ -1045,11 +1045,14 @@ actions.defineHttp({
|
|||
if (req.requestType === actions.GET) {
|
||||
var nrCoordinators;
|
||||
var nrDBServers;
|
||||
var cleanedServers;
|
||||
try {
|
||||
nrCoordinators = ArangoAgency.get("Target/NumberOfCoordinators");
|
||||
nrCoordinators = nrCoordinators.arango.Target.NumberOfCoordinators;
|
||||
nrDBServers = ArangoAgency.get("Target/NumberOfDBServers");
|
||||
nrDBServers = nrDBServers.arango.Target.NumberOfDBServers;
|
||||
cleanedServers = ArangoAgency.get("Target/CleanedServers");
|
||||
cleanedServers = cleanedServers.arango.Target.CleanedServers;
|
||||
}
|
||||
catch (e1) {
|
||||
actions.resultError(req, res, actions.HTTP_SERVICE_UNAVAILABLE,
|
||||
|
@ -1058,7 +1061,8 @@ actions.defineHttp({
|
|||
}
|
||||
actions.resultOk(req, res, actions.HTTP_OK,
|
||||
{numberOfCoordinators: nrCoordinators,
|
||||
numberOfDBServers: nrDBServers});
|
||||
numberOfDBServers: nrDBServers,
|
||||
cleanedServers});
|
||||
} else {
|
||||
var body = actions.getJsonBody(req, res);
|
||||
if (body === undefined) {
|
||||
|
@ -1102,3 +1106,196 @@ actions.defineHttp({
|
|||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @start Docu Block JSF_postCleanOutServer
|
||||
/// (intentionally not in manual)
|
||||
/// @brief triggers activities to clean out a DBServer
|
||||
///
|
||||
/// @ RESTHEADER{POST /_admin/cluster/cleanOutServer, Trigger activities to clean out a DBServers.}
|
||||
///
|
||||
/// @ RESTQUERYPARAMETERS
|
||||
///
|
||||
/// @ RESTDESCRIPTION Triggers activities to clean out a DBServer.
|
||||
/// The body must be a JSON object with attribute "server" that is a string
|
||||
/// with the ID of the server to be cleaned out.
|
||||
///
|
||||
/// @ RESTRETURNCODES
|
||||
///
|
||||
/// @ RESTRETURNCODE{202} is returned when everything went well and the
|
||||
/// job is scheduled.
|
||||
///
|
||||
/// @ RESTRETURNCODE{400} body is not valid JSON.
|
||||
///
|
||||
/// @ RESTRETURNCODE{403} server is not a coordinator or method was not POST.
|
||||
///
|
||||
/// @ RESTRETURNCODE{503} the agency operation did not work.
|
||||
///
|
||||
/// @end Docu Block
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
actions.defineHttp({
|
||||
url: "_admin/cluster/cleanOutServer",
|
||||
allowUseDatabase: true,
|
||||
prefix: false,
|
||||
|
||||
callback: function (req, res) {
|
||||
if (!require("@arangodb/cluster").isCoordinator()) {
|
||||
actions.resultError(req, res, actions.HTTP_FORBIDDEN, 0,
|
||||
"only coordinators can serve this request");
|
||||
return;
|
||||
}
|
||||
if (req.requestType !== actions.POST) {
|
||||
actions.resultError(req, res, actions.HTTP_FORBIDDEN, 0,
|
||||
"only the POST method is allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
var timeout = 60.0;
|
||||
|
||||
// Now get to work:
|
||||
var body = actions.getJsonBody(req, res);
|
||||
if (body === undefined) {
|
||||
return;
|
||||
}
|
||||
if (typeof body !== "object" ||
|
||||
! body.hasOwnProperty("server") ||
|
||||
typeof body.server !== "string") {
|
||||
actions.resultError(req, res, actions.HTTP_BAD,
|
||||
"body must be an object with a string attribute 'server'");
|
||||
return;
|
||||
}
|
||||
var ok = true;
|
||||
try {
|
||||
var id = ArangoClusterInfo.uniqid();
|
||||
var todo = { "type": "cleanOutServer",
|
||||
"server": body.server,
|
||||
"jobId": id,
|
||||
"timeCreated": (new Date()).toISOString(),
|
||||
"creator": ArangoServerState.id() };
|
||||
ArangoAgency.set("Target/ToDo/" + id, todo);
|
||||
}
|
||||
catch (e1) {
|
||||
ok = false;
|
||||
}
|
||||
if (!ok) {
|
||||
actions.resultError(req, res, actions.HTTP_SERVICE_UNAVAILABLE,
|
||||
"Cannot write to agency.");
|
||||
return;
|
||||
}
|
||||
actions.resultOk(req, res, actions.HTTP_ACCEPTED, true);
|
||||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @start Docu Block JSF_postMoveShard
|
||||
/// (intentionally not in manual)
|
||||
/// @brief triggers activities to move a shard
|
||||
///
|
||||
/// @ RESTHEADER{POST /_admin/cluster/moveShard, Trigger activities to move a shard.}
|
||||
///
|
||||
/// @ RESTQUERYPARAMETERS
|
||||
///
|
||||
/// @ RESTDESCRIPTION Triggers activities to move a shard.
|
||||
/// The body must be a JSON document with the following attributes:
|
||||
/// - `"database"`: a string with the name of the database
|
||||
/// - `"collection"`: a string with the name of the collection
|
||||
/// - `"shard"`: a string with the name of the shard to move
|
||||
/// - `"fromServer"`: a string with the ID of a server that is currently
|
||||
/// the leader or a follower for this shard
|
||||
/// - `"toServer"`: a string with the ID of a server that is currently
|
||||
/// not the leader and not a follower for this shard
|
||||
///
|
||||
/// @ RESTRETURNCODES
|
||||
///
|
||||
/// @ RESTRETURNCODE{202} is returned when everything went well and the
|
||||
/// job is scheduled.
|
||||
///
|
||||
/// @ RESTRETURNCODE{400} body is not valid JSON.
|
||||
///
|
||||
/// @ RESTRETURNCODE{403} server is not a coordinator or method was not POST.
|
||||
///
|
||||
/// @ RESTRETURNCODE{503} the agency operation did not work.
|
||||
///
|
||||
/// @end Docu Block
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
actions.defineHttp({
|
||||
url: "_admin/cluster/moveShard",
|
||||
allowUseDatabase: true,
|
||||
prefix: false,
|
||||
|
||||
callback: function (req, res) {
|
||||
if (!require("@arangodb/cluster").isCoordinator()) {
|
||||
actions.resultError(req, res, actions.HTTP_FORBIDDEN, 0,
|
||||
"only coordinators can serve this request");
|
||||
return;
|
||||
}
|
||||
if (req.requestType !== actions.POST) {
|
||||
actions.resultError(req, res, actions.HTTP_FORBIDDEN, 0,
|
||||
"only the POST method is allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
var timeout = 60.0;
|
||||
|
||||
// Now get to work:
|
||||
var body = actions.getJsonBody(req, res);
|
||||
if (body === undefined) {
|
||||
return;
|
||||
}
|
||||
if (typeof body !== "object" ||
|
||||
! body.hasOwnProperty("database") ||
|
||||
typeof body.database !== "string" ||
|
||||
! body.hasOwnProperty("collection") ||
|
||||
typeof body.collection !== "string" ||
|
||||
! body.hasOwnProperty("shard") ||
|
||||
typeof body.shard !== "string" ||
|
||||
! body.hasOwnProperty("fromServer") ||
|
||||
typeof body.fromServer !== "string" ||
|
||||
! body.hasOwnProperty("toServer") ||
|
||||
typeof body.toServer !== "string") {
|
||||
actions.resultError(req, res, actions.HTTP_BAD,
|
||||
"body must be an object with string attributes 'database', 'collection', 'shard', 'fromServer' and 'toServer'");
|
||||
return;
|
||||
}
|
||||
var ok = true;
|
||||
var isLeader;
|
||||
try {
|
||||
var coll = ArangoClusterInfo.getCollectionInfo(body.database,
|
||||
body.collection);
|
||||
var shards = coll.shards;
|
||||
var shard = shards[body.shard];
|
||||
var pos = shard.indexOf(body.fromServer);
|
||||
if (pos === -1) {
|
||||
throw "Banana";
|
||||
} else if (pos === 0) {
|
||||
isLeader = true;
|
||||
} else {
|
||||
isLeader = false;
|
||||
}
|
||||
} catch (e2) {
|
||||
actions.resultError(req, res, actions.HTTP_BAD,
|
||||
"Combination of database, collection, shard and fromServer does not make sense.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var id = ArangoClusterInfo.uniqid();
|
||||
var todo = { "type": "moveShard",
|
||||
"database": body.database,
|
||||
"collection": body.collection,
|
||||
"shard": body.shard,
|
||||
"fromServer": body.fromServer,
|
||||
"toServer": body.toServer,
|
||||
"jobId": id,
|
||||
"timeCreated": (new Date()).toISOString(),
|
||||
"creator": ArangoServerState.id() };
|
||||
ArangoAgency.set("Target/ToDo/" + id, todo);
|
||||
} catch (e1) {
|
||||
actions.resultError(req, res, actions.HTTP_SERVICE_UNAVAILABLE,
|
||||
"Cannot write to agency.");
|
||||
return;
|
||||
}
|
||||
actions.resultOk(req, res, actions.HTTP_ACCEPTED, true);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ var actions = require("@arangodb/actions");
|
|||
|
||||
var API = "_api/index";
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief was docuBlock JSF_get_api_index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -111,10 +110,6 @@ function get_api_index (req, res) {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief was docuBlock JSF_post_api_index_cap
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief was docuBlock JSF_post_api_index_geo
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -2517,9 +2517,9 @@ if (list.length > 0) {
|
|||
<div class="<%=genClass%> mid"><%= node.role %></div>
|
||||
|
||||
<% if(node.status === 'ok') { %>
|
||||
<div class="<%= genClass %> mid"><i class="fa fa-check-circle"></i></div>
|
||||
<div class="<%= genClass %> mid state"><i class="fa fa-check-circle"></i></div>
|
||||
<% } else { %>
|
||||
<div class="<%= genClass %> mid"><i class="fa fa-exclamation-circle"></i></div>
|
||||
<div class="<%= genClass %> mid state"><i class="fa fa-exclamation-circle"></i></div>
|
||||
<% } %>
|
||||
|
||||
</div>
|
||||
|
@ -3129,4 +3129,4 @@ var cutByResolution = function (str) {
|
|||
</div>
|
||||
|
||||
<div id="workMonitorContent" class="innerContent">
|
||||
</div></script></head><body><nav class="navbar" style="display: none"><div class="primary"><div class="navlogo"><a class="logo big" href="#"><img class="arangodbLogo" src="img/arangodb_logo_big.png"></a> <a class="logo small" href="#"><img class="arangodbLogo" src="img/arangodb_logo_small.png"></a> <a class="version"><span>VERSION:</span><span id="currentVersion"></span></a></div><div class="statmenu" id="statisticBar"></div><div class="navmenu" id="navigationBar"></div></div></nav><div id="modalPlaceholder"></div><div class="bodyWrapper" style="display: none"><div class="centralRow"><div id="navbar2" class="navbarWrapper secondary"><div class="subnavmenu" id="subNavigationBar"></div></div><div class="resizecontainer contentWrapper"><div id="loadingScreen" class="loadingScreen" style="display: none"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw margin-bottom"></i> <span class="sr-only">Loading...</span></div><div id="content" class="centralContent"></div><footer class="footer"><div id="footerBar"></div></footer></div></div></div><div id="progressPlaceholder" style="display:none"></div><div id="spotlightPlaceholder" style="display:none"></div><div id="offlinePlaceholder" style="display:none"><div class="offline-div"><div class="pure-u"><div class="pure-u-1-4"></div><div class="pure-u-1-2 offline-window"><div class="offline-header"><h3>You have been disconnected from the server</h3></div><div class="offline-body"><p>The connection to the server has been lost. The server may be under heavy load.</p><p>Trying to reconnect in <span id="offlineSeconds">10</span> seconds.</p><p class="animation_state"><span><button class="button-success">Reconnect now</button></span></p></div></div><div class="pure-u-1-4"></div></div></div></div><div class="arangoFrame" style=""><div class="outerDiv"><div class="innerDiv"></div></div></div><script src="libs.js?version=1465305299972"></script><script src="app.js?version=1465305299972"></script></body></html>
|
||||
</div></script></head><body><nav class="navbar" style="display: none"><div class="primary"><div class="navlogo"><a class="logo big" href="#"><img class="arangodbLogo" src="img/arangodb_logo_big.png"></a> <a class="logo small" href="#"><img class="arangodbLogo" src="img/arangodb_logo_small.png"></a> <a class="version"><span>VERSION:</span><span id="currentVersion"></span></a></div><div class="statmenu" id="statisticBar"></div><div class="navmenu" id="navigationBar"></div></div></nav><div id="modalPlaceholder"></div><div class="bodyWrapper" style="display: none"><div class="centralRow"><div id="navbar2" class="navbarWrapper secondary"><div class="subnavmenu" id="subNavigationBar"></div></div><div class="resizecontainer contentWrapper"><div id="loadingScreen" class="loadingScreen" style="display: none"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw margin-bottom"></i> <span class="sr-only">Loading...</span></div><div id="content" class="centralContent"></div><footer class="footer"><div id="footerBar"></div></footer></div></div></div><div id="progressPlaceholder" style="display:none"></div><div id="spotlightPlaceholder" style="display:none"></div><div id="offlinePlaceholder" style="display:none"><div class="offline-div"><div class="pure-u"><div class="pure-u-1-4"></div><div class="pure-u-1-2 offline-window"><div class="offline-header"><h3>You have been disconnected from the server</h3></div><div class="offline-body"><p>The connection to the server has been lost. The server may be under heavy load.</p><p>Trying to reconnect in <span id="offlineSeconds">10</span> seconds.</p><p class="animation_state"><span><button class="button-success">Reconnect now</button></span></p></div></div><div class="pure-u-1-4"></div></div></div></div><div class="arangoFrame" style=""><div class="outerDiv"><div class="innerDiv"></div></div></div><script src="libs.js?version=1465314759214"></script><script src="app.js?version=1465314759214"></script></body></html>
|
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
|
@ -286,6 +286,11 @@
|
|||
//nav for cluster/nodes view
|
||||
buildNodesSubNav: function(type) {
|
||||
|
||||
//if nothing is set, set default to coordinator
|
||||
if (type === undefined) {
|
||||
type = 'coordinator';
|
||||
}
|
||||
|
||||
if (this.scaleability === undefined) {
|
||||
var self = this;
|
||||
|
||||
|
@ -298,7 +303,7 @@
|
|||
success: function(data) {
|
||||
if (data.numberOfCoordinators !== null && data.numberOfDBServers !== null) {
|
||||
self.scaleability = true;
|
||||
self.buildNodesSubNav();
|
||||
self.buildNodesSubNav(type);
|
||||
}
|
||||
else {
|
||||
self.scaleability = false;
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
<div class="<%=genClass%> mid"><%= node.role %></div>
|
||||
|
||||
<% if(node.status === 'ok') { %>
|
||||
<div class="<%= genClass %> mid"><i class="fa fa-check-circle"></i></div>
|
||||
<div class="<%= genClass %> mid state"><i class="fa fa-check-circle"></i></div>
|
||||
<% } else { %>
|
||||
<div class="<%= genClass %> mid"><i class="fa fa-exclamation-circle"></i></div>
|
||||
<div class="<%= genClass %> mid state"><i class="fa fa-exclamation-circle"></i></div>
|
||||
<% } %>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*jshint browser: true */
|
||||
/*jshint unused: false */
|
||||
/*global Backbone, document, templateEngine, $, arangoHelper, window*/
|
||||
/*global _, Backbone, document, templateEngine, $, arangoHelper, window*/
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
@ -61,14 +61,7 @@
|
|||
}
|
||||
}
|
||||
else {
|
||||
self.collection.fetch({
|
||||
success: function() {
|
||||
self.renderClusterState(true);
|
||||
},
|
||||
error: function() {
|
||||
self.renderClusterState(false);
|
||||
}
|
||||
});
|
||||
this.renderClusterState(isOnline);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -97,34 +90,54 @@
|
|||
},
|
||||
|
||||
renderClusterState: function(connection) {
|
||||
var error = 0;
|
||||
|
||||
if (connection) {
|
||||
$('#offlinePlaceholder').hide();
|
||||
|
||||
this.collection.each(function(value) {
|
||||
if (value.toJSON().status !== 'ok') {
|
||||
error++;
|
||||
var callbackFunction = function(data) {
|
||||
|
||||
var health = data.Health;
|
||||
|
||||
var error = 0;
|
||||
|
||||
_.each(health, function(node) {
|
||||
if (node.Status !== 'GOOD') {
|
||||
error++;
|
||||
}
|
||||
});
|
||||
|
||||
if (error > 0) {
|
||||
$('#healthStatus').removeClass('positive');
|
||||
$('#healthStatus').addClass('negative');
|
||||
if (error === 1) {
|
||||
$('.health-state').html(error + ' NODE ERROR');
|
||||
}
|
||||
else {
|
||||
$('.health-state').html(error + ' NODES ERROR');
|
||||
}
|
||||
$('.health-icon').html('<i class="fa fa-exclamation-circle"></i>');
|
||||
}
|
||||
else {
|
||||
$('#healthStatus').removeClass('negative');
|
||||
$('#healthStatus').addClass('positive');
|
||||
$('.health-state').html('NODES OK');
|
||||
$('.health-icon').html('<i class="fa fa-check-circle"></i>');
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
//check cluster state
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
cache: false,
|
||||
url: arangoHelper.databaseUrl("/_admin/cluster/health"),
|
||||
contentType: "application/json",
|
||||
processData: false,
|
||||
async: true,
|
||||
success: function(data) {
|
||||
callbackFunction(data);
|
||||
}
|
||||
});
|
||||
|
||||
if (error > 0) {
|
||||
$('#healthStatus').removeClass('positive');
|
||||
$('#healthStatus').addClass('negative');
|
||||
if (error === 1) {
|
||||
$('.health-state').html(error + ' NODE ERROR');
|
||||
}
|
||||
else {
|
||||
$('.health-state').html(error + ' NODES ERROR');
|
||||
}
|
||||
$('.health-icon').html('<i class="fa fa-exclamation-circle"></i>');
|
||||
}
|
||||
else {
|
||||
$('#healthStatus').removeClass('negative');
|
||||
$('#healthStatus').addClass('positive');
|
||||
$('.health-state').html('NODES OK');
|
||||
$('.health-icon').html('<i class="fa fa-check-circle"></i>');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$('#healthStatus').removeClass('positive');
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
},
|
||||
|
||||
initialize: function (options) {
|
||||
var self = this;
|
||||
clearInterval(this.intervalFunction);
|
||||
|
||||
if (window.App.isCluster) {
|
||||
|
@ -27,20 +28,56 @@
|
|||
//start polling with interval
|
||||
this.intervalFunction = window.setInterval(function() {
|
||||
if (window.location.hash === '#cNodes' || window.location.hash === '#dNodes' || window.location.hash === '#nodes') {
|
||||
|
||||
console.log("rerender health");
|
||||
|
||||
self.checkNodesState();
|
||||
}
|
||||
}, this.interval);
|
||||
}
|
||||
},
|
||||
|
||||
checkNodesState: function() {
|
||||
var callbackFunction = function(nodes) {
|
||||
|
||||
_.each(nodes, function(node, name) {
|
||||
_.each($('.pure-table-row'), function(element) {
|
||||
if ($(element).attr('node') === name) {
|
||||
if (node.Status === "GOOD") {
|
||||
$(element).removeClass("noHover");
|
||||
$(element).find('.state').html('<i class="fa fa-check-circle"></i>');
|
||||
}
|
||||
else {
|
||||
$(element).addClass("noHover");
|
||||
$(element).find('.state').html('<i class="fa fa-exclamation-circle"></i>');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}.bind(this);
|
||||
|
||||
//check cluster state
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
cache: false,
|
||||
url: arangoHelper.databaseUrl("/_admin/cluster/health"),
|
||||
contentType: "application/json",
|
||||
processData: false,
|
||||
async: true,
|
||||
success: function(data) {
|
||||
callbackFunction(data.Health);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
navigateToNode: function(elem) {
|
||||
|
||||
if (window.location.hash === '#dNodes') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($(elem.currentTarget).hasClass('noHover')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var name = $(elem.currentTarget).attr('node');
|
||||
window.App.navigate("#node/" + encodeURIComponent(name), {trigger: true});
|
||||
},
|
||||
|
@ -76,6 +113,7 @@
|
|||
}));
|
||||
|
||||
window.arangoHelper.buildNodesSubNav(this.toRender);
|
||||
this.checkNodesState();
|
||||
},
|
||||
|
||||
waitForCoordinators: function(callback) {
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
.cluster-nodes {
|
||||
|
||||
.pure-table-row {
|
||||
&.noHover {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
&.noHover:hover {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -79,6 +79,8 @@
|
|||
@import 'pure';
|
||||
// screen hotkeys
|
||||
@import 'hotkeys';
|
||||
// screen nodes
|
||||
@import 'nodes';
|
||||
|
||||
//arangoTable Template
|
||||
@import 'arangoTable';
|
||||
|
|
|
@ -42,8 +42,8 @@ function agencyTestSuite () {
|
|||
/// @brief the agency servers
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var agencyServers = ARGUMENTS;
|
||||
var whoseTurn = 0; // used to do round robin on agencyServers
|
||||
var agencyServers = ARGUMENTS[0].split(" ");
|
||||
var whoseTurn = 0;
|
||||
|
||||
var request = require("@arangodb/request");
|
||||
|
||||
|
@ -53,6 +53,7 @@ function agencyTestSuite () {
|
|||
var res = request({url: agencyServers[whoseTurn] + "/_api/agency/read", method: "POST",
|
||||
followRedirects: true, body: JSON.stringify(list),
|
||||
headers: {"Content-Type": "application/json"}});
|
||||
|
||||
res.bodyParsed = JSON.parse(res.body);
|
||||
return res;
|
||||
}
|
||||
|
@ -62,8 +63,10 @@ function agencyTestSuite () {
|
|||
// response:
|
||||
var res = request({url: agencyServers[whoseTurn] + "/_api/agency/write", method: "POST",
|
||||
followRedirects: true, body: JSON.stringify(list),
|
||||
headers: {"Content-Type": "application/json"}});
|
||||
headers: {"Content-Type": "application/json",
|
||||
"x-arangodb-agency-mode": "waitForCommitted"}});
|
||||
res.bodyParsed = JSON.parse(res.body);
|
||||
wait(0.1);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -100,7 +103,7 @@ function agencyTestSuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testSingleTopLevel : function () {
|
||||
wait(10);
|
||||
wait(1);
|
||||
assertEqual(readAndCheck([["/x"]]), [{}]);
|
||||
writeAndCheck([[{x:12}]]);
|
||||
assertEqual(readAndCheck([["/x"]]), [{x:12}]);
|
||||
|
|
|
@ -789,7 +789,8 @@ function processQuery (query, explain) {
|
|||
};
|
||||
|
||||
|
||||
var label = function (node) {
|
||||
var label = function (node) {
|
||||
var rc, v, e, edgeCols;
|
||||
switch (node.type) {
|
||||
case "SingletonNode":
|
||||
return keyword("ROOT");
|
||||
|
@ -832,7 +833,8 @@ function processQuery (query, explain) {
|
|||
node.minMaxDepth = node.minDepth + ".." + node.maxDepth;
|
||||
node.minMaxDepthLen = node.minMaxDepth.length;
|
||||
|
||||
var rc = keyword("FOR "), parts = [];
|
||||
rc = keyword("FOR ");
|
||||
var parts = [];
|
||||
if (node.hasOwnProperty('vertexOutVariable')) {
|
||||
parts.push(variableName(node.vertexOutVariable) + " " + annotation("/* vertex */"));
|
||||
}
|
||||
|
@ -876,9 +878,9 @@ function processQuery (query, explain) {
|
|||
node.ConditionStr = buildSimpleExpression(node.simpleExpressions);
|
||||
}
|
||||
|
||||
var e = [];
|
||||
e = [];
|
||||
if (node.hasOwnProperty('graphDefinition')) {
|
||||
var v = [];
|
||||
v = [];
|
||||
node.graphDefinition.vertexCollectionNames.forEach(function(vcn) {
|
||||
v.push(collection(vcn));
|
||||
});
|
||||
|
@ -892,7 +894,7 @@ function processQuery (query, explain) {
|
|||
node.edgeCollectionNameStrLen = node.graphDefinition.edgeCollectionNames.join(", ").length;
|
||||
}
|
||||
else {
|
||||
var edgeCols = node.graph || [ ];
|
||||
edgeCols = node.graph || [ ];
|
||||
edgeCols.forEach(function(ecn) {
|
||||
e.push(collection(ecn));
|
||||
});
|
||||
|
@ -902,16 +904,15 @@ function processQuery (query, explain) {
|
|||
}
|
||||
return rc;
|
||||
case "ShortestPathNode":
|
||||
var rc = keyword("FOR "), parts = [];
|
||||
if (node.hasOwnProperty('vertexOutVariable')) {
|
||||
parts.push(variableName(node.vertexOutVariable) + " " + annotation("/* vertex */"));
|
||||
}
|
||||
if (node.hasOwnProperty('edgeOutVariable')) {
|
||||
parts.push(variableName(node.edgeOutVariable) + " " + annotation("/* edge */"));
|
||||
}
|
||||
var translate = ["ANY", "INBOUND", "OUTBOUND"];
|
||||
var defaultDirection = node.directions[0];
|
||||
var rc = `${keyword("FOR")} ${parts.join(", ")} ${keyword("IN") } ${keyword(translate[defaultDirection])} `;
|
||||
translate = ["ANY", "INBOUND", "OUTBOUND"];
|
||||
defaultDirection = node.directions[0];
|
||||
rc = `${keyword("FOR")} ${parts.join(", ")} ${keyword("IN") } ${keyword(translate[defaultDirection])} `;
|
||||
if (node.hasOwnProperty("startVertexId")) {
|
||||
rc += `'${value(node.startVertexId)}'`;
|
||||
} else {
|
||||
|
@ -941,9 +942,9 @@ function processQuery (query, explain) {
|
|||
}
|
||||
|
||||
shortestPathDetails.push(node);
|
||||
var e = [];
|
||||
e = [];
|
||||
if (node.hasOwnProperty('graphDefinition')) {
|
||||
var v = [];
|
||||
v = [];
|
||||
node.graphDefinition.vertexCollectionNames.forEach(function(vcn) {
|
||||
v.push(collection(vcn));
|
||||
});
|
||||
|
@ -957,7 +958,7 @@ function processQuery (query, explain) {
|
|||
node.edgeCollectionNameStrLen = node.graphDefinition.edgeCollectionNames.join(", ").length;
|
||||
}
|
||||
else {
|
||||
var edgeCols = node.graph || [ ];
|
||||
edgeCols = node.graph || [ ];
|
||||
edgeCols.forEach(function(ecn) {
|
||||
e.push(collection(ecn));
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -67,7 +67,7 @@ function DatabaseSuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testVersion : function () {
|
||||
assertMatch(/(^2\.[78])|(-devel$)/, internal.db._version());
|
||||
assertMatch(/(^3\.[0])|(-devel$)/, internal.db._version());
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
/*jshint globalstrict:false, strict:false, maxlen: 500 */
|
||||
/*global assertEqual, assertTrue, fail */
|
||||
/*global assertEqual, assertTrue */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief tests for query language, graph functions
|
||||
|
|
|
@ -36,7 +36,6 @@ var helper = require("@arangodb/aql-helper");
|
|||
var cluster = require("@arangodb/cluster");
|
||||
var getQueryResults = helper.getQueryResults;
|
||||
var getRawQueryResults = helper.getRawQueryResults;
|
||||
var assertQueryError = helper.assertQueryError;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test suite for graph features
|
||||
|
@ -463,7 +462,7 @@ function ahuacatlQueryNeighborsTestSuite () {
|
|||
var v7 = "UnitTestsAhuacatlVertex/v7";
|
||||
var v8 = "UnitTestsAhuacatlVertex/v8";
|
||||
var theFox = "UnitTestsAhuacatlVertex/thefox";
|
||||
var queryStart = `FOR n IN ANY "`
|
||||
var queryStart = `FOR n IN ANY "`;
|
||||
var queryEnd = `" UnitTestsAhuacatlEdge OPTIONS {bfs: true, uniqueVertices: "global"} SORT n._id RETURN n._id`;
|
||||
var queryEndData = `" UnitTestsAhuacatlEdge OPTIONS {bfs: true, uniqueVertices: "global"} SORT n RETURN n`;
|
||||
|
||||
|
@ -518,7 +517,7 @@ function ahuacatlQueryNeighborsTestSuite () {
|
|||
var v8 = "UnitTestsAhuacatlVertex/v8";
|
||||
var theFox = "UnitTestsAhuacatlVertex/thefox";
|
||||
|
||||
var queryStart = `FOR n IN INBOUND "`
|
||||
var queryStart = `FOR n IN INBOUND "`;
|
||||
var queryEnd = `" UnitTestsAhuacatlEdge OPTIONS {bfs: true, uniqueVertices: "global"} SORT n._id RETURN n._id`;
|
||||
var queryEndData = `" UnitTestsAhuacatlEdge OPTIONS {bfs: true, uniqueVertices: "global"} SORT n RETURN n`;
|
||||
|
||||
|
@ -570,7 +569,7 @@ function ahuacatlQueryNeighborsTestSuite () {
|
|||
var v7 = "UnitTestsAhuacatlVertex/v7";
|
||||
var v8 = "UnitTestsAhuacatlVertex/v8";
|
||||
var theFox = "UnitTestsAhuacatlVertex/thefox";
|
||||
var queryStart = `FOR n IN OUTBOUND "`
|
||||
var queryStart = `FOR n IN OUTBOUND "`;
|
||||
var queryEnd = `" UnitTestsAhuacatlEdge OPTIONS {bfs: true, uniqueVertices: "global"} SORT n._id RETURN n._id`;
|
||||
var queryEndData = `" UnitTestsAhuacatlEdge OPTIONS {bfs: true, uniqueVertices: "global"} SORT n RETURN n`;
|
||||
|
||||
|
@ -656,8 +655,6 @@ function ahuacatlQueryShortestPathTestSuite () {
|
|||
var vertexCollection;
|
||||
var edgeCollection;
|
||||
|
||||
var aqlfunctions = require("@arangodb/aql/functions");
|
||||
|
||||
return {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -510,6 +510,9 @@ def restheader(cargo, r=Regexen()):
|
|||
(fp, last) = cargo
|
||||
|
||||
temp = parameters(last).split(',')
|
||||
if temp == "":
|
||||
raise Exception("Invalid restheader value. got empty string. Maybe missing closing bracket? " + path)
|
||||
|
||||
(ucmethod, path) = temp[0].split()
|
||||
|
||||
#TODO: hier checken, ob der letzte alles hatte (responses)
|
||||
|
@ -528,7 +531,7 @@ def restheader(cargo, r=Regexen()):
|
|||
raise Exception("Duplicate route")
|
||||
|
||||
if currentDocuBlock == None:
|
||||
raise Exception("No docublock started for this restheader: " + ucmethod + " " + path )
|
||||
raise Exception("No docublock started for this restheader: " + ucmethod + " " + path)
|
||||
|
||||
if lastDocuBlock != None and currentDocuBlock == lastDocuBlock:
|
||||
raise Exception("No new docublock started for this restheader: " + ucmethod + " " + path + ' : ' + currentDocuBlock)
|
||||
|
|
Loading…
Reference in New Issue