FEATURE: Add tag group entity support to query results (#380)

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`
This commit is contained in:
Linca 2025-07-03 11:32:46 +08:00 committed by GitHub
parent d60c7acbe8
commit 12a26cc821
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 49 additions and 3 deletions

View File

@ -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}}

View File

@ -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);
}

View File

@ -0,0 +1,11 @@
const Group = <template>
{{#if @ctx.tag_group}}
<a
href="{{@ctx.baseuri}}/tag_groups/{{@ctx.id}}"
>{{@ctx.tag_group.name}}</a>
{{else}}
{{@ctx.id}}
{{/if}}
</template>;
export default Group;

View File

@ -118,6 +118,11 @@ module ::DiscourseDataExplorer
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 @@ module ::DiscourseDataExplorer
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 @@ module ::DiscourseDataExplorer
ret[cls] = ActiveModel::ArraySerializer.new(
all_objs,
each_serializer: support_info[:serializer],
only: support_info[:only],
)
end
[ret, col_map]

View File

@ -101,6 +101,23 @@ describe DiscourseDataExplorer::DataExplorer do
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