Есть ли эквивалент в коммитах лампочек для 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 скрипт.

Тогда я создаю TopicModel объекты для каждого тега блога, который был установлен, и я называю 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,

это paramsdict передается непосредственно скрипту 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

Это помогает?

Другие вопросы по тегам