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
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-17 05:31 +0900
1"""Cypherクエリ集
3Neo4jデータベースで使用するCypherクエリを定義する。
4"""
5from typing import Dict, Any, Optional
8class PersonQueries:
9 """人物(Person)ノードに関するクエリ"""
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 """
26 FIND_BY_NAME = """
27 MATCH (p:Person {name: $name})
28 RETURN p
29 """
31 FIND_DECEDENT = """
32 MATCH (p:Person {is_decedent: true})
33 RETURN p
34 """
36 FIND_ALL = """
37 MATCH (p:Person)
38 RETURN p
39 ORDER BY p.name
40 """
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 """
53 DELETE = """
54 MATCH (p:Person {name: $name})
55 DETACH DELETE p
56 """
58 DELETE_ALL = """
59 MATCH (p:Person)
60 DETACH DELETE p
61 """
64class RelationshipQueries:
65 """リレーションシップに関するクエリ"""
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 """
78 FIND_CHILDREN = """
79 MATCH (parent:Person {name: $parent_name})<-[:CHILD_OF]-(child:Person)
80 RETURN child
81 ORDER BY child.name
82 """
84 FIND_PARENTS = """
85 MATCH (child:Person {name: $child_name})-[:CHILD_OF]->(parent:Person)
86 RETURN parent
87 ORDER BY parent.name
88 """
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 """
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 """
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 """
119 FIND_SIBLINGS = """
120 MATCH (person:Person {name: $person_name})-[:SIBLING_OF]-(sibling:Person)
121 RETURN sibling, r
122 ORDER BY sibling.name
123 """
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 """
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 """
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 """
159class InheritanceQueries:
160 """相続計算に関するクエリ"""
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 """
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 """
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 """
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 """
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 """
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 """
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 """
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 """
271class GraphQueries:
272 """グラフ全体に関するクエリ"""
274 # 全ノード・リレーションシップ削除(テスト用)
275 DELETE_ALL = """
276 MATCH (n)
277 DETACH DELETE n
278 """
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 ]
288 # データベース情報取得
289 GET_DATABASE_INFO = """
290 CALL dbms.components() YIELD name, versions, edition
291 RETURN name, versions, edition
292 """
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作成用のパラメータを構築
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: メールアドレス
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 }