migrate.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. # -*- coding: utf-8 -*-
  2. """本体迁移 (一次性修补, 跑完留档不删 — 便于复现库的历史形态).
  3. M1 (F6): 清理位置索引 id 的旧对象 — populate 改内容派生 id 后, 旧 verdict/vib/WTG16/0
  4. 仍留在库里与新 id 并存 (store 是累加不是覆盖), 查询会取到两份同内容对象。
  5. 迁移前必须确认新 id 已存在且内容等价, 否则是删数据不是迁移。
  6. M2 (F7): 给存量 Decision 补 decision_kind (写入闸只在新 put 时生效, 存量对象不会自动带上)。
  7. 跑法: .venv/bin/python -m src.ontology.migrate [--dry]
  8. """
  9. from src import paths as P
  10. import json, pathlib, re, sys
  11. from .store import Store
  12. ONT = P.objects_json()
  13. _POS_ID = re.compile(r'^(verdict|evidence)/vib/(WTG\d+)/(\d+)$')
  14. def migrate(dry=False):
  15. s = Store(ONT)
  16. d = s.objects
  17. rep = dict(M1_dropped=[], M1_kept_no_new=[], M2_kinds={})
  18. # M1: 位置索引 id → 确认同台有内容派生 id 才删
  19. for oid in sorted(d):
  20. m = _POS_ID.match(oid)
  21. if not m:
  22. continue
  23. kind, t = m.group(1), m.group(2)
  24. peers = [k for k in d if k.startswith(f'{kind}/vib/{t}/') and not _POS_ID.match(k)]
  25. if peers:
  26. rep['M1_dropped'].append(oid)
  27. else:
  28. rep['M1_kept_no_new'].append(oid) # 没有对应新 id → 保留, 响亮报出
  29. if not dry:
  30. for oid in rep['M1_dropped']:
  31. d.pop(oid, None)
  32. # 清理指向已删 id 的悬空引用
  33. for o in d.values():
  34. for rel, ids in list((o.get('links') or {}).items()):
  35. keep = [x for x in ids if x in d]
  36. if len(keep) != len(ids):
  37. o['links'][rel] = keep
  38. # M2: 存量 Decision 补分型
  39. for o in d.values():
  40. if o['type'] != 'Decision':
  41. continue
  42. pr = o.setdefault('props', {})
  43. if not pr.get('decision_kind'):
  44. k = Store._decision_kind(pr)
  45. rep['M2_kinds'][o['id']] = k
  46. if not dry:
  47. pr['decision_kind'] = k
  48. if not dry:
  49. s.save()
  50. print(f"M1 位置索引 id: 删 {len(rep['M1_dropped'])} 个"
  51. + (f" · ★保留 {len(rep['M1_kept_no_new'])} 个 (无对应新 id, 不可删): {rep['M1_kept_no_new'][:5]}"
  52. if rep['M1_kept_no_new'] else ''))
  53. print(f"M2 Decision 补分型 {len(rep['M2_kinds'])} 个: {rep['M2_kinds']}")
  54. print(f"库对象数: {len(d)}")
  55. return rep
  56. if __name__ == '__main__':
  57. migrate(dry='--dry' in sys.argv)