python - Falló la operación de WriteBatch con py2neo
neo4j (2)
Su problema parece no estar en batch.set_properties()
sino en el resultado de batch.get_or_create_in_index()
. Si agrega el nodo con batch.create()
, funciona:
db = neo4j.GraphDatabaseService()
batch = neo4j.WriteBatch(db)
# create a node instead of getting it from index
test_node = batch.create({''key'': ''value''})
# set new properties on the node
batch.set_properties(test_node, {''key'': ''foo''})
batch.submit()
Si echa un vistazo a las propiedades del objeto BatchRequest devuelto por batch.create()
y batch.get_or_create_in_index()
existe una diferencia en el URI porque los métodos utilizan diferentes partes de la API de REST neo4j:
test_node = batch.create({''key'': ''value''})
print test_node.uri # node
print test_node.body # {''key'': ''value''}
print test_node.method # POST
index_node = batch.get_or_create_in_index(neo4j.Node, "Users", "user_id", 12345, {})
print index_node.uri # index/node/Users?uniqueness=get_or_create
print index_node.body # {u''value'': 12345, u''key'': ''user_id'', u''properties'': {}}
print index_node.method # POST
batch.submit()
Entonces, ¿supongo que batch.set_properties()
alguna manera no puede manejar el URI del nodo indexado? Es decir, ¿realmente no obtiene el URI correcto para el nodo?
No resuelve el problema, pero podría ser un puntero para otra persona;)?
Estoy tratando de encontrar una solución al siguiente problema. Lo he visto cuasi-descrito en esta pregunta SO , pero realmente no ha sido respondido.
El siguiente código falla, comenzando con un nuevo gráfico:
from py2neo import neo4j
def add_test_nodes():
# Add a test node manually
alice = g.get_or_create_indexed_node("Users", "user_id", 12345, {"user_id":12345})
def do_batch(graph):
# Begin batch write transaction
batch = neo4j.WriteBatch(graph)
# get some updated node properties to add
new_node_data = {"user_id":12345, "name": "Alice"}
# batch requests
a = batch.get_or_create_in_index(neo4j.Node, "Users", "user_id", 12345, {})
batch.set_properties(a, new_node_data) #<-- I''m the problem
# execute batch requests and clear
batch.run()
batch.clear()
if __name__ == ''__main__'':
# Initialize Graph DB service and create a Users node index
g = neo4j.GraphDatabaseService()
users_idx = g.get_or_create_index(neo4j.Node, "Users")
# run the test functions
add_test_nodes()
alice = g.get_or_create_indexed_node("Users", "user_id", 12345)
print alice
do_batch(g)
# get alice back and assert additional properties were added
alice = g.get_or_create_indexed_node("Users", "user_id", 12345)
assert "name" in alice
En resumen, deseo, en una transacción por lotes, actualizar las propiedades de nodo indexadas existentes. La falla se produce en la línea batch.set_properties
, y se debe a que el objeto BatchRequest
devuelto por la línea anterior no se interpreta como un nodo válido. Aunque no es del todo idéntico, parece que estoy intentando algo así como la respuesta publicada aquí
Algunos detalles
>>> import py2neo
>>> py2neo.__version__
''1.6.0''
>>> g = py2neo.neo4j.GraphDatabaseService()
>>> g.neo4j_version
(2, 0, 0, u''M06'')
Actualizar
Si dividí el problema en lotes separados, puede ejecutarse sin error:
def do_batch(graph):
# Begin batch write transaction
batch = neo4j.WriteBatch(graph)
# get some updated node properties to add
new_node_data = {"user_id":12345, "name": "Alice"}
# batch request 1
batch.get_or_create_in_index(neo4j.Node, "Users", "user_id", 12345, {})
# execute batch request and clear
alice = batch.submit()
batch.clear()
# batch request 2
batch.set_properties(a, new_node_data)
# execute batch request and clear
batch.run()
batch.clear()
Esto funciona para muchos nodos también. Aunque no me gusta la idea de dividir el lote, esta podría ser la única forma en este momento. Alguien tiene algunos comentarios sobre esto?
Después de leer todas las características nuevas de Neo4j 2.0.0-M06, parece que se está reemplazando el flujo de trabajo anterior de los índices de nodo y relación. Actualmente existe una cierta divergencia por parte de neo en la forma en que se realiza la indexación. A saber, etiquetas e índices de esquema .
Etiquetas
Las etiquetas se pueden unir arbitrariamente a los nodos y pueden servir como referencia para un índice.
Índices
Los índices se pueden crear en Cypher haciendo referencia a las etiquetas (aquí, User
) y la clave de propiedad del nodo, ( screen_name
):
CREATE INDEX ON :User(screen_name)
Cypher MERGE
Además, los métodos get_or_create
indexados ahora son posibles a través de la nueva función CREE MERGE
, que incorpora Labels y sus índices de manera muy sucinta:
MERGE (me:User{screen_name:"SunPowered"}) RETURN me
Lote
Las consultas de este tipo se pueden py2neo
en py2neo
añadiendo una instancia de CypherQuery al objeto por lotes:
from py2neo import neo4j
graph_db = neo4j.GraphDatabaseService()
cypher_merge_user = neo4j.CypherQuery(graph_db,
"MERGE (user:User {screen_name:{name}}) RETURN user")
def get_or_create_user(screen_name):
"""Return the user if exists, create one if not"""
return cypher_merge_user.execute_one(name=screen_name)
def get_or_create_users(screen_names):
"""Apply the get or create user cypher query to many usernames in a
batch transaction"""
batch = neo4j.WriteBatch(graph_db)
for screen_name in screen_names:
batch.append_cypher(cypher_merge_user, params=dict(name=screen_name))
return batch.submit()
root = get_or_create_user("Root")
users = get_or_create_users(["alice", "bob", "charlie"])
Limitación
Sin embargo, existe una limitación en que los resultados de una consulta de cifrado en una transacción por lotes no se pueden referenciar más adelante en la misma transacción. La pregunta original se refería a la actualización de una colección de propiedades de usuario indexadas en una transacción por lotes. Esto no es posible, por lo que puedo reunir. Por ejemplo, el siguiente fragmento arroja un error:
batch = neo4j.WriteBatch(graph_db)
b1 = batch.append_cypher(cypher_merge_user, params=dict(name="Alice"))
batch.set_properties(b1, dict(last_name="Smith")})
resp = batch.submit()
Por lo tanto, parece que aunque hay un poco menos de sobrecarga en la implementación del get_or_create
sobre un nodo etiquetado que usa py2neo
porque los índices heredados ya no son necesarios, la pregunta original aún necesita 2 transacciones por lotes separadas para completarse.