Статьи

Neo4j Java REST Binding — часть 2 (пакетная обработка)

В первой части мы говорили о настройке соединения с сервером Neo4j с помощью привязки Java REST. Теперь давайте подробно рассмотрим транзакции, пакетирование и то, как на самом деле выглядят запросы REST. Убедитесь, что вы включили ведение журнала (установите для системного свойства org.neo4j.rest.logging_filter to true) as described in Part 1 .

Мы изменим наш код для выполнения этих вызовов API Neo4j.

Пример 1:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
Transaction tx = graphDb.beginTx();
    Map props=new HashMap();
    props.put("id", 100);
    props.put("name","firstNode");
    Node node=graphDb.createNode(props);
 
    props.put("id",200);
    props.put("name","secondNode");
    Node node2=graphDb.createNode(props);
 
    node.createRelationshipTo(node2, DynamicRelationshipType.withName("KNOWS"));
 
    tx.success();
    tx.finish();
     
    result=engine.query("start n=node(*) return count(n) as total", Collections.EMPTY_MAP);
    Iterator iterator=result.iterator();
    if(iterator.hasNext()) {
        Map row= iterator.next();
        out.print("Total nodes: " + row.get("total"));
    }

Проверьте журналы (для меня они появились по умолчанию на консоли Tomcat) и найдите вызовы REST. Код выше производится:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
INFO: 1 * Client out-bound request
1 >  POST http://localhost:7474/db/data/node
1 >  Accept: application/json; stream=true
1 >  X-Stream: true
1 >  Content-Type: application/json
1 >
{"id":100,"name":"firstNode"}
 
INFO: 1 * Client in-bound response
1 < 201
1 < Access-Control-Allow-Origin: *
1 < Transfer-Encoding: chunked
1 < Content-Encoding: UTF-8
1 < Location: http://localhost:7474/db/data/node/1
1 < Content-Type: application/json; stream=true
1 < Server: Jetty(6.1.25)
1 <
{"extensions":{},"paged_traverse":"http://localhost:7474/db/data/node/1/paged/traverse/{returnType}{?pageSize,leaseTime}","outgoing_relationships":"http://localhost:7474/db/data/node/1/relationships/out","traverse":"http://localhost:7474/db/data/node/1/traverse/{returnType}","all_typed_relationships":"http://localhost:7474/db/data/node/1/relationships/all/{-list|&|types}","property":"http://localhost:7474/db/data/node/1/properties/{key}","all_relationships":"http://localhost:7474/db/data/node/1/relationships/all","self":"http://localhost:7474/db/data/node/1","properties":"http://localhost:7474/db/data/node/1/properties","outgoing_typed_relationships":"http://localhost:7474/db/data/node/1/relationships/out/{-list|&|types}","incoming_relationships":"http://localhost:7474/db/data/node/1/relationships/in","incoming_typed_relationships":"http://localhost:7474/db/data/node/1/relationships/in/{-list|&|types}","create_relationship":"http://localhost:7474/db/data/node/1/relationships","data":{"name":"firstNode","id":100}}
 
INFO: 2 * Client out-bound request
2 > POST http://localhost:7474/db/data/node
2 > Accept: application/json; stream=true
2 > X-Stream: true
2 > Content-Type: application/json
2 >
{"id":200,"name":"secondNode"}
 
INFO: 2 * Client in-bound response
2 < 201
2 < Access-Control-Allow-Origin: *
2 < Transfer-Encoding: chunked
2 < Content-Encoding: UTF-8
2 < Location: http://localhost:7474/db/data/node/2
2 < Content-Type: application/json; stream=true
2 < Server: Jetty(6.1.25)
2 <
{"extensions":{},"paged_traverse":"http://localhost:7474/db/data/node/2/paged/traverse/{returnType}{?pageSize,leaseTime}","outgoing_relationships":"http://localhost:7474/db/data/node/2/relationships/out","traverse":"http://localhost:7474/db/data/node/2/traverse/{returnType}","all_typed_relationships":"http://localhost:7474/db/data/node/2/relationships/all/{-list|&|types}","property":"http://localhost:7474/db/data/node/2/properties/{key}","all_relationships":"http://localhost:7474/db/data/node/2/relationships/all","self":"http://localhost:7474/db/data/node/2","properties":"http://localhost:7474/db/data/node/2/properties","outgoing_typed_relationships":"http://localhost:7474/db/data/node/2/relationships/out/{-list|&|types}","incoming_relationships":"http://localhost:7474/db/data/node/2/relationships/in","incoming_typed_relationships":"http://localhost:7474/db/data/node/2/relationships/in/{-list|&|types}","create_relationship":"http://localhost:7474/db/data/node/2/relationships","data":{"name":"secondNode","id":200}}
 
INFO: 3 * Client out-bound request
3 > POST http://localhost:7474/db/data/node/1/relationships
3 > Accept: application/json; stream=true
3 > X-Stream: true
3 > Content-Type: application/json
3 >
{"to":"http://localhost:7474/db/data/node/2","type":"KNOWS"}
 
INFO: 3 * Client in-bound response
3 < 201
3 < Access-Control-Allow-Origin: *
3 < Transfer-Encoding: chunked
3 < Content-Encoding: UTF-8
3 < Location: http://localhost:7474/db/data/relationship/0
3 < Content-Type: application/json; stream=true
3 < Server: Jetty(6.1.25)
3 <
 
