Neo4j过程“无法执行请求的操作,因为它必须在事务中执行...”

时间:2018-10-25 15:36:48

标签: database graph neo4j transactions graph-databases

我正在为Neo4j 3.3.5编写以下过程:

/**
 * This procedure provides best sell products from a category
 *
 * @param category the category node for which we are searching for best sell items
 * @param limit    limit of returned products count
 */
@Procedure(value = "bestSell.category")
@Description("Get best sell items from a specified category")
public Stream<OutputResult> bestSellForCategory(
        @Name("category") Node category,
        @Name(value = "limit", defaultValue = "20") Long limit) {
    RelationshipType inCategoryType = RelationshipType.withName(IN_CATEGORY_RELATION);
    RelationshipType recentlyOrderedType = RelationshipType.withName(RECENTLY_ORDERED_RELATION);

    try {
        List<Node> productsInCateogory = new ArrayList<>();
        List<ScoredResult> scoredProducts = new ArrayList<>();

        for (Relationship relationship : category.getRelationships(inCategoryType, Direction.INCOMING)) {
            Node product = relationship.getOtherNode(category);
            productsInCateogory.add(product);
        }

        productsInCateogory.parallelStream().forEach(node -> {
            long i = 0;
            for (Relationship ignored : node.getRelationships(recentlyOrderedType, Direction.INCOMING)) {
                i++;
            }
            if (i != 0) {
                scoredProducts.add(new ScoredResult((Long) node.getProperty(DOC_ID_PROPERTY), i));
            }
        });
        return scoredProducts.stream().sorted((scoredResult, t1) -> (int) (scoredResult.score - t1.score)).limit(limit).map(s -> new OutputResult(s.docId));
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e.getMessage());
    }
}

这是我为该程序编写的测试:

@Test
public void checkBestSellOutput() throws Throwable {
    try (Driver driver = GraphDatabase.driver(neo4j.boltURI(), Config.build().withoutEncryption().toConfig())) {
        Session session = driver.session();

        try (Transaction tx = session.beginTransaction()) {
            tx.run(
                    "MERGE (c:Category {categoryId:93})" +
                            "MERGE (p1:Product {docId:1})" +
                            "MERGE (p2:Product {docId:2})" +
                            "MERGE (p3:Product {docId:3})" +
                            "MERGE (p1)-[:IN_CATEGORY]->(c)" +
                            "MERGE (p2)-[:IN_CATEGORY]->(c)" +
                            "MERGE (p3)-[:IN_CATEGORY]->(c)" +
                            "MERGE (cust1:Customer {customerId:1})" +
                            "MERGE (cust2:Customer {customerId:2})" +
                            "MERGE (cust1)-[:RECENTLY_ORDERED]->(p1)" +
                            "MERGE (cust2)-[:RECENTLY_ORDERED]->(p1)" +
                            "MERGE (cust1)-[:RECENTLY_ORDERED]->(p2)");
            tx.success();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }

        List<Record> results = session.run("MATCH (c:Category {categoryId: 93}) CALL bestSell.category(c, 20) YIELD docId RETURN docId").list();

        assertThat(results.size(), equalTo(2));
    }
}

当我尝试使用maven运行build时,出现以下错误: 无法调用过程bestSell.category:原因:java.lang.RuntimeException:无法执行请求的操作,因为它必须在事务中执行。确保将操作包装在适当的事务样板中,然后重试。

我尝试了很多,但没有在文档中找到答案。我该如何解决?

1 个答案:

答案 0 :(得分:0)

有关简单的样板,请参见Transaction JavaDoc:

 try ( Transaction tx = graphDb.beginTx() )
 {
     // operations on the graph
     // ...

     tx.success();
 }

这是在过程中如何使用样板的示例。 @Context注释将导致db变量在运行时被注入适当的GraphDatabaseService实例。

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.procedure.Context;
.
.
.

@Context
public GraphDatabaseService db;

/**
 * This procedure provides best sell products from a category
 *
 * @param category the category node for which we are searching for best sell items
 * @param limit    limit of returned products count
 */
@Procedure(value = "bestSell.category")
@Description("Get best sell items from a specified category")
public Stream<OutputResult> bestSellForCategory(
        @Name("category") Node category,
        @Name(value = "limit", defaultValue = "20") Long limit) {
    RelationshipType inCategoryType = RelationshipType.withName(IN_CATEGORY_RELATION);
    RelationshipType recentlyOrderedType = RelationshipType.withName(RECENTLY_ORDERED_RELATION);

    try {
        List<Node> productsInCategory = new ArrayList<>();
        List<ScoredResult> scoredProducts = new ArrayList<>();

        try (Transaction tx = db.beginTx()) {
            for (Relationship relationship : category.getRelationships(inCategoryType, Direction.INCOMING)) {
                Node product = relationship.getOtherNode(category);
                productsInCategory.add(product);
            }
            productsInCategory.parallelStream().forEach(node -> {
                long i = 0;
                for (Relationship ignored : node.getRelationships(recentlyOrderedType, Direction.INCOMING)) {
                    i++;
                }
                if (i != 0) {
                    scoredProducts.add(new ScoredResult((Long) node.getProperty(DOC_ID_PROPERTY), i));
                }
            });
            tx.success();
        }

        return scoredProducts.stream().sorted((scoredResult, t1) -> (int) (scoredResult.score - t1.score)).limit(limit).map(s -> new OutputResult(s.docId));
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e.getMessage());
    }
}