执行多部分事务的最有效方法是将事务封装在Gremlin脚本中,然后将其作为单个请求执行。
这是一个示例,它是我去年为Neo4j Heroku Challenge开发的一个示例应用程序。
该项目被称为灯泡:https://github.com/espeed/lightbulb
自述文件描述了它的作用…
什么是灯泡?
Lightbulb是一个用Python编写的,用于Heroku的,由Git驱动,受Neo4j支持的博客引擎。
您可以在Emacs(或您喜欢的文本编辑器)中编写博客条目,并使用Git进行版本控制,而无需放弃动态应用程序的功能。
用ReStructuredText编写博客条目,然后使用您网站的模板系统设置样式。
当您按下Heroku时,条目元数据将自动保存到Neo4j,并且将从ReStructuredText源文件生成的HTML片段从磁盘提供。
但是,Neo4j放弃了在免费/测试版的Heroku Add On上提供Gremlin的功能,因此Lightbulb将不适用于新Neo4j /
Heroku用户。
明年-在TinkerPop书问世之前-
TinkerPop将发布具有Gremlin全面支持的Rexster Heroku Add On,以便人们在阅读书的过程中可以在Heroku上运行项目。
但就目前而言,您无需担心应用程序的运行-所有相关代码都包含在这两个文件中-Lightbulb应用程序的模型文件及其Gremlin脚本文件:
https://github.com/espeed/lightbulb/blob/master/lightbulb/model.pyhttps://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()方法来处理创建和更新。
要将自定义添加
EntryProxy到
Entry模型中,我只需重写
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()我传入的参数是在模型的自定义
_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#L959
万一用户输入了一些作为位置args和一些作为关键字args的情况,它只是合并了args。
在第一个参数我传递到
_get_params()是
author,这是笔者的用户名,但我不通过用户名到小鬼剧本,我通过了
author_id。将
author_id被缓存,所以我使用的用户名来查找
author_id,并设置为PARAM,我稍后会传递给小鬼
save_blog_entry脚本。
然后,我
Topic
Model为设置的每个Blog标签创建对象,然后调用
get_bundle()每个对象并将其另存为
topic_bundlesin paras列表。
该
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
我将
get_bundle()方法添加到Bulbs中,以提供一种很好的,整洁的将参数捆绑在一起的方法,因此您的Gremlin脚本不会因其签名中的大量参数而被溢出。
最后,对于
Entry,我只需创建一个
entry_bundle并将其存储为参数。
请注意,
_get_params()返回
dict三个PARAMS:
,
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脚本的arg名称与传递的arg名称相同
params:
def save_blog_entry(entry_bundle, author_id, topic_bundles) { // Gremlin pre 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
有帮助吗?



