2
votes

In genetic genealogy X-chromosome data is useful linking to certain ancestors. This is well illustrated at: X-DNA Inheritance Chart

My Neo4j database has nodes for each Person and relationships connecting them of father and mother. Each node has a property sex (for the Person's gender; M or F). A female has two X-chromosomes, one from either parent. A male has one X-chromosome, always from the mother.

You can use reduce to see the genders involved in the inheritance from ancestors:

match p=(n:Person{RN:1})-[:father|mother*..20]->m 
return m.fullname as FullName
,reduce(status ='', q IN nodes(p)| status + q.sex) AS c 
order by length(p), c

So, starting with a male (RN:1), the result for c is MM for his father and MF for his mother, MMM for the paternal grandfather and MFM for the maternal grandfather, etc. This pattern shows that when c contains MM (two Ms together in sequence) that these are NOT contributing to the X-chromosome of the start Person.

I want to remove any node that has the MM pattern. It's easy to do this with external code, but I cannot figure out how to do it within the cypher query.

3

3 Answers

2
votes

This should work for you:

MATCH p=(n:Person { RN:1 })-[:father|mother*..20]->m
WITH m, NODES(p) AS a
WITH m, REDUCE(c = "", i IN RANGE(0, SIZE(a)-1)| CASE
  WHEN c IS NULL OR (i > 0 AND (a[i-1]).sex = "M" AND (a[i]).sex = "M") THEN
    NULL
  ELSE
    c + (a[i]).sex
  END ) AS c
WHERE c IS NOT NULL
RETURN m.fullName AS fullName, c
ORDER BY LENGTH(c);

And here is a console that demonstrates the results.

1
votes

A little late to the party and same thought process as @cybersam's solution.

match p=(n:Person { RN: 1 })-[:father|mother*..20]->(m) 
with p, m, extract( g in nodes(p) | g.sex ) as genders
with p, m, genders, range(0,size(genders) -1,1) as gender_index
unwind gender_index as idx
with p, m, genders, collect([genders[idx], genders[idx+1]]) as pairs
where not ['M','M']  in pairs
return m.fullName
,reduce(status ='', q IN nodes(p)| status + q.sex) AS c 
order by length(p), c
0
votes

This query gets me only ancestors contributing an X-chromosome:

match p=(n:Person{RN:1})-[:father|mother*..20]->(m)     
with m, reduce(status ='', q IN nodes(p)| status + q.sex) AS c     
where c=replace(c,'MM','') 
return m.RN,m.fullname as Name, c

The collection of genders adds a gender for each generation and is filtered to exclude any MM since a male cannot transmit his X to another male (e.g., son).