このコミットが含まれているのは:
@@ -1,21 +1,31 @@
|
||||
# Counts uses recorded values only; history/diff endpoints retain their own contract.
|
||||
class CountsVersionRepr
|
||||
def self.many(rows, resource:, language_code:, at: nil, inclusive: true)
|
||||
new(rows.to_a, resource:, language_code:, at:, inclusive:).serialise
|
||||
# language_code remains a compatibility argument; it never selects recorded values.
|
||||
def self.many(rows, resource:, language_code:, at: nil, inclusive: true, history_flg: false)
|
||||
new(rows.to_a, resource:, language_code:, at:, inclusive:, history_flg:).serialise
|
||||
end
|
||||
|
||||
def initialize(rows, resource:, language_code:, at:, inclusive:)
|
||||
def initialize(rows, resource:, language_code:, at:, inclusive:, history_flg:)
|
||||
@rows = rows
|
||||
HistoryAccess.require_many!(rows)
|
||||
@at = at
|
||||
@inclusive = inclusive
|
||||
@resource = resource
|
||||
@language = language_code
|
||||
@history_flg = history_flg
|
||||
@users = User.where(id: rows.map(&:created_by_user_id).compact).pluck(:id, :name).to_h
|
||||
@languages = master_names(Language)
|
||||
@scripts = master_names(Script)
|
||||
@name_columns = CountsResourceDefinition.new('tags').name_columns
|
||||
@tag_versions = {}
|
||||
load_tag_versions if resource == 'posts'
|
||||
if resource == 'posts'
|
||||
load_tag_versions
|
||||
requests = @rows.flat_map do |row|
|
||||
Array(json(row.tags_json)).filter_map do |entry|
|
||||
[entry['external_tag_id'], parent_point(row)] if entry['external_tag_id']
|
||||
end
|
||||
end.uniq
|
||||
@external_versions = load_related_versions(NicoTagVersion, requests)
|
||||
end
|
||||
load_parent_versions if resource == 'tags'
|
||||
end
|
||||
|
||||
@@ -54,7 +64,7 @@ class CountsVersionRepr
|
||||
scope = scope.where("created_at #{ @inclusive ? '<=' : '<' } ?", @at)
|
||||
end
|
||||
scope.where(["(tag_id, version_no) IN (#{ placeholders })", *batch.flatten])
|
||||
.select(:tag_id, :version_no, :name, :category, :names).each do |version|
|
||||
.select(:tag_id, :version_no, :name, :category, :names, :created_at).each do |version|
|
||||
@tag_versions[[version.tag_id, version.version_no]] = version
|
||||
end
|
||||
end
|
||||
@@ -71,15 +81,35 @@ class CountsVersionRepr
|
||||
end
|
||||
end
|
||||
|
||||
def locale_names(values)
|
||||
@name_columns.to_h do |field|
|
||||
locale = field.fetch(:name_locale)
|
||||
entry = values.find do |value|
|
||||
value[:language_code] == locale[:language_code] &&
|
||||
value[:script_code].to_s.downcase == locale[:script_code].to_s.downcase
|
||||
end
|
||||
[field.fetch(:key).to_sym, entry && entry[:name]]
|
||||
end
|
||||
end
|
||||
|
||||
def post(row)
|
||||
snapshots = Array(json(row.tags_json)).map do |entry|
|
||||
version = @tag_versions[[entry['tag_id'], entry['version_no']]]
|
||||
version = nil if version && version.created_at > parent_point(row)
|
||||
external = @external_versions[[entry['external_tag_id'], parent_point(row).iso8601(6)]]
|
||||
recorded_names = version ? names(version.names) : []
|
||||
aliases = recorded_names.flat_map do |value|
|
||||
value[:aliases].map { _1.merge('language_code' => value[:language_code],
|
||||
'language_name' => value[:language_name]) }
|
||||
end
|
||||
{ id: entry['tag_id'] || entry['external_tag_id'],
|
||||
kind: entry['tag_id'] ? 'internal' : 'external',
|
||||
tag_id: entry['tag_id'], external_tag_id: entry['external_tag_id'],
|
||||
version_no: entry['version_no'], name: entry['name'] || version&.name,
|
||||
version_no: entry['version_no'],
|
||||
name: entry['name'] || version&.name || external&.fetch('name'),
|
||||
category: entry['category'] || version&.category,
|
||||
sections: entry.fetch('sections', []), names: version ? names(version.names) : [] }
|
||||
sections: entry.fetch('sections', []), names: recorded_names, aliases: }
|
||||
.merge(locale_names(recorded_names))
|
||||
end
|
||||
{ title: row.title, url: row.url, thumbnail_base: row.thumbnail_base,
|
||||
tags: snapshots, parents: row.parent_post_ids.to_s.split.map { { id: _1.to_i } },
|
||||
@@ -88,48 +118,57 @@ class CountsVersionRepr
|
||||
original_created_before: row.original_created_before&.iso8601 }
|
||||
end
|
||||
|
||||
def parent_point(row) = @at || row.created_at
|
||||
def parent_point(row)
|
||||
@history_flg ? [@at, row.created_at].compact.min : @at || row.created_at
|
||||
end
|
||||
|
||||
def load_parent_versions
|
||||
@parent_versions = {}
|
||||
requests = @rows.flat_map do |row|
|
||||
row.parent_tag_ids.to_s.split.map { [_1.to_i, parent_point(row)] }
|
||||
end.uniq
|
||||
@parent_versions = load_related_versions(TagVersion, requests)
|
||||
end
|
||||
|
||||
def load_related_versions(model, requests)
|
||||
result = {}
|
||||
table = model.table_name
|
||||
comparison = @inclusive ? '<=' : '<'
|
||||
requests.each_slice(100) do |batch|
|
||||
queries = batch.map do |id, point|
|
||||
scope = HistoryAccess.visible(TagVersion.where(tag_id: id))
|
||||
.where('created_at <= ?', point)
|
||||
.where('NOT EXISTS (SELECT 1 FROM tag_versions newer ' +
|
||||
'WHERE newer.tag_id = tag_versions.tag_id AND newer.created_at <= ? ' +
|
||||
'AND newer.version_no > tag_versions.version_no)', point)
|
||||
quoted = TagVersion.connection.quote(point.iso8601(6))
|
||||
scope.select("tag_id, name, names, #{ quoted } AS reference_point").to_sql
|
||||
scope = HistoryAccess.visible(model.where(tag_id: id))
|
||||
.where("created_at #{ comparison } ?", point)
|
||||
.where("NOT EXISTS (SELECT 1 FROM #{ table } newer " +
|
||||
"WHERE newer.tag_id = #{ table }.tag_id AND newer.created_at #{ comparison } ? " +
|
||||
"AND newer.version_no > #{ table }.version_no)", point)
|
||||
quoted = model.connection.quote(point.iso8601(6))
|
||||
names = model == TagVersion ? 'names' : 'JSON_OBJECT() AS names'
|
||||
scope.select("tag_id, name, #{ names }, #{ quoted } AS reference_point").to_sql
|
||||
end
|
||||
next if queries.empty?
|
||||
|
||||
TagVersion.connection.select_all(queries.map { "(#{ _1 })" }.join(' UNION ALL ')).each do |value|
|
||||
@parent_versions[[value['tag_id'], value['reference_point']]] = value
|
||||
model.connection.select_all(queries.map { "(#{ _1 })" }.join(' UNION ALL ')).each do |value|
|
||||
result[[value['tag_id'], value['reference_point']]] = value
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def tag(row)
|
||||
recorded_names = names(row.names)
|
||||
selected = recorded_names.find { _1[:language_code] == @language }
|
||||
aliases = recorded_names.flat_map do |entry|
|
||||
entry[:aliases].map {
|
||||
_1.merge('language_code' => entry[:language_code],
|
||||
'language_name' => entry[:language_name])
|
||||
}
|
||||
end
|
||||
{ name: selected&.fetch(:name) || row.name, names: recorded_names,
|
||||
{ name: row.name, names: recorded_names,
|
||||
category: row.category, aliases:,
|
||||
parents: row.parent_tag_ids.to_s.split.map do |id|
|
||||
value = @parent_versions[[id.to_i, parent_point(row).iso8601(6)]]
|
||||
snapshots = value && json(value['names'])
|
||||
name = snapshots&.dig(@language, 'name') || value&.fetch('name')
|
||||
{ id: id.to_i, name: }
|
||||
recorded = names(snapshots)
|
||||
{ id: id.to_i, name: value&.fetch('name'), names: recorded }.merge(locale_names(recorded))
|
||||
end,
|
||||
deprecated: row.deprecated_at&.iso8601 }
|
||||
deprecated: row.deprecated_at&.iso8601 }.merge(locale_names(recorded_names))
|
||||
end
|
||||
end
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする