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.
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.
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
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).