Coverage for src/inheritance_calculator_core/database/queries.py: 0%

1"""Cypherクエリ集

2

3 Neo4jデータベースで使用するCypherクエリを定義する。

4"""

5 from typing import Dict, Any, Optional

6

7

8 class PersonQueries:

9 """人物(Person)ノードに関するクエリ"""

10

11 CREATE = """

12 CREATE (p:Person {

13 name: $name,

14 is_alive: $is_alive,

15 is_decedent: $is_decedent,

16 birth_date: date($birth_date),

17 death_date: CASE WHEN $death_date IS NOT NULL THEN date($death_date) ELSE NULL END,

18 gender: $gender,

19 address: $address,

20 phone: $phone,

21 email: $email

22 })

23 RETURN p

24 """

25

26 FIND_BY_NAME = """

27 MATCH (p:Person {name: $name})

28 RETURN p

29 """

30

31 FIND_DECEDENT = """

32 MATCH (p:Person {is_decedent: true})

33 RETURN p

34 """

35

36 FIND_ALL = """

37 MATCH (p:Person)

38 RETURN p

39 ORDER BY p.name

40 """

41

42 UPDATE = """

43 MATCH (p:Person {name: $name})

44 SET p.is_alive = $is_alive,

45 p.death_date = CASE WHEN $death_date IS NOT NULL THEN date($death_date) ELSE NULL END,

46 p.gender = $gender,

47 p.address = $address,

48 p.phone = $phone,

49 p.email = $email

50 RETURN p

51 """

52

53 DELETE = """

54 MATCH (p:Person {name: $name})

55 DETACH DELETE p

56 """

57

58 DELETE_ALL = """

59 MATCH (p:Person)

60 DETACH DELETE p

61 """

62

63

64 class RelationshipQueries:

65 """リレーションシップに関するクエリ"""

66

67 # 親子関係

68 CREATE_CHILD_OF = """

69 MATCH (child:Person {name: $child_name})

70 MATCH (parent:Person {name: $parent_name})

71 CREATE (child)-[r:CHILD_OF {

72 adoption: $adoption,

73 is_biological: $is_biological

74 }]->(parent)

75 RETURN r

76 """

77

78 FIND_CHILDREN = """

79 MATCH (parent:Person {name: $parent_name})<-[:CHILD_OF]-(child:Person)

80 RETURN child

81 ORDER BY child.name

82 """

83

84 FIND_PARENTS = """

85 MATCH (child:Person {name: $child_name})-[:CHILD_OF]->(parent:Person)

86 RETURN parent

87 ORDER BY parent.name

88 """

89

90 # 配偶者関係

91 CREATE_SPOUSE_OF = """

92 MATCH (person1:Person {name: $person1_name})

93 MATCH (person2:Person {name: $person2_name})

94 CREATE (person1)-[r:SPOUSE_OF {

95 marriage_date: CASE WHEN $marriage_date IS NOT NULL THEN date($marriage_date) ELSE NULL END,

96 divorce_date: CASE WHEN $divorce_date IS NOT NULL THEN date($divorce_date) ELSE NULL END,

97 is_current: $is_current

98 }]->(person2)

99 RETURN r

100 """

101

102 FIND_SPOUSE = """

103 MATCH (person:Person {name: $person_name})-[r:SPOUSE_OF]-(spouse:Person)

104 WHERE r.is_current = true

105 RETURN spouse

106 """

107

108 # 兄弟姉妹関係

109 CREATE_SIBLING_OF = """

110 MATCH (person1:Person {name: $person1_name})

111 MATCH (person2:Person {name: $person2_name})

112 CREATE (person1)-[r:SIBLING_OF {

113 blood_type: $blood_type,

114 shared_parent: $shared_parent

115 }]->(person2)

116 RETURN r

117 """

118

119 FIND_SIBLINGS = """

120 MATCH (person:Person {name: $person_name})-[:SIBLING_OF]-(sibling:Person)

121 RETURN sibling, r

122 ORDER BY sibling.name

123 """

124

125 # 相続放棄

126 CREATE_RENOUNCED = """

127 MATCH (person:Person {name: $person_name})

128 MATCH (decedent:Person {name: $decedent_name})

129 CREATE (person)-[r:RENOUNCED {

130 renounce_date: date($renounce_date),

131 reason: $reason

132 }]->(decedent)

133 RETURN r

134 """

135

136 # 相続欠格

137 CREATE_DISQUALIFIED = """

138 MATCH (person:Person {name: $person_name})

139 MATCH (decedent:Person {name: $decedent_name})

140 CREATE (person)-[r:DISQUALIFIED {

141 reason: $reason,

142 date: date($date)

143 }]->(decedent)

144 RETURN r

145 """

