1
0
Fork 0
arangodb/3rdParty/velocypack/src/Collection.cpp

413 lines
9.7 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @brief Library to build up VPack documents.
///
/// DISCLAIMER
///
/// Copyright 2015 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Max Neunhoeffer
/// @author Jan Steemann
/// @author Copyright 2015, ArangoDB GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include <unordered_map>
#include "velocypack/velocypack-common.h"
#include "velocypack/Collection.h"
#include "velocypack/Iterator.h"
#include "velocypack/Slice.h"
#include "velocypack/Value.h"
#include "velocypack/ValueType.h"
using namespace arangodb::velocypack;
// convert a vector of strings into an unordered_set of strings
static inline std::unordered_set<std::string> ToSet (std::vector<std::string> const& keys) {
std::unordered_set<std::string> s;
for (auto const& it : keys) {
s.emplace(it);
}
return s;
}
void Collection::forEach (Slice const& slice, std::function<bool(Slice const&, ValueLength)> const& cb) {
ArrayIterator it(slice);
ValueLength index = 0;
while (it.valid()) {
if (! cb(it.value(), index)) {
// abort
return;
}
it.next();
++index;
}
}
Builder Collection::filter (Slice const& slice, std::function<bool(Slice const&, ValueLength)> const& cb) {
// construct a new Array
Builder b;
b.add(Value(ValueType::Array));
ArrayIterator it(slice);
ValueLength index = 0;
while (it.valid()) {
Slice s = it.value();
if (cb(s, index)) {
b.add(s);
}
it.next();
++index;
}
b.close();
return b;
}
Slice Collection::find (Slice const& slice, std::function<bool(Slice const&, ValueLength)> const& cb) {
ArrayIterator it(slice);
ValueLength index = 0;
while (it.valid()) {
Slice s = it.value();
if (cb(s, index)) {
return s;
}
it.next();
++index;
}
return Slice();
}
bool Collection::contains (Slice const& slice, std::function<bool(Slice const&, ValueLength)> const& cb) {
ArrayIterator it(slice);
ValueLength index = 0;
while (it.valid()) {
Slice s = it.value();
if (cb(s, index)) {
return true;
}
it.next();
++index;
}
return false;
}
bool Collection::all (Slice const& slice, std::function<bool(Slice const&, ValueLength)> const& cb) {
ArrayIterator it(slice);
ValueLength index = 0;
while (it.valid()) {
Slice s = it.value();
if (! cb(s, index)) {
return false;
}
it.next();
++index;
}
return true;
}
bool Collection::any (Slice const& slice, std::function<bool(Slice const&, ValueLength)> const& cb) {
ArrayIterator it(slice);
ValueLength index = 0;
while (it.valid()) {
Slice s = it.value();
if (cb(s, index)) {
return true;
}
it.next();
++index;
}
return false;
}
std::vector<std::string> Collection::keys (Slice const& slice) {
std::vector<std::string> result;
keys(slice, result);
return result;
}
void Collection::keys (Slice const& slice, std::vector<std::string>& result) {
// pre-allocate result vector
result.reserve(slice.length());
ObjectIterator it(slice);
while (it.valid()) {
result.emplace_back(std::move(it.key().copyString()));
it.next();
}
}
void Collection::keys (Slice const& slice, std::unordered_set<std::string>& result) {
ObjectIterator it(slice);
while (it.valid()) {
result.emplace(std::move(it.key().copyString()));
it.next();
}
}
Builder Collection::values (Slice const& slice) {
Builder b;
b.add(Value(ValueType::Array));
ObjectIterator it(slice);
while (it.valid()) {
b.add(it.value());
it.next();
}
b.close();
return b;
}
Builder Collection::keep (Slice const& slice, std::vector<std::string> const& keys) {
// check if there are so many keys that we want to use the hash-based version
// cut-off values are arbitrary...
if (keys.size() >= 4 && slice.length() > 10) {
return keep(slice, ToSet(keys));
}
Builder b;
b.add(Value(ValueType::Object));
ObjectIterator it(slice);
while (it.valid()) {
auto key = std::move(it.key().copyString());
if (std::find(keys.begin(), keys.end(), key) != keys.end()) {
b.add(key, it.value());
}
it.next();
}
b.close();
return b;
}
Builder Collection::keep (Slice const& slice, std::unordered_set<std::string> const& keys) {
Builder b;
b.add(Value(ValueType::Object));
ObjectIterator it(slice);
while (it.valid()) {
auto key = std::move(it.key().copyString());
if (keys.find(key) != keys.end()) {
b.add(key, it.value());
}
it.next();
}
b.close();
return b;
}
Builder Collection::remove (Slice const& slice, std::vector<std::string> const& keys) {
// check if there are so many keys that we want to use the hash-based version
// cut-off values are arbitrary...
if (keys.size() >= 4 && slice.length() > 10) {
return remove(slice, ToSet(keys));
}
Builder b;
b.add(Value(ValueType::Object));
ObjectIterator it(slice);
while (it.valid()) {
auto key = std::move(it.key().copyString());
if (std::find(keys.begin(), keys.end(), key) == keys.end()) {
b.add(key, it.value());
}
it.next();
}
b.close();
return b;
}
Builder Collection::remove (Slice const& slice, std::unordered_set<std::string> const& keys) {
Builder b;
b.add(Value(ValueType::Object));
ObjectIterator it(slice);
while (it.valid()) {
auto key = std::move(it.key().copyString());
if (keys.find(key) == keys.end()) {
b.add(key, it.value());
}
it.next();
}
b.close();
return b;
}
Builder Collection::merge (Slice const& left, Slice const& right, bool mergeValues) {
if (! left.isObject() || ! right.isObject()) {
throw Exception(Exception::InvalidValueType, "Expecting type Object");
}
Builder b;
b.add(Value(ValueType::Object));
std::unordered_map<std::string, Slice> rightValues;
{
ObjectIterator it(right);
while (it.valid()) {
rightValues.emplace(std::move(it.key().copyString()), it.value());
it.next();
}
}
{
ObjectIterator it(left);
while (it.valid()) {
auto key = std::move(it.key().copyString());
auto found = rightValues.find(key);
if (found == rightValues.end()) {
// use left value
b.add(key, it.value());
}
else if (mergeValues && it.value().isObject() && (*found).second.isObject()) {
// merge both values
Builder sub = Collection::merge(it.value(), (*found).second, true);
b.add(key, sub.slice());
}
else {
// use right value
b.add(key, (*found).second);
// clear the value in the map so its not added again
(*found).second = Slice();
}
it.next();
}
}
// add remaining values that were only in right
for (auto& it : rightValues) {
auto s = it.second;
if (! s.isNone()) {
b.add(std::move(it.first), it.second);
}
}
b.close();
return b;
}
template<Collection::VisitationOrder order>
static bool doVisit (Slice const& slice, std::function<bool(Slice const& key, Slice const& value)> const& func);
template<Collection::VisitationOrder order>
static bool visitObject (Slice const& value, std::function<bool(Slice const& key, Slice const& value)> const& func) {
ObjectIterator it(value);
while (it.valid()) {
// sub-object?
Slice v = it.value();
bool const isCompound = (v.isObject() || v.isArray());
if (isCompound && order == Collection::PreOrder) {
if (! doVisit<order>(v, func)) {
return false;
}
}
if (! func(it.key(), v)) {
return false;
}
if (isCompound && order == Collection::PostOrder) {
if (! doVisit<order>(v, func)) {
return false;
}
}
it.next();
}
return true;
}
template<Collection::VisitationOrder order>
static bool visitArray (Slice const& value, std::function<bool(Slice const& key, Slice const& value)> const& func) {
ArrayIterator it(value);
while (it.valid()) {
// sub-object?
Slice v = it.value();
bool const isCompound = (v.isObject() || v.isArray());
if (isCompound && order == Collection::PreOrder) {
if (! doVisit<order>(v, func)) {
return false;
}
}
if (! func(Slice(), v)) {
return false;
}
if (isCompound && order == Collection::PostOrder) {
if (! doVisit<order>(v, func)) {
return false;
}
}
it.next();
}
return true;
}
template<Collection::VisitationOrder order>
static bool doVisit (Slice const& slice, std::function<bool(Slice const& key, Slice const& value)> const& func) {
if (slice.isObject()) {
return visitObject<order>(slice, func);
}
if (slice.isArray()) {
return visitArray<order>(slice, func);
}
throw Exception(Exception::InvalidValueType, "Expecting type Object or Array");
}
void Collection::visitRecursive (Slice const& slice, Collection::VisitationOrder order, std::function<bool(Slice const&, Slice const&)> const& func) {
if (order == Collection::PreOrder) {
doVisit<Collection::PreOrder>(slice, func);
}
else {
doVisit<Collection::PostOrder>(slice, func);
}
}