cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Conditional modeling in neo4j

Dear all, Is the following scenario suitable for modeling with NEO4J:
to say, The students have examination results for 10 courses, The corresponding mark for each course are as follows:
[ 90 -100 ] --> A
[ 80 - 89 ] --> B
[ 60 - 79 ] --> C
else [ 0 - 59] --> D

if a student get 7A+ and no D, then he or she is considered "outstanding"
else if 5A+ and no D, then "good"
else if no D, then "passed"
else "failed"

So those are the rules for classifying students by grades, my input data is the scores of a student in each subject (percentage scale) ,
the output is a classification label, how to deal with this case in neo4j?
Thanks in advance.

8 REPLIES 8

it's tough to answer that question in the abstract because to have a neo4j answer you need to have a neo4j data model. But suffice to say that if you had a data model that stored (:Student)-[:TOOK { grade: 'C' }]->(:Class) you can write a cypher query that will return a table of all of the passing, outstanding, good, and failed students.

This is just a matter of getting the data into the database and writing a cypher query.

What have you tried so far?

Dear David, I'm a novice with Neo4J, I just want to confirm whether neo4J is suitable for storing this type of data about rules, I have no idea how to write this model. Now, without thinking about any specific students, how should I store this business rule with Neo4j?
I also want to know that in the above rule, the constraint is related to the class (e.g., the student class), how should data about individuals be stored and class constraints applied to individual data?
Thanks a lot.

If you're asking whether you can write a Cypher query to MATCH to your data for grades students have received, categorize the grades by score, sum the number of each letter grade, and then output some result, then yes that is possible. A graph database would not be the recommended tool for this kind of use case, since it's not especially graphy, and doesn't seem to require a lot of relationships or traversals, but it is doable. If your aim is to find a database that allows storage of logic and categorization like this, Neo4j probably isn't the right tool, as the logic for this would live in the query you submit, it isn't in something you store.

To show how this would work in Neo4j, you would probably have :Student nodes, :Course nodes, some relationship between, and some score for the examination. The examination might be represented as a relationship, or as a node between the student and the course.

Let's try the latter:

(:Student)-[:ENROLLED_IN]->(:Course)
(:Student)-[:TOOK_EXAM]->(:Exam)-[:EXAM_FOR_COURSE]->(:Course)

There are other things that would probably occur in a real data set, like terms, and other things, but let's go with this. An :Exam will have a score, and it would be best if the letter grade was set at the time the scoring was saved in the graph, something like this when setting an exam score for an existing student and course:

MATCH (student:Student {id:$studentId})-[:ENROLLED_IN]->(course:Course {id:$courseId})
MERGE (student)-[:TOOK_EXAM]->(ex:Exam {number:$examId})-[:EXAM_FOR_COURSE]->(course)
SET ex.score = $score
SET ex.letterGrade = CASE WHEN 90 <= $score <= 100 THEN 'A' 
 ELSE WHEN 80 <= $score <= 89 THEN 'B'
 ELSE WHEN 60 <= $score <= 79 THEN 'C'
 ELSE 'D' END

With that data saved, here's a query to output, per student, their rating per course:

MATCH (s:Student)
CALL {
 WITH s
 MATCH (s)-[:TOOK_EXAM]->(ex:Exam)-[:EXAM_FOR_COURSE]->(course:Course)
 WITH course, collect(ex.letterGrade) as grades
 WITH course, any(grade IN grades WHERE grade = 'D') as receivedDGrade, apoc.coll.occurrences(grades, 'A') as gradeAs
 WITH course, CASE WHEN receivedDGrade THEN 'failed'
   WHEN (7 <= gradeAs) THEN 'outstanding'
   WHEN 5 <= gradeAs THEN 'good'
   ELSE 'passed' END as rating
 RETURN rating
}
RETURN s.name as student, rating

Thank you , Andrew, thank you for writing the detailed code.
My idea was to store some business rules about classes in Neo4J, Individual data is also stored separately, executing a stored procedure when needed, So that I can get some inferential properties of these individuals according to the rules. I really wanted to use a graph database to do this, because it makes it easy to manage and visualize classes and properties, and relationships between classes and individuals. Of course, could this example also be done with SWRL or shACL?
I also wondered if neo4j could support functional or conditional assignment of attributes, that looks like :