146

147 # 相続廃除

148 CREATE_DISINHERITED = """

149 MATCH (person:Person {name: $person_name})

150 MATCH (decedent:Person {name: $decedent_name})

151 CREATE (person)-[r:DISINHERITED {

152 reason: $reason,

153 court_decision_date: date($court_decision_date)

154 }]->(decedent)

155 RETURN r

156 """

157

158

159 class InheritanceQueries:

160 """相続計算に関するクエリ"""

161

162 # 配偶者取得

163 GET_SPOUSE = """

164 MATCH (decedent:Person {is_decedent: true})-[r:SPOUSE_OF]-(spouse:Person)

165 WHERE spouse.is_alive = true

166 AND r.is_current = true

167 AND NOT EXISTS((spouse)-[:RENOUNCED]->(decedent))

168 RETURN spouse

169 """

170

171 # 第1順位相続人（子）取得

172 GET_FIRST_RANK_HEIRS = """

173 MATCH (decedent:Person {is_decedent: true})<-[:CHILD_OF]-(child:Person)

174 WHERE child.is_alive = true

175 AND NOT EXISTS((child)-[:RENOUNCED]->(decedent))

176 AND NOT EXISTS((child)-[:DISQUALIFIED]->(decedent))

177 AND NOT EXISTS((child)-[:DISINHERITED]->(decedent))

178 RETURN child

179 ORDER BY child.name

180 """

181

182 # 代襲相続人取得（子の代襲）

183 GET_SUBSTITUTION_HEIRS_CHILDREN = """

184 MATCH (decedent:Person {is_decedent: true})<-[:CHILD_OF]-(child:Person)

185 WHERE child.is_alive = false

186 AND child.death_date < decedent.death_date

187 AND NOT EXISTS((child)-[:RENOUNCED]->(decedent))

188 MATCH (child)<-[:CHILD_OF*]-(descendant:Person)

189 WHERE descendant.is_alive = true

190 AND NOT EXISTS((descendant)-[:RENOUNCED]->(decedent))

191 AND NOT EXISTS((descendant)-[:DISQUALIFIED]->(decedent))

192 AND NOT EXISTS((descendant)-[:DISINHERITED]->(decedent))

193 RETURN descendant, child, size((child)<-[:CHILD_OF*]-(descendant)) as generation

194 ORDER BY generation ASC, descendant.name

195 """

196

197 # 第2順位相続人（直系尊属）取得

198 GET_SECOND_RANK_HEIRS = """

199 MATCH (decedent:Person {is_decedent: true})

200 WHERE NOT EXISTS((decedent)<-[:CHILD_OF]-(:Person {is_alive: true}))

201 AND NOT EXISTS((decedent)<-[:CHILD_OF]-(:Person {is_alive: false})<-[:CHILD_OF]-(:Person {is_alive: true}))

202 MATCH (decedent)-[:CHILD_OF*]->(ancestor:Person)

203 WHERE ancestor.is_alive = true

204 AND NOT EXISTS((ancestor)-[:RENOUNCED]->(decedent))

205 WITH ancestor, size((decedent)-[:CHILD_OF*]->(ancestor)) as generation

206 WITH min(generation) as min_gen

207 MATCH (decedent)-[:CHILD_OF*]->(ancestor:Person)

208 WHERE ancestor.is_alive = true

209 AND size((decedent)-[:CHILD_OF*]->(ancestor)) = min_gen

210 AND NOT EXISTS((ancestor)-[:RENOUNCED]->(decedent))

211 RETURN ancestor

212 ORDER BY ancestor.name

213 """

214

215 # 第3順位相続人（兄弟姉妹）取得

216 GET_THIRD_RANK_HEIRS = """

217 MATCH (decedent:Person {is_decedent: true})

218 WHERE NOT EXISTS((decedent)<-[:CHILD_OF]-(:Person {is_alive: true}))

219 AND NOT EXISTS((decedent)<-[:CHILD_OF]-(:Person {is_alive: false})<-[:CHILD_OF]-(:Person {is_alive: true}))

220 AND NOT EXISTS((decedent)-[:CHILD_OF*]->(:Person {is_alive: true}))

221 MATCH (decedent)-[r:SIBLING_OF]-(sibling:Person)

222 WHERE sibling.is_alive = true

223 AND NOT EXISTS((sibling)-[:RENOUNCED]->(decedent))

224 AND NOT EXISTS((sibling)-[:DISQUALIFIED]->(decedent))

225 AND NOT EXISTS((sibling)-[:DISINHERITED]->(decedent))

226 RETURN sibling, r.blood_type as blood_type

227 ORDER BY sibling.name

228 """

