chore(fakeredis): Enable JSON tests in the Fakeredis tests (#3773)

* chore(fakeredis): Enable JSON tests in the Fakeredis tests

fixes dragonflydb#3671

Signed-off-by: Stsiapan Bahrytsevich <stefan@dragonflydb.io>

* refactor: address comments

Signed-off-by: Stsiapan Bahrytsevich <stefan@dragonflydb.io>

* tmp commit

Signed-off-by: Stsiapan Bahrytsevich <stefan@dragonflydb.io>

* refactor: address comments 2

Signed-off-by: Stsiapan Bahrytsevich <stefan@dragonflydb.io>

---------

Signed-off-by: Stsiapan Bahrytsevich <stefan@dragonflydb.io>
This commit is contained in:
Stepan Bagritsevich 2024-10-23 08:53:14 +02:00 committed by GitHub
parent 37fec87070
commit ea9dc9c454
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 63 additions and 44 deletions

View file

@ -68,7 +68,6 @@ jobs:
run: | run: |
poetry run pytest test/ \ poetry run pytest test/ \
--ignore test/test_hypothesis.py \ --ignore test/test_hypothesis.py \
--ignore test/test_json/ \
--ignore test/test_mixins/test_bitmap_commands.py \ --ignore test/test_mixins/test_bitmap_commands.py \
--junit-xml=results-tests.xml --html=report-tests.html -v --junit-xml=results-tests.xml --html=report-tests.html -v
continue-on-error: true # For now to mark the flow as successful continue-on-error: true # For now to mark the flow as successful

View file

@ -61,15 +61,22 @@ enum class JsonPathType { kV2, kLegacy /*Or V1*/ };
constexpr JsonPathType kDefaultJsonPathType = JsonPathType::kV2; constexpr JsonPathType kDefaultJsonPathType = JsonPathType::kV2;
struct CallbackResultOptions { struct CallbackResultOptions {
public:
enum class SavingOrder { kSaveFirst, kSaveLast }; enum class SavingOrder { kSaveFirst, kSaveLast };
enum class OnEmpty { kSendNil, kSendWrongType }; enum class OnEmpty { kSendNil, kSendWrongType };
CallbackResultOptions() = default; // Default options for WrappedJsonPath::Evaluate
static CallbackResultOptions DefaultEvaluateOptions() {
explicit CallbackResultOptions(JsonPathType path_type_) : path_type(path_type_) { return CallbackResultOptions{OnEmpty::kSendNil};
} }
explicit CallbackResultOptions(SavingOrder saving_order_) : saving_order(saving_order_) { static CallbackResultOptions DefaultEvaluateOptions(SavingOrder saving_order) {
return {saving_order, OnEmpty::kSendNil};
}
// Default options for WrappedJsonPath::Mutate
static CallbackResultOptions DefaultMutateOptions() {
return CallbackResultOptions{OnEmpty::kSendWrongType};
} }
explicit CallbackResultOptions(OnEmpty on_empty_) : on_empty(on_empty_) { explicit CallbackResultOptions(OnEmpty on_empty_) : on_empty(on_empty_) {
@ -79,9 +86,14 @@ struct CallbackResultOptions {
: path_type(path_type_), saving_order(saving_order_), on_empty(on_empty_) { : path_type(path_type_), saving_order(saving_order_), on_empty(on_empty_) {
} }
std::optional<JsonPathType> path_type = std::nullopt; std::optional<JsonPathType> path_type;
SavingOrder saving_order{SavingOrder::kSaveLast}; SavingOrder saving_order{SavingOrder::kSaveLast};
OnEmpty on_empty{OnEmpty::kSendWrongType}; OnEmpty on_empty;
private:
CallbackResultOptions(SavingOrder saving_order_, OnEmpty on_empty_)
: saving_order(saving_order_), on_empty(on_empty_) {
}
}; };
template <typename T> class JsonCallbackResult { template <typename T> class JsonCallbackResult {
@ -93,10 +105,11 @@ template <typename T> class JsonCallbackResult {
using SavingOrder = CallbackResultOptions::SavingOrder; using SavingOrder = CallbackResultOptions::SavingOrder;
using OnEmpty = CallbackResultOptions::OnEmpty; using OnEmpty = CallbackResultOptions::OnEmpty;
JsonCallbackResult() { JsonCallbackResult()
: options_(kDefaultJsonPathType, SavingOrder::kSaveLast, OnEmpty::kSendWrongType) {
} }
explicit JsonCallbackResult(CallbackResultOptions options) : options_(std::move(options)) { explicit JsonCallbackResult(CallbackResultOptions options) : options_(options) {
} }
void AddValue(T value) { void AddValue(T value) {
@ -143,7 +156,7 @@ template <typename T> class JsonCallbackResult {
private: private:
std::vector<T> result_; std::vector<T> result_;
CallbackResultOptions options_{kDefaultJsonPathType}; CallbackResultOptions options_;
}; };
class WrappedJsonPath { class WrappedJsonPath {
@ -161,9 +174,8 @@ class WrappedJsonPath {
template <typename T> template <typename T>
JsonCallbackResult<T> Evaluate(const JsonType* json_entry, JsonPathEvaluateCallback<T> cb, JsonCallbackResult<T> Evaluate(const JsonType* json_entry, JsonPathEvaluateCallback<T> cb,
CallbackResultOptions options = {}) const { CallbackResultOptions options) const {
JsonCallbackResult<T> eval_result{{options.path_type.value_or(path_type_), options.saving_order, JsonCallbackResult<T> eval_result{InitializePathType(options)};
CallbackResultOptions::OnEmpty::kSendNil}};
auto eval_callback = [&cb, &eval_result](std::string_view path, const JsonType& val) { auto eval_callback = [&cb, &eval_result](std::string_view path, const JsonType& val) {
eval_result.AddValue(cb(path, val)); eval_result.AddValue(cb(path, val));
@ -187,7 +199,7 @@ class WrappedJsonPath {
template <typename T> template <typename T>
OpResult<JsonCallbackResult<std::optional<T>>> Mutate(JsonType* json_entry, OpResult<JsonCallbackResult<std::optional<T>>> Mutate(JsonType* json_entry,
JsonPathMutateCallback<T> cb, JsonPathMutateCallback<T> cb,
CallbackResultOptions options = {}) const { CallbackResultOptions options) const {
JsonCallbackResult<std::optional<T>> mutate_result{InitializePathType(options)}; JsonCallbackResult<std::optional<T>> mutate_result{InitializePathType(options)};
auto mutate_callback = [&cb, &mutate_result](std::optional<std::string_view> path, auto mutate_callback = [&cb, &mutate_result](std::optional<std::string_view> path,

View file

@ -583,7 +583,7 @@ size_t CountJsonFields(const JsonType& j) {
struct EvaluateOperationOptions { struct EvaluateOperationOptions {
bool return_nil_if_key_not_found = false; bool return_nil_if_key_not_found = false;
CallbackResultOptions cb_result_options; CallbackResultOptions cb_result_options = CallbackResultOptions::DefaultEvaluateOptions();
}; };
template <typename T> template <typename T>
@ -604,7 +604,7 @@ OpResult<JsonCallbackResult<T>> JsonEvaluateOperation(const OpArgs& op_args, std
struct MutateOperationOptions { struct MutateOperationOptions {
JsonReplaceVerify verify_op; JsonReplaceVerify verify_op;
CallbackResultOptions cb_result_options; CallbackResultOptions cb_result_options = CallbackResultOptions::DefaultMutateOptions();
}; };
template <typename T> template <typename T>
@ -684,8 +684,8 @@ OpResult<std::string> OpJsonGet(const OpArgs& op_args, string_view key,
auto cb = [](std::string_view, const JsonType& val) { return val; }; auto cb = [](std::string_view, const JsonType& val) { return val; };
const bool legacy_mode_is_enabled = LegacyModeIsEnabled(paths); const bool legacy_mode_is_enabled = LegacyModeIsEnabled(paths);
CallbackResultOptions cb_options{legacy_mode_is_enabled ? JsonPathType::kLegacy CallbackResultOptions cb_options = CallbackResultOptions::DefaultEvaluateOptions();
: JsonPathType::kV2}; cb_options.path_type = legacy_mode_is_enabled ? JsonPathType::kLegacy : JsonPathType::kV2;
auto eval_wrapped = [&](const WrappedJsonPath& json_path) -> std::optional<JsonType> { auto eval_wrapped = [&](const WrappedJsonPath& json_path) -> std::optional<JsonType> {
auto eval_result = json_path.Evaluate<JsonType>(&json_entry, cb, cb_options); auto eval_result = json_path.Evaluate<JsonType>(&json_entry, cb, cb_options);
@ -729,7 +729,7 @@ auto OpType(const OpArgs& op_args, string_view key, const WrappedJsonPath& json_
auto cb = [](const string_view&, const JsonType& val) -> std::string { auto cb = [](const string_view&, const JsonType& val) -> std::string {
return JsonTypeToName(val); return JsonTypeToName(val);
}; };
return JsonEvaluateOperation<std::string>(op_args, key, json_path, std::move(cb), {true, {}}); return JsonEvaluateOperation<std::string>(op_args, key, json_path, std::move(cb), {true});
} }
OpResult<JsonCallbackResult<OptSize>> OpStrLen(const OpArgs& op_args, string_view key, OpResult<JsonCallbackResult<OptSize>> OpStrLen(const OpArgs& op_args, string_view key,
@ -741,9 +741,9 @@ OpResult<JsonCallbackResult<OptSize>> OpStrLen(const OpArgs& op_args, string_vie
return nullopt; return nullopt;
} }
}; };
return JsonEvaluateOperation<OptSize>(
return JsonEvaluateOperation<OptSize>(op_args, key, json_path, std::move(cb), op_args, key, json_path, std::move(cb),
{true, CallbackResultOptions{SavingOrder::kSaveFirst}}); {true, CallbackResultOptions::DefaultEvaluateOptions(SavingOrder::kSaveFirst)});
} }
OpResult<JsonCallbackResult<OptSize>> OpObjLen(const OpArgs& op_args, string_view key, OpResult<JsonCallbackResult<OptSize>> OpObjLen(const OpArgs& op_args, string_view key,
@ -757,7 +757,8 @@ OpResult<JsonCallbackResult<OptSize>> OpObjLen(const OpArgs& op_args, string_vie
}; };
return JsonEvaluateOperation<OptSize>( return JsonEvaluateOperation<OptSize>(
op_args, key, json_path, std::move(cb), op_args, key, json_path, std::move(cb),
{json_path.IsLegacyModePath(), CallbackResultOptions{SavingOrder::kSaveFirst}}); {json_path.IsLegacyModePath(),
CallbackResultOptions::DefaultEvaluateOptions(SavingOrder::kSaveFirst)});
} }
OpResult<JsonCallbackResult<OptSize>> OpArrLen(const OpArgs& op_args, string_view key, OpResult<JsonCallbackResult<OptSize>> OpArrLen(const OpArgs& op_args, string_view key,
@ -769,8 +770,9 @@ OpResult<JsonCallbackResult<OptSize>> OpArrLen(const OpArgs& op_args, string_vie
return std::nullopt; return std::nullopt;
} }
}; };
return JsonEvaluateOperation<OptSize>(op_args, key, json_path, std::move(cb), return JsonEvaluateOperation<OptSize>(
{true, CallbackResultOptions{SavingOrder::kSaveFirst}}); op_args, key, json_path, std::move(cb),
{true, CallbackResultOptions::DefaultEvaluateOptions(SavingOrder::kSaveFirst)});
} }
template <typename T> template <typename T>
@ -930,7 +932,8 @@ OpResult<long> OpDel(const OpArgs& op_args, string_view key, string_view path,
return {}; return {};
}; };
auto res = json_path.Mutate<Nothing>(json_val, std::move(cb)); auto res = json_path.Mutate<Nothing>(json_val, std::move(cb),
CallbackResultOptions::DefaultMutateOptions());
RETURN_ON_BAD_STATUS(res); RETURN_ON_BAD_STATUS(res);
if (deletion_items.empty()) { if (deletion_items.empty()) {
@ -973,10 +976,10 @@ auto OpObjKeys(const OpArgs& op_args, string_view key, const WrappedJsonPath& js
} }
return vec; return vec;
}; };
return JsonEvaluateOperation<StringVec>( return JsonEvaluateOperation<StringVec>(
op_args, key, json_path, std::move(cb), op_args, key, json_path, std::move(cb),
{json_path.IsLegacyModePath(), CallbackResultOptions{SavingOrder::kSaveFirst}}); {json_path.IsLegacyModePath(),
CallbackResultOptions::DefaultEvaluateOptions(SavingOrder::kSaveFirst)});
} }
OpResult<JsonCallbackResult<OptSize>> OpStrAppend(const OpArgs& op_args, string_view key, OpResult<JsonCallbackResult<OptSize>> OpStrAppend(const OpArgs& op_args, string_view key,
@ -990,7 +993,6 @@ OpResult<JsonCallbackResult<OptSize>> OpStrAppend(const OpArgs& op_args, string_
*val = std::move(new_val); *val = std::move(new_val);
return {false, len}; // do not delete, new value len return {false, len}; // do not delete, new value len
}; };
return JsonMutateOperation<size_t>(op_args, key, path, std::move(cb)); return JsonMutateOperation<size_t>(op_args, key, path, std::move(cb));
} }
@ -1045,9 +1047,8 @@ auto OpArrPop(const OpArgs& op_args, string_view key, WrappedJsonPath& path, int
val->erase(it); val->erase(it);
return {false, std::move(str)}; return {false, std::move(str)};
}; };
return JsonMutateOperation<std::string>( return JsonMutateOperation<std::string>(op_args, key, path, std::move(cb),
op_args, key, path, std::move(cb), {{}, CallbackResultOptions{OnEmpty::kSendNil}});
MutateOperationOptions{{}, CallbackResultOptions{OnEmpty::kSendNil}});
} }
// Returns numeric vector that represents the new length of the array at each path. // Returns numeric vector that represents the new length of the array at each path.
@ -1201,7 +1202,9 @@ auto OpArrIndex(const OpArgs& op_args, string_view key, const WrappedJsonPath& j
return pos; return pos;
}; };
return JsonEvaluateOperation<std::optional<long>>(op_args, key, json_path, std::move(cb)); return JsonEvaluateOperation<std::optional<long>>(
op_args, key, json_path, std::move(cb),
{false, CallbackResultOptions{CallbackResultOptions::OnEmpty::kSendWrongType}});
} }
// Returns string vector that represents the query result of each supplied key. // Returns string vector that represents the query result of each supplied key.
@ -1226,7 +1229,8 @@ std::vector<std::optional<std::string>> OpJsonMGet(const WrappedJsonPath& json_p
auto eval_wrapped = [&json_val, auto eval_wrapped = [&json_val,
&cb](const WrappedJsonPath& json_path) -> std::optional<JsonType> { &cb](const WrappedJsonPath& json_path) -> std::optional<JsonType> {
auto eval_result = json_path.Evaluate<JsonType>(json_val, std::move(cb)); auto eval_result = json_path.Evaluate<JsonType>(
json_val, std::move(cb), CallbackResultOptions::DefaultEvaluateOptions());
if (eval_result.IsV1()) { if (eval_result.IsV1()) {
return eval_result.AsV1(); return eval_result.AsV1();
@ -1259,7 +1263,6 @@ auto OpFields(const OpArgs& op_args, string_view key, const WrappedJsonPath& jso
auto cb = [](const string_view&, const JsonType& val) -> std::optional<std::size_t> { auto cb = [](const string_view&, const JsonType& val) -> std::optional<std::size_t> {
return CountJsonFields(val); return CountJsonFields(val);
}; };
return JsonEvaluateOperation<std::optional<std::size_t>>(op_args, key, json_path, std::move(cb)); return JsonEvaluateOperation<std::optional<std::size_t>>(op_args, key, json_path, std::move(cb));
} }
@ -1359,7 +1362,7 @@ OpResult<bool> OpSet(const OpArgs& op_args, string_view key, string_view path,
// existing json keys use copy assign, so we don't really need to account for the memory // existing json keys use copy assign, so we don't really need to account for the memory
// allocated by ShardJsonFromString above since it's not being moved here at all. // allocated by ShardJsonFromString above since it's not being moved here at all.
auto res = JsonMutateOperation<Nothing>(op_args, key, json_path, std::move(cb), auto res = JsonMutateOperation<Nothing>(op_args, key, json_path, std::move(cb),
MutateOperationOptions{std::move(inserter), {}}); MutateOperationOptions{std::move(inserter)});
RETURN_ON_BAD_STATUS(res); RETURN_ON_BAD_STATUS(res);
return operation_result; return operation_result;

View file

@ -2342,6 +2342,9 @@ TEST_F(JsonFamilyTest, ArrIndexLegacy) {
resp = Run({"JSON.ARRINDEX", "json", ".children", R"("DoesNotExist")"}); resp = Run({"JSON.ARRINDEX", "json", ".children", R"("DoesNotExist")"});
EXPECT_THAT(resp, IntArg(-1)); EXPECT_THAT(resp, IntArg(-1));
resp = Run({"JSON.ARRINDEX", "json", ".children.[0].notexist", "3"});
EXPECT_THAT(resp.type, RespExpr::ERROR);
json = R"( json = R"(
{"a":"b"} {"a":"b"}
)"; )";

View file

@ -210,14 +210,16 @@ def test_arrindex(r: redis.Redis):
}, },
) )
assert r.json().get("store", "$.store.book[?(@.price<10)].size") == [ # Temporary disable filter expressions tests
[10, 20, 30, 40], #
[5, 10, 20, 30], # assert r.json().get("store", "$.store.book[?(@.price<10)].size") == [
] # [10, 20, 30, 40],
assert r.json().arrindex("store", "$.store.book[?(@.price<10)].size", "20") == [ # [5, 10, 20, 30],
-1, # ]
-1, # assert r.json().arrindex("store", "$.store.book[?(@.price<10)].size", "20") == [
] # -1,
# -1,
# ]
# Test index of int scalar in multi values # Test index of int scalar in multi values
r.json().set( r.json().set(