lab3
.pdf
try:
# Подключение к базе данных Neo4j
with GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USERNAME, NEO4J_PASSWORD)) as driver:
with driver.session(database=NEO4J_DBNAME) as session:
# Очистка базы данных
clear_db(session)
# Заполнение БД create_cars_by_query(session) create_customers_by_query(session) create_sellers_by_query(session) create_orders_by_query(session) session.execute_write(create_relationships)
except Exception as e: print(f"Произошла ошибка: {e}")
# Запуск основной функции if __name__ == "__main__":
main()
Результат выполнения листинга 3 представлен на рисунке 7.
Рисунок 7 - Результат вставки
Листинг 4. Код программы для визуализации результатов запросов import os
from dotenv import load_dotenv from neo4j import GraphDatabase
from prettytable import PrettyTable
# Загрузка переменных окружения
load_dotenv()
# Получение учетных данных Neo4j из переменных окружения
NEO4J_URI = os.getenv("NEO4J_URI")
NEO4J_USERNAME = os.getenv("NEO4J_USERNAME")
NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD")
NEO4J_DBNAME = os.getenv("NEO4J_DBNAME")
# Нахождение всех автомобилей бренда Audi def func1(session):
query = """
MATCH (c:Car) WHERE c.brand = 'Audi' RETURN c.id as Id, c.model as Model
"""
cars = session.run(query) print("\nВсе авто бренда Audi:")
cars_table = PrettyTable()
cars_table.field_names = [ "ID автомобиля", "Модель автомобиля",
]
for car in cars: cars_table.add_row(
[
car["Id"], car["Model"]
]
)
print(cars_table)
# Нахождение продавцов с зарплатой больше 55 тысяч рублей def func2(session):
query = """
MATCH (s:Seller) WHERE s.salary > 55000 RETURN s.full_name as FullName, s.salary as Salary
"""
sellers = session.run(query)
print("\nВсе продавцы с зарплатой > 55000:") sellers_table = PrettyTable()
sellers_table.field_names = [ "ФИО продавца", "Зарплата продавца",
]
for seller in sellers: sellers_table.add_row(
[
seller["FullName"], seller["Salary"]
]
)
print(sellers_table)
# Получение всех заказов, оплаченных наличными def func3(session):
query = """
MATCH (o:Order) WHERE o.payment_method = 'Наличные' RETURN o.id as OrderId, o.purchase_date as PurchaseDate
"""
orders = session.run(query)
print("\nВсе заказы, оплаченные наличными: ") orders_table = PrettyTable()
orders_table.field_names = [ "Идентификатор заказа", "Дата заказа",
]
for order in orders: orders_table.add_row(
[
order["OrderId"], order["PurchaseDate"]
]
)
print(orders_table)
# Получение всех автомобилей, доступных на складе def func4(session):
query = """
MATCH (c:Car) WHERE c.availability = 'Да' RETURN c.id as Id, c.brand as Brand, c.model as Model
"""
orders = session.run(query)
print("\nПолучение всех автомобилей, доступных на складе: ") orders_table = PrettyTable()
orders_table.field_names = [ "Идентификатор автомобиля", "Бренд автомобиля", "Модель автомобиля"
]
for order in orders: orders_table.add_row(
[
order["Id"], order["Brand"], order["Model"]
]
)
print(orders_table)
# Нахождение всех клиентов, которые совершили покупку автомобиля стоимость свыше 20 тысяч рублей
def func5(session): query = """
MATCH (c:Customer)-[:MADE_ORDER]->(o:Order)-[:INCLUDES_CAR]-
>(car:Car)
WHERE car.price > 20000
RETURN c.full_name AS FullName, car.brand AS Brand, car.model AS
Model
ORDER BY car.price DESC
"""
orders = session.run(query)
print("\nПолучение всех клиентов, купиших автомобили, стоимость которых выше 20000 рублей: ")
orders_table = PrettyTable() orders_table.field_names = [
"ФИО покупателя", "Бренд автомобиля", "Модель автомобиля"
]
for order in orders: orders_table.add_row(
[
order["FullName"], order["Brand"], order["Model"]
]
)
print(orders_table)
# Нахождение продавца, обслужившего больше всего заказов def func6(session):
query = """
MATCH (s:Seller)-[:HANDLED_ORDER]->(o:Order)
RETURN s.full_name AS FullName, COUNT(o) AS NumOrders ORDER BY NumOrders DESC
LIMIT 1
"""
orders = session.run(query)
print("\nПродавец, обслуживший больше всего заказов: ") orders_table = PrettyTable()
orders_table.field_names = [ "ФИО продавца", "Количество заказов",
]
for order in orders: orders_table.add_row(
[
order["FullName"], order["NumOrders"],
]
)
print(orders_table)
# Нахождение всех автомобилей, включенных в заказы за октябрь 2023 def func7(session):
query = """
MATCH (o:Order)-[:INCLUDES_CAR]->(c:Car) WHERE o.purchase_date STARTS WITH '2023-10' RETURN c.brand AS Brand, c.model AS Model ORDER BY c.brand ASC
"""
orders = session.run(query)
print("\nПолучение всех автомобилей, включенных в заказы за октябрь
2023: ")
orders_table = PrettyTable()
orders_table.field_names = [ "Бренд автомобиля", "Модель автомобиля"
]
for order in orders:
orders_table.add_row(
[
order["Brand"], order["Model"]
]
)
print(orders_table)
# Получение общего дохода от продажи автомобилей в октябре 2023 года def func8(session):
query = """
MATCH (o:Order)-[:INCLUDES_CAR]->(c:Car) WHERE o.purchase_date STARTS WITH '2023-10' RETURN SUM(c.price) AS TotalIncome
"""
orders = session.run(query)
print("\nПолучение общего дохода от продажи автомобилей в октябре
2023 года: ")
orders_table = PrettyTable()
orders_table.field_names = [ "Общий доход"
]
for order in orders: orders_table.add_row(
[
order["TotalIncome"]
]
)
print(orders_table)
# Получение страны, автомобили из которой чаще всего покупают. def func9(session):
query = """
MATCH (o:Order)-[:INCLUDES_CAR]->(c:Car)
RETURN c.country AS Country, COUNT(*) AS NumPurchases ORDER BY NumPurchases DESC
LIMIT 1
"""
orders = session.run(query)
print("\nПолучение страны, автомобили из которой чаще всего покупают: ")
orders_table = PrettyTable()
orders_table.field_names = [ "Страна производителя", "Количество автомобилей",
]
for order in orders: orders_table.add_row(
[
order["Country"], order["NumPurchases"],
]
)
print(orders_table)
# Получение покупателя, сделавшего больше всего заказов. def func10(session):
query = """
MATCH (c:Customer)-[:MADE_ORDER]->(o:Order)
RETURN c.full_name AS FullName, COUNT(o) AS NumOrders ORDER BY NumOrders DESC
LIMIT 1
"""
orders = session.run(query)
print("\nПолучение покупателя, сделавшего больше всего заказов: ") orders_table = PrettyTable()
orders_table.field_names = [ "ФИО покупателя",
"Количество сделанныз заказов",
]
for order in orders: orders_table.add_row(
[
order["FullName"], order["NumOrders"],
]
)
print(orders_table)
# Основной блок выполнения def main():
try:
# Подключение к базе данных Neo4j
with GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USERNAME, NEO4J_PASSWORD)) as driver:
with driver.session(database=NEO4J_DBNAME) as session: func1(session)
func2(session)
func3(session)
func4(session)
func5(session)
func6(session)
func7(session)
func8(session)
func9(session)
func10(session)
except Exception as e: print(f"Произошла ошибка: {e}")
# Запуск основной функции if __name__ == "__main__":
main()
Результаты выполнения запросов представлены на рисунках 8-10.
Рисунок 8 - Результат выполнения
Рисунок 9 - Результат выполнения
Рисунок 10 - Результат выполнения
Вывод
Лабораторная работа посвящена изучению возможностей графовых баз данных,
представленных системой Neo4j, и освоению методов взаимодействия с ней средствами языка программирования Python.
Графовые базы данных отличаются от традиционных реляционных моделей,
представляя данные в виде вершин (узлов) и ребер (связей). Такая структура позволяет легко моделировать сложные взаимосвязи объектов, делая запросы интуитивно понятнее и эффективнее. Интеграция Neo4j с языком Python осуществляется посредством драйвера
GraphDatabase. Использование удобных конструкций, таких как контекстные менеджеры
(with) и генераторы сессий, упрощает написание безопасного и поддерживаемого кода.
При разработке приложений особое внимание уделено вопросам безопасности и удобства сопровождения. Переменные подключения к базе данных вынесены в отдельный конфигурационный файл .env с использованием пакета python-dotenv, что обеспечивает гибкость и безопасность разработки.
Использование Cypher-запросов значительно облегчает процесс формирования и выполнения операций над графовыми структурами. Например, выборка всех автомобилей определенной марки «Audi» выполняется одним простым выражением, демонстрируя эффективность подхода.
Применение библиотеки prettytable позволило удобно отображать результаты запросов в структурированном виде, облегчая восприятие больших объемов данных.
Проблема:
При попытке использовать конструкцию из листинга 4, возникла ошибка: Expected exactly
one statement per query but got: 30
Листинг 4. Проблемный участок кода
session.run("""
MATCH |
(c:Customer {id: 'CLIENT001'}), (o:Order {id: 'ORDER001'}) |
MERGE (c)-[:MADE_ORDER]->(o); |
|
MATCH (c:Customer {id: 'CLIENT002'}), (o:Order {id: 'ORDER002'}) |
|
MERGE |
(c)-[:MADE_ORDER]->(o); |
Решение:
Проблема, возникшая в представленном фрагменте кода, связана с нарушением синтаксического правила языка запросов Cypher, используемого в графовом хранилище данных Neo4j. Согласно спецификации, запросы к серверу Neo4j допускают выполнение ровно одной операции на одну сессионную команду. Для устранения ошибки достаточно разделить весь объем выполняемых команд на отдельные части и последовательно передавать каждую операцию на выполнение по отдельности. Обновленная версия представлена в листинге 5. Таким образом, каждое действие теперь передается отдельной командой, позволяя правильно исполнить все нужные изменения базы данных.
Листинг 5. Код с решение проблемы from neo4j import GraphDatabase
# Подключаемся к БД
driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))
def create_relationships(tx):
# Устанавливаем связь MADE_ORDER между клиентом и заказом tx.run("MATCH (c:Customer {id: 'CLIENT001'}), (o:Order {id:
'ORDER001'}) "
"MERGE (c)-[:MADE_ORDER]->(o)")
tx.run("MATCH (c:Customer {id: 'CLIENT002'}), (o:Order {id: 'ORDER002'}) "
"MERGE (c)-[:MADE_ORDER]->(o)")
...
#Аналогично устанавливаем остальные связи между остальными парами клиентов-заказов,
#продавцов-заказов и заказов-автомобилей
with driver.session() as session: session.write_transaction(create_relationships)
...
