I've created a Neo4j graph of Questionnaire completions which may have 1 or more FreeTextResponse answers. Each FreeTextResponse has any number of Words in it.
Words are related to other Words in the graph with an IS_RELATED_TO relationship that has a similarityValue property denoting how similar the words are semantically.
Any number of FreeTextResponses could be linked to the Words.
Ultimately I would like to find clusters of related Questionnaire completions based on the similarity of the FreeTextResponse answers.
My first step would seem to be writing a query that traverses through the graph from a Questionnaire completion node, through the FreeTextResponse answers, through the words, and through any FreeTextResponse answers that those Words also exist in.
Then the same again, but doing it through two or more word nodes and calculating a weight based on their cumulative similarity value.
The graph structure looks like this:
I've tried a number of approaches, but am struggling with the number of variables there are at play.
My best attempt at the first step is this:
match (qr:Questionnaire{QuestionnaireReturnID:186406})<-[:IN]-(ftr:FreeTextResponse)<-[:IN]-(w1:WordGraph_Word)-[:IN]->(ftr2:FreeTextResponse)-[:IN]->(qr2:Questionnaire)
WITH qr2, ftr, count(distinct w1) as frequency
WITH distinct qr2, length(split((ftr.fullSentenceString), ' ')) as wordCount, frequency
WITH
COLLECT({
QuestionnaireReturnID: qr2.QuestionnaireReturnID,
frequency: frequency,
wordCount: wordCount,
percentageMatch: (toFloat(frequency) / toFloat(wordCount)) * 100
}) as associatedQuestionnaires
UNWIND(associatedQuestionnaires) as a
WITH a order by a.percentageMatch desc where a.percentageMatch > 15
return a
...but I'm not sure that I trust the results and I don't think they take into account that there could be any number of FreeTextResponses for a Questionnaire.
What is the best approach for this kind of problem please?
Thank you!
Update:
I think I may have just worked out step 1, linking the directly linked Questionnaires as follows:
match (qr:Questionnaire{QuestionnaireReturnID:186406})<-[:IN]-(ftr:FreeTextResponse)<-[:IN]-(w:WordGraph_Word)
WITH COLLECT(w) as wordList, count(w) as allWordsInSourceCount
UNWIND(wordList) as words
WITH words, allWordsInSourceCount
MATCH (words)-[relIn:IN]->(ftr2:FreeTextResponse)-[:IN]->(qr2:Questionnaire) WHERE qr2.QuestionnaireReturnID <> 186406
WITH qr2, count(distinct words) as matchFrequency, allWordsInSourceCount
WITH qr2, matchFrequency, allWordsInSourceCount, (toFloat(matchFrequency) / toFloat(allWordsInSourceCount)) * 100 as percentageMatch
WITH qr2, matchFrequency, allWordsInSourceCount, percentageMatch order by percentageMatch desc where percentageMatch > 30
RETURN qr2.QuestionnaireReturnID, matchFrequency, allWordsInSourceCount, percentageMatch
