blob: 638fb83402fac38f14bfa0af89d0ba4ccfe20ed4 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*
*/
#include "search/plan_executor.h"
#include <gtest/gtest.h>
#include <memory>
#include "config/config.h"
#include "search/executors/mock_executor.h"
#include "search/indexer.h"
#include "search/interval.h"
#include "search/ir.h"
#include "search/ir_plan.h"
#include "search/value.h"
#include "string_util.h"
#include "test_base.h"
#include "types/redis_json.h"
using namespace kqir;
static auto exe_end = ExecutorNode::Result(ExecutorNode::end);
static IndexMap MakeIndexMap() {
auto f1 = FieldInfo("f1", std::make_unique<redis::TagFieldMetadata>());
auto f2 = FieldInfo("f2", std::make_unique<redis::NumericFieldMetadata>());
auto f3 = FieldInfo("f3", std::make_unique<redis::NumericFieldMetadata>());
auto hnsw_field_meta = std::make_unique<redis::HnswVectorFieldMetadata>();
hnsw_field_meta->vector_type = redis::VectorType::FLOAT64;
hnsw_field_meta->dim = 3;
hnsw_field_meta->distance_metric = redis::DistanceMetric::L2;
auto f4 = FieldInfo("f4", std::move(hnsw_field_meta));
auto ia = std::make_unique<IndexInfo>("ia", redis::IndexMetadata(), "search_ns");
ia->metadata.on_data_type = redis::IndexOnDataType::JSON;
ia->prefixes.prefixes.emplace_back("test2:");
ia->prefixes.prefixes.emplace_back("test4:");
ia->Add(std::move(f1));
ia->Add(std::move(f2));
ia->Add(std::move(f3));
ia->Add(std::move(f4));
IndexMap res;
res.Insert(std::move(ia));
return res;
}
static auto index_map = MakeIndexMap();
static auto NextRow(ExecutorContext& ctx) {
auto n = ctx.Next();
EXPECT_EQ(n.Msg(), Status::ok_msg);
auto v = std::move(n).GetValue();
EXPECT_EQ(v.index(), 1);
return std::get<ExecutorNode::RowType>(std::move(v));
}
TEST(PlanExecutorTest, Mock) {
auto op = std::make_unique<Mock>(std::vector<ExecutorNode::RowType>{});
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
op = std::make_unique<Mock>(std::vector<ExecutorNode::RowType>{{"a"}, {"b"}, {"c"}});
ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "a");
ASSERT_EQ(NextRow(ctx).key, "b");
ASSERT_EQ(NextRow(ctx).key, "c");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
static auto IndexI() -> const IndexInfo* { return index_map.Find("ia", "search_ns")->second.get(); }
static auto FieldI(const std::string& f) -> const FieldInfo* { return &IndexI()->fields.at(f); }
static auto N(double n) { return MakeValue<Numeric>(n); }
static auto T(const std::string& v) { return MakeValue<StringArray>(util::Split(v, ",")); }
static auto V(const std::vector<double>& vals) { return MakeValue<NumericArray>(vals); }
TEST(PlanExecutorTest, TopN) {
std::vector<ExecutorNode::RowType> data{
{"a", {{FieldI("f3"), N(4)}}, IndexI()}, {"b", {{FieldI("f3"), N(2)}}, IndexI()},
{"c", {{FieldI("f3"), N(7)}}, IndexI()}, {"d", {{FieldI("f3"), N(3)}}, IndexI()},
{"e", {{FieldI("f3"), N(1)}}, IndexI()}, {"f", {{FieldI("f3"), N(6)}}, IndexI()},
{"g", {{FieldI("f3"), N(8)}}, IndexI()},
};
{
auto op = std::make_unique<TopN>(
std::make_unique<Mock>(data),
std::make_unique<SortByClause>(SortByClause::ASC, std::make_unique<FieldRef>("f3", FieldI("f3"))),
std::make_unique<LimitClause>(0, 4));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "e");
ASSERT_EQ(NextRow(ctx).key, "b");
ASSERT_EQ(NextRow(ctx).key, "d");
ASSERT_EQ(NextRow(ctx).key, "a");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
auto op = std::make_unique<TopN>(
std::make_unique<Mock>(data),
std::make_unique<SortByClause>(SortByClause::ASC, std::make_unique<FieldRef>("f3", FieldI("f3"))),
std::make_unique<LimitClause>(1, 4));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "b");
ASSERT_EQ(NextRow(ctx).key, "d");
ASSERT_EQ(NextRow(ctx).key, "a");
ASSERT_EQ(NextRow(ctx).key, "f");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
}
TEST(PlanExecutorTest, Filter) {
std::vector<ExecutorNode::RowType> data{
{"a", {{FieldI("f3"), N(4)}}, IndexI()}, {"b", {{FieldI("f3"), N(2)}}, IndexI()},
{"c", {{FieldI("f3"), N(7)}}, IndexI()}, {"d", {{FieldI("f3"), N(3)}}, IndexI()},
{"e", {{FieldI("f3"), N(1)}}, IndexI()}, {"f", {{FieldI("f3"), N(6)}}, IndexI()},
{"g", {{FieldI("f3"), N(8)}}, IndexI()},
};
{
auto field = std::make_unique<FieldRef>("f3", FieldI("f3"));
auto op = std::make_unique<Filter>(
std::make_unique<Mock>(data),
AndExpr::Create(Node::List<QueryExpr>(
std::make_unique<NumericCompareExpr>(NumericCompareExpr::GT, field->CloneAs<FieldRef>(),
std::make_unique<NumericLiteral>(2)),
std::make_unique<NumericCompareExpr>(NumericCompareExpr::LET, field->CloneAs<FieldRef>(),
std::make_unique<NumericLiteral>(6)))));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "a");
ASSERT_EQ(NextRow(ctx).key, "d");
ASSERT_EQ(NextRow(ctx).key, "f");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
auto field = std::make_unique<FieldRef>("f3", FieldI("f3"));
auto op = std::make_unique<Filter>(
std::make_unique<Mock>(data),
OrExpr::Create(Node::List<QueryExpr>(
std::make_unique<NumericCompareExpr>(NumericCompareExpr::GET, field->CloneAs<FieldRef>(),
std::make_unique<NumericLiteral>(6)),
std::make_unique<NumericCompareExpr>(NumericCompareExpr::LT, field->CloneAs<FieldRef>(),
std::make_unique<NumericLiteral>(2)))));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "c");
ASSERT_EQ(NextRow(ctx).key, "e");
ASSERT_EQ(NextRow(ctx).key, "f");
ASSERT_EQ(NextRow(ctx).key, "g");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
data = {{"a", {{FieldI("f1"), T("cpp,java")}}, IndexI()}, {"b", {{FieldI("f1"), T("python,cpp,c")}}, IndexI()},
{"c", {{FieldI("f1"), T("c,perl")}}, IndexI()}, {"d", {{FieldI("f1"), T("rust,python")}}, IndexI()},
{"e", {{FieldI("f1"), T("java,kotlin")}}, IndexI()}, {"f", {{FieldI("f1"), T("c,rust")}}, IndexI()},
{"g", {{FieldI("f1"), T("c,cpp,java")}}, IndexI()}};
{
auto field = std::make_unique<FieldRef>("f1", FieldI("f1"));
auto op = std::make_unique<Filter>(
std::make_unique<Mock>(data),
AndExpr::Create(Node::List<QueryExpr>(
std::make_unique<TagContainExpr>(field->CloneAs<FieldRef>(), std::make_unique<StringLiteral>("c")),
std::make_unique<TagContainExpr>(field->CloneAs<FieldRef>(), std::make_unique<StringLiteral>("cpp")))));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "b");
ASSERT_EQ(NextRow(ctx).key, "g");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
auto field = std::make_unique<FieldRef>("f1", FieldI("f1"));
auto op = std::make_unique<Filter>(
std::make_unique<Mock>(data),
OrExpr::Create(Node::List<QueryExpr>(
std::make_unique<TagContainExpr>(field->CloneAs<FieldRef>(), std::make_unique<StringLiteral>("rust")),
std::make_unique<TagContainExpr>(field->CloneAs<FieldRef>(), std::make_unique<StringLiteral>("perl")))));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "c");
ASSERT_EQ(NextRow(ctx).key, "d");
ASSERT_EQ(NextRow(ctx).key, "f");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
data = {{"a", {{FieldI("f4"), V({1, 2, 3})}}, IndexI()}, {"b", {{FieldI("f4"), V({9, 10, 11})}}, IndexI()},
{"c", {{FieldI("f4"), V({4, 5, 6})}}, IndexI()}, {"d", {{FieldI("f4"), V({1, 2, 3})}}, IndexI()},
{"e", {{FieldI("f4"), V({2, 3, 4})}}, IndexI()}, {"f", {{FieldI("f4"), V({12, 13, 14})}}, IndexI()},
{"g", {{FieldI("f4"), V({1, 2, 3})}}, IndexI()}};
{
auto field = std::make_unique<FieldRef>("f4", FieldI("f4"));
std::vector<double> vector = {11, 12, 13};
auto op = std::make_unique<Filter>(
std::make_unique<Mock>(data),
std::make_unique<VectorRangeExpr>(field->CloneAs<FieldRef>(), std::make_unique<NumericLiteral>(4),
std::make_unique<VectorLiteral>(std::move(vector))));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "b");
ASSERT_EQ(NextRow(ctx).key, "f");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
auto field = std::make_unique<FieldRef>("f4", FieldI("f4"));
std::vector<double> vector = {2, 3, 4};
auto op = std::make_unique<Filter>(
std::make_unique<Mock>(data),
std::make_unique<VectorRangeExpr>(field->CloneAs<FieldRef>(), std::make_unique<NumericLiteral>(5),
std::make_unique<VectorLiteral>(std::move(vector))));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "a");
ASSERT_EQ(NextRow(ctx).key, "c");
ASSERT_EQ(NextRow(ctx).key, "d");
ASSERT_EQ(NextRow(ctx).key, "e");
ASSERT_EQ(NextRow(ctx).key, "g");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
}
TEST(PlanExecutorTest, Limit) {
std::vector<ExecutorNode::RowType> data{
{"a", {{FieldI("f3"), N(4)}}, IndexI()}, {"b", {{FieldI("f3"), N(2)}}, IndexI()},
{"c", {{FieldI("f3"), N(7)}}, IndexI()}, {"d", {{FieldI("f3"), N(3)}}, IndexI()},
{"e", {{FieldI("f3"), N(1)}}, IndexI()}, {"f", {{FieldI("f3"), N(6)}}, IndexI()},
{"g", {{FieldI("f3"), N(8)}}, IndexI()},
};
{
auto op = std::make_unique<Limit>(std::make_unique<Mock>(data), std::make_unique<LimitClause>(1, 2));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "b");
ASSERT_EQ(NextRow(ctx).key, "c");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
auto field = std::make_unique<FieldRef>("f3", FieldI("f3"));
auto op = std::make_unique<Limit>(std::make_unique<Mock>(data), std::make_unique<LimitClause>(0, 3));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "a");
ASSERT_EQ(NextRow(ctx).key, "b");
ASSERT_EQ(NextRow(ctx).key, "c");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
}
TEST(PlanExecutorTest, Merge) {
std::vector<ExecutorNode::RowType> data1{
{"a", {{FieldI("f3"), N(4)}}, IndexI()},
{"b", {{FieldI("f3"), N(2)}}, IndexI()},
};
std::vector<ExecutorNode::RowType> data2{{"c", {{FieldI("f3"), N(7)}}, IndexI()},
{"d", {{FieldI("f3"), N(3)}}, IndexI()},
{"e", {{FieldI("f3"), N(1)}}, IndexI()}};
{
auto op =
std::make_unique<Merge>(Node::List<PlanOperator>(std::make_unique<Mock>(data1), std::make_unique<Mock>(data2)));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "a");
ASSERT_EQ(NextRow(ctx).key, "b");
ASSERT_EQ(NextRow(ctx).key, "c");
ASSERT_EQ(NextRow(ctx).key, "d");
ASSERT_EQ(NextRow(ctx).key, "e");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
auto op = std::make_unique<Merge>(
Node::List<PlanOperator>(std::make_unique<Mock>(decltype(data1){}), std::make_unique<Mock>(data1)));
auto ctx = ExecutorContext(op.get());
ASSERT_EQ(NextRow(ctx).key, "a");
ASSERT_EQ(NextRow(ctx).key, "b");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
}
class PlanExecutorTestC : public TestBase {
protected:
explicit PlanExecutorTestC() : json_(std::make_unique<redis::Json>(storage_.get(), "search_ns")) {}
~PlanExecutorTestC() override = default;
void SetUp() override {}
void TearDown() override {}
std::unique_ptr<redis::Json> json_;
};
TEST_F(PlanExecutorTestC, FullIndexScan) {
json_->Set(*ctx_, "test1:a", "$", "{}");
json_->Set(*ctx_, "test1:b", "$", "{}");
json_->Set(*ctx_, "test2:c", "$", "{\"f3\": 6}");
json_->Set(*ctx_, "test3:d", "$", "{}");
json_->Set(*ctx_, "test4:e", "$", "{\"f3\": 7}");
json_->Set(*ctx_, "test4:f", "$", "{\"f3\": 2}");
json_->Set(*ctx_, "test4:g", "$", "{\"f3\": 8}");
json_->Set(*ctx_, "test5:h", "$", "{}");
json_->Set(*ctx_, "test5:i", "$", "{}");
json_->Set(*ctx_, "test5:g", "$", "{}");
{
auto op = std::make_unique<FullIndexScan>(std::make_unique<IndexRef>("ia", IndexI()));
auto ctx = ExecutorContext(op.get(), storage_.get());
ASSERT_EQ(NextRow(ctx).key, "test2:c");
ASSERT_EQ(NextRow(ctx).key, "test4:e");
ASSERT_EQ(NextRow(ctx).key, "test4:f");
ASSERT_EQ(NextRow(ctx).key, "test4:g");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
auto op = std::make_unique<Filter>(
std::make_unique<FullIndexScan>(std::make_unique<IndexRef>("ia", IndexI())),
std::make_unique<NumericCompareExpr>(NumericCompareExpr::GT, std::make_unique<FieldRef>("f3", FieldI("f3")),
std::make_unique<NumericLiteral>(3)));
auto ctx = ExecutorContext(op.get(), storage_.get());
ASSERT_EQ(NextRow(ctx).key, "test2:c");
ASSERT_EQ(NextRow(ctx).key, "test4:e");
ASSERT_EQ(NextRow(ctx).key, "test4:g");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
}
struct ScopedUpdate {
engine::Context* db_ctx;
redis::GlobalIndexer::RecordResult rr;
std::string_view key;
std::string ns;
static auto Create(engine::Context& ctx, redis::GlobalIndexer& indexer, std::string_view key, const std::string& ns) {
auto s = indexer.Record(ctx, key, ns);
EXPECT_EQ(s.Msg(), Status::ok_msg);
return *s;
}
ScopedUpdate(engine::Context* ctx, redis::GlobalIndexer& indexer, std::string_view key, const std::string& ns)
: db_ctx(ctx), rr(Create(*ctx, indexer, key, ns)), key(key), ns(ns) {}
ScopedUpdate(const ScopedUpdate&) = delete;
ScopedUpdate(ScopedUpdate&&) = delete;
ScopedUpdate& operator=(const ScopedUpdate&) = delete;
ScopedUpdate& operator=(ScopedUpdate&&) = delete;
~ScopedUpdate() {
auto s = redis::GlobalIndexer::Update(*db_ctx, rr);
EXPECT_EQ(s.Msg(), Status::ok_msg);
}
};
std::vector<std::unique_ptr<ScopedUpdate>> ScopedUpdates(engine::Context& ctx, redis::GlobalIndexer& indexer,
const std::vector<std::string_view>& keys,
const std::string& ns) {
std::vector<std::unique_ptr<ScopedUpdate>> sus;
sus.reserve(keys.size());
for (auto key : keys) {
sus.emplace_back(std::make_unique<ScopedUpdate>(&ctx, indexer, key, ns));
}
return sus;
}
TEST_F(PlanExecutorTestC, NumericFieldScan) {
redis::GlobalIndexer indexer(storage_.get());
indexer.Add(std::make_unique<redis::IndexUpdater>(IndexI()));
{
engine::Context ctx(storage_.get());
auto updates = ScopedUpdates(
*ctx_, indexer, {"test2:a", "test2:b", "test2:c", "test2:d", "test2:e", "test2:f", "test2:g"}, "search_ns");
json_->Set(*ctx_, "test2:a", "$", "{\"f2\": 6}");
json_->Set(*ctx_, "test2:b", "$", "{\"f2\": 3}");
json_->Set(*ctx_, "test2:c", "$", "{\"f2\": 8}");
json_->Set(*ctx_, "test2:d", "$", "{\"f2\": 14}");
json_->Set(*ctx_, "test2:e", "$", "{\"f2\": 1}");
json_->Set(*ctx_, "test2:f", "$", "{\"f2\": 3}");
json_->Set(*ctx_, "test2:g", "$", "{\"f2\": 9}");
}
{
auto op = std::make_unique<NumericFieldScan>(std::make_unique<FieldRef>("f2", FieldI("f2")), Interval(3, 9),
SortByClause::ASC);
auto ctx = ExecutorContext(op.get(), storage_.get());
ASSERT_EQ(NextRow(ctx).key, "test2:b");
ASSERT_EQ(NextRow(ctx).key, "test2:f");
ASSERT_EQ(NextRow(ctx).key, "test2:a");
ASSERT_EQ(NextRow(ctx).key, "test2:c");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
auto op = std::make_unique<NumericFieldScan>(std::make_unique<FieldRef>("f2", FieldI("f2")), Interval(3, 9),
SortByClause::DESC);
auto ctx = ExecutorContext(op.get(), storage_.get());
ASSERT_EQ(NextRow(ctx).key, "test2:c");
ASSERT_EQ(NextRow(ctx).key, "test2:a");
ASSERT_EQ(NextRow(ctx).key, "test2:f");
ASSERT_EQ(NextRow(ctx).key, "test2:b");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
}
TEST_F(PlanExecutorTestC, TagFieldScan) {
redis::GlobalIndexer indexer(storage_.get());
indexer.Add(std::make_unique<redis::IndexUpdater>(IndexI()));
{
engine::Context ctx(storage_.get());
auto updates = ScopedUpdates(
*ctx_, indexer, {"test2:a", "test2:b", "test2:c", "test2:d", "test2:e", "test2:f", "test2:g"}, "search_ns");
json_->Set(*ctx_, "test2:a", "$", "{\"f1\": \"c,cpp,java\"}");
json_->Set(*ctx_, "test2:b", "$", "{\"f1\": \"python,c\"}");
json_->Set(*ctx_, "test2:c", "$", "{\"f1\": \"java,scala\"}");
json_->Set(*ctx_, "test2:d", "$", "{\"f1\": \"rust,python,perl\"}");
json_->Set(*ctx_, "test2:e", "$", "{\"f1\": \"python,cpp\"}");
json_->Set(*ctx_, "test2:f", "$", "{\"f1\": \"c,cpp\"}");
json_->Set(*ctx_, "test2:g", "$", "{\"f1\": \"cpp,rust\"}");
}
{
auto op = std::make_unique<TagFieldScan>(std::make_unique<FieldRef>("f1", FieldI("f1")), "cpp");
auto ctx = ExecutorContext(op.get(), storage_.get());
ASSERT_EQ(NextRow(ctx).key, "test2:a");
ASSERT_EQ(NextRow(ctx).key, "test2:e");
ASSERT_EQ(NextRow(ctx).key, "test2:f");
ASSERT_EQ(NextRow(ctx).key, "test2:g");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
auto op = std::make_unique<TagFieldScan>(std::make_unique<FieldRef>("f1", FieldI("f1")), "python");
auto ctx = ExecutorContext(op.get(), storage_.get());
ASSERT_EQ(NextRow(ctx).key, "test2:b");
ASSERT_EQ(NextRow(ctx).key, "test2:d");
ASSERT_EQ(NextRow(ctx).key, "test2:e");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
}
TEST_F(PlanExecutorTestC, HnswVectorFieldScans) {
redis::GlobalIndexer indexer(storage_.get());
indexer.Add(std::make_unique<redis::IndexUpdater>(IndexI()));
{
auto updates = ScopedUpdates(*ctx_, indexer,
{"test2:a", "test2:b", "test2:c", "test2:d", "test2:e", "test2:f", "test2:g",
"test2:h", "test2:i", "test2:j", "test2:k", "test2:l", "test2:m", "test2:n"},
"search_ns");
json_->Set(*ctx_, "test2:a", "$", "{\"f4\": [1,2,3]}");
json_->Set(*ctx_, "test2:b", "$", "{\"f4\": [4,5,6]}");
json_->Set(*ctx_, "test2:c", "$", "{\"f4\": [7,8,9]}");
json_->Set(*ctx_, "test2:d", "$", "{\"f4\": [10,11,12]}");
json_->Set(*ctx_, "test2:e", "$", "{\"f4\": [13,14,15]}");
json_->Set(*ctx_, "test2:f", "$", "{\"f4\": [23,24,25]}");
json_->Set(*ctx_, "test2:g", "$", "{\"f4\": [26,27,28]}");
json_->Set(*ctx_, "test2:h", "$", "{\"f4\": [77,78,79]}");
json_->Set(*ctx_, "test2:i", "$", "{\"f4\": [80,81,82]}");
json_->Set(*ctx_, "test2:j", "$", "{\"f4\": [83,84,85]}");
json_->Set(*ctx_, "test2:k", "$", "{\"f4\": [86,87,88]}");
json_->Set(*ctx_, "test2:l", "$", "{\"f4\": [89,90,91]}");
json_->Set(*ctx_, "test2:m", "$", "{\"f4\": [1026,1027,1028]}");
json_->Set(*ctx_, "test2:n", "$", "{\"f4\": [2226,2227,2228]}");
}
{
std::vector<double> target_vector = {14, 15, 16};
auto op =
std::make_unique<HnswVectorFieldKnnScan>(std::make_unique<FieldRef>("f4", FieldI("f4")), target_vector, 5);
auto ctx = ExecutorContext(op.get(), storage_.get());
ASSERT_EQ(NextRow(ctx).key, "test2:e");
ASSERT_EQ(NextRow(ctx).key, "test2:d");
ASSERT_EQ(NextRow(ctx).key, "test2:c");
ASSERT_EQ(NextRow(ctx).key, "test2:f");
ASSERT_EQ(NextRow(ctx).key, "test2:b");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
std::vector<double> target_vector = {24, 25, 26};
auto op =
std::make_unique<HnswVectorFieldKnnScan>(std::make_unique<FieldRef>("f4", FieldI("f4")), target_vector, 3);
auto ctx = ExecutorContext(op.get(), storage_.get());
ASSERT_EQ(NextRow(ctx).key, "test2:f");
ASSERT_EQ(NextRow(ctx).key, "test2:g");
ASSERT_EQ(NextRow(ctx).key, "test2:e");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
std::vector<double> query_vector = {11, 12, 13};
auto op =
std::make_unique<HnswVectorFieldRangeScan>(std::make_unique<FieldRef>("f4", FieldI("f4")), query_vector, 25);
auto ctx = ExecutorContext(op.get(), storage_.get());
ASSERT_EQ(NextRow(ctx).key, "test2:d");
ASSERT_EQ(NextRow(ctx).key, "test2:e");
ASSERT_EQ(NextRow(ctx).key, "test2:c");
ASSERT_EQ(NextRow(ctx).key, "test2:b");
ASSERT_EQ(NextRow(ctx).key, "test2:a");
ASSERT_EQ(NextRow(ctx).key, "test2:f");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
{
std::vector<double> query_vector = {12, 13, 14};
auto op =
std::make_unique<HnswVectorFieldRangeScan>(std::make_unique<FieldRef>("f4", FieldI("f4")), query_vector, 5000);
auto ctx = ExecutorContext(op.get(), storage_.get());
ASSERT_EQ(NextRow(ctx).key, "test2:e");
ASSERT_EQ(NextRow(ctx).key, "test2:d");
ASSERT_EQ(NextRow(ctx).key, "test2:c");
ASSERT_EQ(NextRow(ctx).key, "test2:b");
ASSERT_EQ(NextRow(ctx).key, "test2:a");
ASSERT_EQ(NextRow(ctx).key, "test2:f");
ASSERT_EQ(NextRow(ctx).key, "test2:g");
ASSERT_EQ(NextRow(ctx).key, "test2:h");
ASSERT_EQ(NextRow(ctx).key, "test2:i");
ASSERT_EQ(NextRow(ctx).key, "test2:j");
ASSERT_EQ(NextRow(ctx).key, "test2:k");
ASSERT_EQ(NextRow(ctx).key, "test2:l");
ASSERT_EQ(NextRow(ctx).key, "test2:m");
ASSERT_EQ(NextRow(ctx).key, "test2:n");
ASSERT_EQ(ctx.Next().GetValue(), exe_end);
}
}