1
0
Fork 0

Merge branch 'devel' into HLC

This commit is contained in:
Max Neunhoeffer 2016-07-12 22:51:40 +02:00
commit 18925faf8f
16 changed files with 216 additions and 50 deletions

View File

@ -23,12 +23,24 @@ devel
* When disabling Foxx development mode the setup script is now re-run
* Foxx now provides an easy way to directly serve GraphQL requests using the
`@arangodb/foxx/graphql` module and the bundled `graphql-sync` dependency
v3.0.3 (XXXX-XX-XX)
-------------------
* fixed AQL MERGE() function with External objects originating from traversals
* fixed some logfile recovery errors with error message "document not found"
* fixed issue #1937
* fixed issue #1936
* improved performance of arangorestore in clusters with synchronous
replication
v3.0.2 (2016-07-09)
-------------------
@ -442,9 +454,25 @@ v3.0.0-rc1 (2015-06-10)
using a backwards-compatible "legacy mode"
v2.8.10 (XXXX-XX-XX)
v2.8.11 (XXXX-XX-XX)
--------------------
* fixed issue #1937
v2.8.10 (2016-07-01)
--------------------
* make sure next local _rev value used for a document is at least as high as the
_rev value supplied by external sources such as replication
* make adding a collection in both read- and write-mode to a transaction behave as
expected (write includes read). This prevents the `unregister collection used in
transaction` error
* fixed sometimes invalid result for `byExample(...).count()` when an index plus
post-filtering was used
* fixed "collection is a nullptr" issue when starting a traversal from a transaction
* honor the value of startup option `--database.wait-for-sync` (that is used to control

View File

@ -0,0 +1,105 @@
!CHAPTER Using GraphQL in Foxx
`const createGraphQLRouter = require('@arangodb/foxx/graphql');`
Foxx bundles the [`graphql-sync` module](https://github.com/arangodb/graphql-sync), which is a synchronous wrapper for the official JavaScript GraphQL reference implementation, to allow writing GraphQL schemas directly inside Foxx.
Additionally the `@arangodb/foxx/graphql` lets you create routers for serving GraphQL requests, which closely mimicks the behaviour of the [`express-graphql` module](https://github.com/graphql/express-graphql).
For more information on `graphql-sync` see the [`graphql-js` API reference](http://graphql.org/docs/api-reference-graphql/) (note that `graphql-sync` always uses raw values instead of wrapping them in promises).
For an example of a GraphQL schema in Foxx that resolves fields using the database see [the GraphQL example service](https://github.com/arangodb-foxx/demo-graphql) (also available from the Foxx store).
**Examples**
```js
const graphql = require('graphql-sync');
const graphqlSchema = new graphql.GraphQLSchema({
// ...
});
// Mounting a graphql endpoint directly in a service:
module.context.use('/graphql', createGraphQLRouter({
schema: graphqlSchema,
graphiql: true
}));
// Or at the service's root URL:
module.context.use(createGraphQLRouter({
schema: graphqlSchema,
graphiql: true
}));
// Or inside an existing router:
router.get('/hello', function (req, res) {
res.write('Hello world!');
});
router.use('/graphql', createGraphQLRouter({
schema: graphqlSchema,
graphiql: true
}));
```
!SECTION Creating a router
`createGraphQLRouter(options): Router`
This returns a new router object with POST and GET routes for serving GraphQL requests.
**Arguments**
* **options**: `object`
An object with any of the following properties:
* **schema**: `GraphQLSchema`
A GraphQL Schema object from `graphql-sync`.
* **context**: `any` (optional)
The GraphQL context that will be passed to the `graphql()` function from `graphql-sync` to handle GraphQL queries.
* **rootValue**: `object` (optional)
The GraphQL root value that will be passed to the `graphql()` function from `graphql-sync` to handle GraphQL queries.
* **pretty**: `boolean` (Default: `false`)
If `true`, JSON responses will be pretty-printed.
* **formatError**: `Function` (optional)
A function that will be used to format errors produced by `graphql-sync`. If omitted, the `formatError` function from `graphql-sync` will be used instead.
* **validationRules**: `Array<any>` (optional)
Additional validation rules queries must satisfy in addition to those defined in the GraphQL spec.
* **graphiql**: `boolean` (Default: `false`)
If `true`, the [GraphiQL](https://github.com/graphql/graphiql) explorer will be served when loaded directly from a browser.
If a GraphQL Schema object is passed instead of an options object it will be interpreted as the *schema* option.
!SECTION Generated routes
The router handles GET and POST requests to its root path and accepts the following parameters, which can be provided either as query parameters or as the POST request body:
* **query**: `string`
A GraphQL query that will be executed.
* **variables**: `object | string` (optional)
An object or a string containing a JSON object with runtime values to use for any GraphQL query variables.
* **operationName**: `string` (optional)
If the provided `query` contains multiple named operations, this specifies which operation should be executed.
* **raw**: `boolean` (Default: `false`)
Forces a JSON response even if *graphiql* is enabled and the request was made using a browser.
The POST request body can be provided as JSON or as query string using `application/x-www-form-urlencoded`. A request body passed as `application/graphql` will be interpreted as the `query` parameter.

View File

@ -74,6 +74,7 @@
* [Middleware](Foxx/Router/Middleware.md)
* [Request](Foxx/Router/Request.md)
* [Response](Foxx/Router/Response.md)
* [Using GraphQL](Foxx/GraphQL.md)
* [Sessions middleware](Foxx/Sessions/README.md)
* [Session storages](Foxx/Sessions/Storages/README.md)
* [Collection storage](Foxx/Sessions/Storages/Collection.md)

View File

@ -525,7 +525,7 @@ void Constituent::run() {
} else if (_role == CANDIDATE) {
callElection(); // Run for office
} else {
int32_t left = 100000.0 * config().minPing;
int32_t left = static_cast<int32_t>(100000.0 * config().minPing);
long rand_wait = static_cast<long>(left);
{
CONDITION_LOCKER(guardv, _cv);

View File

@ -2365,7 +2365,7 @@ void AstNode::stringify(arangodb::basics::StringBuffer* buffer, bool verbose,
auto returnNode = getMember(4);
if (returnNode != nullptr && returnNode != Ast::getNodeNop()) {
buffer->appendText(TRI_CHAR_LENGTH_PAIR(" RETURN "));
returnNode->getMember(0)->stringify(buffer, verbose, failIfLong);
returnNode->stringify(buffer, verbose, failIfLong);
}
buffer->appendChar(')');

View File

@ -254,13 +254,12 @@ bool SkiplistLookupBuilder::next() {
return false;
}
SkiplistInLookupBuilder::SkiplistInLookupBuilder(
Transaction* trx,
std::vector<std::vector<arangodb::aql::AstNode const*>>& ops,
arangodb::aql::Variable const* var, bool reverse) : BaseSkiplistLookupBuilder(trx), _dataBuilder(trx) {
arangodb::aql::Variable const* var, bool reverse) : BaseSkiplistLookupBuilder(trx), _dataBuilder(trx), _done(false) {
TRI_ASSERT(!ops.empty()); // We certainly do not need IN here
VPackBuilder tmp;
TransactionBuilderLeaser tmp(trx);
std::set<VPackSlice, arangodb::basics::VelocyPackHelper::VPackSorted<true>>
unique_set(
(arangodb::basics::VelocyPackHelper::VPackSorted<true>(reverse)));
@ -293,10 +292,10 @@ SkiplistInLookupBuilder::SkiplistInLookupBuilder(
} else {
// Case: x.a IN value
TRI_ASSERT(value->numMembers() > 0);
tmp.clear();
tmp->clear();
unique_set.clear();
value->toVelocyPackValue(tmp);
for (auto const& it : VPackArrayIterator(tmp.slice())) {
value->toVelocyPackValue(*(tmp.get()));
for (auto const& it : VPackArrayIterator(tmp->slice())) {
unique_set.emplace(it);
}
_inPositions.emplace_back(i, 0, unique_set.size());
@ -366,10 +365,10 @@ SkiplistInLookupBuilder::SkiplistInLookupBuilder(
TRI_ASSERT(upper == nullptr);
TRI_ASSERT(lower == nullptr);
TRI_ASSERT(value->numMembers() > 0);
tmp.clear();
tmp->clear();
unique_set.clear();
value->toVelocyPackValue(tmp);
for (auto const& it : VPackArrayIterator(tmp.slice())) {
value->toVelocyPackValue(*(tmp.get()));
for (auto const& it : VPackArrayIterator(tmp->slice())) {
unique_set.emplace(it);
}
_inPositions.emplace_back(ops.size() - 1, 0, unique_set.size());
@ -380,6 +379,7 @@ SkiplistInLookupBuilder::SkiplistInLookupBuilder(
_dataBuilder->close();
_isEquality = true;
_dataBuilder->close();
buildSearchValues();
return;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_EQ:
@ -388,6 +388,7 @@ SkiplistInLookupBuilder::SkiplistInLookupBuilder(
value->toVelocyPackValue(*(_dataBuilder.get()));
_isEquality = true;
_dataBuilder->close();
buildSearchValues();
return;
default:
@ -408,11 +409,12 @@ SkiplistInLookupBuilder::SkiplistInLookupBuilder(
}
_dataBuilder->close();
_dataBuilder->close();
buildSearchValues();
}
bool SkiplistInLookupBuilder::next() {
if (!forwardInPosition()) {
if (_done || !forwardInPosition()) {
return false;
}
buildSearchValues();
@ -423,14 +425,15 @@ bool SkiplistInLookupBuilder::forwardInPosition() {
std::list<PosStruct>::reverse_iterator it = _inPositions.rbegin();
while (it != _inPositions.rend()) {
it->current++;
TRI_ASSERT(it->max > 0);
if (it->current < it->max) {
TRI_ASSERT(it->_max > 0);
if (it->current < it->_max) {
return true;
// Okay we increased this, next search value;
}
it->current = 0;
it++;
}
_done = true;
// If we get here all positions are reset to 0.
// We are done, no further combination
return false;
@ -529,7 +532,6 @@ TRI_doc_mptr_t* SkiplistIterator::next() {
return tmp->document()->document();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Checks if the interval is valid. It is declared invalid if
/// one border is nullptr or the right is lower than left.
@ -553,14 +555,12 @@ bool SkiplistIterator2::intervalValid(Node* left, Node* right) const {
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Reset the cursor
////////////////////////////////////////////////////////////////////////////////
void SkiplistIterator2::reset() {
// If _interals is empty at this point
// If _intervals is empty at this point
// the cursor does not contain any
// document at all. Reset is pointless
if (!_intervals.empty()) {
@ -686,7 +686,6 @@ void SkiplistIterator2::initNextInterval() {
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create the skiplist index
////////////////////////////////////////////////////////////////////////////////

View File

@ -60,7 +60,7 @@ class BaseSkiplistLookupBuilder {
arangodb::velocypack::Slice _upperSlice;
public:
BaseSkiplistLookupBuilder(Transaction* trx) :
explicit BaseSkiplistLookupBuilder(Transaction* trx) :
_lowerBuilder(trx), _upperBuilder(trx)
{
_isEquality = true;
@ -127,9 +127,9 @@ class SkiplistInLookupBuilder : public BaseSkiplistLookupBuilder {
struct PosStruct {
size_t field;
size_t current;
size_t max;
size_t _max; // thanks, windows.h!
PosStruct(size_t f, size_t c, size_t m) : field(f), current(c), max(m) {}
PosStruct(size_t f, size_t c, size_t m) : field(f), current(c), _max(m) {}
};
TransactionBuilderLeaser _dataBuilder;
@ -137,6 +137,8 @@ class SkiplistInLookupBuilder : public BaseSkiplistLookupBuilder {
/// values. (field, inPosition, maxPosition)
std::list<PosStruct> _inPositions;
bool _done;
public:
SkiplistInLookupBuilder(
Transaction* trx,

View File

@ -279,7 +279,7 @@ int TraditionalKeyGenerator::validate(std::string const& key, bool isRestore) {
/// @brief track usage of a key
////////////////////////////////////////////////////////////////////////////////
void TraditionalKeyGenerator::track(std::string const&) {}
void TraditionalKeyGenerator::track(char const*, VPackValueLength) {}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a VPack representation of the generator
@ -398,9 +398,9 @@ int AutoIncrementKeyGenerator::validate(std::string const& key,
/// @brief track usage of a key
////////////////////////////////////////////////////////////////////////////////
void AutoIncrementKeyGenerator::track(std::string const& key) {
void AutoIncrementKeyGenerator::track(char const* p, VPackValueLength length) {
// check the numeric key part
uint64_t value = StringUtils::uint64(key);
uint64_t value = StringUtils::uint64(p, length);
if (value > _lastValue) {
// and update our last value

View File

@ -104,7 +104,7 @@ class KeyGenerator {
/// @brief track usage of a key
//////////////////////////////////////////////////////////////////////////////
virtual void track(std::string const&) = 0;
virtual void track(char const*, VPackValueLength) = 0;
//////////////////////////////////////////////////////////////////////////////
/// @brief return a VelocyPack representation of the generator
@ -176,7 +176,7 @@ class TraditionalKeyGenerator : public KeyGenerator {
/// @brief track usage of a key
//////////////////////////////////////////////////////////////////////////////
void track(std::string const&) override;
void track(char const*, VPackValueLength) override final;
//////////////////////////////////////////////////////////////////////////////
/// @brief return the generator name (must be lowercase)
@ -229,7 +229,7 @@ class AutoIncrementKeyGenerator : public KeyGenerator {
/// @brief track usage of a key
//////////////////////////////////////////////////////////////////////////////
void track(std::string const&) override;
void track(char const*, VPackValueLength) override final;
//////////////////////////////////////////////////////////////////////////////
/// @brief return the generator name (must be lowercase)

View File

@ -276,7 +276,7 @@ class Traverser {
/// @brief Constructor. This is an abstract only class.
//////////////////////////////////////////////////////////////////////////////
Traverser(TraverserOptions& opts)
explicit Traverser(TraverserOptions& opts)
: _readDocuments(0),
_filteredPaths(0),
_pruneNext(false),

View File

@ -794,7 +794,9 @@ static int OpenIteratorHandleDocumentMarker(TRI_df_marker_t const* marker,
Transaction::extractKeyAndRevFromDocument(slice, keySlice, revisionId);
SetRevision(document, revisionId, false);
document->_keyGenerator->track(keySlice.copyString());
VPackValueLength length;
char const* p = keySlice.getString(length);
document->_keyGenerator->track(p, length);
++state->_documents;
@ -891,7 +893,9 @@ static int OpenIteratorHandleDeletionMarker(TRI_df_marker_t const* marker,
Transaction::extractKeyAndRevFromDocument(slice, keySlice, revisionId);
document->setLastRevision(revisionId, false);
document->_keyGenerator->track(keySlice.copyString());
VPackValueLength length;
char const* p = keySlice.getString(length);
document->_keyGenerator->track(p, length);
++state->_deletions;

View File

@ -486,6 +486,7 @@ bool RecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* data,
options.recoveryMarker = envelope;
options.isRestore = true;
options.waitForSync = false;
options.ignoreRevs = true;
// try an insert first
TRI_ASSERT(VPackSlice(ptr).isObject());

View File

@ -22,14 +22,6 @@
// / @author Alan Plum
// //////////////////////////////////////////////////////////////////////////////
// * schema: Object the schema
// * context: ?mixed graphql context
// * rootValue: ?Object graphql rootValue
// * pretty: ?boolean prettier output
// * formatError: ?Function formatError for graphql errors
// * validationRules: ?Array<any> additional validation rules???
// * graphiql: ?boolean enable graphiql
const dd = require('dedent');
const assert = require('assert');
const joi = require('joi');

View File

@ -705,6 +705,45 @@ function arrayAccessTestSuite () {
var expected = [ 4, 8, 12 ];
var result = AQL_EXECUTE("RETURN @value[**** FILTER CURRENT % 2 == 0 LIMIT 3 RETURN CURRENT * 2]", { value : data }).json;
assertEqual([ expected ], result);
},
testCollapseWithData : function () {
var data = [ {
"last": "2016-07-12",
"name": "a1",
"st": [ {
"last": "2016-07-12",
"name": "a11",
"id": "a2",
"dx": [ {
"last": "2016-07-12",
"name": "a21",
"id": "a3",
"docs": []
}, {
"last": "2016-07-12",
"name": "a22",
"id": "a4",
"docs": []
} ]
} ]
} ];
var expected = [
{
"st" : [
{ "id" : "a2", "date" : "2016-07-12" }
],
"dx" : [
{ "id" : "a3", "date" : "2016-07-12" },
{ "id" : "a4", "date" : "2016-07-12" }
],
"docs" : [ ]
}
];
var result = AQL_EXECUTE("FOR c IN @value COLLECT st = c.st[* RETURN { id: CURRENT.id, date: CURRENT.last }], dx = c.st[*].dx[* RETURN { id: CURRENT.id, date: CURRENT.last }][**], docs = c.st[*].dx[*][**].docs[* RETURN { id: CURRENT.id, date: CURRENT.last }][**] RETURN { st , dx, docs }", { value : data }).json;
assertEqual(expected, result);
}
};

View File

@ -993,23 +993,18 @@ bool TRI_fsync(int fd) {
char* TRI_SlurpFile(TRI_memory_zone_t* zone, char const* filename,
size_t* length) {
TRI_string_buffer_t result;
int fd;
fd = TRI_OPEN(filename, O_RDONLY | TRI_O_CLOEXEC);
int fd = TRI_OPEN(filename, O_RDONLY | TRI_O_CLOEXEC);
if (fd == -1) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
return nullptr;
}
TRI_string_buffer_t result;
TRI_InitStringBuffer(&result, zone);
while (true) {
int res;
ssize_t n;
res = TRI_ReserveStringBuffer(&result, READBUFFER_SIZE);
int res = TRI_ReserveStringBuffer(&result, READBUFFER_SIZE);
if (res != TRI_ERROR_NO_ERROR) {
TRI_CLOSE(fd);
@ -1019,7 +1014,7 @@ char* TRI_SlurpFile(TRI_memory_zone_t* zone, char const* filename,
return nullptr;
}
n = TRI_READ(fd, (void*)TRI_EndStringBuffer(&result), READBUFFER_SIZE);
ssize_t n = TRI_READ(fd, (void*)TRI_EndStringBuffer(&result), READBUFFER_SIZE);
if (n == 0) {
break;

View File

@ -205,7 +205,7 @@ void Logger::log(char const* function, char const* file, long int line,
std::string const& message) {
#ifdef _WIN32
if (level == LogLevel::FATAL || level == LogLevel::ERR) {
if (ArangoGlobalContext::CONTEXT->useEventLog()) {
if (ArangoGlobalContext::CONTEXT != nullptr && ArangoGlobalContext::CONTEXT->useEventLog()) {
TRI_LogWindowsEventlog(function, file, line, message);
}
}