Это дополнительный вопрос из раздела Как моделировать отношения между наборами узлов. Я повторю рекламу на случай, если этот вопрос будет удален или существенно изменен:
В настоящее время я изучаю моделирование высших учебных заведений и других подобных объектов (MATH101, BIOL360, BSc и т. д.), и один из вариантов, который мы рассматриваем, - это графические базы данных. Я не знаком с базами данных графов, кроме как в теории.
Одним из вариантов использования этой базы данных является запрос возможных путей прохождения курсов; например, отвечая на вопрос "какие минимальные комбинации курсов действительны для выполнения требований для получения степени бакалавра компьютерных наук с отличием?". Некоторые из требований будут простыми (квалификация требует, чтобы вы выполнили Comp101, Math101 и Comp201), а некоторые будут предоставлять варианты (требуется, чтобы вы выполнили 80 баллов по статьям, классифицируемым как "научные" работы на уровне 100 или выше).
Я нашел списки neo4j, которые мне показались действительно многообещающими, но то, что я действительно хочу, - это иметь возможность возвращать список списков, где каждый список компонентов представляет один потенциальный путь. Однако я не вижу способа создать такой список списков, поэтому я предполагаю, что у меня что-то не так на концептуальном уровне.
Один из способов сделать это - создать цикл, который просматривает квалификационный узел, выбирает одну возможную комбинацию узлов, рекурсивно выполняет требования этого узла, а затем переходит к следующей возможной комбинации. Как разработчик базы данных, идея использования цикла для того, что теоретически решаемо как операция на основе набора, не дает мне спокойно спать по ночам, поэтому я приложу все усилия, чтобы избежать такой мерзости. Как создать запрос для создания такого набора наборов?
Опять же, я пометил Neo4J, потому что я склоняюсь к нему, поскольку (насколько я могу судить) это наиболее широко известная/используемая графическая СУБД (и у меня было довольно элегантное решение моего предыдущего вопроса, которое работает в этом) , но я открыт для решений и в других базах данных (на самом деле, если это возможно в самом новом предложении SQL Server, это, вероятно, было бы идеально, поскольку на нем есть другая инфраструктура).
Ответ 1 · рейтинг 2 · cybersam
Резюмируя мои комментарии: использование рекурсии или циклов должно быть ненужным, и использование списков, вероятно, также не является правильным подходом. Вы должны просто использовать хорошую модель данных, ориентированную на граф (которая хорошо использует отношения и использует индексирование для запуска запросов).
Рискуя переборщить с этой точкой зрения, приведенный ниже запрос использует довольно простую модель данных для построения части графика для школы:
CREATE
(sci:Area {name: 'Science'}),
(hum:Area {name: 'Humanities'}),
(bio:Department {name: 'Biology'})-[:IN_AREA]->(sci),
(phy:Department {name: 'Physics'})-[:IN_AREA]->(sci),
(che:Department {name: 'Chemistry'})-[:IN_AREA]->(sci),
(eng:Department {name: 'English'})-[:IN_AREA]->(hum),
(his:Department {name: 'History'})-[:IN_AREA]->(hum),
(soc:Department {name: 'Sociology'})-[:IN_AREA]->(hum),
(bioMaj:Major {name: 'Biology'})-[:IN_DEPT]->(bio),
(phyMaj:Major {name: 'Physics'})-[:IN_DEPT]->(phy),
(bio101:Course {id: 'Bio101', name: 'Introductory Biology', level: 101, credits: 3}) -[:IN_DEPT]->(bio),
(che101:Course {id: 'Chem101', name: 'Introductory Chemistry', level: 101, credits: 4})-[:IN_DEPT]->(che),
(phy101:Course {id: 'Phys101', name: 'Newtonian Physics', level: 101, credits: 5}) -[:IN_DEPT]->(phy),
(phy201:Course {id: 'Phys201', name: 'Mechanics', level: 201, credits: 4}) -[:IN_DEPT]->(phy),
(phy202:Course {id: 'Phys202', name: 'Elec & Mag', level: 202, credits: 4}) -[:IN_DEPT]->(phy),
(eng101:Course {id: 'Eng101', name: 'Intro to Poetry', level: 101, credits: 3}) -[:IN_DEPT]->(eng),
(eng102:Course {id: 'Eng102', name: 'Intro to Drama', level: 102, credits: 3}) -[:IN_DEPT]->(eng),
(eng103:Course {id: 'Eng103', name: 'Intro to Fiction', level: 103, credits: 3}) -[:IN_DEPT]->(eng),
(eng202:Course {id: 'Eng201', name: 'Medieval Literature', level: 201, credits: 4}) -[:IN_DEPT]->(eng),
(his100:Course {id: 'Hist100', name: 'Global History', level: 100, credits: 3}) -[:IN_DEPT]->(his),
(soc100:Course {id: 'Soc100', name: 'Intro to Sociology', level: 100, credits: 3}) -[:IN_DEPT]->(soc),
(fred:Student {id: 123456, name: 'Fred Smith'})-[:HAS_MAJOR]->(bioMaj),
(sue:Student {id: 987654, name: 'Sue Jones'})-[:HAS_MAJOR]->(phyMaj),
(fred)-[:ENROLLED_IN {year: 2017, term: 1, grade: 3.73}]->(bio101),
(fred)-[:ENROLLED_IN {year: 2017, term: 1, grade: 3.62}]->(eng101),
(fred)-[:ENROLLED_IN {year: 2017, term: 2, grade: 3.55}]->(che101),
(fred)-[:ENROLLED_IN {year: 2017, term: 2, grade: 2.95}]->(eng102),
(fred)-[:ENROLLED_IN {year: 2018, term: 1, grade: 3.13}]->(eng202),
(fred)-[:ENROLLED_IN {year: 2018, term: 1, grade: 3.68}]->(phy101),
(sue) -[:ENROLLED_IN {year: 2017, term: 1, grade: 3.55}]->(che101),
(sue) -[:ENROLLED_IN {year: 2017, term: 1, grade: 3.66}]->(eng101),
(sue) -[:ENROLLED_IN {year: 2017, term: 2, grade: 3.77}]->(phy201),
(sue) -[:ENROLLED_IN {year: 2017, term: 2, grade: 3.44}]->(soc100),
(sue) -[:ENROLLED_IN {year: 2018, term: 1, grade: 3.33}]->(eng202),
(sue) -[:ENROLLED_IN {year: 2018, term: 1, grade: 3.22}]->(phy101);
Предположим, существует требование, согласно которому все специалисты по естественным наукам должны получить оценку 3.0+ по трем гуманитарным курсам, и по крайней мере один из этих гуманитарных курсов должен иметь уровень 200+.
Мы можем найти всех студентов, изучающих естественные науки, которые выполнили это требование (например, Sue Jones) следующим образом:
MATCH (a1:Area)<-[:IN_AREA]-()<-[:IN_DEPT]-()<-[:HAS_MAJOR]-(student)-[e:ENROLLED_IN]->(course)-[:IN_DEPT]->()-[:IN_AREA]->(a2:Area)
WHERE a1.name = 'Science' AND a2.name = 'Humanities' AND e.grade >= 3.0
WITH student, COLLECT(course) AS courses
WHERE SIZE(courses) >= 3 AND ANY(c IN courses WHERE c.level >= 200)
RETURN student;
И наоборот, мы можем найти всех студентов естественных наук, которые еще не выполнили это требование (например, Fred Smith), следующим образом. (Ниже, если OPTIONAL MATCH и его WHERE не находят соответствия, courses будет пустой коллекцией).
MATCH (a1:Area)<-[:IN_AREA]-()<-[:IN_DEPT]-()<-[:HAS_MAJOR]-(student)
WHERE a1.name = 'Science'
OPTIONAL MATCH (student)-[e:ENROLLED_IN]->(course)-[:IN_DEPT]->()-[:IN_AREA]->(a2:Area)
WHERE a2.name = 'Humanities' AND e.grade >= 3.0
WITH student, COLLECT(course) AS courses
WHERE SIZE(courses) < 3 OR NONE(c IN courses WHERE c.level >= 200)
RETURN student;