229

230 # 代襲相続人取得（兄弟姉妹の代襲：1代限り）

231 GET_SUBSTITUTION_HEIRS_SIBLINGS = """

232 MATCH (decedent:Person {is_decedent: true})

233 WHERE NOT EXISTS((decedent)<-[:CHILD_OF]-(:Person {is_alive: true}))

234 AND NOT EXISTS((decedent)<-[:CHILD_OF]-(:Person {is_alive: false})<-[:CHILD_OF]-(:Person {is_alive: true}))

235 AND NOT EXISTS((decedent)-[:CHILD_OF*]->(:Person {is_alive: true}))

236 MATCH (decedent)-[r:SIBLING_OF]-(sibling:Person)

237 WHERE sibling.is_alive = false

238 AND sibling.death_date < decedent.death_date

239 AND NOT EXISTS((sibling)-[:RENOUNCED]->(decedent))

240 MATCH (sibling)<-[:CHILD_OF]-(nephew_niece:Person)

241 WHERE nephew_niece.is_alive = true

242 AND NOT EXISTS((nephew_niece)-[:RENOUNCED]->(decedent))

243 AND NOT EXISTS((nephew_niece)-[:DISQUALIFIED]->(decedent))

244 AND NOT EXISTS((nephew_niece)-[:DISINHERITED]->(decedent))

245 RETURN nephew_niece, sibling, r.blood_type as blood_type

246 ORDER BY nephew_niece.name

247 """

248

249 # 家系図取得（全リレーションシップ）

250 GET_FAMILY_TREE = """

251 MATCH (decedent:Person {is_decedent: true})

252 OPTIONAL MATCH (decedent)-[r1]-(related:Person)

253 OPTIONAL MATCH (related)-[r2]-(indirect:Person)

254 WHERE indirect <> decedent

255 RETURN decedent, r1, related, r2, indirect

256 """

257

258 # 統計情報取得

259 GET_STATISTICS = """

260 MATCH (p:Person)

261 WITH count(p) as total_persons,

262 count(CASE WHEN p.is_alive THEN 1 END) as alive_persons,

263 count(CASE WHEN p.is_decedent THEN 1 END) as decedent_count

264 MATCH ()-[r]->()

265 WITH total_persons, alive_persons, decedent_count,

266 count(r) as total_relationships

267 RETURN total_persons, alive_persons, decedent_count, total_relationships

268 """

269

270

271 class GraphQueries:

272 """グラフ全体に関するクエリ"""

273

274 # 全ノード・リレーションシップ削除（テスト用）

275 DELETE_ALL = """

276 MATCH (n)

277 DETACH DELETE n

278 """

279

280 # 制約とインデックスの作成

281 CREATE_CONSTRAINTS = [

282 "CREATE CONSTRAINT person_name_unique IF NOT EXISTS FOR (p:Person) REQUIRE p.name IS UNIQUE",

283 "CREATE INDEX person_name_index IF NOT EXISTS FOR (p:Person) ON (p.name)",

284 "CREATE INDEX person_decedent_index IF NOT EXISTS FOR (p:Person) ON (p.is_decedent)",

285 "CREATE INDEX person_alive_index IF NOT EXISTS FOR (p:Person) ON (p.is_alive)",

286 ]

287

288 # データベース情報取得

289 GET_DATABASE_INFO = """

290 CALL dbms.components() YIELD name, versions, edition

291 RETURN name, versions, edition

292 """

293

294

295 def build_person_params(

296 name: str,

297 is_alive: bool = True,

298 is_decedent: bool = False,

299 birth_date: Optional[str] = None,

300 death_date: Optional[str] = None,

301 gender: Optional[str] = None,

302 address: Optional[str] = None,

303 phone: Optional[str] = None,

304 email: Optional[str] = None

305) -> Dict[str, Any]:

306 """

307 Person作成用のパラメータを構築

308

309 Args:

310 name: 氏名

311 is_alive: 生存状態

312 is_decedent: 被相続人フラグ

313 birth_date: 生年月日（YYYY-MM-DD形式）

314 death_date: 死亡日（YYYY-MM-DD形式）

315 gender: 性別

316 address: 住所

317 phone: 電話番号

318 email: メールアドレス

319

320 Returns:

321 Cypherクエリ用のパラメータ辞書

322 """

323 return {

324 "name": name,

325 "is_alive": is_alive,

326 "is_decedent": is_decedent,

327 "birth_date": birth_date,

328 "death_date": death_date,

329 "gender": gender,

330 "address": address,

331 "phone": phone,

332 "email": email

333 }