From 7cfc25aeeb2eba7820e6068e506185473e6c1258 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Thu, 19 May 2016 20:17:05 +0200 Subject: [PATCH] Implement tag deletion. --- src/tag/CMakeLists.txt | 1 + src/tag/commandline.cpp | 3 +- src/tag/main.cpp | 72 +++++++++++++++++++++++++++-------- src/tag/tag_postgres.cpp | 82 ++++++++++++++++++++++++++++++++++++++++ src/tag/tag_postgres.hpp | 5 +++ 5 files changed, 146 insertions(+), 17 deletions(-) diff --git a/src/tag/CMakeLists.txt b/src/tag/CMakeLists.txt index 8032bdf..e642dc2 100644 --- a/src/tag/CMakeLists.txt +++ b/src/tag/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable(${PROJECT_NAME} target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. PRIVATE ${CMAKE_SOURCE_DIR}/lib/glob2regex/include + PRIVATE ${CMAKE_SOURCE_DIR}/lib/better-enums ) target_link_libraries(${PROJECT_NAME} diff --git a/src/tag/commandline.cpp b/src/tag/commandline.cpp index 44af616..f59eb5a 100644 --- a/src/tag/commandline.cpp +++ b/src/tag/commandline.cpp @@ -27,7 +27,8 @@ namespace din { bool parse_commandline (int parArgc, char* parArgv[], po::variables_map& parVarMap) { po::options_description set_options(ACTION_NAME " options"); set_options.add_options() - //("switch,s", "Help message") + ("delete,d", "Delete tags instead of adding") + ("alltags,a", "Only allowed when --delete is also specified - ignores any given tag list and delete all tags associated to the matching elements instead") //("option,o", po::value()->default_value("default_value"), "Help message") ("ids", po::value(), "Comma-separated list of IDs of files to be tagged") ("set,s", po::value(), "Limit matching to files belonging to the specified set ID") diff --git a/src/tag/main.cpp b/src/tag/main.cpp index 2cafe8d..3ae8614 100644 --- a/src/tag/main.cpp +++ b/src/tag/main.cpp @@ -21,6 +21,7 @@ #include "tag_postgres.hpp" #include "dindexer-common/split_tags.hpp" #include "glob2regex/glob2regex.hpp" +#include "enum.h" #include #include #include @@ -29,10 +30,9 @@ #include namespace { - enum TaggingModes { - TaggingMode_Glob, - TaggingMode_ID - }; + BETTER_ENUM(TaggingMode, char, + Glob, ID + ); std::vector globs_to_regex_list (const std::vector& parGlobs) { std::vector retval; @@ -46,10 +46,7 @@ namespace { return retval; } - int tag_files (const dinlib::SettingsDB& parDB, TaggingModes parMode, const boost::program_options::variables_map& parVM, const std::vector& parTags) { - using boost::lexical_cast; - using boost::string_ref; - + din::OwnerSetInfo make_owner_set_info (const boost::program_options::variables_map& parVM) { din::OwnerSetInfo set_info; if (parVM.count("set")) { set_info.is_valid = true; @@ -59,9 +56,17 @@ namespace { set_info.is_valid = false; set_info.group_id = 0; } + return set_info; + } + + int tag_files (const dinlib::SettingsDB& parDB, TaggingMode parMode, const boost::program_options::variables_map& parVM, const std::vector& parTags) { + using boost::lexical_cast; + using boost::string_ref; + + const din::OwnerSetInfo set_info = make_owner_set_info(parVM); switch (parMode) { - case TaggingMode_ID: + case TaggingMode::ID: { auto ids_string = dinlib::split_tags(parVM["ids"].as()); std::vector ids; @@ -71,7 +76,7 @@ namespace { return 0; } - case TaggingMode_Glob: + case TaggingMode::Glob: { const auto regexes(globs_to_regex_list(parVM["globs"].as>())); din::tag_files(parDB, regexes, parTags, set_info); @@ -83,6 +88,42 @@ namespace { return 1; } } + + int delete_tags (const dinlib::SettingsDB& parDB, TaggingMode parMode, const boost::program_options::variables_map& parVM, const std::vector& parTags) { + using boost::lexical_cast; + using boost::string_ref; + + assert(parVM.count("delete")); + + switch (parMode) { + case TaggingMode::ID: + { + auto ids_string = dinlib::split_tags(parVM["ids"].as()); + std::vector ids; + ids.reserve(ids_string.size()); + std::transform(ids_string.begin(), ids_string.end(), std::back_inserter(ids), &lexical_cast); + if (parVM.count("alltags")) + din::delete_all_tags(parDB, ids, make_owner_set_info(parVM)); + else + din::delete_tags(parDB, ids, parTags, make_owner_set_info(parVM)); + return 0; + } + + case TaggingMode::Glob: + { + const auto regexes(globs_to_regex_list(parVM["globs"].as>())); + if (parVM.count("alltags")) + din::delete_all_tags(parDB, regexes, make_owner_set_info(parVM)); + else + din::delete_tags(parDB, regexes, parTags, make_owner_set_info(parVM)); + return 0; + } + + default: + assert(false); + return 1; + } + } } //unnamed namespace int main (int parArgc, char* parArgv[]) { @@ -122,11 +163,10 @@ int main (int parArgc, char* parArgv[]) { const auto master_tags_string = vm["tags"].as(); const std::vector tags = dinlib::split_tags(master_tags_string); + const auto mode = (glob_mode ? TaggingMode::Glob : TaggingMode::ID); - return tag_files( - settings.db, - (glob_mode ? TaggingMode_Glob : TaggingMode_ID), - vm, - tags - ); + if (not vm.count("delete")) + return tag_files(settings.db, mode, vm, tags); + else + return delete_tags(settings.db, mode, vm, tags); } diff --git a/src/tag/tag_postgres.cpp b/src/tag/tag_postgres.cpp index 6d46a51..7a41096 100644 --- a/src/tag/tag_postgres.cpp +++ b/src/tag/tag_postgres.cpp @@ -70,4 +70,86 @@ namespace din { } } } + + void delete_tags (const dinlib::SettingsDB& parDB, const std::vector& parFiles, const std::vector& parTags, OwnerSetInfo parSet) { + pq::Connection conn(std::string(parDB.username), std::string(parDB.password), std::string(parDB.dbname), std::string(parDB.address), parDB.port); + conn.connect(); + + if (parTags.size() == 1) { + if (parSet.is_valid) { + const std::string query = "UPDATE \"files\" SET \"tags\" = ARRAY_REMOVE(tags, $1) WHERE \"id\" = ANY($2) AND \"group_id\" = $3;"; + conn.query(query, parTags.front(), parFiles, parSet.group_id); + } + else { + const std::string query = "UPDATE \"files\" SET \"tags\" = ARRAY_REMOVE(tags, $1) WHERE \"id\" = ANY($2);"; + conn.query(query, parTags.front(), parFiles); + } + } + else { + if (parSet.is_valid) { + const std::string query = "UPDATE \"files\" SET \"tags\" = ARRAY(SELECT UNNEST(\"tags\") EXCEPT SELECT UNNEST($1)) WHERE \"id\" = ANY($2) AND \"group_id\" = $3;"; + conn.query(query, parTags, parFiles, parSet.group_id); + } + else { + const std::string query = "UPDATE \"files\" SET \"tags\" = ARRAY(SELECT UNNEST(\"tags\") EXCEPT SELECT UNNEST($1)) WHERE \"id\" = ANY($2);"; + conn.query(query, parTags, parFiles); + } + } + } + + void delete_tags (const dinlib::SettingsDB& parDB, const std::vector& parRegexes, const std::vector& parTags, OwnerSetInfo parSet) { + pq::Connection conn(std::string(parDB.username), std::string(parDB.password), std::string(parDB.dbname), std::string(parDB.address), parDB.port); + conn.connect(); + + if (parTags.size() == 1) { + if (parSet.is_valid) { + const std::string query = "UPDATE \"files\" SET \"tags\" = ARRAY_REMOVE(tags, $1) WHERE \"group_id\" = $3 AND \"path\" ~ ANY($3);"; + conn.query(query, parTags.front(), parSet.group_id, parRegexes); + } + else { + const std::string query = "UPDATE \"files\" SET \"tags\" = ARRAY_REMOVE(tags, $1) WHERE \"path\" ~ ANY($2);"; + conn.query(query, parTags.front(), parRegexes); + } + } + else { + if (parSet.is_valid) { + const std::string query = "UPDATE \"files\" SET \"tags\" = ARRAY(SELECT UNNEST(\"tags\") EXCEPT SELECT UNNEST($1)) WHERE \"group_id\" = $2 AND \"path\" ~ ANY($3);"; + conn.query(query, parTags, parSet.group_id, parRegexes); + } + else { + const std::string query = "UPDATE \"files\" SET \"tags\" = ARRAY(SELECT UNNEST(\"tags\") EXCEPT SELECT UNNEST($1)) WHERE \"path\" = ANY($2);"; + conn.query(query, parTags, parRegexes); + } + } + } + + void delete_all_tags (const dinlib::SettingsDB& parDB, const std::vector& parFiles, OwnerSetInfo parSet) { + pq::Connection conn(std::string(parDB.username), std::string(parDB.password), std::string(parDB.dbname), std::string(parDB.address), parDB.port); + conn.connect(); + + if (parSet.is_valid) { + const std::string query = + "UPDATE \"files\" SET \"tags\" = '{}' WHERE \"id\"=ANY($1) AND \"group_id\"=$2;"; + conn.query(query, parFiles, parSet.group_id); + } + else { + const std::string query = + "UPDATE \"files\" SET \"tags\" = '{}' WHERE \"id\"=ANY($1);"; + conn.query(query, parFiles); + } + } + + void delete_all_tags (const dinlib::SettingsDB& parDB, const std::vector& parRegexes, OwnerSetInfo parSet) { + pq::Connection conn(std::string(parDB.username), std::string(parDB.password), std::string(parDB.dbname), std::string(parDB.address), parDB.port); + conn.connect(); + + if (parSet.is_valid) { + const std::string query = "UPDATE \"files\" SET \"tags\" = '{}' WHERE \"group_id\"=$1 AND \"path\" ~ ANY($2);"; + conn.query(query, parSet.group_id, parRegexes); + } + else { + const std::string query = "UPDATE \"files\" SET \"tags\" = '{}' WHERE \"path\" ~ ANY($2);"; + conn.query(query, parRegexes); + } + } } //namespace din diff --git a/src/tag/tag_postgres.hpp b/src/tag/tag_postgres.hpp index 70e5877..5fd2608 100644 --- a/src/tag/tag_postgres.hpp +++ b/src/tag/tag_postgres.hpp @@ -34,6 +34,11 @@ namespace din { void tag_files ( const dinlib::SettingsDB& parDB, const std::vector& parFiles, const std::vector& parTags, OwnerSetInfo parSet ); void tag_files ( const dinlib::SettingsDB& parDB, const std::vector& parRegexes, const std::vector& parTags, OwnerSetInfo parSet ); + + void delete_tags ( const dinlib::SettingsDB& parDB, const std::vector& parFiles, const std::vector& parTags, OwnerSetInfo parSet ); + void delete_tags ( const dinlib::SettingsDB& parDB, const std::vector& parRegexes, const std::vector& parTags, OwnerSetInfo parSet ); + void delete_all_tags ( const dinlib::SettingsDB& parDB, const std::vector& parFiles, OwnerSetInfo parSet ); + void delete_all_tags ( const dinlib::SettingsDB& parDB, const std::vector& parRegexes, OwnerSetInfo parSet ); } //namespace din #endif