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

时间:2013-05-26 13:17:26

标签: python neo4j bulbs

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

修改:

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

1 个答案:

答案 0 :(得分:7)

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

以下是一个如何操作的示例 - 来自我去年参加Neo4j Heroku挑战赛的示例应用程序。

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

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

  

什么是灯泡?

     

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

     

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

     

在ReStructuredText中写下博客条目,并使用您的   网站的模板系统。

     

当您推送到Heroku时,将自动输入条目元数据   保存到Neo4j,并从中生成HTML片段   ReStructuredText源文件将在磁盘上提供。

然而,Neo4j退出提供Gremlin免费/测试Heroku Add On so Lightbulb不适合新Neo4j / Heroku用户。

在明年 - TinkerPop book出来之前 - TinkerPop将发布一个Rexster Heroku Add On并提供完整的Gremlin支持,这样人们就可以在Heroku上完成他们的项目。 / p>

但就目前而言,您不必担心自己运行应用程序 - 所有相关代码都包含在这两个文件中 - 灯泡应用程序的模型文件及其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包含自定义Entry模型执行的自定义Gremlin脚本 - 此Gremlin脚本封装整个多部分事务,以便可以作为单个请求执行。

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

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

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

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

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

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

return transaction(save_blog_entry);

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

然后,如果您查看内部save_blog_entry闭包中的代码,它只是调用上面定义的其他闭包,使用我在Python中调用脚本时从Python传入的参数{1}}型号:

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)中定义的函数: https://github.com/espeed/bulbs/blob/master/bulbs/element.py#L959

它只是合并了args,以防用户输入一些作为位置参数,一些作为关键字参数。

我传入bulbs.element的第一个参数是_get_params(),这是作者的用户名,但我没有将用户名传递给Gremlin脚本,我通过{ {1}}。 author已缓存,因此我使用用户名查找author_id并将其设置为参数,我稍后会将其传递给Gremlin author_id脚本。

然后我为每个设置的博客标记创建author_id save_blog_entry个对象,并在每个博客标记上调用Topic并将其保存为参数中的Model列表。

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

它只返回一个包含模型实例的topic_bundlesget_bundle()和索引data的元组:

index_name

我向灯泡添加了keys方法,以便提供一种将params捆绑在一起的漂亮而整洁的方式,这样你的Gremlin脚本就不会因其签名中的大量args而被覆盖。

最后,对于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()并将其存储为参数。

请注意,Entry会返回entry_bundle三个参数:_get_params()dictauthor_id

topic_bundle entry_bundle直接传递给Gremlin脚本:

params

Gremlin脚本与dict传递的arg名称具有相同的arg名称:

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脚本中 - 没有什么特别的。

现在我已经创建了自定义模型和Gremlin脚本,我构建了一个自定义Graph对象,它封装了所有代理和相应的模型:

params

您现在可以直接从您应用的def save_blog_entry(entry_bundle, author_id, topic_bundles) { // Gremlin code omitted for brevity } 导入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

这有帮助吗?