Есть ли эквивалент в коммитах лампочек для neo4j
Я строю приложение на Python с интенсивным использованием данных, основанное на neo4j, и по соображениям производительности мне нужно создавать / восстанавливать несколько узлов и отношений во время каждой транзакции. Есть ли эквивалент SQLAlchemy session.commit()
Скажите в лампочках?
Редактировать:
для тех, кто заинтересован, был разработан интерфейс к лампам, который реализует ту функцию, которая изначально работает, а в остальном функционирует почти так же, как SQLAlchemy: https://github.com/chefjerome/graphalchemy
1 ответ
Наиболее эффективный способ выполнить многокомпонентную транзакцию - это инкапсулировать транзакцию в сценарии Gremlin и выполнить ее как один запрос.
Вот пример того, как это сделать - это пример приложения, которое я разработал в прошлом году для Neo4j Heroku Challenge.
Проект называется Lightbulb: https://github.com/espeed/lightbulb
README описывает, что он делает...
Что такое лампочка?
Lightbulb - это движок Git с поддержкой Neo4j для Heroku, написанный на Python.
Вы можете писать записи в блоге в Emacs (или в вашем любимом текстовом редакторе) и использовать Git для контроля версий, не отказываясь от функций динамического приложения.
Пишите записи в блоге в ReStructuredText и стилизуйте их, используя систему шаблонов вашего сайта.
Когда вы нажимаете на Heroku, метаданные записи автоматически сохраняются в Neo4j, а фрагмент HTML, сгенерированный из исходного файла ReStructuredText, будет передаваться с диска.
Тем не менее, Neo4j прекратил предлагать Gremlin на своем бесплатном / тестовом дополнении Heroku, поэтому Lightbulb не будет работать для новых пользователей Neo4j/Heroku.
В течение следующего года - до выхода книги TinkerPop - TinkerPop выпустит Rexster Heroku Add On с полной поддержкой Gremlin, чтобы люди могли запускать свои проекты на Heroku по мере продвижения по книге.
Но сейчас вам не нужно беспокоиться о запуске приложения - весь соответствующий код содержится в этих двух файлах - файле модели приложения Lightbulb и его файле сценария Gremlin:
https://github.com/espeed/lightbulb/blob/master/lightbulb/model.py https://github.com/espeed/lightbulb/blob/master/lightbulb/gremlin.groovy
model.py
предоставляет пример для создания пользовательских моделей луковиц и пользовательских луковиц Graph
учебный класс.
gremlin.groovy
содержит собственный скрипт Gremlin, который Entry
модель выполняется - этот скрипт Gremlin инкапсулирует всю транзакцию, состоящую из нескольких частей, так что она может быть выполнена как один запрос.
Обратите внимание на model.py
файл выше, я настраиваю EntryProxy
переопределив create()
а также update()
методы и вместо этого определяют единственное число save()
метод для обработки создает и обновляет.
Зацепить кастом EntryProxy
в Entry
модель, я просто переопределить Entry
модели get_proxy_class
метод, так что он возвращает EntryProxy
класс вместо значения по умолчанию NodeProxy
учебный класс.
Все остальное в Entry
Модель предназначена для создания данных для save_blog_entry
Скрипт Gremlin (определен в файле gremlin.groovy выше).
Заметьте, что в гремлине.груовом save_blog_entry()
Метод длинный и содержит несколько замыканий. Вы можете определить каждое замыкание как независимый метод и выполнить его с несколькими вызовами Python, но тогда у вас будут накладные расходы на выполнение нескольких запросов к серверу, и, поскольку запросы раздельные, не будет никакого способа обернуть их все в транзакцию.
Используя один скрипт Gremlin, вы объединяете все в один транзакционный запрос. Это намного быстрее, и это транзакционный.
Вы можете увидеть, как весь скрипт выполняется в последней строке метода Gremlin:
return transaction(save_blog_entry);
Здесь я просто обертываю закрытие транзакции вокруг всех команд во внутреннем save_blog_entry
закрытие. Создание закрытия транзакции сохраняет код изолированным и намного чище, чем встраивание логики транзакции в другие замыкания.
Тогда если вы посмотрите на код во внутреннем save_blog_entry
замыкание, это просто вызов других замыканий, которые я определил выше, используя параметры, которые я передал из Python, когда я вызывал скрипт в Entry
модель:
def _save(self, _data, kwds):
script = self._client.scripts.get('save_blog_entry')
params = self._get_params(_data, kwds)
result = self._client.gremlin(script, params).one()
Параметры, которые я передаю, создаются в пользовательской модели. _get_parms()
метод:
def _get_params(self, _data, kwds):
params = dict()
# Get the property data, regardless of how it was entered
data = build_data(_data, kwds)
# Author
author = data.pop('author')
params['author_id'] = cache.get("username:%s" % author)
# Topic Tags
tags = (tag.strip() for tag in data.pop('tags').split(','))
topic_bundles = []
for topic_name in tags:
#slug = slugify(topic_name)
bundle = Topic(self._client).get_bundle(name=topic_name)
topic_bundles.append(bundle)
params['topic_bundles'] = topic_bundles
# Entry
# clean off any extra kwds that aren't defined as an Entry Property
desired_keys = self.get_property_keys()
data = extract(desired_keys, data)
params['entry_bundle'] = self.get_bundle(data)
return params
Вот что _get_params()
делается...
buld_data(_data, kwds)
это функция, определенная в bulbs.element
: https://github.com/espeed/bulbs/blob/master/bulbs/element.py
Он просто объединяет аргументы в случае, если пользователь ввел некоторые в качестве позиционных аргументов, а некоторые в качестве аргументов ключевых слов.
Первый параметр, который я передаю в _get_params()
является author
, которое является именем пользователя автора, но я не передаю имя пользователя скрипту Gremlin, я передаю author_id
, author_id
кэшируется, поэтому я использую имя пользователя для поиска author_id
и установите это как параметр, который я позже передам Гремлин save_blog_entry
скрипт.
Тогда я создаю Topic
Model
объекты для каждого тега блога, который был установлен, и я называю get_bundle()
на каждом и сохранить их в виде списка topic_bundles
в парам.
get_bundle()
Метод определен в bulbs.model: https://github.com/espeed/bulbs/blob/master/bulbs/model.py
Он просто возвращает кортеж, содержащий data
, index_name
и индекс keys
для экземпляра модели:
def get_bundle(self, _data=None, **kwds):
"""
Returns a tuple containing the property data, index name, and index keys.
:param _data: Data that was passed in via a dict.
:type _data: dict
:param kwds: Data that was passed in via name/value pairs.
:type kwds: dict
:rtype: tuple
"""
self._set_property_defaults()
self._set_keyword_attributes(_data, kwds)
data = self._get_property_data()
index_name = self.get_index_name(self._client.config)
keys = self.get_index_keys()
return data, index_name, keys
Я добавил get_bundle()
метод Bulbs, чтобы обеспечить хороший и аккуратный способ объединения параметров, чтобы ваш сценарий Gremlin не был переполнен множеством аргументов в своей подписи.
Наконец, для Entry
Я просто создаю entry_bundle
и сохранить его как параметр.
Заметить, что _get_params()
возвращает dict
из трех параметров: author_id
, topic_bundle
, а также entry_bundle
,
это params
dict
передается непосредственно скрипту Gremlin:
def _save(self, _data, kwds):
script = self._client.scripts.get('save_blog_entry')
params = self._get_params(_data, kwds)
result = self._client.gremlin(script, params).one()
self._initialize(result)
И сценарий Gremlin имеет те же имена аргументов, что и передаваемые params
:
def save_blog_entry(entry_bundle, author_id, topic_bundles) {
// Gremlin code omitted for brevity
}
Затем параметры просто используются в скрипте Gremlin по мере необходимости - ничего особенного не происходит.
Итак, теперь, когда я создал свою собственную модель и скрипт Gremlin, я создаю собственный объект Graph, который инкапсулирует все прокси и соответствующие модели:
class Graph(Neo4jGraph):
def __init__(self, config=None):
super(Graph, self).__init__(config)
# Node Proxies
self.people = self.build_proxy(Person)
self.entries = self.build_proxy(Entry)
self.topics = self.build_proxy(Topic)
# Relationship Proxies
self.tagged = self.build_proxy(Tagged)
self.author = self.build_proxy(Author)
# Add our custom Gremlin-Groovy scripts
scripts_file = get_file_path(__file__, "gremlin.groovy")
self.scripts.update(scripts_file)
Теперь вы можете импортировать Graph
прямо из вашего приложения model.py
и создать экземпляр Graph
объект вроде нормальный.
>> from lightbulb.model import Graph
>> g = Graph()
>> data = dict(username='espeed',tags=['gremlin','bulbs'],docid='42',title="Test")
>> g.entries.save(data) # execute transaction via Gremlin script
Это помогает?