5

私は neo4j に基づいてデータ集約型の Python アプリケーションを構築しています。パフォーマンス上の理由から、各トランザクション中に複数のノードとリレーションを作成/回復する必要があります。電球に SQLAlchemysession.commit()ステートメントに相当するものはありますか?

編集:

興味のある方のために、その機能をネイティブに実装する電球へのインターフェースが開発されており、それ以外の場合はSQLAlchemyとほとんど同じように機能します:

4

1 に答える 1

7

マルチパート トランザクションを実行する最もパフォーマンスの高い方法は、トランザクションを Gremlin スクリプトにカプセル化し、単一の要求として実行することです。

その方法の例を次に示します。これは、昨年の Neo4j Heroku チャレンジで作成したサンプル アプリからのものです。

プロジェクトは電球と呼ばれます: https://github.com/espeed/lightbulb

README はそれが何をするかを説明しています...

電球とは?

Lightbulb は、Python で記述された Heroku 用の Git を利用した Neo4j を利用したブログ エンジンです。

動的アプリの機能を放棄することなく、Emacs (またはお気に入りのテキスト エディター) でブログ エントリを作成し、Git を使用してバージョン管理を行うことができます。

ReStructuredText でブログ エントリを作成し、Web サイトのテンプレート システムを使用してスタイルを設定します。

Heroku にプッシュすると、エントリ メタデータが自動的に Neo4j に保存され、ReStructuredText ソース ファイルから生成された HTML フラグメントがディスクから提供されます。

ただし、Neo4j は無料/テスト用の Heroku アドオンで Gremlin の提供を終了したため、Lightbulb は新しい Neo4j/Heroku ユーザーには機能しません。

来年中に、TinkerPop 本が出版される前に、TinkerPop は、Gremlin を完全にサポートする Rexster Heroku Add On をリリースし、本を読みながら Heroku でプロジェクトを実行できるようにします。

ただし、現時点では、アプリの実行について気にする必要はありません。関連するすべてのコードは、これら 2 つのファイル (Lightbulb アプリのモデル ファイルとその Gremlin スクリプト ファイル) に含まれています。

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

model.pyカスタム Bulbs モデルとカスタム BulbsGraphクラスを作成する例を示します。

gremlin.groovyカスタム モデルが実行するカスタム Gremlin スクリプトが含まれています。Entryこの Gremlin スクリプトは、単一の要求として実行できるように、マルチパート トランザクション全体をカプセル化します。

上記のファイルで、メソッドとメソッドをオーバーライドしてmodel.pyカスタマイズし、代わりに作成と更新を処理する単一のメソッドを定義していることに注意してください。EntryProxycreate()update()save()

カスタムEntryProxyをモデルにフックするには、モデルのメソッドをEntryオーバーライドして、デフォルトクラスではなくクラスを返すようにします。Entryget_proxy_classEntryProxyNodeProxy

モデル内の他のすべては、 Gremlin スクリプト (上記の gremlin.groovy ファイルで定義)Entryのデータを構築することを中心に設計されています。save_blog_entry

gremlin.groovy で、save_blog_entry()メソッドが長く、いくつかのクロージャが含まれていることに注意してください。各クロージャーを独立したメソッドとして定義し、複数の Python 呼び出しで実行することもできますが、複数のサーバー リクエストを作成するオーバーヘッドが発生し、リクエストが個別であるため、それらすべてをトランザクションでラップする方法はありません。

単一の Gremlin スクリプトを使用することで、すべてを 1 つのトランザクション リクエストに結合します。これははるかに高速であり、トランザクションです。

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#L959

ユーザーが位置引数としていくつかを入力し、キーワード引数としていくつかを入力した場合、引数を単純にマージします。

最初に渡すパラメータ_get_params()author、作成者のユーザー名ですが、Gremlin スクリプトにはユーザー名を渡さず、author_id. はauthor_idキャッシュされているので、ユーザー名を使用して を検索し、author_idそれをパラメーターとして設定します。これは後で Gremlinsave_blog_entryスクリプトに渡します。

次にTopic Model、設定されたブログ タグごとにオブジェクトを作成し、それぞれを呼び出してin paramsget_bundle()のリストとして保存します。topic_bundles

このget_bundle()メソッドは、bulbs.model で定義されています: https://github.com/espeed/bulbs/blob/master/bulbs/model.py#L363

モデル インスタンスのdataindex_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に追加して、 params をまとめる適切で整然とした方法を提供し、Gremlin スクリプトが署名内の大量の引数でオーバーランしないようにしました。

最後に、 forEntryを作成しentry_bundleて param として保存するだけです。

、、およびの 3 つのパラメータのを_get_params()返すことに注意してください。dictauthor_idtopic_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)

また、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

それは役に立ちますか?

于 2013-05-26T21:54:45.010 に答える