if (condition1) then
nodeA.property1= value1
else if (condition2) then
nodeA.property1= value2
else
nodeA.property1= value3

  • The condition here is the value of the function associated with the attributes of some other node, such as: nodeX.property1+nodeY.property2 > num

I'm afraid I can't speak for SWRL or shACL.

You could use a stored procedure to encapsulate the business logic for this.

Otherwise, for conditional assignment, you would have to do something like what I provided in the above query:

SET ex.letterGrade = CASE WHEN 90 <= $score <= 100 THEN 'A' 
 ELSE WHEN 80 <= $score <= 89 THEN 'B'
 ELSE WHEN 60 <= $score <= 79 THEN 'C'
 ELSE 'D' END

If the condition is based on the values of other nodes, that should be easy enough.

Dear Andrew, David:
I'm trying to do this model the way Andrew indicated, but I still need to learn the online tutorial to write user-defined stored procedures in neo4j.
I rewrite the case so that it could be read more clearly:

Students have test scores for 10 courses, the corresponding mark for each course are as follows:

rule1:
      if test.score between [ 90-100 ]   then    course.grade = A
       else if      [ 76 - 89 ]  -->  B
       else if      [ 60 - 75 ]  -->  C
       else if      [ 0   - 59 ]  -->  D

rule2:
       if a student get more than 6A and no D, then studentX.rating= "Outstanding"
         else if  4A+ and no D, --> "Good"
         else if no D, -->  "Passed"
         else   --> "Failed"

So those are the relevant rules, now a student named “Bob” has Scores like follows:

	Bob_test0.score = 90        #–>A
	Bob_test1.score = 91        #–>A
	Bob_test2.score = 92        #–>A
	Bob_test3.score = 93        #–>A
	Bob_test4.score = 94        #–>A
	Bob_test5.score = 95        #–>A
	Bob_test6.score = 96        #–>A
	Bob_test7.score = 87        #–>B
	Bob_test8.score = 78        #–>B
	Bob_test8.score = 69        #–>C

thus Bob get 7A, and no D, a query or stored procedure should get the result of :
Bob.rating = “Outstanding”
3X_4_2_42a0415780ef91cc8256ea01dcdf3eff5ead4108.png

I don’t know if I can write and store some business rules (such as in the example above) in neo4j, specific data (such as Bob’s scores) is then processed according to rules through neo4j’s general stored procedures or query statements. And the rules are about classes. How to apply this rules to individuals of the class?
This user functional requirement is important and typical for many business scenarios and domains.
Thank you for providing professional guidance!

Thanks, I'v got it done !
The initial data looks like this:
student {"name":"Bob","rating":"null"}
Test {"name":"Test0","score":90,"grade":"null"}
......

###---------------------set each test.grade 
MATCH (student:Student)-[:tookTest]->(test:Test)
set test.grade= 
 case 
  when 90<= test.score <=100 then "A"
  when 75<= test.score < 90 then "B"
  when 60<= test.score < 75 then "C"
  else "D"
 end

Then:

###----------------------set student.rating
MATCH (s:Student)
CALL {
 WITH s
 MATCH (s:Student)-[:tookTest]->(test:Test)
 return apoc.coll.occurrences(collect(test.grade), 'A') as Anum,
        apoc.coll.occurrences(collect(test.grade), 'D') as Dnum
}
set s.rating=
	case 
	  when  0<Dnum then "Failed"
                  when  6<Anum and Dnum =0 then "Outstanding"
                  when  4<Anum<=6 and Dnum =0 then "Good"
                  else "Passed"
                end
Return s.name,  s.rating

and I got result like this:

s.name           s.rating 
Bob              Outstanding

My question now is: how can I save these two business rules as graphs in Neo4J, so that it can be run when needed? These rules also have attributes such as name, version, annotation,etc..

Can neo4J provide a general approach such as:

if    condition1  then 
      action1
else if   condition2 then
      action2
else
      defaultAction

The conditions are the logical relationships of Node,relationship, and attributes. the action is usually a set of Property assignment statements.This will be very useful.

As mentioned previously, the logic for this is in the query, or encapsulated in a stored procedure, but either way Neo4j is used for storing data, not business rules and logic: