Neo4j процедура "Запрошенная операция не может быть выполнена, потому что она должна быть выполнена в транзакции ..." - PullRequest
0 голосов
/ 25 октября 2018

Я пишу следующую процедуру для 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 я получаю следующую ошибку: Не удалось вызвать процедуру bestSell.category: Причина: java.lang.RuntimeException: Запрошенная операция не может быть выполнена, поскольку она должна быть выполнена в транзакции.Убедитесь, что вы помещаете свою операцию в соответствующий шаблон транзакции и повторите попытку.

Я много пробовал и не нашел ответа в документации.Как я могу это исправить?

1 Ответ

0 голосов
/ 26 октября 2018

См. Транзакция 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());
    }
}
...