mirror of https://gitee.com/bigwinds/arangodb
First go at babies variant of replace. RestHandler case missing.
This commit is contained in:
parent
298c96f2cc
commit
2cfa417858
|
@ -990,9 +990,10 @@ OperationResult Transaction::replace(std::string const& collectionName,
|
|||
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
|
||||
}
|
||||
|
||||
if (oldValue.isArray() || newValue.isArray()) {
|
||||
// multi-document variant is not yet implemented
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
|
||||
if ((!oldValue.isArray() ^ !newValue.isArray()) ||
|
||||
(oldValue.isArray() && oldValue.length() != newValue.length())) {
|
||||
// must provide two lists or two single documents
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
|
||||
}
|
||||
|
||||
OperationOptions optionsCopy = options;
|
||||
|
@ -1014,6 +1015,10 @@ OperationResult Transaction::replaceCoordinator(std::string const& collectionNam
|
|||
VPackSlice const& oldValue,
|
||||
VPackSlice const& newValue,
|
||||
OperationOptions& options) {
|
||||
if (oldValue.isArray()) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
|
||||
}
|
||||
|
||||
auto headers = std::make_unique<std::map<std::string, std::string>>();
|
||||
arangodb::rest::HttpResponse::HttpResponseCode responseCode;
|
||||
std::map<std::string, std::string> resultHeaders;
|
||||
|
@ -1078,19 +1083,34 @@ OperationResult Transaction::replaceLocal(std::string const& collectionName,
|
|||
return OperationResult(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
// read key and expected revision
|
||||
std::string const key(Transaction::extractKey(&oldValue));
|
||||
TRI_voc_rid_t const expectedRevision = Transaction::extractRevisionId(&oldValue);
|
||||
|
||||
// generate a new tick value
|
||||
TRI_voc_tick_t const revisionId = TRI_NewTickServer();
|
||||
// TODO: clean this up
|
||||
TRI_document_collection_t* document = documentCollection(trxCollection(cid));
|
||||
|
||||
// Replace is a read and a write, let's get the write lock already
|
||||
// for the read operation:
|
||||
int res = lock(trxCollection(cid), TRI_TRANSACTION_WRITE);
|
||||
|
||||
VPackBuilder builder;
|
||||
builder.openObject();
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return OperationResult(res);
|
||||
}
|
||||
|
||||
VPackBuilder builder; // used repeatedly in closure
|
||||
VPackBuilder resultBuilder; // building the complete result
|
||||
|
||||
auto workForOneDocument = [&](VPackSlice const oldVal,
|
||||
VPackSlice const newVal) -> int {
|
||||
|
||||
// read key and expected revision
|
||||
std::string const key(Transaction::extractKey(&oldVal));
|
||||
TRI_voc_rid_t const expectedRevision
|
||||
= Transaction::extractRevisionId(&oldVal);
|
||||
|
||||
// generate a new tick value
|
||||
TRI_voc_tick_t const revisionId = TRI_NewTickServer();
|
||||
|
||||
builder.clear();
|
||||
{
|
||||
VPackObjectBuilder b(&builder);
|
||||
VPackObjectIterator it(newValue);
|
||||
while (it.valid()) {
|
||||
// let all but the system attributes pass
|
||||
|
@ -1107,48 +1127,56 @@ OperationResult Transaction::replaceLocal(std::string const& collectionName,
|
|||
}
|
||||
// finally add the (new) _rev attributes
|
||||
builder.add(TRI_VOC_ATTRIBUTE_REV, VPackValue(std::to_string(revisionId)));
|
||||
builder.close();
|
||||
}
|
||||
|
||||
VPackSlice sanitized = builder.slice();
|
||||
|
||||
if (orderDitch(trxCollection(cid)) == nullptr) {
|
||||
return OperationResult(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
TRI_doc_mptr_t mptr;
|
||||
TRI_voc_rid_t actualRevision = 0;
|
||||
TRI_doc_update_policy_t policy(expectedRevision == 0 ? TRI_DOC_UPDATE_LAST_WRITE : TRI_DOC_UPDATE_ERROR, expectedRevision, &actualRevision);
|
||||
TRI_doc_update_policy_t policy(expectedRevision == 0 ?
|
||||
TRI_DOC_UPDATE_LAST_WRITE :
|
||||
TRI_DOC_UPDATE_ERROR,
|
||||
expectedRevision, &actualRevision);
|
||||
|
||||
int res = lock(trxCollection(cid), TRI_TRANSACTION_WRITE);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return OperationResult(res);
|
||||
}
|
||||
|
||||
res = document->replace(this, &oldValue, &sanitized, &mptr, &policy, options, !isLocked(document, TRI_TRANSACTION_WRITE));
|
||||
res = document->replace(this, &oldValue, &sanitized, &mptr, &policy,
|
||||
options, !isLocked(document, TRI_TRANSACTION_WRITE));
|
||||
|
||||
if (res == TRI_ERROR_ARANGO_CONFLICT) {
|
||||
// still return
|
||||
VPackBuilder resultBuilder;
|
||||
buildDocumentIdentity(resultBuilder, cid, key, mptr.revisionId(), "");
|
||||
|
||||
return OperationResult(resultBuilder.steal(), nullptr, "",
|
||||
TRI_ERROR_ARANGO_CONFLICT,
|
||||
options.waitForSync || document->_info.waitForSync());
|
||||
return TRI_ERROR_ARANGO_CONFLICT;
|
||||
} else if (res != TRI_ERROR_NO_ERROR) {
|
||||
return OperationResult(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
TRI_ASSERT(mptr.getDataPtr() != nullptr);
|
||||
|
||||
if (options.silent) {
|
||||
return OperationResult(TRI_ERROR_NO_ERROR);
|
||||
}
|
||||
|
||||
VPackBuilder resultBuilder;
|
||||
if (!options.silent) {
|
||||
buildDocumentIdentity(resultBuilder, cid, key, std::to_string(revisionId), std::to_string(actualRevision));
|
||||
}
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
};
|
||||
|
||||
return OperationResult(resultBuilder.steal(), nullptr, "", TRI_ERROR_NO_ERROR,
|
||||
res = TRI_ERROR_NO_ERROR;
|
||||
|
||||
if (oldValue.isArray()) {
|
||||
// We already know that both oldValue and newValue are arrays and that
|
||||
// they have equal length!
|
||||
VPackArrayBuilder b(&resultBuilder);
|
||||
VPackArrayIterator oIt(oldValue);
|
||||
VPackArrayIterator nIt(newValue);
|
||||
while (oIt.valid() && nIt.valid()) {
|
||||
res = workForOneDocument(oIt.value(), nIt.value());
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
break;
|
||||
}
|
||||
++oIt;
|
||||
++nIt;
|
||||
}
|
||||
} else {
|
||||
res = workForOneDocument(oldValue, newValue);
|
||||
}
|
||||
return OperationResult(resultBuilder.steal(), nullptr, "", res,
|
||||
options.waitForSync);
|
||||
}
|
||||
|
||||
|
|
|
@ -379,14 +379,28 @@ static void ReplaceVocbaseCol(bool useCollection,
|
|||
|
||||
if (argLength < 2) {
|
||||
TRI_V8_THROW_EXCEPTION_USAGE(
|
||||
"replace(<document>, <data>, {overwrite: booleanValue, waitForSync: "
|
||||
"replace(<document(s)>, <data>, {overwrite: booleanValue, waitForSync: "
|
||||
"booleanValue})");
|
||||
}
|
||||
|
||||
// we're only accepting "real" object documents
|
||||
if (!args[1]->IsObject() || args[1]->IsArray()) {
|
||||
// we're only accepting "real" object documents or arrays of such, if this
|
||||
// is the collection method
|
||||
if (!args[1]->IsObject()) {
|
||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
|
||||
}
|
||||
if (args[1]->IsArray() && !useCollection) {
|
||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
|
||||
}
|
||||
if (args[0]->IsArray() ^ args[1]->IsArray()) {
|
||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
|
||||
}
|
||||
if (args[0]->IsArray()) { // then both are arrays, check equal length
|
||||
auto a = v8::Local<v8::Array>::Cast(args[0]);
|
||||
auto b = v8::Local<v8::Array>::Cast(args[1]);
|
||||
if (a->Length() != b->Length()) {
|
||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
if (args.Length() > 2) {
|
||||
if (args[2]->IsObject()) {
|
||||
|
@ -435,54 +449,84 @@ static void ReplaceVocbaseCol(bool useCollection,
|
|||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!args[1]->IsObject() || args[1]->IsArray()) {
|
||||
// we're only accepting "real" object documents
|
||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
|
||||
}
|
||||
|
||||
VPackBuilder builder;
|
||||
std::string collectionName;
|
||||
|
||||
auto transactionContext = std::make_shared<V8TransactionContext>(vocbase, true);
|
||||
|
||||
VPackBuilder builder; // to build the search value
|
||||
std::string collectionName;
|
||||
|
||||
if (!useCollection) { // the db._replace case
|
||||
int res = ParseDocumentOrDocumentHandle(
|
||||
isolate, vocbase, transactionContext->getResolver(), col, collectionName, builder,
|
||||
!overwrite, args[0]);
|
||||
|
||||
LocalCollectionGuard g(useCollection ? nullptr
|
||||
: const_cast<TRI_vocbase_col_t*>(col));
|
||||
|
||||
isolate, vocbase, transactionContext->getResolver(), col,
|
||||
collectionName, builder, !overwrite, args[0]);
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_THROW_EXCEPTION(res);
|
||||
}
|
||||
}
|
||||
|
||||
// In case of useCollection we already have a collection object and
|
||||
// do not need to free it later.
|
||||
LocalCollectionGuard g(useCollection ? nullptr
|
||||
: const_cast<TRI_vocbase_col_t*>(col));
|
||||
|
||||
TRI_ASSERT(col != nullptr);
|
||||
|
||||
TRI_ASSERT(!collectionName.empty());
|
||||
VPackSlice search = builder.slice();
|
||||
TRI_ASSERT(search.isObject());
|
||||
|
||||
SingleCollectionTransaction trx(transactionContext, collectionName, TRI_TRANSACTION_WRITE);
|
||||
// In either case we now have a collection object col
|
||||
|
||||
SingleCollectionTransaction trx(transactionContext, collectionName,
|
||||
TRI_TRANSACTION_WRITE);
|
||||
if (!useCollection || !args[0]->IsArray()) {
|
||||
trx.addHint(TRI_TRANSACTION_HINT_SINGLE_OPERATION, false);
|
||||
}
|
||||
|
||||
res = trx.begin();
|
||||
|
||||
int res = trx.begin();
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_THROW_EXCEPTION(res);
|
||||
}
|
||||
|
||||
VPackBuilder updateBuilder;
|
||||
res = TRI_V8ToVPack(isolate, updateBuilder, args[1], false);
|
||||
|
||||
auto workOnOneSearchVal = [&](v8::Local<v8::Value> const searchVal) {
|
||||
std::string collName;
|
||||
if (!ExtractDocumentHandle(isolate, searchVal, collName, builder, false)) {
|
||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD);
|
||||
}
|
||||
};
|
||||
|
||||
auto workOnOneDocument = [&](v8::Local<v8::Value> const newVal) {
|
||||
auto savePos = updateBuilder.buffer()->size();
|
||||
int res = TRI_V8ToVPack(isolate, updateBuilder, newVal, false);
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_THROW_EXCEPTION(res);
|
||||
}
|
||||
|
||||
VPackSlice update = updateBuilder.slice();
|
||||
|
||||
if (update.isNone()) {
|
||||
if (savePos == updateBuilder.buffer()->size()) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_errno(), "<data> is no valid JSON");
|
||||
}
|
||||
};
|
||||
|
||||
// In the !useCollection case the search value is already prepared, we
|
||||
// only have to set up the single replacement document:
|
||||
if (!useCollection) {
|
||||
workOnOneDocument(args[1]);
|
||||
} else if (args[0]->IsArray()) {
|
||||
// we deal with the single document case:
|
||||
workOnOneSearchVal(args[0]);
|
||||
workOnOneDocument(args[1]);
|
||||
} else { // finally, the array case, note that we already know that the two
|
||||
// arrays have equal length!
|
||||
VPackArrayBuilder b1(&builder);
|
||||
VPackArrayBuilder b2(&updateBuilder);
|
||||
auto searchVals = v8::Local<v8::Array>::Cast(args[0]);
|
||||
auto documents = v8::Local<v8::Array>::Cast(args[1]);
|
||||
for (uint32_t i = 0; i < searchVals->Length(); i++) {
|
||||
workOnOneSearchVal(searchVals->Get(i));
|
||||
workOnOneDocument(documents->Get(i));
|
||||
}
|
||||
}
|
||||
|
||||
VPackSlice const search = builder.slice();
|
||||
VPackSlice const update = updateBuilder.slice();
|
||||
|
||||
OperationResult opResult = trx.replace(collectionName, search, update, options);
|
||||
|
||||
|
|
Loading…
Reference in New Issue