Skip to content

Commit

Permalink
fix: fixes
Browse files Browse the repository at this point in the history
Signed-off-by: Vladislav Oleshko <vlad@dragonflydb.io>
  • Loading branch information
dranikpg committed May 6, 2023
1 parent a733349 commit 2859d92
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 91 deletions.
19 changes: 8 additions & 11 deletions src/core/search/ast_expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ AstTermNode::AstTermNode(std::string term)
: term_{move(term)}, pattern_{"\\b" + term_ + "\\b", std::regex::icase} {
}

bool AstTermNode::Check(SearchInput* input) const {
return input->Check([this](string_view str) {
bool AstTermNode::Check(SearchInput input) const {
return input.Check([this](string_view str) {
return regex_search(str.begin(), str.begin() + str.size(), pattern_);
});
}
Expand All @@ -27,15 +27,15 @@ string AstTermNode::Debug() const {
return "term{" + term_ + "}";
}

bool AstNegateNode::Check(SearchInput* input) const {
bool AstNegateNode::Check(SearchInput input) const {
return !node_->Check(input);
}

string AstNegateNode::Debug() const {
return "not{" + node_->Debug() + "}";
}

bool AstLogicalNode::Check(SearchInput* input) const {
bool AstLogicalNode::Check(SearchInput input) const {
return op_ == kOr ? (l_->Check(input) || r_->Check(input))
: (l_->Check(input) && r_->Check(input));
}
Expand All @@ -45,19 +45,16 @@ string AstLogicalNode::Debug() const {
return op + "{" + l_->Debug() + "," + r_->Debug() + "}";
}

bool AstFieldNode::Check(SearchInput* input) const {
input->SelectField(field_);
bool res = node_->Check(input);
input->ClearField();
return res;
bool AstFieldNode::Check(SearchInput input) const {
return node_->Check(SearchInput{input, field_});
}

string AstFieldNode::Debug() const {
return "field:" + field_ + "{" + node_->Debug() + "}";
}

bool AstRangeNode::Check(SearchInput* input) const {
return input->Check([this](string_view str) {
bool AstRangeNode::Check(SearchInput input) const {
return input.Check([this](string_view str) {
int64_t v;
if (!absl::SimpleAtoi(str, &v))
return false;
Expand Down
28 changes: 19 additions & 9 deletions src/core/search/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,31 @@

namespace dfly::search {

// Represents input to a search tree.
struct SearchInput {
// Interface for accessing hashset values with different data structures underneath.
struct HSetAccessor {
// Callback that's supplied with field values.
using FieldConsumer = std::function<bool(std::string_view)>;

virtual ~SearchInput() = default;
virtual bool Check(FieldConsumer f, std::string_view active_field) const = 0;
};

// Wrapper around hashset accessor and optional active field.
struct SearchInput {
SearchInput(const HSetAccessor* hset, std::string_view active_field = {})
: hset_{hset}, active_field_{active_field} {
}

// Check if the given callback returns true on any of the active fields.
virtual bool Check(FieldConsumer) = 0;
SearchInput(const SearchInput& base, std::string_view active_field)
: hset_{base.hset_}, active_field_{active_field} {
}

// Sets a single active field.
virtual void SelectField(std::string_view field) = 0;
bool Check(HSetAccessor::FieldConsumer f) {
return hset_->Check(move(f), active_field_);
}

// Removes current single active field, all fields are active.
virtual void ClearField() = 0;
private:
const HSetAccessor* hset_;
std::string_view active_field_;
};

} // namespace dfly::search
134 changes: 63 additions & 71 deletions src/core/search/search_parser_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class SearchParserTest : public ::testing::Test {
expr_ = query_driver_.Get();
}

bool Check(SearchInput* input) const {
bool Check(SearchInput input) const {
return expr_->Check(input);
}

Expand All @@ -52,22 +52,17 @@ class SearchParserTest : public ::testing::Test {
QueryDriver query_driver_;
};

class MockedSearchInput : public SearchInput {
class MockedHSetAccessor : public HSetAccessor {
public:
using Map = std::unordered_map<std::string, std::string>;

MockedSearchInput() = default;
MockedSearchInput(std::string test_field) : hset_{{"field", test_field}} {
MockedHSetAccessor() = default;
MockedHSetAccessor(std::string test_field) : hset_{{"field", test_field}} {
}

MockedSearchInput& operator=(Map hset) {
hset_ = move(hset);
return *this;
}

bool Check(FieldConsumer f) override {
if (!active_field_.empty()) {
auto it = hset_.find(active_field_);
bool Check(HSetAccessor::FieldConsumer f, string_view active_field) const override {
if (!active_field.empty()) {
auto it = hset_.find(string{active_field});
return f(it != hset_.end() ? it->second : "");
} else {
for (const auto& [k, v] : hset_) {
Expand All @@ -78,17 +73,11 @@ class MockedSearchInput : public SearchInput {
}
}

void SelectField(std::string_view field) override {
CHECK(active_field_.empty());
active_field_ = field;
}

void ClearField() override {
active_field_.clear();
void Set(Map hset) {
hset_ = hset;
}

private:
std::string active_field_{};
Map hset_{};
};

Expand Down Expand Up @@ -116,20 +105,20 @@ class MockedSearchInput : public SearchInput {
ASSERT_TRUE(caught); \
}

#define CHECK_ALL(...) \
{ \
for (auto str : {__VA_ARGS__}) { \
MockedSearchInput input{str}; \
EXPECT_TRUE(Check(&input)) << str << " failed on " << DebugExpr(); \
} \
#define CHECK_ALL(...) \
{ \
for (auto str : {__VA_ARGS__}) { \
MockedHSetAccessor hset{str}; \
EXPECT_TRUE(Check(SearchInput{&hset})) << str << " failed on " << DebugExpr(); \
} \
}

#define CHECK_NONE(...) \
{ \
for (auto str : {__VA_ARGS__}) { \
MockedSearchInput input{str}; \
EXPECT_FALSE(Check(&input)) << str << " failed on " << DebugExpr(); \
} \
#define CHECK_NONE(...) \
{ \
for (auto str : {__VA_ARGS__}) { \
MockedHSetAccessor hset{str}; \
EXPECT_FALSE(Check(SearchInput{&hset})) << str << " failed on " << DebugExpr(); \
} \
}

TEST_F(SearchParserTest, Scanner) {
Expand Down Expand Up @@ -249,75 +238,78 @@ TEST_F(SearchParserTest, CheckParenthesisPriority) {
TEST_F(SearchParserTest, MatchField) {
ParseExpr("@f1:foo @f2:bar @f3:baz");

MockedSearchInput input{};
MockedHSetAccessor hset{};
SearchInput input{&hset};

input = {{"f1", "foo"}, {"f2", "bar"}, {"f3", "baz"}};
EXPECT_TRUE(Check(&input));
hset.Set({{"f1", "foo"}, {"f2", "bar"}, {"f3", "baz"}});
EXPECT_TRUE(Check(input));

input = {{"f1", "foo"}, {"f2", "bar"}, {"f3", "last is wrong"}};
EXPECT_FALSE(Check(&input));
hset.Set({{"f1", "foo"}, {"f2", "bar"}, {"f3", "last is wrong"}});
EXPECT_FALSE(Check(input));

input = {{"f1", "its"}, {"f2", "totally"}, {"f3", "wrong"}};
EXPECT_FALSE(Check(&input));
hset.Set({{"f1", "its"}, {"f2", "totally"}, {"f3", "wrong"}});
EXPECT_FALSE(Check(input));

input = {{"f1", "im foo but its only me and"}, {"f2", "bar"}};
EXPECT_FALSE(Check(&input));
hset.Set({{"f1", "im foo but its only me and"}, {"f2", "bar"}});
EXPECT_FALSE(Check(input));

input = MockedSearchInput::Map{};
EXPECT_FALSE(Check(&input));
hset.Set({});
EXPECT_FALSE(Check(input));
}

TEST_F(SearchParserTest, MatchRange) {
ParseExpr("@f1:[1 10] @f2:[50 100]");

MockedSearchInput input{};
MockedHSetAccessor hset{};
SearchInput input{&hset};

input = {{"f1", "5"}, {"f2", "50"}};
EXPECT_TRUE(Check(&input));
hset.Set({{"f1", "5"}, {"f2", "50"}});
EXPECT_TRUE(Check(input));

input = {{"f1", "1"}, {"f2", "100"}};
EXPECT_TRUE(Check(&input));
hset.Set({{"f1", "1"}, {"f2", "100"}});
EXPECT_TRUE(Check(input));

input = {{"f1", "10"}, {"f2", "50"}};
EXPECT_TRUE(Check(&input));
hset.Set({{"f1", "10"}, {"f2", "50"}});
EXPECT_TRUE(Check(input));

input = {{"f1", "11"}, {"f2", "49"}};
EXPECT_FALSE(Check(&input));
hset.Set({{"f1", "11"}, {"f2", "49"}});
EXPECT_FALSE(Check(input));

input = {{"f1", "0"}, {"f2", "101"}};
EXPECT_FALSE(Check(&input));
hset.Set({{"f1", "0"}, {"f2", "101"}});
EXPECT_FALSE(Check(input));
}

TEST_F(SearchParserTest, CheckExprInField) {
ParseExpr("@f1:(a|b) @f2:(c d) @f3:-e");

MockedSearchInput input{};
MockedHSetAccessor hset{};
SearchInput input{&hset};

input = {{"f1", "a"}, {"f2", "c and d"}, {"f3", "right"}};
EXPECT_TRUE(Check(&input));
hset.Set({{"f1", "a"}, {"f2", "c and d"}, {"f3", "right"}});
EXPECT_TRUE(Check(input));

input = {{"f1", "b"}, {"f2", "d and c"}, {"f3", "ok"}};
EXPECT_TRUE(Check(&input));
hset.Set({{"f1", "b"}, {"f2", "d and c"}, {"f3", "ok"}});
EXPECT_TRUE(Check(input));

input = {{"f1", "none"}, {"f2", "only d"}, {"f3", "ok"}};
EXPECT_FALSE(Check(&input));
hset.Set({{"f1", "none"}, {"f2", "only d"}, {"f3", "ok"}});
EXPECT_FALSE(Check(input));

input = {{"f1", "b"}, {"f2", "d and c"}, {"f3", "it has an e"}};
EXPECT_FALSE(Check(&input)) << DebugExpr();
hset.Set({{"f1", "b"}, {"f2", "d and c"}, {"f3", "it has an e"}});
EXPECT_FALSE(Check(input)) << DebugExpr();

ParseExpr({"@f1:(a (b | c) -(d | e)) @f2:-(a|b)"});

input = {{"f1", "a b w"}, {"f2", "c"}};
EXPECT_TRUE(Check(&input));
hset.Set({{"f1", "a b w"}, {"f2", "c"}});
EXPECT_TRUE(Check(input));

input = {{"f1", "a b d"}, {"f2", "c"}};
EXPECT_FALSE(Check(&input));
hset.Set({{"f1", "a b d"}, {"f2", "c"}});
EXPECT_FALSE(Check(input));

input = {{"f1", "a b w"}, {"f2", "a"}};
EXPECT_FALSE(Check(&input));
hset.Set({{"f1", "a b w"}, {"f2", "a"}});
EXPECT_FALSE(Check(input));

input = {{"f1", "a w"}, {"f2", "c"}};
EXPECT_FALSE(Check(&input));
hset.Set({{"f1", "a w"}, {"f2", "c"}});
EXPECT_FALSE(Check(input));
}

} // namespace search
Expand Down

0 comments on commit 2859d92

Please sign in to comment.