INFO: 4 * Client out-bound request
4 > POST http://localhost:7474/db/data/cypher
4 > Accept: application/json; stream=true
4 > X-Stream: true
4 > Content-Type: application/json
4 >
{"query":"start n=node(*) return count(n) as total","params":{}}
 
INFO: 4 * Client in-bound response
4 < 200
4 < Access-Control-Allow-Origin: *
4 < Transfer-Encoding: chunked
4 < Content-Encoding: UTF-8
4 < Content-Type: application/json; stream=true
4 < Server: Jetty(6.1.25)
4 <
{"columns":["total"],"data":[[3]]}

В общей сложности 4 вызова REST по проводам для этого крошечного куска кода. Вы определенно хотите избежать этого, насколько это возможно. Вариант 1 — использовать Cypher, насколько это возможно. Мы могли бы преобразовать первые три вызова REST в один, не используя API встроенного стиля и переключившись на Cypher.

Пример 2:

01
02
03
04
05
06
07
08
09
10
11
12
13
Map<String,Object> props=new HashMap<String, Object>();
 props.put("id", 100);
 props.put("name","firstNode");
      
 Map<String,Object> props2=new HashMap<String, Object>();
 props2.put("id",200);
 props2.put("name","secondNode");
     
  Map<String,Object> params=new HashMap<String, Object>();
  params.put("props1",props);
  params.put("props2",props2);
         
 engine.query("create (n1 {props1})-[:KNOWS]->(n2 {props2})", params);

Это производит:

01
02
03
04
05
06
07
08
09
10
11
12
13
1 > POST http://localhost:7474/db/data/cypher
{"query":"create (n1 {props1})-[:KNOWS]->(n2 {props2})","params":{"props1":{"id":100,"name":"firstNode"},"props2":{"id":100,"name":"firstNode"}}}
 
Jul 24, 2013 10:38:47 PM com.sun.jersey.api.client.filter.LoggingFilter log
INFO: 1 * Client in-bound response
1 < 200
1 < Access-Control-Allow-Origin: *
1 < Transfer-Encoding: chunked
1 < Content-Encoding: UTF-8
1 < Content-Type: application/json; stream=true
1 < Server: Jetty(6.1.25)
1 <
{"columns":[],"data":[]}

Пакетирование всех операций внутри транзакции

Документация по адресу https://github.com/neo4j/java-rest-binding гласит:

«В 1.8 он пытается собрать все операции внутри tx как пакетную операцию, которая затем будет выполнена на сервере. Это означает, что результаты, полученные в этом «tx», не сразу доступны, а только после того, как вы вызвали tx.success и tx.finish «

Однако обратите внимание, что это НЕ поведение по умолчанию, как вы можете видеть из примера 1. Чтобы включить это, вам нужно установить следующее системное свойство: org.neo4j.rest.batch_transaction=true

После установки системного свойства и повторного запуска примера 1 вызовы REST выглядят так (только запросы):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
INFO: 1 * Client out-bound request
1 > POST http://localhost:7474/db/data/batch
1 > Accept: application/json; stream=true
1 > X-Stream: true
1 > Content-Type: application/json
1 >
[{"id":1,"to":"node","body":{"id":200,"name":"secondNode"},"method":"POST"},{"id":2,"to":"node","body":{"id":200,"name":"secondNode"},"method":"POST"},{"id":3,"to":"{1}/relationships","body":{"to":"{2}","type":"KNOWS"},"method":"POST"}]
 
INFO: 2 * Client out-bound request
2 > POST http://localhost:7474/db/data/cypher
2 > Accept: application/json; stream=true
2 > X-Stream: true
2 > Content-Type: application/json
2 >
{"query":"start n=node(*) return count(n) as total","params":{}}

Вы также можете явно создавать пакетные операции следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
List<Node> response =graphDb.executeBatch(new BatchCallback<List<Node>>() {
 
            @Override
            public List<Node> recordBatch(RestAPI batchRestApi) {
                List<Node> nodes=new ArrayList<Node>();
 
                Map props=new HashMap<String, Object>();
                props.put("id",600);
                nodes.add(batchRestApi.createNode(props));
 
                Map props2=new HashMap<String, Object>();
                props2.put("id",500);
                nodes.add(batchRestApi.createNode(props2));
                return nodes;
            }
        });

Это переводится на:

1
2
3
4
5
6
7
INFO: 1 * Client out-bound request
1 > POST http://localhost:7474/db/data/batch
1 > Accept: application/json; stream=true
1 > X-Stream: true
1 > Content-Type: application/json
1 >
[{"id":1,"to":"node","body":{"id":600},"method":"POST"},{"id":2,"to":"node","body":{"id":500},"method":"POST"}]

Любой из подходов Cypher / Batching настоятельно рекомендуется для более тонкого Java Neo4j API. В последнем посте мы рассмотрим поведение транзакций в контексте привязки REST.

Ссылка: Neo4j Java REST Binding — часть 2 ( пакетная обработка ) от нашего партнера по JCG Луанны Мискитта в блоге Thought Bytes .