テーブル定義
0) 事前ENUM(種別/優先度/並び/作成モード など)
CREATE TYPE view_type AS ENUM ('form','list','kanban','calendar','search','graph','pivot','gantt','other');
CREATE TYPE view_priority AS ENUM ('high','standard','low'); -- UI表示用(内部priority数値は別カラムで保持)
CREATE TYPE sort_order AS ENUM ('asc','desc');
CREATE TYPE create_mode AS ENUM ('inline','modal','quick'); -- 作成時の動作(インライン/モーダル/クイック)
CREATE TYPE cal_default_view AS ENUM ('month','week','day','agenda'); -- カレンダー既定表示
CREATE TYPE open_target AS ENUM ('current','new'); -- 画面アクションの開き方
1) Odoo実体の“生取り込み”用テーブル(ミラー+スナップショット)
Odoo側の変更を正しく比較できるよう、生値をまず保持します。
-- Odooのビューを取り込む生テーブル(ir_ui_viewのミラー)
CREATE TABLE odoo_view (
id BIGINT PRIMARY KEY, -- ir_ui_view.id をそのまま
xmlid TEXT, -- 完全XMLID(例:sale.view_order_form)※NULL可
model TEXT NOT NULL, -- 例:'sale.order'
view_type view_type NOT NULL, -- ir_ui_view.type をENUMへマップ
name_i18n JSONB, -- Odoo18は多言語JSON(nameがjsonbの環境に対応)
name_text TEXT, -- nameがtext環境のフォールバック
priority_num INT NOT NULL DEFAULT 16, -- ir_ui_view.priority(数値)
custom BOOLEAN NOT NULL DEFAULT FALSE,-- Studio作成かどうか
inherit_of BIGINT, -- 継承元(inherit_id)
arch_db TEXT NOT NULL, -- ビューXML
write_date TIMESTAMPTZ,
UNIQUE (xmlid) DEFERRABLE INITIALLY DEFERRED
);
-- 取り込み時の補助:Odooのaction(ir_actions_act_window)
CREATE TABLE odoo_action_window (
id BIGINT PRIMARY KEY,
xmlid TEXT,
name_i18n JSONB,
name_text TEXT,
res_model TEXT NOT NULL, -- 例:'sale.order'
view_mode TEXT, -- 例:'tree,form'
domain_json JSONB, -- domain のJSON化
context_json JSONB, -- context のJSON化
target open_target, -- 'current' or 'new'
write_date TIMESTAMPTZ,
UNIQUE (xmlid) DEFERRABLE INITIALLY DEFERRED
);
-- 必要ならメニューも
CREATE TABLE odoo_menu (
id BIGINT PRIMARY KEY,
parent_id BIGINT,
name_i18n JSONB,
name_text TEXT,
action_id BIGINT, -- ir.actions.act_window のID(あれば)
sequence INT,
write_date TIMESTAMPTZ
);
2) ポータル側の“正規化されたビュー定義”(共通+種別別)
ここに取り込み時に変換して格納します。sale.order 限定ではなく全モデル対応だが、
model='sale.order'でフィルタすればOK。
-- 1レコード=1ビュー(共通ヘッダ)
CREATE TABLE portal_view (
id BIGSERIAL PRIMARY KEY,
source_view_id BIGINT, -- odoo_view.id(追跡用)
source_xmlid TEXT, -- odoo_view.xmlid
model TEXT NOT NULL, -- 'sale.order'
view_type view_type NOT NULL, -- form/list/...
view_name TEXT NOT NULL, -- UI名(取り込み時は name_i18n->>'ja_JP' 等を優先)
priority_ui view_priority NOT NULL DEFAULT 'standard', -- High/Standard/Low(UI表現)
priority_num INT NOT NULL DEFAULT 16, -- 数値の実priority(比較用)
enabled BOOLEAN NOT NULL DEFAULT TRUE,
ai_purpose TEXT, -- 「ビューの意味」AI学習用
arch_db_snapshot TEXT, -- 取り込み時点のarch_db(差分比較用)
notes TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_portal_view_model_type ON portal_view(model, view_type);
-- 共通設定(どのビューでも)
CREATE TABLE portal_view_common (
view_id BIGINT PRIMARY KEY REFERENCES portal_view(id) ON DELETE CASCADE,
display_fields JSONB, -- ["name","partner_id","date_order",...]
sort_field TEXT,
sort_dir sort_order,
default_group_by TEXT, -- 例:'partner_id'(初期グループ化)
default_filters JSONB -- {"state":"sale"} など(Action/searchへコンパイル)
);
-- 操作権限制御(画面上の可視)
CREATE TABLE portal_view_permissions (
view_id BIGINT PRIMARY KEY REFERENCES portal_view(id) ON DELETE CASCADE,
can_create BOOLEAN,
can_edit BOOLEAN,
can_delete BOOLEAN,
inline_edit BOOLEAN, -- listのインライン編集許可
mass_edit BOOLEAN,
show_invisible BOOLEAN
);
-- レコード作成時の動作
CREATE TABLE portal_view_creation (
view_id BIGINT PRIMARY KEY REFERENCES portal_view(id) ON DELETE CASCADE,
create_mode create_mode, -- inline/modal/quick
default_values JSONB -- {"default_field":"value",...}
);
種別別テーブル
-- Form
CREATE TABLE portal_form_settings (
view_id BIGINT PRIMARY KEY REFERENCES portal_view(id) ON DELETE CASCADE,
show_header BOOLEAN,
show_footer BOOLEAN
);
-- List (Tree)
CREATE TABLE portal_list_settings (
view_id BIGINT PRIMARY KEY REFERENCES portal_view(id) ON DELETE CASCADE,
inline_edit BOOLEAN, -- 重複だがUX的にここにも
page_hint INT, -- ページ当たり行数の“希望値”(純正はビュー単位制御なし)
export_policy TEXT -- 'inherit'|'force_hide'|'force_show' 等の意図
);
-- Kanban
CREATE TABLE portal_kanban_settings (
view_id BIGINT PRIMARY KEY REFERENCES portal_view(id) ON DELETE CASCADE,
quick_create BOOLEAN,
group_create BOOLEAN,
group_by_field TEXT, -- 列の基準(Action/search に反映)
draggable_field TEXT -- ドラッグで更新されるフィールド(多くは group_by_field と同一)
);
-- Calendar
CREATE TABLE portal_calendar_settings (
view_id BIGINT PRIMARY KEY REFERENCES portal_view(id) ON DELETE CASCADE,
date_start_field TEXT NOT NULL,
date_stop_field TEXT,
color_field TEXT,
default_view cal_default_view
);
-- Search
CREATE TABLE portal_search_settings (
view_id BIGINT PRIMARY KEY REFERENCES portal_view(id) ON DELETE CASCADE,
priority_ui view_priority, -- Searchビュー自身の優先度
priority_num INT, -- 数値
search_fields JSONB, -- 検索バー対象
filters JSONB, -- [{name,label,domain,default_on:true},...]
group_by_filters JSONB
);
3) アクション&メニュー(初期フィルタ・グループ化の格納先)
-- 画面アクション(ir.actions.act_window 相当)
CREATE TABLE portal_action (
view_id BIGINT PRIMARY KEY REFERENCES portal_view(id) ON DELETE CASCADE,
source_action_id BIGINT, -- odoo_action_window.id(追跡用)
source_action_xmlid TEXT,
view_mode TEXT, -- "tree,form" 等
domain JSONB, -- 初期絞り込み
context JSONB, -- {'group_by':'partner_id', ...}
target open_target
);
-- メニュー(必要なら)
CREATE TABLE portal_menu (
view_id BIGINT PRIMARY KEY REFERENCES portal_view(id) ON DELETE CASCADE,
create_menu BOOLEAN DEFAULT FALSE,
parent_xmlid TEXT,
label_i18n JSONB, -- {"ja_JP":"販売注文","en_US":"Sales Orders"}
sequence INT
);
補足:取り込み(ETL)の運用ポイント
- 優先度
- Odoo数値(
odoo_view.priority_num)をそのままportal_view.priority_numに保存。 - UI表示用の
priority_uiは閾値でマップ(例:<=8: high/9-24: standard/>=25: low)。
- Odoo数値(
- display_fields の抽出
arch_dbから<field name="...">をパースして配列化。- List/Treeはその順序を保持、Formは主要タブ・セクションごとに分けたい場合は
JSONBの構造を深くしてOK。
- ソート/グループ化
<tree default_order="date_order desc">を解析 →sort_field='date_order',sort_dir='desc'。- 初期グループ化は Actionの
contextのgroup_byや Searchビューのfilter default="1"を見てdefault_group_by/filters.default_onを埋める。
- 作成時の動作
- Listのインライン編集 →
<tree editable="bottom|top">を見てcreate_mode='inline'。 - モーダル →
act_window.target='new'。 - Kanbanの
quick_create/group_createはarch_db属性から。
- Listのインライン編集 →
「sale.order の既存ビューだけ」取り込むときの流れ(概要)
ir_ui_viewからmodel='sale.order'を抽出 →odoo_viewへ格納ir_actions_act_windowからres_model='sale.order'を抽出 →odoo_action_windowへ- 必要なら
ir_ui_menuも取得 →odoo_menuへ - 取り込みスクリプトで
odoo_view→portal_view系へ正規化arch_db解析でdisplay_fields/sort_field/sort_dirなどを埋める- 種別ごとの設定テーブルへ分配(kanban/calendar/search など)
- 以後、ポータルで編集 → AIがXML再生成 → Odooへ反映 → 再度
odoo_viewを更新 → 差分比較
これで、sale.order に紐づく既存のビューを、あなたの「ビュー項目一覧」構成に忠実に取り込めるようになります。
OdooのViewのメタデータはフィールドデータが抜き出せないため、断念。
代わりにフィールドを選択できるように候補をDBで持っておく。
9・12追記
指定モデルの 直下フィールド(レベル0) と 一階層下の子テーブル(one2many / many2many)の直下フィールド(レベル1) を候補として返します。params.target_model を NULL にすれば全モデル分が取得できます。
候補の検出SQL
まずは日本語付きの候補選択
many2one を「一階層」扱いで含めたい場合:x2m の f.ttype IN ('one2many','many2many') を ('one2many','many2many','many2one') に拡張すると、partner_id.name のような “逆方向” 参照候補も作れます(UI上で関連を辿って表示する用途)。
除外フィールド(監査・技術系など)を外す場合は、lvl0 / lvl1 に AND f.name NOT IN (...) を追加してください。
WITH
params AS (
-- ★ 対象モデル。全モデル対象なら NULL / ベース言語は必要に応じて変更
SELECT
'res.partner'::text AS target_model,
'en_US'::text AS base_lang
),
-- 各フィールドに紐づく権限グループ XMLID を配列化
field_groups AS (
SELECT
f.id AS field_id,
ARRAY_AGG(DISTINCT (imd.module || '.' || imd.name))
FILTER (WHERE imd.id IS NOT NULL) AS groups_xml_ids
FROM ir_model_fields f
LEFT JOIN ir_model_fields_group_rel rel ON rel.field_id = f.id
LEFT JOIN res_groups g ON g.id = rel.group_id
LEFT JOIN ir_model_data imd
ON imd.model = 'res.groups' AND imd.res_id = g.id
GROUP BY f.id
),
-- 対象モデル一覧
models AS (
SELECT m.id AS model_id, m.model AS model_tech, replace(m.model,'.','_') AS model_table
FROM ir_model m
CROSS JOIN params p
WHERE p.target_model IS NULL OR m.model = p.target_model
),
-- レベル0:モデル直下の「葉」フィールド(x2many 以外)
lvl0 AS (
SELECT
mo.model_tech AS model_tech,
mo.model_table AS model_table,
0 AS lvl,
f.name AS field_name,
f.ttype AS ttype,
CASE WHEN f.ttype IN ('many2one','one2many','many2many') THEN f.relation ELSE NULL END AS relation_model,
f.field_description AS label_src,
f.help AS help_src,
f.required AS required,
COALESCE(f.translate, FALSE) AS translate,
COALESCE(g.groups_xml_ids, ARRAY[]::text[]) AS groups_xml_ids,
NULL::text AS via_field,
NULL::text AS via_ttype,
NULL::text AS child_model_tech,
NULL::text AS child_model_table
FROM models mo
JOIN ir_model_fields f ON f.model_id = mo.model_id
LEFT JOIN field_groups g ON g.field_id = f.id
WHERE f.ttype NOT IN ('one2many','many2many')
),
-- モデル直下の x2many(子テーブル入口)
x2m AS (
SELECT
mo.model_id,
mo.model_tech,
mo.model_table,
f.name AS via_field, -- 経路フィールド(例: line_ids)
f.ttype AS via_ttype, -- one2many / many2many
f.relation AS child_model_tech -- 子モデル技術名
FROM models mo
JOIN ir_model_fields f
ON f.model_id = mo.model_id
AND f.ttype IN ('one2many','many2many')
AND f.relation IS NOT NULL
),
-- 子モデルのID解決
child_models AS (
SELECT
m.id AS child_model_id,
m.model AS child_model_tech,
replace(m.model,'.','_') AS child_model_table
FROM ir_model m
),
-- レベル1:子モデルの「葉」フィールド(x2many 以外)
lvl1 AS (
SELECT
x.model_tech AS model_tech,
x.model_table AS model_table,
1 AS lvl,
cf.name AS field_name,
cf.ttype AS ttype,
CASE WHEN cf.ttype IN ('many2one','one2many','many2many') THEN cf.relation ELSE NULL END AS relation_model,
cf.field_description AS label_src,
cf.help AS help_src,
cf.required AS required,
COALESCE(cf.translate, FALSE) AS translate,
COALESCE(g.groups_xml_ids, ARRAY[]::text[]) AS groups_xml_ids,
x.via_field AS via_field,
x.via_ttype AS via_ttype,
x.child_model_tech AS child_model_tech,
cm.child_model_table AS child_model_table
FROM x2m x
JOIN child_models cm
ON cm.child_model_tech = x.child_model_tech
JOIN ir_model_fields cf
ON cf.model_id = cm.child_model_id
AND cf.ttype NOT IN ('one2many','many2many')
LEFT JOIN field_groups g ON g.field_id = cf.id
)
SELECT
v.model_tech AS "モデル技術名",
v.model_table AS "モデル物理名",
v.lvl AS "レベル", -- 0:直下 / 1:子直下
CASE WHEN v.lvl=0 THEN v.field_name
ELSE v.via_field || '.' || v.field_name
END AS "表示パス",
v.via_field AS "経路フィールド",
v.via_ttype AS "経路タイプ", -- one2many / many2many
v.child_model_tech AS "子モデル技術名",
v.child_model_table AS "子モデル物理名",
v.field_name AS "フィールド名",
v.ttype AS "フィールド型", -- char, integer, many2one, ...
v.relation_model AS "関連モデル",
-- ★ ラベル/ヘルプ:ベース言語の値
v.label_src AS "フィールド名(ベース)",
v.help_src AS "ヘルプ(ベース)",
-- ★ 日本語列(いまは NULL。後続の /fields/translate で埋める想定)
NULL::text AS "フィールド名(日本語)",
NULL::text AS "ヘルプ(日本語)",
-- ★ i18n JSON(ベース言語のみ。ja_JP は含めない → 欠落扱いにして翻訳補完対象にする)
jsonb_build_object(p.base_lang, v.label_src) AS "label_i18n",
jsonb_build_object(p.base_lang, v.help_src) AS "help_i18n",
v.required AS "必須",
v.translate AS "翻訳",
v.groups_xml_ids AS "権限グループXMLID配列"
FROM (
SELECT * FROM lvl0
UNION ALL
SELECT * FROM lvl1
) v
CROSS JOIN params p
ORDER BY v.model_tech, v.lvl, "表示パス", v.field_name;
選んで作る” 用の検索ビューをDB側に用意しましょう。
「モデル直下 + 一階層下の子(x2many)」の候補を集約し、日本語名で検索できるようにします(日本語が無い箇所はベース言語で代替)。
更新は REFRESH MATERIALIZED VIEW 一発です。
1) 検索用マテビューを作成(日本語列あり)
まずは 読み取り専用の集約を作ります。大量でも高速検索できるよう、あとでGIN/pg_trgm索引を貼ります。
-- 文字列あいまい検索に必要
CREATE EXTENSION IF NOT EXISTS pg_trgm;
DROP MATERIALIZED VIEW IF EXISTS mv_portal_field_candidates CASCADE;
CREATE MATERIALIZED VIEW mv_portal_field_candidates AS
WITH
params AS (
SELECT
NULL::text AS target_model, -- ★特定モデルだけなら 'res.partner' 等
'en_US'::text AS base_lang -- ベース言語(通常 en_US)
),
field_groups AS (
SELECT
f.id AS field_id,
ARRAY_AGG(DISTINCT (imd.module || '.' || imd.name))
FILTER (WHERE imd.id IS NOT NULL) AS groups_xml_ids
FROM ir_model_fields f
LEFT JOIN ir_model_fields_group_rel rel ON rel.field_id = f.id
LEFT JOIN res_groups g ON g.id = rel.group_id
LEFT JOIN ir_model_data imd
ON imd.model = 'res.groups' AND imd.res_id = g.id
GROUP BY f.id
),
models AS (
SELECT m.id AS model_id, m.model AS model_tech, replace(m.model,'.','_') AS model_table
FROM ir_model m
CROSS JOIN params p
WHERE p.target_model IS NULL OR m.model = p.target_model
),
-- レベル0:モデル直下の葉(x2many以外)
lvl0 AS (
SELECT
mo.model_tech, mo.model_table,
0::smallint AS lvl,
f.name AS field_name,
f.ttype AS ttype,
CASE WHEN f.ttype IN ('many2one','one2many','many2many') THEN f.relation END AS relation_model,
f.field_description AS label_src,
f.help AS help_src,
f.required AS required,
COALESCE(f.translate, FALSE) AS translate,
COALESCE(g.groups_xml_ids, ARRAY[]::text[]) AS groups_xml_ids,
NULL::text AS via_field,
NULL::text AS via_ttype,
NULL::text AS child_model_tech,
NULL::text AS child_model_table
FROM models mo
JOIN ir_model_fields f ON f.model_id = mo.model_id
LEFT JOIN field_groups g ON g.field_id = f.id
WHERE f.ttype NOT IN ('one2many','many2many')
),
-- レベル1入口:x2many
x2m AS (
SELECT
mo.model_id, mo.model_tech, mo.model_table,
f.name AS via_field, f.ttype AS via_ttype, f.relation AS child_model_tech
FROM models mo
JOIN ir_model_fields f
ON f.model_id = mo.model_id
AND f.ttype IN ('one2many','many2many')
AND f.relation IS NOT NULL
),
child_models AS (
SELECT m.id AS child_model_id, m.model AS child_model_tech, replace(m.model,'.','_') AS child_model_table
FROM ir_model m
),
-- レベル1:子の葉(x2many以外)
lvl1 AS (
SELECT
x.model_tech, x.model_table,
1::smallint AS lvl,
cf.name AS field_name,
cf.ttype AS ttype,
CASE WHEN cf.ttype IN ('many2one','one2many','many2many') THEN cf.relation END AS relation_model,
cf.field_description AS label_src,
cf.help AS help_src,
cf.required AS required,
COALESCE(cf.translate, FALSE) AS translate,
COALESCE(g.groups_xml_ids, ARRAY[]::text[]) AS groups_xml_ids,
x.via_field, x.via_ttype,
x.child_model_tech, cm.child_model_table
FROM x2m x
JOIN child_models cm ON cm.child_model_tech = x.child_model_tech
JOIN ir_model_fields cf ON cf.model_id = cm.child_model_id
LEFT JOIN field_groups g ON g.field_id = cf.id
WHERE cf.ttype NOT IN ('one2many','many2many')
),
unioned AS (
SELECT * FROM lvl0
UNION ALL
SELECT * FROM lvl1
)
SELECT
u.model_tech AS "モデル技術名",
u.model_table AS "モデル物理名",
u.lvl AS "レベル", -- 0:直下 / 1:子直下
CASE WHEN u.lvl=0 THEN u.field_name ELSE u.via_field || '.' || u.field_name END
AS "表示パス",
u.via_field AS "経路フィールド",
u.via_ttype AS "経路タイプ", -- one2many / many2many
u.child_model_tech AS "子モデル技術名",
u.child_model_table AS "子モデル物理名",
u.field_name AS "フィールド名",
u.ttype AS "フィールド型",
u.relation_model AS "関連モデル",
-- ベース言語のラベル/ヘルプ
u.label_src AS "フィールド名(ベース)",
u.help_src AS "ヘルプ(ベース)",
-- 日本語(現時点では NULL。後でアプリ側翻訳や辞書で埋める想定)
NULL::text AS "フィールド名(日本語)",
NULL::text AS "ヘルプ(日本語)",
-- 検索用の統合テキスト(日本語が無ければベース→技術名へフォールバック)
COALESCE(NULLIF(NULLIF(NULLIF(NULL::text,'') ,''),
''), u.label_src, u.field_name)::text
AS "検索用ラベル",
COALESCE(NULL::text, u.help_src)::text AS "検索用ヘルプ",
u.required AS "必須",
u.translate AS "翻訳",
u.groups_xml_ids AS "権限グループXMLID配列"
FROM unioned u, params p
WITH NO DATA; -- ★作成直後は空。REFRESHで詰める
※「フィールド名(日本語)」「ヘルプ(日本語)」は今は
NULL。
後でアプリ側の翻訳や手動辞書を反映したい場合は、次節の「辞書テーブル」を併用できます。
2) 検索用インデックス
-- 中身を構築
REFRESH MATERIALIZED VIEW mv_portal_field_candidates;
-- GIN(trigram) で日本語も部分一致/あいまい検索を高速化
CREATE INDEX IF NOT EXISTS idx_mv_pvfc_label_trgm
ON mv_portal_field_candidates USING GIN ("検索用ラベル" gin_trgm_ops);
CREATE INDEX IF NOT EXISTS idx_mv_pvfc_help_trgm
ON mv_portal_field_candidates USING GIN ("検索用ヘルプ" gin_trgm_ops);
-- よく絞るキー
CREATE INDEX IF NOT EXISTS idx_mv_pvfc_model
ON mv_portal_field_candidates ("モデル技術名");
CREATE INDEX IF NOT EXISTS idx_mv_pvfc_level
ON mv_portal_field_candidates ("レベル");
CREATE INDEX IF NOT EXISTS idx_mv_pvfc_path_trgm
ON mv_portal_field_candidates USING GIN ("表示パス" gin_trgm_ops);
3) 日本語名を埋める「任意の辞書」テーブル(オプション)
翻訳APIの前に、とりあえずよく使う標準フィールドだけ日本語を持たせたいときに便利。
CREATE TABLE IF NOT EXISTS portal_field_name_ja_dict (
model_tech TEXT NOT NULL,
field_name TEXT NOT NULL,
label_ja TEXT,
help_ja TEXT,
PRIMARY KEY (model_tech, field_name)
);
-- 例: res.partner.name → 顧客名
INSERT INTO portal_field_name_ja_dict(model_tech, field_name, label_ja)
VALUES ('res.partner','name','顧客名')
ON CONFLICT (model_tech, field_name) DO UPDATE SET label_ja=EXCLUDED.label_ja;
この辞書を検索ビューに反映したい場合は、マテビュー定義を少し拡張して LEFT JOIN portal_field_name_ja_dict d を入れ、"フィールド名(日本語)" := d.label_ja、"検索用ラベル" := COALESCE(d.label_ja, u.label_src, u.field_name) にしてください。
(再作成→REFRESH MATERIALIZED VIEW で反映)
4) 使い方サンプル(検索クエリ)
-- 日本語で「顧客」「住所」を含むもの(AND検索)
SELECT *
FROM mv_portal_field_candidates
WHERE "検索用ラベル" ILIKE ALL (ARRAY['%顧客%','%住所%'])
OR "検索用ヘルプ" ILIKE ALL (ARRAY['%顧客%','%住所%'])
ORDER BY
GREATEST(similarity("検索用ラベル",'顧客 住所'),
similarity("検索用ヘルプ" ,'顧客 住所'),
similarity("表示パス" ,'顧客 住所')) DESC,
"モデル技術名", "レベル", "表示パス"
LIMIT 100;
-- モデルで絞って、部分一致(例: res.partner)
SELECT "表示パス","フィールド型","フィールド名(日本語)","フィールド名(ベース)"
FROM mv_portal_field_candidates
WHERE "モデル技術名"='res.partner'
AND ("検索用ラベル" ILIKE '%名%'
OR "表示パス" ILIKE '%name%')
ORDER BY "レベル","表示パス";
similarity()は pg_trgm のスコアです。ORDER BYに入れると使い心地が上がります。
運用フロー例
REFRESH MATERIALIZED VIEW mv_portal_field_candidates;- よく使うフィールドだけ
portal_field_name_ja_dictに投入(任意) - 再度
REFRESH MATERIALIZED VIEW(辞書反映) - UI 側はこのマテビューに対して 日本語キーワードで検索して候補を表示
- 選ばれたものを
portal_fieldsに取り込み → あとは既存の/fields/translate?target=ja_JPで不足言語を補完
コメントを残す