From 9e7f242c3949460031ebbea320e3a7e9a8bb35a1 Mon Sep 17 00:00:00 2001 From: Lhc_fl Date: Thu, 3 Jul 2025 02:17:30 +0800 Subject: [PATCH 1/2] FEAT: Add tag group entity support to query results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The data explorer has special logic (“Automatic Entity Resolution”) to show links to various objects whenever a column name is `user_id`, `group_id`, `topic_id`, `category_id` or `badge_id`. This commit adds the support for `tag_group_id` --- .../discourse/components/query-result.gjs | 12 ++++++++++++ .../discourse/components/query-row-content.gjs | 4 ++-- .../discourse/components/result-types/tag-group.gjs | 11 +++++++++++ lib/discourse_data_explorer/data_explorer.rb | 8 +++++++- 4 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 assets/javascripts/discourse/components/result-types/tag-group.gjs diff --git a/assets/javascripts/discourse/components/query-result.gjs b/assets/javascripts/discourse/components/query-result.gjs index 3d89dfe7..70b40443 100644 --- a/assets/javascripts/discourse/components/query-result.gjs +++ b/assets/javascripts/discourse/components/query-result.gjs @@ -19,6 +19,7 @@ import HtmlViewComponent from "./result-types/html"; import JsonViewComponent from "./result-types/json"; import PostViewComponent from "./result-types/post"; import ReltimeViewComponent from "./result-types/reltime"; +import TagGroupViewComponent from "./result-types/tag-group"; import TextViewComponent from "./result-types/text"; import TopicViewComponent from "./result-types/topic"; import UrlViewComponent from "./result-types/url"; @@ -36,6 +37,7 @@ const VIEW_COMPONENTS = { html: HtmlViewComponent, json: JsonViewComponent, category: CategoryViewComponent, + tag_group: TagGroupViewComponent, }; export default class QueryResult extends Component { @@ -146,6 +148,10 @@ export default class QueryResult extends Component { return transformedRelTable(this.args.content.relations.topic); } + get transformedTagGroupTable() { + return transformedRelTable(this.args.content.relations.tag_group); + } + get transformedGroupTable() { return transformedRelTable(this.site.groups); } @@ -203,6 +209,10 @@ export default class QueryResult extends Component { return this.transformedTopicTable[id]; } + lookupTagGroup(id) { + return this.transformedTagGroupTable[id]; + } + lookupGroup(id) { return this.transformedGroupTable[id]; } @@ -368,11 +378,13 @@ export default class QueryResult extends Component { @lookupBadge={{this.lookupBadge}} @lookupPost={{this.lookupPost}} @lookupTopic={{this.lookupTopic}} + @lookupTagGroup={{this.lookupTagGroup}} @lookupGroup={{this.lookupGroup}} @lookupCategory={{this.lookupCategory}} @transformedPostTable={{this.transformedPostTable}} @transformedBadgeTable={{this.transformedBadgeTable}} @transformedUserTable={{this.transformedUserTable}} + @transformedTagGroupTable={{this.transformedTagGroupTable}} @transformedGroupTable={{this.transformedGroupTable}} @transformedTopicTable={{this.transformedTopicTable}} @site={{this.site}} diff --git a/assets/javascripts/discourse/components/query-row-content.gjs b/assets/javascripts/discourse/components/query-row-content.gjs index cab2b3aa..bf7e4767 100644 --- a/assets/javascripts/discourse/components/query-row-content.gjs +++ b/assets/javascripts/discourse/components/query-row-content.gjs @@ -1,6 +1,6 @@ import Component from "@glimmer/component"; import { cached } from "@glimmer/tracking"; -import { capitalize } from "@ember/string"; +import { classify } from "@ember/string"; import getURL from "discourse/lib/get-url"; import { escapeExpression } from "discourse/lib/utilities"; import TextViewComponent from "./result-types/text"; @@ -31,7 +31,7 @@ export default class QueryRowContent extends Component { } const lookupFunc = - this.args[`lookup${capitalize(componentDefinition.name)}`]; + this.args[`lookup${classify(componentDefinition.name)}`]; if (lookupFunc) { ctx[componentDefinition.name] = lookupFunc.call(this.args, id); } diff --git a/assets/javascripts/discourse/components/result-types/tag-group.gjs b/assets/javascripts/discourse/components/result-types/tag-group.gjs new file mode 100644 index 00000000..177d10f4 --- /dev/null +++ b/assets/javascripts/discourse/components/result-types/tag-group.gjs @@ -0,0 +1,11 @@ +const Group = ; + +export default Group; diff --git a/lib/discourse_data_explorer/data_explorer.rb b/lib/discourse_data_explorer/data_explorer.rb index bffb96e4..8f01b1d4 100644 --- a/lib/discourse_data_explorer/data_explorer.rb +++ b/lib/discourse_data_explorer/data_explorer.rb @@ -118,6 +118,11 @@ def self.extra_data_pluck_fields fields: %i[id title slug posts_count locale], serializer: BasicTopicSerializer, }, + tag_group: { + class: TagGroup, + fields: %i[id name], + only: %i[id name], + }, group: { class: Group, ignore: true, @@ -141,7 +146,7 @@ def self.extra_data_pluck_fields def self.column_regexes @column_regexes ||= extra_data_pluck_fields - .map { |key, val| /(#{val[:class].to_s.downcase})_id$/ if val[:class] } + .map { |key, val| /(#{val[:class].to_s.underscore})_id$/ if val[:class] } .compact end @@ -196,6 +201,7 @@ def self.add_extra_data(pg_result) ret[cls] = ActiveModel::ArraySerializer.new( all_objs, each_serializer: support_info[:serializer], + only: support_info[:only], ) end [ret, col_map] From 4867b77f4107e575b51eeae6cbd024129613496e Mon Sep 17 00:00:00 2001 From: Lhc_fl Date: Thu, 3 Jul 2025 02:35:27 +0800 Subject: [PATCH 2/2] add tests --- spec/data_explorer_spec.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/spec/data_explorer_spec.rb b/spec/data_explorer_spec.rb index bef36cc9..af0385b1 100644 --- a/spec/data_explorer_spec.rb +++ b/spec/data_explorer_spec.rb @@ -101,6 +101,23 @@ records.map { |t| BasicTopicSerializer.new(t, root: false).as_json } }.not_to raise_error end + + it "chooses the correct serializer for tag_group" do + tag_group = Fabricate(:tag_group) + tag1 = Fabricate(:tag) + tag2 = Fabricate(:tag) + tag_group.tags = [tag1, tag2] + + query = Fabricate(:query, sql: "SELECT tag_id, tag_group_id FROM tag_group_memberships") + + pg_result = described_class.run_query(query)[:pg_result] + relations, colrender = DiscourseDataExplorer::DataExplorer.add_extra_data(pg_result) + + expect(colrender).to eq({ 1 => :tag_group }) + expect(relations[:tag_group].as_json).to include( + { "id" => tag_group.id, "name" => tag_group.name }, + ) + end end end end