是否有相当于neo4j的灯泡框架中的提交

chi*_*ffa 5 python neo4j bulbs

我正在构建一个基于neo4j的数据密集型Python应用程序,出于性能原因,我需要在每次事务中创建/恢复多个节点和关系.session.commit()在灯泡中是否有相当于SQLAlchemy的声明?

编辑:

对于那些感兴趣的人来说,开发了一个可以原生实现该功能的灯泡接口,其功能与SQLAlchemy非常相似:https: //github.com/chefjerome/graphalchemy

esp*_*eed 7

执行多部分事务的最高性能方法是将事务封装在Gremlin脚本中,并将其作为单个请求执行.

这是一个如何做到的例子 - 它来自我去年为Neo4j Heroku Challenge工作的示例应用程序.

该项目名为Lightbulb:https://github.com/espeed/lightbulb

自述文件描述了它的作用......

什么是灯泡?

Lightbulb是一个Git驱动的Neo4j支持的Heroku博客引擎,用Python编写.

您可以在Emacs(或您喜欢的文本编辑器)中编写博客条目,并使用Gi​​t进行版本控制,而无需放弃动态应用程序的功能.

在ReStructuredText中写下博客条目,并使用您网站的模板系统对其进行样式设置.

当您推送到Heroku时,条目元数据将自动保存到Neo4j,并且从ReStructuredText源文件生成的HTML片段将从磁盘提供.

然而,Neo4j退出提供Gremlin免费/测试Heroku Add On所以Lightbulb不适用于新的Neo4j/Heroku用户.

在明年 - 在TinkerPop出版之前--TinkerPop将发布Rexster Heroku Add On并提供完整的Gremlin支持,这样人们就可以在Heroku上完成他们的项目.

但就目前而言,您无需担心自己运行应用程序 - 所有相关代码都包含在这两个文件中 - 灯泡应用程序的模型文件及其Gremlin脚本文件:

https://github.com/espeed/lightbulb/blob/master/lightbulb/model.py https://github.com/espeed/lightbulb/blob/master/lightbulb/gremlin.groovy

model.py提供了构建自定义Bulbs模型和自定义Bulbs Graph类的示例.

gremlin.groovy包含自定义Entry模型执行的自定义Gremlin脚本- 此Gremlin脚本封装整个多部分事务,以便它可以作为单个请求执行.

请注意,在model.py上面的文件中,我EntryProxy通过覆盖create()update()方法进行自定义,而是定义一个单一的save()方法来处理创建和更新.

要将自定义挂钩EntryProxyEntry模型中,我只需覆盖Entry模型的get_proxy_class方法,以便它返回EntryProxy类而不是默认NodeProxy类.

Entry模型中的其他所有内容都是围绕为save_blog_entryGremlin脚本构建数据而设计的(在上面的gremlin.groovy文件中定义).

请注意,在gremlin.groovy中,该save_blog_entry()方法很长并且包含几个闭包.您可以将每个闭包定义为一个独立的方法,并使用多个Python调用执行它们,但随后您将产生多个服务器请求的开销,并且由于请求是分开的,因此无法将它们全部包装在事务中.

通过使用单个Gremlin脚本,您可以将所有内容组合到单个事务请求中.这要快得多,而且是交易性的.

您可以在Gremlin方法的最后一行中看到整个脚本的执行方式:

return transaction(save_blog_entry);

在这里,我只是围绕内部save_blog_entry闭包中的所有命令包装事务闭包.进行事务闭包可以使代码保持隔离状态,并且比将事务逻辑嵌入到其他闭包中更加清晰.

然后,如果你看一下内部save_blog_entry闭包中的代码,它只是调用我在上面定义的其他闭包,使用我在Entry模型中调用脚本时从Python传入的参数:

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() 
Run Code Online (Sandbox Code Playgroud)

我传入的参数是在模型的自定义_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
Run Code Online (Sandbox Code Playgroud)

_get_params()是在做什么......

buld_data(_data, kwds)是一个函数定义于bulbs.element:https: //github.com/espeed/bulbs/blob/master/bulbs/element.py#L959

它只是合并args,以防用户输入一些作为位置args而一些作为关键字args.

在第一个参数我传递到_get_params()author,这是笔者的用户名,但我不的用户名传递给小鬼剧本,我通过了author_id.这author_id是缓存的,所以我使用用户名查找author_id并将其设置为param,我稍后将其传递给Gremlin save_blog_entry脚本.

然后我Topic Model为每个设置的博客标签创建对象,然后我调用get_bundle()每个对象并将它们保存为topic_bundles参数列表.

get_bundle()方法在bulbs.model中定义:https: //github.com/espeed/bulbs/blob/master/bulbs/model.py#L363

它只是返回一个包含一个元组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
Run Code Online (Sandbox Code Playgroud)

我将这个get_bundle()方法添加到Bulbs中,以提供一种将params捆绑在一起的漂亮而整洁的方式,这样你的Gremlin脚本就不会因其签名中的大量args而被覆盖.

最后,Entry我只是创建一个entry_bundle并将其存储为param.

请注意,_get_params()返回dict三个PARAMS: ,author_id,topic_bundleentry_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)
Run Code Online (Sandbox Code Playgroud)

Gremlin脚本具有与传入的arg名称相同的arg名称params:

def save_blog_entry(entry_bundle, author_id, topic_bundles) {

   // Gremlin code omitted for brevity 

}
Run Code Online (Sandbox Code Playgroud)

然后根据需要将params简单地用在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)
Run Code Online (Sandbox Code Playgroud)

您现在可以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
Run Code Online (Sandbox Code Playgroud)

这有帮助吗?