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

35 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-17 05:31 +0900

1"""Cypherクエリ集 

2 

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

4""" 

5from typing import Dict, Any, Optional 

6 

7 

8class 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 

64class 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 

159class 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 

271class 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 

295def 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 }