diff --git a/src/.idea/codeStyles/Project.xml b/src/.idea/codeStyles/Project.xml index 213c5ea87..550d10440 100644 --- a/src/.idea/codeStyles/Project.xml +++ b/src/.idea/codeStyles/Project.xml @@ -23,7 +23,7 @@ - + diff --git a/src/init.cpp b/src/init.cpp index d5bbb2c15..c2a982459 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -665,6 +665,7 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-sqltimeout", strprintf("Timeout for ReadOnly sql querys (default: %ds)", 10), ArgsManager::ALLOW_ANY, OptionsCategory::SQLITE); argsman.AddArg("-sqlsharedcache", strprintf("Experimental: enable shared cache for sqlite connections (default: disabled)"), ArgsManager::ALLOW_ANY, OptionsCategory::SQLITE); argsman.AddArg("-sqlcachesize", strprintf("Experimental: Cache size for SQLite connection in megabytes (default: %d mb)", 5), ArgsManager::ALLOW_ANY, OptionsCategory::SQLITE); + argsman.AddArg("-withoutweb", strprintf("Disable WEB part of database (default: %u)", false), ArgsManager::ALLOW_ANY, OptionsCategory::SQLITE); #if HAVE_DECL_DAEMON @@ -1641,7 +1642,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA PocketWeb::PocketFrontendInst.Init(); - if (args.GetBoolArg("-api", DEFAULT_API_ENABLE)) + // Always start WEB DB building thread + if (!args.GetBoolArg("-withoutweb", false)) PocketServices::WebPostProcessorInst.Start(threadGroup); // ********************************************************* Step 4b: Additional settings diff --git a/src/pocketdb/consensus/Reputation.h b/src/pocketdb/consensus/Reputation.h index 0ede93c6e..31e6bb3df 100644 --- a/src/pocketdb/consensus/Reputation.h +++ b/src/pocketdb/consensus/Reputation.h @@ -282,8 +282,8 @@ namespace PocketConsensus { 1180000, 0, [](int height) { return make_shared(height); }}, { 1324655, 65000, [](int height) { return make_shared(height); }}, { 1324655, 75000, [](int height) { return make_shared(height); }}, - { 1680000, 761000, [](int height) { return make_shared(height); }}, - { 1680000, 772000, [](int height) { return make_shared(height); }}, + { 1700000, 761000, [](int height) { return make_shared(height); }}, + { 1700000, 772000, [](int height) { return make_shared(height); }}, }; public: shared_ptr Instance(int height) diff --git a/src/pocketdb/consensus/moderation/Flag.hpp b/src/pocketdb/consensus/moderation/Flag.hpp index 43bbb12be..a36a1f5c0 100644 --- a/src/pocketdb/consensus/moderation/Flag.hpp +++ b/src/pocketdb/consensus/moderation/Flag.hpp @@ -137,7 +137,7 @@ namespace PocketConsensus private: const vector> m_rules = { { 0, -1, [](int height) { return make_shared(height); }}, - { 1680000, 761000, [](int height) { return make_shared(height); }}, + { 1700000, 761000, [](int height) { return make_shared(height); }}, }; public: shared_ptr Instance(int height) diff --git a/src/pocketdb/repositories/web/SearchRepository.cpp b/src/pocketdb/repositories/web/SearchRepository.cpp index 4bf789f07..c447ce988 100644 --- a/src/pocketdb/repositories/web/SearchRepository.cpp +++ b/src/pocketdb/repositories/web/SearchRepository.cpp @@ -231,152 +231,59 @@ namespace PocketDb return result; } - UniValue SearchRepository::GetRecomendedAccountsBySubscriptions(const string& address, int cntOut) + //TODO (o1q): remove it + vector SearchRepository::GetRecommendedAccountByAddressSubscriptionsOld(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth, int cntSubscriptions) { auto func = __func__; - UniValue result(UniValue::VARR); + vector ids; if (address.empty()) - return result; - - string sql = R"sql( - select - recommendation.address, - p.String2 as name, - p.String3 as avatar - - , ifnull(( - select r.Value - from Ratings r indexed by Ratings_Type_Id_Last_Height - where r.Type=0 and r.Id=u.Id and r.Last=1) - ,0) as Reputation - - , ( - select count(*) - from Transactions subs indexed by Transactions_Type_Last_String2_Height - where subs.Type in (302,303) and subs.Height is not null and subs.Last = 1 and subs.String2 = u.String1 - ) as SubscribersCount - from ( - select - t.String2 address - from Transactions t indexed by Transactions_Type_Last_String1_String2_Height - where t.Last = 1 - and t.Type in (302,303) - and t.Height is not null - and t.String2 != ? - and t.String1 in (select s.String1 - from Transactions s indexed by Transactions_Type_Last_String2_Height - where s.Type in (302,303) - and s.Last = 1 - and s.Height is not null - and s.String2 = ?) - group by t.String2 - order by count(*) desc - limit ? - )recommendation - cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id on u.String1 = recommendation.address - and u.Type in (100,101,102) - and u.Last=1 - and u.Height is not null - cross join Payload p on p.TxHash = u.Hash - - )sql"; - - TryTransactionStep(__func__, [&]() - { - auto stmt = SetupSqlStatement(sql); - - int i = 1; - TryBindStatementText(stmt, i++, address); - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, cntOut); - - while (sqlite3_step(*stmt) == SQLITE_ROW) - { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("address", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 1); ok) record.pushKV("name", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 2); ok) record.pushKV("avatar", value); - if (auto[ok, value] = TryGetColumnInt(*stmt, 3); ok) record.pushKV("reputation", value / 10.0); - if (auto[ok, value] = TryGetColumnInt(*stmt, 4); ok) record.pushKV("subscribers_count", value); - result.push_back(record); - } - - FinalizeSqlStatement(*stmt); - }); + return ids; - return result; - } + string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); - UniValue SearchRepository::GetRecomendedAccountsByScoresOnSimilarAccounts(const string& address, const vector& contentTypes, int nHeight, int depth, int cntOut) - { - auto func = __func__; - UniValue result(UniValue::VARR); + string excludeAddressFilter = "?"; + if (!addressExclude.empty()) + excludeAddressFilter += ", ?"; - if (address.empty()) - return result; + string langFilter = ""; + if (!lang.empty()) + langFilter = "cross join Payload lang on lang.TxHash = u.Hash and lang.String1 = ?"; - string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); + int minReputation = 300; string sql = R"sql( - select recommendation.address, - p.String2 as name, - p.String3 as avatar - - , ifnull(( - select r.Value - from Ratings r indexed by Ratings_Type_Id_Last_Height - where r.Type=0 and r.Id=u.Id and r.Last=1) - ,0) as Reputation - - , ( - select count(*) - from Transactions subs indexed by Transactions_Type_Last_String2_Height - where subs.Type in (302,303) and subs.Height is not null and subs.Last = 1 and subs.String2 = u.String1 - ) as SubscribersCount - from ( - select - tOtherContents.String1 as address - from Transactions tOtherContents - indexed by Transactions_Type_Last_String1_String2_Height - where tOtherContents.String2 in ( - select tOtherLikes.String2 as OtherLikedContent - from Transactions tOtherlikes - where tOtherLikes.String1 in ( - select tLikes.String1 as Liker - from Transactions tLikes - where tLikes.String2 in ( - select tContents.String2 as BloggerContent - from Transactions tContents - where tContents.Type in ( )sql" + contentTypesFilter + R"sql( ) - and tContents.Last = 1 - and tContents.String1 = ? - and tContents.Height >= ? - ) - and tLikes.Type in (300) - and tLikes.Last in (1, 0) - and tLikes.Int1 > 3 - and tLikes.Height >= ? - ) - and tOtherLikes.Type in (300) - and tOtherLikes.Last in (1, 0) - and tOtherLikes.Int1 > 3 - and tOtherLikes.Height >= ? + select Contents.String1 + from Transactions Rates indexed by Transactions_Type_Last_String1_Height_Id + cross join Transactions Contents indexed by Transactions_Type_Last_String2_Height + on Contents.String2 = Rates.String2 + and Contents.Last = 1 + and Contents.Height > 0 + and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) + and Contents.String1 not in ( ? ) + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.String1 = Contents.String1 + and u.Type in (100) + and u.Last = 1 + and u.Height > 0 + )sql" + langFilter + R"sql( + where Rates.Type in (300) + and Rates.Int1 = 5 + and Rates.Height > ? + and Rates.Last in (0, 1) + and Rates.String1 in ( + select subscribes.String2 + from Transactions subscribes indexed by Transactions_Type_Last_String1_Height_Id + where subscribes.Type in (302, 303) + and subscribes.Last = 1 + and subscribes.Height > 0 + and subscribes.String1 = ? + limit ? ) - and tOtherContents.Type in ( )sql" + contentTypesFilter + R"sql( ) - and tOtherContents.String1 != ? - and tOtherContents.Last = 1 - and tOtherContents.Height >= ? - group by tOtherContents.String1 - order by count(*) desc - limit ? - )recommendation - cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id - on u.String1 = recommendation.address - and u.Type in (100,101,102) - and u.Last=1 - and u.Height is not null - cross join Payload p on p.TxHash = u.Hash + group by Contents.String1 + having count(*) > 1 + order by count (*) desc + limit ? )sql"; TryTransactionStep(__func__, [&]() @@ -386,253 +293,97 @@ namespace PocketDb int i = 1; for (const auto& contenttype: contentTypes) TryBindStatementInt(stmt, i++, contenttype); - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); - for (const auto& contenttype: contentTypes) - TryBindStatementInt(stmt, i++, contenttype); - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, cntOut); - - while (sqlite3_step(*stmt) == SQLITE_ROW) - { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("address", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 1); ok) record.pushKV("name", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 2); ok) record.pushKV("avatar", value); - if (auto[ok, value] = TryGetColumnInt(*stmt, 3); ok) record.pushKV("reputation", value / 10.0); - if (auto[ok, value] = TryGetColumnInt(*stmt, 4); ok) record.pushKV("subscribers_count", value); - result.push_back(record); - } - FinalizeSqlStatement(*stmt); - }); - - return result; - } - - UniValue SearchRepository::GetRecomendedAccountsByScoresFromAddress(const string& address, const vector& contentTypes, int nHeight, int depth, int cntOut) - { - auto func = __func__; - UniValue result(UniValue::VARR); + TryBindStatementText(stmt, i++, address); + if (!addressExclude.empty()) + TryBindStatementText(stmt, i++, addressExclude); - if (address.empty()) - return result; + if (!lang.empty()) + TryBindStatementText(stmt, i++, lang); - string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); + TryBindStatementInt(stmt, i++, nHeight-depth); - string sql = R"sql( - select recommendation.address, - p.String2 as name, - p.String3 as avatar - - , ifnull(( - select r.Value - from Ratings r indexed by Ratings_Type_Id_Last_Height - where r.Type=0 and r.Id=u.Id and r.Last=1) - ,0) as Reputation - - , ( - select count(*) - from Transactions subs indexed by Transactions_Type_Last_String2_Height - where subs.Type in (302,303) and subs.Height is not null and subs.Last = 1 and subs.String2 = u.String1 - ) as SubscribersCount - from ( - select - tOtherContents.String1 as address - from Transactions tOtherContents - indexed by Transactions_Type_Last_String2_Height - where tOtherContents.String2 in ( - select tOtherLikes.String2 as OtherLikedContent - from Transactions tOtherlikes - where tOtherLikes.String1 in ( - select tLikes.String1 as Liker - from Transactions tLikes - where tLikes.String2 in ( - select tAddressLikes.String2 as ContentsLikedByAddress - from Transactions tAddressLikes - where tAddressLikes.String1 = ? - and tAddressLikes.Type in (300) - and tAddressLikes.Last in (1, 0) - and tAddressLikes.Int1 > 3 - and tAddressLikes.Height >= ? - ) - and tLikes.Type in (300) - and tLikes.Last in (1, 0) - and tLikes.Int1 > 3 - and tLikes.Height >= ? - ) - and tOtherLikes.Type in (300) - and tOtherLikes.Last in (1, 0) - and tOtherLikes.Int1 > 3 - and tOtherLikes.Height >= ? - ) - and tOtherContents.Type in ( )sql" + contentTypesFilter + R"sql( ) - and tOtherContents.String1 != ? - and tOtherContents.Last = 1 - and tOtherContents.Height >= ? - group by tOtherContents.String1 - order by count(*) desc - limit ? - )recommendation - cross join Transactions u on u.String1 = recommendation.address - and u.Type in (100,101,102) - and u.Last=1 - and u.Height is not null - cross join Payload p on p.TxHash = u.Hash - )sql"; + TryBindStatementText(stmt, i++, address); - TryTransactionStep(__func__, [&]() - { - auto stmt = SetupSqlStatement(sql); + TryBindStatementInt(stmt, i++, cntSubscriptions); - int i = 1; - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); - for (const auto& contenttype: contentTypes) - TryBindStatementInt(stmt, i++, contenttype); - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, nHeight - depth); TryBindStatementInt(stmt, i++, cntOut); while (sqlite3_step(*stmt) == SQLITE_ROW) { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("address", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 1); ok) record.pushKV("name", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 2); ok) record.pushKV("avatar", value); - if (auto[ok, value] = TryGetColumnInt(*stmt, 3); ok) record.pushKV("reputation", value / 10.0); - if (auto[ok, value] = TryGetColumnInt(*stmt, 4); ok) record.pushKV("subscribers_count", value); - result.push_back(record); + if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) + ids.push_back(value); } FinalizeSqlStatement(*stmt); }); - return result; + return ids; } - UniValue SearchRepository::GetRecomendedAccountsByTags(const vector& tags, int nHeight, int depth, int cntOut) + //TODO (o1q): remove it + vector SearchRepository::GetRecommendedContentByAddressSubscriptionsOld(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth, int cntSubscriptions) { auto func = __func__; - UniValue result(UniValue::VARR); - - if (tags.empty()) - return result; - - string tagsFilter = join(vector(tags.size(), "?"), ","); - - string sql = R"sql( - select - recommendation.address, - p.String2 as name, - p.String3 as avatar - - , ifnull(( - select r.Value - from Ratings r indexed by Ratings_Type_Id_Last_Height - where r.Type=0 and r.Id=u.Id and r.Last=1) - ,0) as Reputation - - , ( - select count(*) - from Transactions subs indexed by Transactions_Type_Last_String2_Height - where subs.Type in (302,303) and subs.Height is not null and subs.Last = 1 and subs.String2 = u.String1 - ) as SubscribersCount - from ( - select authors.string1 address - from ( - select c.String1 - from Transactions sc indexed by Transactions_Type_Last_Height_Id - cross join Transactions c indexed by Transactions_Type_Last_String2_Height - on c.String2 = sc.String2 and c.Type in (200, 201, 202) and c.Height > 0 and c.Last = 1 - and c.id in (select tm.ContentId - from web.Tags tag indexed by Tags_Lang_Value_Id - join web.TagsMap tm indexed by TagsMap_TagId_ContentId - on tag.Id = tm.TagId - where tag.Value in ( )sql" + join(vector(tags.size(), "?"), ",") + R"sql( )) - where sc.Type in (300) - and sc.Last in (0, 1) - and sc.Height > ? - and sc.Int1 = 5 - ) authors - group by authors.string1 - order by count(*) desc - limit ? - )recommendation - cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id on u.String1 = recommendation.address - and u.Type in (100,101,102) - and u.Last=1 - and u.Height is not null - cross join Payload p on p.TxHash = u.Hash - - )sql"; - - TryTransactionStep(__func__, [&]() - { - auto stmt = SetupSqlStatement(sql); - - int i = 1; - for (const auto& tag: tags) - TryBindStatementText(stmt, i++, tag); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, cntOut); - - while (sqlite3_step(*stmt) == SQLITE_ROW) - { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("address", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 1); ok) record.pushKV("name", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 2); ok) record.pushKV("avatar", value); - if (auto[ok, value] = TryGetColumnInt(*stmt, 3); ok) record.pushKV("reputation", value / 10.0); - if (auto[ok, value] = TryGetColumnInt(*stmt, 4); ok) record.pushKV("subscribers_count", value); - result.push_back(record); - } + vector ids; - FinalizeSqlStatement(*stmt); - }); + if (contentAddress.empty()) + return ids; - return result; - } + string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); - UniValue SearchRepository::GetRecomendedContentsByScoresOnSimilarContents(const string& contentid, const vector& contentTypes, int depth, int cntOut) - { - auto func = __func__; - UniValue result(UniValue::VARR); + string excludeAddressFilter = "?"; + if (!address.empty()) + excludeAddressFilter += ", ?"; - if (contentid.empty()) - return result; + string langFilter = ""; + if (!lang.empty()) + langFilter = "cross join Payload lang on lang.TxHash = Contents.Hash and lang.String1 = ?"; - string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); + int minReputation = 300; string sql = R"sql( - select OtherRaters.String2 OtherScoredContent, count(*) cnt - from Transactions OtherRaters indexed by Transactions_Type_Last_String1_Height_Id + select recomendations.Id + from ( + select Contents.String1, + Contents.Id, + Rates.String2, + count(*) count + from Transactions Rates indexed by Transactions_Type_Last_String1_Height_Id cross join Transactions Contents indexed by Transactions_Type_Last_String2_Height - on OtherRaters.String2 = Contents.String2 and Contents.Last = 1 and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) and Contents.Height > 0 - where OtherRaters.Type in (300) - and OtherRaters.Int1 > 3 - and OtherRaters.Last in (1, 0) - and OtherRaters.Height >= (select Height - from Transactions indexed by Transactions_Type_Last_String2_Height - where Type in ( )sql" + contentTypesFilter + R"sql( ) - and String2 = ? - and Last = 1) - ? - and OtherRaters.String1 in ( - select String1 as Rater - from Transactions Raters indexed by Transactions_Type_Last_String2_Height - where Raters.Type in (300) - and Raters.Int1 > 3 - and Raters.String2 = ? - and Raters.Last in (1, 0) - ) - and OtherRaters.String2 != ? - group by OtherRaters.String2 + on Contents.String2 = Rates.String2 + and Contents.Last = 1 + and Contents.Height > 0 + and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) + and Contents.String1 not in ( )sql" + excludeAddressFilter + R"sql( ) + )sql" + langFilter + R"sql( + where Rates.Type in (300) + and Rates.Int1 = 5 + and Rates.Height > ? + and Rates.Last in (0, 1) + and Rates.String1 in ( + select subscribers.String2 + from Transactions subscribers indexed by Transactions_Type_Last_String1_Height_Id + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.Type in (100) + and u.Last = 1 and u.Height > 0 + and u.String1 = subscribers.String1 + cross join Ratings r indexed by Ratings_Type_Id_Last_Value + on r.Id = u.Id + and r.Type = 0 + and r.Last = 1 + and r.Value > ? + where subscribers.Type in (302, 303) + and subscribers.Last = 1 + and subscribers.Height > 0 + and subscribers.String1 = ? + limit ? + ) + group by Rates.String2 + having count(*) > 1 order by count(*) desc + ) recomendations + group by recomendations.String1 limit ? )sql"; @@ -643,95 +394,36 @@ namespace PocketDb int i = 1; for (const auto& contenttype: contentTypes) TryBindStatementInt(stmt, i++, contenttype); - for (const auto& contenttype: contentTypes) - TryBindStatementInt(stmt, i++, contenttype); - TryBindStatementText(stmt, i++, contentid); - TryBindStatementInt(stmt, i++, depth); - TryBindStatementText(stmt, i++, contentid); - TryBindStatementText(stmt, i++, contentid); - TryBindStatementInt(stmt, i++, cntOut); - - while (sqlite3_step(*stmt) == SQLITE_ROW) - { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("contentid", value); - result.push_back(record); - } - - FinalizeSqlStatement(*stmt); - }); - return result; - } - - UniValue SearchRepository::GetRecomendedContentsByScoresFromAddress(const string& address, const vector& contentTypes, int nHeight, int depth, int cntOut) - { - auto func = __func__; - UniValue result(UniValue::VARR); + TryBindStatementText(stmt, i++, contentAddress); + if (!address.empty()) + TryBindStatementText(stmt, i++, address); - if (address.empty()) - return result; + if (!lang.empty()) + TryBindStatementText(stmt, i++, lang); - string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); + TryBindStatementInt(stmt, i++, nHeight-depth); - string sql = R"sql( - select OtherRaters.String2 as OtherScoredContent, count(*) cnt - from Transactions OtherRaters indexed by Transactions_Type_Last_String1_Height_Id - cross join Transactions Contents indexed by Transactions_Type_Last_String2_Height - on OtherRaters.String2 = Contents.String2 and Contents.Last = 1 and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) and Contents.Height > 0 - where OtherRaters.String1 in ( - select Scores.String1 as Rater - from Transactions Scores indexed by Transactions_Type_Last_String2_Height - where Scores.String2 in ( - select addressScores.String2 as ContentsScoredByAddress - from Transactions addressScores indexed by Transactions_Type_Last_String1_Height_Id - where addressScores.String1 = ? - and addressScores.Type in (300) - and addressScores.Last in (1, 0) - and addressScores.Int1 > 3 - and addressScores.Height >= ? - ) - and Scores.Type in (300) - and Scores.Last in (1, 0) - and Scores.Int1 > 3 - and Scores.Height >= ? - ) - and OtherRaters.Type in (300) - and OtherRaters.Last in (1, 0) - and OtherRaters.Int1 > 3 - and OtherRaters.Height >= ? - group by OtherRaters.String2 - order by count(*) desc - limit ? - )sql"; + TryBindStatementInt(stmt, i++, minReputation); + TryBindStatementText(stmt, i++, contentAddress); - TryTransactionStep(__func__, [&]() - { - auto stmt = SetupSqlStatement(sql); + TryBindStatementInt(stmt, i++, cntSubscriptions); - int i = 1; - for (const auto& contenttype: contentTypes) - TryBindStatementInt(stmt, i++, contenttype); - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); TryBindStatementInt(stmt, i++, cntOut); while (sqlite3_step(*stmt) == SQLITE_ROW) { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("contentid", value); - result.push_back(record); + if (auto[ok, value] = TryGetColumnInt64(*stmt, 0); ok) + ids.push_back(value); } FinalizeSqlStatement(*stmt); }); - return result; + return ids; } - vector SearchRepository::GetRecommendedAccountByAddressSubscriptions(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth, int cntSubscriptions) + vector SearchRepository::GetRecommendedAccountByAddressSubscriptions(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth) { auto func = __func__; vector ids; @@ -749,7 +441,10 @@ namespace PocketDb if (!lang.empty()) langFilter = "cross join Payload lang on lang.TxHash = u.Hash and lang.String1 = ?"; - int minReputation = 30; + int minReputation = 300; + int limitSubscriptions = 30; + int limitSubscriptionsTotal = 30; + int cntRates = 1; string sql = R"sql( select Contents.String1 @@ -759,7 +454,7 @@ namespace PocketDb and Contents.Last = 1 and Contents.Height > 0 and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) - and Contents.String1 not in ( ? ) + and Contents.String1 not in ( )sql" + excludeAddressFilter + R"sql( ) cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id on u.String1 = Contents.String1 and u.Type in (100) @@ -770,17 +465,56 @@ namespace PocketDb and Rates.Int1 = 5 and Rates.Height > ? and Rates.Last in (0, 1) - and Rates.String1 in ( - select subscribes.String2 - from Transactions subscribes indexed by Transactions_Type_Last_String1_Height_Id - where subscribes.Type in (302, 303) - and subscribes.Last = 1 - and subscribes.Height > 0 - and subscribes.String1 = ? - limit ? - ) + and Rates.String1 in + ( + select address + from ( + select rnk, address + from ( + select 1 as rnk, subscribes.String2 as address + from Transactions subscribes indexed by Transactions_String1_Last_Height + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.Type in (100) + and u.Last = 1 and u.Height > 0 + and u.String1 = subscribes.String2 + cross join Ratings r indexed by Ratings_Type_Id_Last_Value + on r.Id = u.Id + and r.Type = 0 + and r.Last = 1 + and r.Value > ? + where subscribes.Type in (302, 303) + and subscribes.Last = 1 + and subscribes.String1 = ? + and subscribes.Height is not null + order by subscribes.Height desc + limit ? + ) + union + select rnk, address + from ( + select 2 as rnk, subscribers.String1 as address + from Transactions subscribers indexed by Transactions_Type_Last_String2_Height + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.Type in (100) + and u.Last = 1 and u.Height > 0 + and u.String1 = subscribers.String1 + cross join Ratings r indexed by Ratings_Type_Id_Last_Value + on r.Id = u.Id + and r.Type = 0 + and r.Last = 1 + and r.Value > ? + where subscribers.Type in (302, 303) + and subscribers.Last = 1 + and subscribers.String2 = ? + and subscribers.Height is not null + order by random() + limit ? + )) + order by rnk + limit ? + ) group by Contents.String1 - having count(*) > 1 + having count(*) > ? order by count (*) desc limit ? )sql"; @@ -802,9 +536,17 @@ namespace PocketDb TryBindStatementInt(stmt, i++, nHeight-depth); + TryBindStatementInt(stmt, i++, minReputation); TryBindStatementText(stmt, i++, address); + TryBindStatementInt(stmt, i++, limitSubscriptions); - TryBindStatementInt(stmt, i++, cntSubscriptions); + TryBindStatementInt(stmt, i++, minReputation); + TryBindStatementText(stmt, i++, address); + TryBindStatementInt(stmt, i++, limitSubscriptions); + + TryBindStatementInt(stmt, i++, limitSubscriptionsTotal); + + TryBindStatementInt(stmt, i++, cntRates); TryBindStatementInt(stmt, i++, cntOut); @@ -820,7 +562,7 @@ namespace PocketDb return ids; } - vector SearchRepository::GetRecommendedContentByAddressSubscriptions(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth, int cntSubscriptions) + vector SearchRepository::GetRecommendedContentByAddressSubscriptions(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth) { auto func = __func__; vector ids; @@ -836,52 +578,87 @@ namespace PocketDb string langFilter = ""; if (!lang.empty()) - langFilter = "cross join Payload lang on lang.TxHash = Contents.Hash and lang.String1 = ?"; + langFilter = "cross join Payload lang indexed by Payload_String1_TxHash on lang.TxHash = Contents.Hash and lang.String1 = ?"; - int minReputation = 30; + int minReputation = 300; + int limitSubscriptions = 50; + int limitSubscriptionsTotal = 30; + int cntRates = 1; string sql = R"sql( select recomendations.Id from ( - select Contents.String1, - Contents.Id, - Rates.String2, - count(*) count - from Transactions Rates indexed by Transactions_Type_Last_String1_Height_Id - cross join Transactions Contents indexed by Transactions_Type_Last_String2_Height - on Contents.String2 = Rates.String2 - and Contents.Last = 1 - and Contents.Height > 0 - and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) - and Contents.String1 not in ( )sql" + excludeAddressFilter + R"sql( ) - )sql" + langFilter + R"sql( - where Rates.Type in (300) - and Rates.Int1 = 5 - and Rates.Height > ?--0 - and Rates.Last in (0, 1) - and Rates.String1 in ( - select subscribers.String2 - from Transactions subscribers indexed by Transactions_Type_Last_String1_Height_Id - cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id - on u.Type in (100) - and u.Last = 1 and u.Height > 0 - and u.String1 = subscribers.String1 - cross join Ratings r indexed by Ratings_Type_Id_Last_Value - on r.Id = u.Id - and r.Type = 0 - and r.Last = 1 - and r.Value > ? - where subscribers.Type in (302, 303) - and subscribers.Last = 1 - and subscribers.Height > 0 - and subscribers.String1 = ? - limit ? - ) - group by Rates.String2 - having count(*) > 1 - order by count(*) desc - ) recomendations + select Contents.String1, + Contents.Id, + --Rates.String2, + count(*) count + from Transactions Rates indexed by Transactions_Type_Last_String1_Height_Id + cross join Transactions Contents indexed by Transactions_Type_Last_String2_Height + on Contents.String2 = Rates.String2 + and Contents.Last = 1 + and Contents.Height > 0 + and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) + and Contents.String1 not in ( )sql" + excludeAddressFilter + R"sql( ) + )sql" + langFilter + R"sql( + where Rates.Type in (300) + and Rates.Int1 = 5 + and Rates.Height > ? + and Rates.Last in (0, 1) + and Rates.String1 in + ( + select address + from ( + select rnk, address + from ( + select 1 as rnk, subscribes.String2 as address + from Transactions subscribes indexed by Transactions_String1_Last_Height + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.Type in (100) + and u.Last = 1 and u.Height > 0 + and u.String1 = subscribes.String2 + cross join Ratings r indexed by Ratings_Type_Id_Last_Value + on r.Id = u.Id + and r.Type = 0 + and r.Last = 1 + and r.Value > ? + where subscribes.Type in (302, 303) + and subscribes.Last = 1 + and subscribes.String1 = ? + and subscribes.Height is not null + order by subscribes.Height desc + limit ? + ) + union + select rnk, address + from ( + select 2 as rnk, subscribers.String1 as address + from Transactions subscribers indexed by Transactions_Type_Last_String2_Height + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.Type in (100) + and u.Last = 1 and u.Height > 0 + and u.String1 = subscribers.String1 + cross join Ratings r indexed by Ratings_Type_Id_Last_Value + on r.Id = u.Id + and r.Type = 0 + and r.Last = 1 + and r.Value > ? + where subscribers.Type in (302, 303) + and subscribers.Last = 1 + and subscribers.String2 = ? + and subscribers.Height is not null + order by random() + limit ? + )) + order by rnk + limit ? + ) + --group by Rates.String2 + group by Contents.Id + having count(*) > ? + order by count(*) desc + ) recomendations group by recomendations.String1 + order by recomendations.count desc limit ? )sql"; @@ -904,8 +681,15 @@ namespace PocketDb TryBindStatementInt(stmt, i++, minReputation); TryBindStatementText(stmt, i++, contentAddress); + TryBindStatementInt(stmt, i++, limitSubscriptions); - TryBindStatementInt(stmt, i++, cntSubscriptions); + TryBindStatementInt(stmt, i++, minReputation); + TryBindStatementText(stmt, i++, contentAddress); + TryBindStatementInt(stmt, i++, limitSubscriptions); + + TryBindStatementInt(stmt, i++, limitSubscriptionsTotal); + + TryBindStatementInt(stmt, i++, cntRates); TryBindStatementInt(stmt, i++, cntOut); diff --git a/src/pocketdb/repositories/web/SearchRepository.h b/src/pocketdb/repositories/web/SearchRepository.h index 2ee3f07f7..18ccf9083 100644 --- a/src/pocketdb/repositories/web/SearchRepository.h +++ b/src/pocketdb/repositories/web/SearchRepository.h @@ -34,15 +34,12 @@ namespace PocketDb vector SearchUsersOld(const SearchRequest& request); vector SearchUsers(const string& keyword); - UniValue GetRecomendedAccountsBySubscriptions(const string& address, int cntOut = 10); - UniValue GetRecomendedAccountsByScoresOnSimilarAccounts(const string& address, const vector& contentTypes, int nHeight, int depth = 1000, int cntOut = 10); - UniValue GetRecomendedAccountsByScoresFromAddress(const string& address, const vector& contentTypes, int nHeight, int depth = 1000, int cntOut = 10); - UniValue GetRecomendedAccountsByTags(const vector& tags, int nHeight, int depth = 1000, int cntOut = 10); - UniValue GetRecomendedContentsByScoresOnSimilarContents(const string& contentid, const vector& contentTypes, int depth = 1000, int cntOut = 10); - UniValue GetRecomendedContentsByScoresFromAddress(const string& address, const vector& contentTypes, int nHeight, int depth = 1000, int cntOut = 10); - - vector GetRecommendedAccountByAddressSubscriptions(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */, int cntSubscriptions = 100); - vector GetRecommendedContentByAddressSubscriptions(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */, int cntSubscriptions = 100); + //TODO (o1q): remove it + vector GetRecommendedAccountByAddressSubscriptionsOld(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */, int cntSubscriptions = 100); + vector GetRecommendedContentByAddressSubscriptionsOld(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */, int cntSubscriptions = 100); + + vector GetRecommendedAccountByAddressSubscriptions(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */); + vector GetRecommendedContentByAddressSubscriptions(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */); vector GetRandomContentByAddress(const string& contentAddress, const vector& contentTypes, const string& lang, int cntOut); }; diff --git a/src/pocketdb/repositories/web/WebRpcRepository.cpp b/src/pocketdb/repositories/web/WebRpcRepository.cpp index 852477cfb..505d54870 100644 --- a/src/pocketdb/repositories/web/WebRpcRepository.cpp +++ b/src/pocketdb/repositories/web/WebRpcRepository.cpp @@ -2826,19 +2826,19 @@ namespace PocketDb string langFilter; if (!lang.empty()) - langFilter += " join Payload p indexed by Payload_String1_TxHash on p.TxHash = t.Hash and p.String1 = ? "; + langFilter += " cross join Payload p indexed by Payload_String1_TxHash on p.TxHash = t.Hash and p.String1 = ? "; string sql = R"sql( select t.Id - from Transactions t indexed by Transactions_Last_Id_Height + from Transactions t indexed by Transactions_Type_Last_Height_Id - join Ratings cr indexed by Ratings_Type_Id_Last_Value + cross join Ratings cr indexed by Ratings_Type_Id_Last_Value on cr.Type = 2 and cr.Last = 1 and cr.Id = t.Id and cr.Value > 0 )sql" + langFilter + R"sql( - join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id on u.Type in (100) and u.Last = 1 and u.Height > 0 and u.String1 = t.String1 left join Ratings ur indexed by Ratings_Type_Id_Last_Height @@ -2846,7 +2846,7 @@ namespace PocketDb where t.Type in )sql" + contentTypesWhere + R"sql( and t.Last = 1 - and t.String3 is null + --and t.String3 is null and t.Height > ? and t.Height <= ? @@ -2884,7 +2884,7 @@ namespace PocketDb ) )sql"; } - sql += " order by t.Id desc "; + sql += " order by cr.Value desc "; sql += " limit ? "; // --------------------------------------------- diff --git a/src/pocketdb/web/PocketContentRpc.cpp b/src/pocketdb/web/PocketContentRpc.cpp index 0dd533ca0..7ce041f68 100644 --- a/src/pocketdb/web/PocketContentRpc.cpp +++ b/src/pocketdb/web/PocketContentRpc.cpp @@ -100,17 +100,19 @@ namespace PocketWeb::PocketWebRpc } } - // feed's address if (request.params.size() > 10) { - RPCTypeCheckArgument(request.params[10], UniValue::VSTR); - address_feed = request.params[10].get_str(); - if (!address_feed.empty()) + // feed's address + if (request.params[10].isStr()) { - CTxDestination dest = DecodeDestination(address_feed); + address_feed = request.params[10].get_str(); + if (!address_feed.empty()) + { + CTxDestination dest = DecodeDestination(address_feed); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address_feed); + if (!IsValidDestination(dest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address_feed); + } } } } diff --git a/src/pocketdb/web/PocketRpc.cpp b/src/pocketdb/web/PocketRpc.cpp index 79c7c5e31..6636e54f1 100644 --- a/src/pocketdb/web/PocketRpc.cpp +++ b/src/pocketdb/web/PocketRpc.cpp @@ -45,15 +45,6 @@ static const CRPCCommand commands[] = {"search", "searchusers", &SearchUsers, {"keyword", "fieldtypes", "orderbyrank"}}, // Recomendations - // TODO (o1q): Remove below methods when the client gui switches to new methods - {"search", "getrecomendedaccountsbysubscriptions", &GetRecomendedAccountsBySubscriptions, {"address", "count"}}, - {"search", "getrecomendedaccountsbyscoresonsimilaraccounts", &GetRecomendedAccountsByScoresOnSimilarAccounts, {"address", "contenttypes", "height", "depth", "count"}}, - {"search", "getrecomendedaccountsbyscoresfromaddress", &GetRecomendedAccountsByScoresFromAddress, {"address", "contenttypes", "height", "depth", "count"}}, - {"search", "getrecomendedaccountsbytags", &GetRecomendedAccountsByTags, {"tags", "count"}}, - {"search", "getrecomendedcontentsbyscoresonsimilarcontents", &GetRecomendedContentsByScoresOnSimilarContents, {"contentid", "contenttypes", "depth", "count"}}, - {"search", "getrecomendedcontentsbyscoresfromaddress", &GetRecomendedContentsByScoresFromAddress, {"address", "contenttypes", "height", "depth", "count"}}, - // TODO (o1q): Remove above methods when the client gui switches to new (below) methods - {"search", "getrecommendedcontentbycontentid", &GetRecommendedContentByContentId, {"contentid", "address", "contenttypes", "lang", "count"}}, {"search", "getrecommendedcontentbyaddress", &GetRecommendedContentByAddress, {"address", "addressExclude", "contenttypes", "lang", "count"}}, {"search", "getrecommendedaccountbyaddress", &GetRecommendedAccountByAddress, {"address", "addressExclude", "contenttypes", "lang", "count"}}, diff --git a/src/pocketdb/web/SearchRpc.cpp b/src/pocketdb/web/SearchRpc.cpp index c6dc30110..276f5b241 100644 --- a/src/pocketdb/web/SearchRpc.cpp +++ b/src/pocketdb/web/SearchRpc.cpp @@ -322,442 +322,101 @@ namespace PocketWeb::PocketWebRpc }; } - #pragma region Recomendations OLD - // TODO (o1q): Remove below methods when the client gui switches to new methods - RPCHelpMan GetRecomendedAccountsBySubscriptions() + RPCHelpMan GetRecommendedContentByAddress() { - return RPCHelpMan{"getrecomendedaccountsbysubscriptions", - "\nAccounts recommendations by subscriptions.\n", - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "Address for recommendations"}, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10"}, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedaccountsbysubscriptions", "") + - HelpExampleRpc("getrecomendedaccountsbysubscriptions", "") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue + return RPCHelpMan{"GetRecommendedContentByAddress", + "\n\n", // TODO (rpc): provide description + { + // TODO (rpc): args + }, + { + // TODO (rpc): provide return description + }, + RPCExamples{ + // TODO (team): examples + HelpExampleCli("GetRecommendedContentByAddress", "") + + HelpExampleRpc("GetRecommendedContentByAddress", "") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = request.params[0].get_str(); - CTxDestination dest = DecodeDestination(address); - - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address); - - int cntOut = 10; - if (request.params.size() > 1 && request.params[1].isNum()) - cntOut = request.params[1].get_int(); + // if (request.fHelp) + // throw runtime_error( + // "getrecommendedcontentbyaddress \"address\", \"addressExclude\", \"contenttypes\", \"lang\", count\n" + // "\nContents recommendations by content address.\n" + // "\nArguments:\n" + // "1. \"address\" (string) Address for recommendations\n" + // "2. \"addressExclude\" (string, optional) Address for exclude from recommendations\n" + // "3. \"contenttypes\" (string or array of strings, optional) type(s) of content posts/videos/articles\n" + // "3. \"lang\" (string, optional) Language for recommendations\n" + // "4. \"count\" (int, optional) Number of recommendations records and number of other contents from addres. Default 15\n" + // ); - return request.DbConnection()->SearchRepoInst->GetRecomendedAccountsBySubscriptions(address, cntOut); - }, - }; - } - - RPCHelpMan GetRecomendedAccountsByScoresOnSimilarAccounts() - { - return RPCHelpMan{"getrecomendedaccountsbyscoresonsimilaraccounts", - "\nAccounts recommendations by likes based on address.\n", - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "Address for recommendations" }, - {"contenttypes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "type(s) of content posts/video", - { - {"contenttype", RPCArg::Type::STR, RPCArg::Optional::NO, "" }, - } - }, - {"height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Maximum search height. Default is current chain height" }, - {"depth", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Depth of statistic. Default 1000 blocks" }, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10" }, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedaccountsbyscoresonsimilaraccounts", "\"address\", \"contenttypes\", height, depth, count") + - HelpExampleRpc("getrecomendedaccountsbyscoresonsimilaraccounts", "\"address\", \"contenttypes\", height, depth, count") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = request.params[0].get_str(); - CTxDestination dest = DecodeDestination(address); - - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address); - - vector contentTypes; - ParseRequestContentTypes(request.params[1], contentTypes); - - int nHeight = ChainActive().Height(); - int depth = 1000; - int cntOut = 10; - - if (request.params.size() > 2 && request.params[2].isNum() && request.params[2].get_int() > 0) - nHeight = request.params[2].get_int(); - - if (request.params.size() > 3 && request.params[3].isNum()) - depth = request.params[3].get_int(); - - if (request.params.size() > 4 && request.params[4].isNum()) - cntOut = request.params[4].get_int(); - - return request.DbConnection()->SearchRepoInst->GetRecomendedAccountsByScoresOnSimilarAccounts(address, contentTypes, nHeight, depth, cntOut); - }, - }; - } + string address = ""; + if (request.params.size() > 0 && request.params[0].isStr()) { + address = request.params[0].get_str(); + + if(!address.empty()) { + CTxDestination dest = DecodeDestination(address); + if (!IsValidDestination(dest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + address); + } + } - RPCHelpMan GetRecomendedAccountsByScoresFromAddress() - { - return RPCHelpMan{"getrecomendedaccountsbyscoresfromaddress", - "\nAccounts recommendations by likes.\n", - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "Address for recommendations" }, - {"contenttypes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "type(s) of content posts/video", - { - {"contenttype", RPCArg::Type::STR, RPCArg::Optional::NO, "" } - } - }, - {"height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Maximum search height. Default is current chain height" }, - {"depth", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Depth of statistic. Default 1000 blocks" }, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10" }, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedaccountsbyscoresfromaddress", "\"address\", \"contenttypes\", height, depth, count") + - HelpExampleRpc("getrecomendedaccountsbyscoresfromaddress", "\"address\", \"contenttypes\", height, depth, count") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = request.params[0].get_str(); - CTxDestination dest = DecodeDestination(address); + string addressExclude = ""; + if (request.params.size() > 1 && request.params[1].isStr()) { + addressExclude = request.params[1].get_str(); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address); + if(!addressExclude.empty()) { + CTxDestination dest = DecodeDestination(addressExclude); + if (!IsValidDestination(dest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + addressExclude); + } + } vector contentTypes; - ParseRequestContentTypes(request.params[1], contentTypes); + if(request.params.size()>2) + ParseRequestContentTypes(request.params[2], contentTypes); - int nHeight = ChainActive().Height(); - int depth = 1000; - int cntOut = 10; - - if (request.params.size() > 2 && request.params[2].isNum() && request.params[2].get_int() > 0) - nHeight = request.params[2].get_int(); - - if (request.params.size() > 3 && request.params[3].isNum()) - depth = request.params[3].get_int(); + string lang = ""; + if (request.params.size() > 3 && request.params[3].isStr()) + lang = request.params[3].get_str(); + int cntOut = 15; if (request.params.size() > 4 && request.params[4].isNum()) cntOut = request.params[4].get_int(); - return request.DbConnection()->SearchRepoInst->GetRecomendedAccountsByScoresFromAddress(address, contentTypes, nHeight, depth, cntOut); - }, - }; - } - - RPCHelpMan GetRecomendedAccountsByTags() - { - return RPCHelpMan{"getrecomendedaccountsbytags", - "\nAccounts recommendations by tags.\n", - { - {"tags", RPCArg::Type::ARR, RPCArg::Optional::NO, "Tags for recommendations", - { - {"tag", RPCArg::Type::STR, RPCArg::Optional::NO, "" } - } - }, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10" }, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedaccountsbytags", "\"tags\", count") + - HelpExampleRpc("getrecomendedaccountsbytags", "\"tags\", count") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - vector tags; - if (request.params.size() > 0) - ParseRequestTags(request.params[0], tags); - - if (tags.empty()) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("There are no tags in the input parameters.")); - int nHeight = ChainActive().Height(); - int depth = 60 * 24 * 30; // about 1 month - - int cntOut = 10; - if (request.params.size() > 1 && request.params[1].isNum()) - cntOut = request.params[1].get_int(); + if (request.params.size() > 5 && request.params[5].isNum() && request.params[5].get_int() > 0) + nHeight = request.params[5].get_int(); - return request.DbConnection()->SearchRepoInst->GetRecomendedAccountsByTags(tags, nHeight, depth, cntOut); - }, - }; - } - - RPCHelpMan GetRecomendedContentsByScoresOnSimilarContents() - { - return RPCHelpMan{"getrecomendedcontentsbyscoresonsimilarcontents", - "\n\n", // TODO (team): provide description - { - {"contentid", RPCArg::Type::STR, RPCArg::Optional::NO, "Content hash for recommendations" }, - {"contenttypes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "type(s) of content posts/video", - { - {"contenttype", RPCArg::Type::STR, RPCArg::Optional::NO, "" } - } - }, - {"depth", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Depth of statistic. Default 1000 blocks" }, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10" }, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedcontentsbyscoresonsimilarcontents", "\"contentid\", \"contenttypes\", depth, count") + - HelpExampleRpc("getrecomendedcontentsbyscoresonsimilarcontents", "\"contentid\", \"contenttypes\", depth, count") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string contentid = request.params[0].get_str(); - - vector contentTypes; - ParseRequestContentTypes(request.params[1], contentTypes); - - int depth = 1000; - int cntOut = 10; - - if (request.params.size() > 2 && request.params[2].isNum()) - depth = request.params[2].get_int(); - - if (request.params.size() > 3 && request.params[3].isNum()) - cntOut = request.params[3].get_int(); - - return request.DbConnection()->SearchRepoInst->GetRecomendedContentsByScoresOnSimilarContents(contentid, contentTypes, depth, cntOut); - }, - }; - } - - RPCHelpMan GetRecomendedContentsByScoresFromAddress() - { - return RPCHelpMan{"getrecomendedcontentsbyscoresfromaddress", - "\nContents recommendations for address by likes.\n", // TODO (team): provide description - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "Address for recommendations" }, - {"contenttypes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "type(s) of content posts/video", - { - {"contenttype", RPCArg::Type::STR, RPCArg::Optional::NO, "" } - } - }, - {"height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Maximum search height. Default is current chain height" }, - {"depth", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Depth of statistic. Default 1000 blocks" }, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10" }, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedcontentsbyscoresfromaddress", "\"address\", \"contenttypes\", height, depth, count") + - HelpExampleRpc("getrecomendedcontentsbyscoresfromaddress", "\"address\", \"contenttypes\", height, depth, count") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = request.params[0].get_str(); - CTxDestination dest = DecodeDestination(address); - - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address); - - vector contentTypes; - ParseRequestContentTypes(request.params[1], contentTypes); - - int nHeight = ChainActive().Height(); - int depth = 1000; - int cntOut = 10; - - if (request.params.size() > 2 && request.params[2].isNum() && request.params[2].get_int() > 0) - nHeight = request.params[2].get_int(); + int depth = (60 * 24 * 30 * 3); //about 3 month as default + if (request.params.size() > 6 && request.params[6].isNum()) + { + depth = std::max(request.params[6].get_int(), (60 * 24 * 30 * 6)); // not greater than about 6 month + } - if (request.params.size() > 3 && request.params[3].isNum()) - depth = request.params[3].get_int(); + UniValue resultContent(UniValue::VARR); + auto ids = request.DbConnection()->SearchRepoInst->GetRecommendedContentByAddressSubscriptions(address, addressExclude, contentTypes, lang, cntOut, nHeight, depth); + if (!ids.empty()) + { + auto contents = request.DbConnection()->WebRpcRepoInst->GetContentsData(ids, ""); + resultContent.push_backV(contents); + } - if (request.params.size() > 4 && request.params[4].isNum()) - cntOut = request.params[4].get_int(); + ids = request.DbConnection()->SearchRepoInst->GetRandomContentByAddress(address, contentTypes, lang, cntOut); + if (!ids.empty()) + { + auto contents = request.DbConnection()->WebRpcRepoInst->GetContentsData(ids, ""); + resultContent.push_backV(contents); + } - return request.DbConnection()->SearchRepoInst->GetRecomendedContentsByScoresFromAddress(address, contentTypes, nHeight, depth, cntOut); + UniValue result(UniValue::VOBJ); + result.pushKV("contents", resultContent); + return result; }, }; } - #pragma endregion - - RPCHelpMan GetRecommendedContentByContentId() - { - return RPCHelpMan{"GetRecommendedContentByContentId", - "\n\n", // TODO (rpc): provide description - { - // TODO (rpc): args - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("GetRecommendedContentByContentId", "") + - HelpExampleRpc("GetRecommendedContentByContentId", "") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - if (request.fHelp) - throw runtime_error( - "getrecommendedcontentbycontentid \"contentid\", \"address\", \"contenttypes\", \"lang\", count\n" - "\nContents recommendations by contentid for address.\n" - "\nArguments:\n" - "1. \"contentid\" (string) Content Id Hash for recommendations\n" - "2. \"address\" (string, optional) Address for which recommendations\n" - "3. \"contenttypes\" (string or array of strings, optional) type(s) of content posts/videos/articles\n" - "3. \"lang\" (string, optional) Language for recommendations\n" - "4. \"count\" (int, optional) Number of resulting records. Default 15\n" - ); - - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string contenthash = request.params[0].get_str(); - - string address = ""; - if (request.params.size() > 1 && request.params[1].isStr()) { - address = request.params[1].get_str(); - - if(!address.empty()) { - CTxDestination dest = DecodeDestination(address); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + address); - } - } - - vector contentTypes; - if(request.params.size()>2) - ParseRequestContentTypes(request.params[2], contentTypes); - - string lang = ""; - if (request.params.size() > 3 && request.params[3].isStr()) - lang = request.params[3].get_str(); - - int cntOut = 15; - if (request.params.size() > 4 && request.params[4].isNum()) - cntOut = request.params[4].get_int(); - - auto contentsAddresses = request.DbConnection()->WebRpcRepoInst->GetContentsAddresses(vector{contenthash}); - - UniValue result(UniValue::VARR); - auto ids = request.DbConnection()->SearchRepoInst->GetRecommendedContentByAddressSubscriptions(contentsAddresses[contenthash], address, contentTypes, lang, cntOut, ChainActive().Height(), (60 * 24 * 30 * 6), 100); - if (!ids.empty()) - { - auto contents = request.DbConnection()->WebRpcRepoInst->GetContentsData(ids, address); - result.push_backV(contents); - } - - return result; - }, - }; - } - - RPCHelpMan GetRecommendedContentByAddress() - { - return RPCHelpMan{"GetRecommendedContentByAddress", - "\n\n", // TODO (rpc): provide description - { - // TODO (rpc): args - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("GetRecommendedContentByAddress", "") + - HelpExampleRpc("GetRecommendedContentByAddress", "") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - if (request.fHelp) - throw runtime_error( - "getrecommendedcontentbyaddress \"address\", \"addressExclude\", \"contenttypes\", \"lang\", count\n" - "\nContents recommendations by content address.\n" - "\nArguments:\n" - "1. \"address\" (string) Address for recommendations\n" - "2. \"addressExclude\" (string, optional) Address for exclude from recommendations\n" - "3. \"contenttypes\" (string or array of strings, optional) type(s) of content posts/videos/articles\n" - "3. \"lang\" (string, optional) Language for recommendations\n" - "4. \"count\" (int, optional) Number of recommendations records and number of other contents from addres. Default 15\n" - ); - - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = ""; - if (request.params.size() > 0 && request.params[0].isStr()) { - address = request.params[0].get_str(); - - if(!address.empty()) { - CTxDestination dest = DecodeDestination(address); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + address); - } - } - - string addressExclude = ""; - if (request.params.size() > 1 && request.params[1].isStr()) { - addressExclude = request.params[1].get_str(); - - if(!addressExclude.empty()) { - CTxDestination dest = DecodeDestination(addressExclude); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + addressExclude); - } - } - - vector contentTypes; - if(request.params.size()>2) - ParseRequestContentTypes(request.params[2], contentTypes); - - string lang = ""; - if (request.params.size() > 3 && request.params[3].isStr()) - lang = request.params[3].get_str(); - - int cntOut = 15; - if (request.params.size() > 4 && request.params[4].isNum()) - cntOut = request.params[4].get_int(); - - UniValue resultContent(UniValue::VARR); - auto ids = request.DbConnection()->SearchRepoInst->GetRecommendedContentByAddressSubscriptions(address, addressExclude, contentTypes, lang, cntOut, ChainActive().Height(), (60 * 24 * 30 * 3), 20); - if (!ids.empty()) - { - auto contents = request.DbConnection()->WebRpcRepoInst->GetContentsData(ids, ""); - resultContent.push_backV(contents); - } - - ids = request.DbConnection()->SearchRepoInst->GetRandomContentByAddress(address, contentTypes, lang, cntOut); - if (!ids.empty()) - { - auto contents = request.DbConnection()->WebRpcRepoInst->GetContentsData(ids, ""); - resultContent.push_backV(contents); - } - - UniValue result(UniValue::VOBJ); - result.pushKV("contents", resultContent); - return result; - }, - }; - } RPCHelpMan GetRecommendedAccountByAddress() { @@ -774,66 +433,75 @@ namespace PocketWeb::PocketWebRpc HelpExampleCli("GetRecommendedContentByAddress", "") + HelpExampleRpc("GetRecommendedContentByAddress", "") }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - if (request.fHelp) - throw runtime_error( - "getrecommendedaccountbyaddress \"address\", \"addressExclude\", \"contenttypes\", \"lang\", count\n" - "\nAccounts recommendations by address.\n" - "\nArguments:\n" - "1. \"address\" (string) Address for recommendations\n" - "2. \"addressExclude\" (string, optional) Address for exclude from recommendations\n" - "3. \"contenttypes\" (string or array of strings, optional) type(s) of content posts/videos/articles\n" - "3. \"lang\" (string, optional) Language for recommendations\n" - "4. \"count\" (int, optional) Number of recommendations records and number of other contents from addres. Default 15\n" - ); - - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = ""; - if (request.params.size() > 0 && request.params[0].isStr()) { - address = request.params[0].get_str(); - - if(!address.empty()) { - CTxDestination dest = DecodeDestination(address); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + address); - } - } - - string addressExclude = ""; - if (request.params.size() > 1 && request.params[1].isStr()) { - addressExclude = request.params[1].get_str(); - - if(!addressExclude.empty()) { - CTxDestination dest = DecodeDestination(addressExclude); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + addressExclude); - } - } - - vector contentTypes; - if(request.params.size()>2) - ParseRequestContentTypes(request.params[2], contentTypes); - - string lang = ""; - if (request.params.size() > 3 && request.params[3].isStr()) - lang = request.params[3].get_str(); - - int cntOut = 15; - if (request.params.size() > 4 && request.params[4].isNum()) - cntOut = request.params[4].get_int(); - - UniValue result(UniValue::VARR); - auto ids = request.DbConnection()->SearchRepoInst->GetRecommendedAccountByAddressSubscriptions(address, addressExclude, contentTypes, lang, cntOut, ChainActive().Height(), (60 * 24 * 30 * 3), 10); - if (!ids.empty()) - { - auto profiles = request.DbConnection()->WebRpcRepoInst->GetAccountProfiles(ids, true); - for (const auto[id, record] : profiles) - result.push_back(record); - } - - return result; - }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue + { + // if (request.fHelp) + // throw runtime_error( + // "getrecommendedaccountbyaddress \"address\", \"addressExclude\", \"contenttypes\", \"lang\", count\n" + // "\nAccounts recommendations by address.\n" + // "\nArguments:\n" + // "1. \"address\" (string) Address for recommendations\n" + // "2. \"addressExclude\" (string, optional) Address for exclude from recommendations\n" + // "3. \"contenttypes\" (string or array of strings, optional) type(s) of content posts/videos/articles\n" + // "3. \"lang\" (string, optional) Language for recommendations\n" + // "4. \"count\" (int, optional) Number of recommendations records and number of other contents from addres. Default 15\n" + // ); + RPCTypeCheckArgument(request.params[0], UniValue::VSTR); + string address = ""; + if (request.params.size() > 0 && request.params[0].isStr()) { + address = request.params[0].get_str(); + + if(!address.empty()) { + CTxDestination dest = DecodeDestination(address); + if (!IsValidDestination(dest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + address); + } + } + + string addressExclude = ""; + if (request.params.size() > 1 && request.params[1].isStr()) { + addressExclude = request.params[1].get_str(); + + if(!addressExclude.empty()) { + CTxDestination dest = DecodeDestination(addressExclude); + if (!IsValidDestination(dest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + addressExclude); + } + } + + vector contentTypes; + if(request.params.size()>2) + ParseRequestContentTypes(request.params[2], contentTypes); + + string lang = ""; + if (request.params.size() > 3 && request.params[3].isStr()) + lang = request.params[3].get_str(); + + int cntOut = 15; + if (request.params.size() > 4 && request.params[4].isNum()) + cntOut = request.params[4].get_int(); + + int nHeight = ChainActive().Height(); + if (request.params.size() > 5 && request.params[5].isNum() && request.params[5].get_int() > 0) + nHeight = request.params[5].get_int(); + + int depth = (60 * 24 * 30 * 3); //about 3 month as default + if (request.params.size() > 6 && request.params[6].isNum()) + { + depth = std::max(request.params[6].get_int(), (60 * 24 * 30 * 6)); // not greater than about 6 month + } + + UniValue result(UniValue::VARR); + auto ids = request.DbConnection()->SearchRepoInst->GetRecommendedAccountByAddressSubscriptions(address, addressExclude, contentTypes, lang, cntOut, nHeight, depth); + if (!ids.empty()) + { + auto profiles = request.DbConnection()->WebRpcRepoInst->GetAccountProfiles(ids, true); + for (const auto[id, record] : profiles) + result.push_back(record); + } + + return result; + }, }; } } diff --git a/src/pocketdb/web/SearchRpc.h b/src/pocketdb/web/SearchRpc.h index 79a877735..656bc160b 100644 --- a/src/pocketdb/web/SearchRpc.h +++ b/src/pocketdb/web/SearchRpc.h @@ -22,37 +22,7 @@ namespace PocketWeb::PocketWebRpc RPCHelpMan SearchLinks(); RPCHelpMan SearchContents(); - #pragma region Recomendations OLD - // TODO (o1q): Remove below methods when the client gui switches to new methods - // Accounts recommendations based on subscriptions - // Get some accounts that were followed by people who've followed this Account - // This should be run only if Account already has followers - RPCHelpMan GetRecomendedAccountsBySubscriptions(); - - // Accounts recommendations based on scores on other Account - // Get some Accounts that were scored by people who've scored this Account (not long ago - several blocks ago) - RPCHelpMan GetRecomendedAccountsByScoresOnSimilarAccounts(); - - // Accounts recommendations based on address scores - // Get some Accounts that were scored by people who've scored like address (not long ago - several blocks ago) - // Get address scores -> Get scored contents -> Get Scores to these contents -> Get scores accounts -> Get their scores -> Get their scored contents -> Get these contents Authors - RPCHelpMan GetRecomendedAccountsByScoresFromAddress(); - - // Accounts recommendations based on tags - RPCHelpMan GetRecomendedAccountsByTags(); - - // Contents recommendations by others contents - // Get some contents that were liked by people who've seen this content (not long ago - several blocks ago) - // This should be run only if Content already has >XXXX likes - RPCHelpMan GetRecomendedContentsByScoresOnSimilarContents(); - - // Contents recommendations by address scores - // Get some contents that were liked by people who've scored like address (not long ago - several blocks ago) - RPCHelpMan GetRecomendedContentsByScoresFromAddress(); - #pragma endregion - #pragma region Recomendations - RPCHelpMan GetRecommendedContentByContentId(); RPCHelpMan GetRecommendedContentByAddress(); RPCHelpMan GetRecommendedAccountByAddress(); #pragma endregion diff --git a/src/rpc/cache.h b/src/rpc/cache.h index 79514bd22..88bda9a37 100644 --- a/src/rpc/cache.h +++ b/src/rpc/cache.h @@ -36,6 +36,7 @@ class RPCCache "gethierarchicalfeed", "gethierarchicalstrip", "gethotposts", + "gettopfeed", "getuserprofile", "getprofilefeed", "getsubscribesfeed", @@ -50,15 +51,15 @@ class RPCCache "getusersubscribes", "getusersubscribers", "getuserblockings", + "gettopaccounts", "getaddressscores", "getpostscores", "getstatisticbyhours", "getstatisticbydays", "getstatisticcontentbyhours", "getstatisticcontentbydays", - "getrecomendedaccountsbytags", - "getrecomendedcontentsbyscoresonsimilarcontents", - "getrecommendedcontentbycontentid", + "getrecommendedcontentbyaddress", + "getrecommendedaccountbyaddress", "getaddressinfo", "getcompactblock", "getlastblocks", diff --git a/src/validation.cpp b/src/validation.cpp index 72b055891..146746b1a 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2512,7 +2512,7 @@ bool CChainState::ConnectBlock(const CBlock& block, const PocketBlockRef& pocket // ----------------------------------------------------------------------------------------------------------------- // Extend WEB database - if (gArgs.GetBoolArg("-api", DEFAULT_API_ENABLE) && enablePocketConnect) + if (!gArgs.GetBoolArg("-withoutweb", false) && enablePocketConnect) PocketServices::WebPostProcessorInst.Enqueue(block.GetHash().GetHex()); // -----------------------------------------------------------------------------------------------------------------