From bf5e4a92ddd42b540a29c7f26bb6f23d29f3baf8 Mon Sep 17 00:00:00 2001 From: Pranav Date: Sat, 15 Mar 2025 14:10:12 -0700 Subject: [PATCH] chore: Limit the number of articles retrieved by widget (#11095) The UI displays only six articles, and this update introduces a per_page parameter to control the number of articles returned per API call. The value is capped between 1 and 100, with a default fallback if a lower number is set. This change is necessary due to high website traffic, where excessive payloads are returned without adding value. **Changes:** - Add index to status, account_id, portal_id, views. - Add per_page param in the API. - Update the code in the frontend to fetch only 6 --- .../api/v1/portals/articles_controller.rb | 14 +++++++++++--- app/javascript/widget/api/endPoints.js | 1 + app/models/article.rb | 4 ++++ .../api/v1/portals/articles/index.json.jbuilder | 2 +- app/views/widget_tests/index.html.erb | 3 ++- .../20250315202035_add_index_to_articles.rb | 8 ++++++++ db/schema.rb | 6 +++++- .../api/v1/portals/articles_controller_spec.rb | 17 +++++++++++++++++ 8 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20250315202035_add_index_to_articles.rb diff --git a/app/controllers/public/api/v1/portals/articles_controller.rb b/app/controllers/public/api/v1/portals/articles_controller.rb index f0fcf403d..d07dbcb9d 100644 --- a/app/controllers/public/api/v1/portals/articles_controller.rb +++ b/app/controllers/public/api/v1/portals/articles_controller.rb @@ -6,17 +6,25 @@ class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::B layout 'portal' def index - @articles = @portal.articles.published + @articles = @portal.articles.published.includes(:category, :author) @articles_count = @articles.count search_articles order_by_sort_param - @articles = @articles.page(list_params[:page]) if list_params[:page].present? + limit_results end def show; end private + def limit_results + return if list_params[:per_page].blank? + + per_page = [list_params[:per_page].to_i, 100].min + per_page = 25 if per_page < 1 + @articles = @articles.page(list_params[:page]).per(per_page) + end + def search_articles @articles = @articles.search(list_params) if list_params.present? end @@ -45,7 +53,7 @@ class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::B end def list_params - params.permit(:query, :locale, :sort, :status, :page) + params.permit(:query, :locale, :sort, :status, :page, :per_page) end def permitted_params diff --git a/app/javascript/widget/api/endPoints.js b/app/javascript/widget/api/endPoints.js index e6ada5914..b595fdf00 100755 --- a/app/javascript/widget/api/endPoints.js +++ b/app/javascript/widget/api/endPoints.js @@ -103,6 +103,7 @@ const getMostReadArticles = (slug, locale) => ({ page: 1, sort: 'views', status: 1, + per_page: 6, }, }); diff --git a/app/models/article.rb b/app/models/article.rb index ace793016..48e0529a2 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -23,9 +23,13 @@ # # Indexes # +# index_articles_on_account_id (account_id) # index_articles_on_associated_article_id (associated_article_id) # index_articles_on_author_id (author_id) +# index_articles_on_portal_id (portal_id) # index_articles_on_slug (slug) UNIQUE +# index_articles_on_status (status) +# index_articles_on_views (views) # class Article < ApplicationRecord include PgSearch::Model diff --git a/app/views/public/api/v1/portals/articles/index.json.jbuilder b/app/views/public/api/v1/portals/articles/index.json.jbuilder index 1927d5a09..43a9a173a 100644 --- a/app/views/public/api/v1/portals/articles/index.json.jbuilder +++ b/app/views/public/api/v1/portals/articles/index.json.jbuilder @@ -4,5 +4,5 @@ json.payload do end json.meta do - json.articles_count @articles.published.size + json.articles_count @articles_count end diff --git a/app/views/widget_tests/index.html.erb b/app/views/widget_tests/index.html.erb index cfc084384..9471c44c2 100644 --- a/app/views/widget_tests/index.html.erb +++ b/app/views/widget_tests/index.html.erb @@ -1,5 +1,6 @@ - + + <% user_id = 1 diff --git a/db/migrate/20250315202035_add_index_to_articles.rb b/db/migrate/20250315202035_add_index_to_articles.rb new file mode 100644 index 000000000..8b26344de --- /dev/null +++ b/db/migrate/20250315202035_add_index_to_articles.rb @@ -0,0 +1,8 @@ +class AddIndexToArticles < ActiveRecord::Migration[7.0] + def change + add_index :articles, :status unless index_exists?(:articles, :status) + add_index :articles, :views unless index_exists?(:articles, :views) + add_index :articles, :portal_id unless index_exists?(:articles, :portal_id) + add_index :articles, :account_id unless index_exists?(:articles, :account_id) + end +end diff --git a/db/schema.rb b/db/schema.rb index a68593c68..0818d1117 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2025_02_28_185548) do +ActiveRecord::Schema[7.0].define(version: 2025_03_15_202035) do # These extensions should be enabled to support this database enable_extension "pg_stat_statements" enable_extension "pg_trgm" @@ -159,9 +159,13 @@ ActiveRecord::Schema[7.0].define(version: 2025_02_28_185548) do t.string "slug", null: false t.integer "position" t.string "locale", default: "en", null: false + t.index ["account_id"], name: "index_articles_on_account_id" t.index ["associated_article_id"], name: "index_articles_on_associated_article_id" t.index ["author_id"], name: "index_articles_on_author_id" + t.index ["portal_id"], name: "index_articles_on_portal_id" t.index ["slug"], name: "index_articles_on_slug", unique: true + t.index ["status"], name: "index_articles_on_status" + t.index ["views"], name: "index_articles_on_views" end create_table "attachments", id: :serial, force: :cascade do |t| diff --git a/spec/controllers/public/api/v1/portals/articles_controller_spec.rb b/spec/controllers/public/api/v1/portals/articles_controller_spec.rb index 249d1e78a..08c5206c1 100644 --- a/spec/controllers/public/api/v1/portals/articles_controller_spec.rb +++ b/spec/controllers/public/api/v1/portals/articles_controller_spec.rb @@ -58,6 +58,23 @@ RSpec.describe 'Public Articles API', type: :request do expect(response_data[1][:views]).to eq(1) expect(response_data.last[:id]).to eq(article.id) end + + it 'limits results based on per_page parameter' do + get "/hc/#{portal.slug}/#{category.locale}/articles.json", params: { per_page: 2 } + + expect(response).to have_http_status(:success) + response_data = JSON.parse(response.body, symbolize_names: true)[:payload] + expect(response_data.length).to eq(2) + expect(JSON.parse(response.body, symbolize_names: true)[:meta][:articles_count]).to eq(5) + end + + it 'uses default items per page if per_page is less than 1' do + get "/hc/#{portal.slug}/#{category.locale}/articles.json", params: { per_page: 0 } + + expect(response).to have_http_status(:success) + response_data = JSON.parse(response.body, symbolize_names: true)[:payload] + expect(response_data.length).to eq(3) + end end describe 'GET /public/api/v1/portals/:slug/articles/:id' do