mirror of
https://github.com/typesense/typesense.git
synced 2025-05-20 05:32:30 +08:00
Preset API.
This commit is contained in:
parent
3be68b38c9
commit
facdda2a03
@ -64,6 +64,8 @@ private:
|
||||
|
||||
spp::sparse_hash_map<std::string, std::string> collection_symlinks;
|
||||
|
||||
spp::sparse_hash_map<std::string, nlohmann::json> preset_configs;
|
||||
|
||||
// Auto incrementing ID assigned to each collection
|
||||
// Using a ID instead of a collection's name makes renaming possible
|
||||
std::atomic<uint32_t> next_collection_id;
|
||||
@ -95,6 +97,7 @@ public:
|
||||
|
||||
static constexpr const char* NEXT_COLLECTION_ID_KEY = "$CI";
|
||||
static constexpr const char* SYMLINK_PREFIX = "$SL";
|
||||
static constexpr const char* PRESET_PREFIX = "$PS";
|
||||
static constexpr const char* BATCHED_INDEXER_STATE_KEY = "$BI";
|
||||
|
||||
static CollectionManager & get_instance() {
|
||||
@ -159,6 +162,8 @@ public:
|
||||
|
||||
static std::string get_symlink_key(const std::string & symlink_name);
|
||||
|
||||
static std::string get_preset_key(const std::string & preset_name);
|
||||
|
||||
Store* get_store();
|
||||
|
||||
ThreadPool* get_thread_pool() const;
|
||||
@ -177,4 +182,13 @@ public:
|
||||
Option<bool> upsert_symlink(const std::string & symlink_name, const std::string & collection_name);
|
||||
|
||||
Option<bool> delete_symlink(const std::string & symlink_name);
|
||||
|
||||
// presets
|
||||
spp::sparse_hash_map<std::string, nlohmann::json> get_presets() const;
|
||||
|
||||
Option<nlohmann::json> get_preset(const std::string & preset_name) const;
|
||||
|
||||
Option<bool> upsert_preset(const std::string & preset_name, const nlohmann::json& preset_config);
|
||||
|
||||
Option<bool> delete_preset(const std::string & preset_name);
|
||||
};
|
@ -45,6 +45,16 @@ bool put_upsert_alias(const std::shared_ptr<http_req>& req, const std::shared_pt
|
||||
|
||||
bool del_alias(const std::shared_ptr<http_req>& req, const std::shared_ptr<http_res>& res);
|
||||
|
||||
// Presets
|
||||
|
||||
bool get_presets(const std::shared_ptr<http_req>& req, const std::shared_ptr<http_res>& res);
|
||||
|
||||
bool get_preset(const std::shared_ptr<http_req>& req, const std::shared_ptr<http_res>& res);
|
||||
|
||||
bool put_upsert_preset(const std::shared_ptr<http_req>& req, const std::shared_ptr<http_res>& res);
|
||||
|
||||
bool del_preset(const std::shared_ptr<http_req>& req, const std::shared_ptr<http_res>& res);
|
||||
|
||||
// Overrides
|
||||
|
||||
bool get_overrides(const std::shared_ptr<http_req>& req, const std::shared_ptr<http_res>& res);
|
||||
|
@ -207,6 +207,8 @@ Option<bool> CollectionManager::load(const size_t collection_batch_size, const s
|
||||
return num_processed == num_collections;
|
||||
});
|
||||
|
||||
// load aliases
|
||||
|
||||
std::string symlink_prefix_key = std::string(SYMLINK_PREFIX) + "_";
|
||||
rocksdb::Iterator* iter = store->scan(symlink_prefix_key);
|
||||
while(iter->Valid() && iter->key().starts_with(symlink_prefix_key)) {
|
||||
@ -218,6 +220,19 @@ Option<bool> CollectionManager::load(const size_t collection_batch_size, const s
|
||||
|
||||
delete iter;
|
||||
|
||||
// load presets
|
||||
|
||||
std::string preset_prefix_key = std::string(PRESET_PREFIX) + "_";
|
||||
iter = store->scan(preset_prefix_key);
|
||||
while(iter->Valid() && iter->key().starts_with(preset_prefix_key)) {
|
||||
std::vector<std::string> parts;
|
||||
StringUtils::split(iter->key().ToString(), parts, preset_prefix_key);
|
||||
preset_configs[parts[0]] = iter->value().ToString();
|
||||
iter->Next();
|
||||
}
|
||||
|
||||
delete iter;
|
||||
|
||||
LOG(INFO) << "Loaded " << num_collections << " collection(s).";
|
||||
|
||||
loading_pool.shutdown();
|
||||
@ -245,6 +260,8 @@ void CollectionManager::dispose() {
|
||||
}
|
||||
|
||||
collections.clear();
|
||||
collection_symlinks.clear();
|
||||
preset_configs.clear();
|
||||
store->close();
|
||||
}
|
||||
|
||||
@ -596,6 +613,8 @@ Option<bool> CollectionManager::do_search(std::map<std::string, std::string>& re
|
||||
const char *EXHAUSTIVE_SEARCH = "exhaustive_search";
|
||||
const char *SPLIT_JOIN_TOKENS = "split_join_tokens";
|
||||
|
||||
const char *PRESET = "preset";
|
||||
|
||||
if(req_params.count(NUM_TYPOS) == 0) {
|
||||
req_params[NUM_TYPOS] = "2";
|
||||
}
|
||||
@ -1234,3 +1253,47 @@ Option<bool> CollectionManager::load_collection(const nlohmann::json &collection
|
||||
|
||||
return Option<bool>(true);
|
||||
}
|
||||
|
||||
spp::sparse_hash_map<std::string, nlohmann::json> CollectionManager::get_presets() const {
|
||||
std::shared_lock lock(mutex);
|
||||
return preset_configs;
|
||||
}
|
||||
|
||||
Option<nlohmann::json> CollectionManager::get_preset(const string& preset_name) const {
|
||||
std::shared_lock lock(mutex);
|
||||
|
||||
const auto& it = preset_configs.find(preset_name);
|
||||
if(it != preset_configs.end()) {
|
||||
return Option<nlohmann::json>(it->second);
|
||||
}
|
||||
|
||||
return Option<nlohmann::json>(404, "Not found.");
|
||||
}
|
||||
|
||||
Option<bool> CollectionManager::upsert_preset(const string& preset_name, const nlohmann::json& preset_config) {
|
||||
std::unique_lock lock(mutex);
|
||||
|
||||
bool inserted = store->insert(get_preset_key(preset_name), preset_config.dump());
|
||||
if(!inserted) {
|
||||
return Option<bool>(500, "Unable to insert into store.");
|
||||
}
|
||||
|
||||
preset_configs[preset_name] = preset_config;
|
||||
return Option<bool>(true);
|
||||
}
|
||||
|
||||
std::string CollectionManager::get_preset_key(const string& preset_name) {
|
||||
return std::string(PRESET_PREFIX) + "_" + preset_name;
|
||||
}
|
||||
|
||||
Option<bool> CollectionManager::delete_preset(const string& preset_name) {
|
||||
std::unique_lock lock(mutex);
|
||||
|
||||
bool removed = store->remove(get_preset_key(preset_name));
|
||||
if(!removed) {
|
||||
return Option<bool>(500, "Unable to delete from store.");
|
||||
}
|
||||
|
||||
preset_configs.erase(preset_name);
|
||||
return Option<bool>(true);
|
||||
}
|
||||
|
114
src/core_api.cpp
114
src/core_api.cpp
@ -323,13 +323,23 @@ bool post_multi_search(const std::shared_ptr<http_req>& req, const std::shared_p
|
||||
}
|
||||
|
||||
nlohmann::json req_json;
|
||||
const auto preset_it = req->params.find("preset");
|
||||
|
||||
try {
|
||||
req_json = nlohmann::json::parse(req->body);
|
||||
} catch(const std::exception& e) {
|
||||
LOG(ERROR) << "JSON error: " << e.what();
|
||||
res->set_400("Bad JSON.");
|
||||
return false;
|
||||
if(preset_it != req->params.end()) {
|
||||
const auto preset_op = CollectionManager::get_instance().get_preset(preset_it->second);
|
||||
if(preset_op.ok()) {
|
||||
req_json = preset_op.get();
|
||||
}
|
||||
}
|
||||
|
||||
if(req_json.empty()) {
|
||||
try {
|
||||
req_json = nlohmann::json::parse(req->body);
|
||||
} catch(const std::exception& e) {
|
||||
LOG(ERROR) << "JSON error: " << e.what();
|
||||
res->set_400("Bad JSON.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(req_json.count("searches") == 0) {
|
||||
@ -1486,3 +1496,95 @@ bool is_doc_del_route(uint64_t route_hash) {
|
||||
bool found = server->get_route(route_hash, &rpath);
|
||||
return found && (rpath->handler == del_remove_document || rpath->handler == del_remove_documents);
|
||||
}
|
||||
|
||||
bool get_presets(const std::shared_ptr<http_req>& req, const std::shared_ptr<http_res>& res) {
|
||||
CollectionManager & collectionManager = CollectionManager::get_instance();
|
||||
const spp::sparse_hash_map<std::string, nlohmann::json> & presets = collectionManager.get_presets();
|
||||
nlohmann::json res_json = nlohmann::json::object();
|
||||
res_json["presets"] = nlohmann::json::array();
|
||||
|
||||
for(const auto& preset_kv: presets) {
|
||||
nlohmann::json preset;
|
||||
preset["name"] = preset_kv.first;
|
||||
preset["value"] = preset_kv.second;
|
||||
res_json["presets"].push_back(preset);
|
||||
}
|
||||
|
||||
res->set_200(res_json.dump());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_preset(const std::shared_ptr<http_req>& req, const std::shared_ptr<http_res>& res) {
|
||||
const std::string & preset_name = req->params["preset_name"];
|
||||
CollectionManager & collectionManager = CollectionManager::get_instance();
|
||||
Option<nlohmann::json> preset_op = collectionManager.get_preset(preset_name);
|
||||
|
||||
if(!preset_op.ok()) {
|
||||
res->set_404();
|
||||
return false;
|
||||
}
|
||||
|
||||
nlohmann::json res_json;
|
||||
res_json["name"] = preset_name;
|
||||
res_json["value"] = preset_op.get();
|
||||
|
||||
res->set_200(res_json.dump());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool put_upsert_preset(const std::shared_ptr<http_req>& req, const std::shared_ptr<http_res>& res) {
|
||||
nlohmann::json req_json;
|
||||
|
||||
try {
|
||||
req_json = nlohmann::json::parse(req->body);
|
||||
} catch(const std::exception& e) {
|
||||
LOG(ERROR) << "JSON error: " << e.what();
|
||||
res->set_400("Bad JSON.");
|
||||
return false;
|
||||
}
|
||||
|
||||
CollectionManager & collectionManager = CollectionManager::get_instance();
|
||||
const std::string & preset_name = req->params["name"];
|
||||
|
||||
const char* PRESET_VALUE = "value";
|
||||
|
||||
if(req_json.count(PRESET_VALUE) == 0) {
|
||||
res->set_400(std::string("Parameter `") + PRESET_VALUE + "` is required.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Option<bool> success_op = collectionManager.upsert_preset(preset_name, req_json[PRESET_VALUE]);
|
||||
if(!success_op.ok()) {
|
||||
res->set_500(success_op.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
req_json["name"] = preset_name;
|
||||
|
||||
res->set_200(req_json.dump());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool del_preset(const std::shared_ptr<http_req>& req, const std::shared_ptr<http_res>& res) {
|
||||
const std::string & preset_name = req->params["name"];
|
||||
CollectionManager & collectionManager = CollectionManager::get_instance();
|
||||
|
||||
Option<nlohmann::json> preset_op = collectionManager.get_preset(preset_name);
|
||||
if(!preset_op.ok()) {
|
||||
res->set_404();
|
||||
return false;
|
||||
}
|
||||
|
||||
Option<bool> delete_op = collectionManager.delete_preset(preset_name);
|
||||
|
||||
if(!delete_op.ok()) {
|
||||
res->set_500(delete_op.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
nlohmann::json res_json;
|
||||
res_json["name"] = preset_name;
|
||||
res_json["value"] = preset_op.get();
|
||||
res->set_200(res_json.dump());
|
||||
return true;
|
||||
}
|
||||
|
@ -58,6 +58,11 @@ void master_server_routes() {
|
||||
server->post("/keys", post_create_key);
|
||||
server->del("/keys/:id", del_key);
|
||||
|
||||
server->get("/presets", get_presets);
|
||||
server->get("/presets/:name", get_preset);
|
||||
server->put("/presets/:name", put_upsert_preset);
|
||||
server->del("/presets/:name", del_preset);
|
||||
|
||||
// meta
|
||||
server->get("/metrics.json", get_metrics_json);
|
||||
server->get("/stats.json", get_stats_json);
|
||||
|
@ -673,3 +673,56 @@ TEST_F(CollectionManagerTest, ParseSortByClause) {
|
||||
sort_by_parsed = CollectionManager::parse_sort_by_str(",,", sort_fields);
|
||||
ASSERT_FALSE(sort_by_parsed);
|
||||
}
|
||||
|
||||
TEST_F(CollectionManagerTest, Presets) {
|
||||
// try getting on a blank slate
|
||||
auto presets = collectionManager.get_presets();
|
||||
ASSERT_TRUE(presets.empty());
|
||||
|
||||
// insert some presets
|
||||
nlohmann::json preset_obj;
|
||||
|
||||
preset_obj["query_by"] = "foo";
|
||||
collectionManager.upsert_preset("preset1", preset_obj);
|
||||
|
||||
preset_obj["query_by"] = "bar";
|
||||
collectionManager.upsert_preset("preset2", preset_obj);
|
||||
|
||||
ASSERT_EQ(2, collectionManager.get_presets().size());
|
||||
|
||||
// try fetching individual presets
|
||||
auto preset_op = collectionManager.get_preset("preset1");
|
||||
ASSERT_TRUE(preset_op.ok());
|
||||
ASSERT_EQ(1, preset_op.get().size());
|
||||
ASSERT_EQ("foo", preset_op.get()["query_by"]);
|
||||
|
||||
preset_op = collectionManager.get_preset("preset2");
|
||||
ASSERT_TRUE(preset_op.ok());
|
||||
ASSERT_EQ(1, preset_op.get().size());
|
||||
ASSERT_EQ("bar", preset_op.get()["query_by"]);
|
||||
|
||||
// delete a preset
|
||||
auto del_op = collectionManager.delete_preset("preset2");
|
||||
ASSERT_TRUE(del_op.ok());
|
||||
|
||||
std::string val;
|
||||
auto status = store->get(CollectionManager::get_preset_key("preset2"), val);
|
||||
ASSERT_EQ(StoreStatus::NOT_FOUND, status);
|
||||
|
||||
ASSERT_EQ(1, collectionManager.get_presets().size());
|
||||
preset_op = collectionManager.get_preset("preset2");
|
||||
ASSERT_FALSE(preset_op.ok());
|
||||
ASSERT_EQ(404, preset_op.code());
|
||||
|
||||
// should be able to restore state on init
|
||||
collectionManager.dispose();
|
||||
delete store;
|
||||
|
||||
store = new Store("/tmp/typesense_test/coll_manager_test_db");
|
||||
collectionManager.init(store, 1.0, "auth_key", quit);
|
||||
collectionManager.load(8, 1000);
|
||||
|
||||
ASSERT_EQ(1, collectionManager.get_presets().size());
|
||||
preset_op = collectionManager.get_preset("preset1");
|
||||
ASSERT_TRUE(preset_op.ok());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user