Php 提高Neo4j的速度

Php 提高Neo4j的速度,php,neo4j,recommendation-engine,graphaware,Php,Neo4j,Recommendation Engine,Graphaware,我正在尝试使用Neo4j和Reco4HP创建一个简单的推荐引擎 数据模型由以下节点和关系组成: (用户)-[:HAS_bunded]->(产品{category_id:int} )-[:设计人]->(设计师) 在这个系统中,我想推荐产品,并使用与用户已经购买的产品相同的设计师来推销产品。为了创建推荐,我使用了一个发现类和一个后处理器类来提升产品。见下文。这是可行的,但速度很慢。完成此过程需要5秒钟以上,而datamodel可容纳约1000个产品和约100名设计师 // Disovery clas

我正在尝试使用Neo4j和Reco4HP创建一个简单的推荐引擎

数据模型由以下节点和关系组成:

(用户)-[:HAS_bunded]->(产品{category_id:int} )-[:设计人]->(设计师)

在这个系统中,我想推荐产品,并使用与用户已经购买的产品相同的设计师来推销产品。为了创建推荐,我使用了一个发现类和一个后处理器类来提升产品。见下文。这是可行的,但速度很慢。完成此过程需要5秒钟以上,而datamodel可容纳约1000个产品和约100名设计师

// Disovery class
    <?php
namespace App\Reco4PHP\Discovery;
use GraphAware\Common\Cypher\Statement;
use GraphAware\Common\Type\NodeInterface;
use GraphAware\Reco4PHP\Engine\SingleDiscoveryEngine;

class InCategory extends SingleDiscoveryEngine {

    protected $categoryId;

    public function __construct($categoryId) {
        $this->categoryId = $categoryId;
    }

    /**
     * @return string The name of the discovery engine
     */
    public function name() {
        return 'in_category';
    }

    /**
     * The statement to be executed for finding items to be recommended
     *
     * @param \GraphAware\Common\Type\NodeInterface $input
     * @return \GraphAware\Common\Cypher\Statement
     */
    public function discoveryQuery(NodeInterface $input) {

        $query = "
            MATCH (reco:Card)
            WHERE reco.category_id = {category_id}
            RETURN reco, 1 as score
        ";

        return Statement::create($query, ['category_id' => $this->categoryId]);
    }
}

// Boost shared designers
class RewardSharedDesigners extends RecommendationSetPostProcessor {

    public function buildQuery(NodeInterface $input, Recommendations $recommendations)
    {
        $ids = [];
        foreach ($recommendations->getItems() as $recommendation) {
            $ids[] = $recommendation->item()->identity();
        }

        $query = 'UNWIND {ids} as id
        MATCH (reco) WHERE id(reco) = id
        MATCH (user:User) WHERE id(user) = {userId}
        MATCH (user)-[:HAS_BOUGHT]->(product:Product)-[:DESIGNED_BY]->()<-[:DESIGNED_BY]-(reco)

        RETURN id, count(product) as sharedDesignedBy';

        return Statement::create($query, ['ids' => $ids, 'userId' => $input->identity()]);
    }

    public function postProcess(Node $input, Recommendation $recommendation, Record $record) {
        $recommendation->addScore($this->name(), new SingleScore((int)$record->get('sharedDesignedBy')));
    }

    public function name() {
        return 'reward_shared_designers';
    }
}
//Disovery类

我只能对Cypher进行评论,即使这样,也不会太多,因为您没有包含函数GetItems()或data(Cypher dump)。 但鲜有突出的东西

  • 它将更快地使用标签上(记录),我认为这是产品

  • 另外,我假设这是可以放入-[:DESIGNED_BY]->()的设计器标签。主要问题在于后处理器查询。目标是:

    根据我购买的产品数量增加推荐 设计了推荐项目的设计师

    因此,您可以稍微修改查询以直接匹配设计器并在其上进行聚合,而且最好在
    展开之前首先找到用户,否则它将在产品ID的每次迭代中匹配用户:

    MATCH (user) WHERE id(user) = {userId}
    UNWIND {ids} as productId
    MATCH (product:Product)-[:DESIGNED_BY]->(designer)
    WHERE id(product) = productId
    WITH productId, designer, user
    MATCH (user)-[:BOUGHT]->(p)-[:DESIGNED_BY]->(designer)
    RETURN productId as id, count(*) as score
    
    完整的后处理器如下所示:

        public function buildQuery(NodeInterface $input, Recommendations $recommendations)
        {
            $ids = [];
            foreach ($recommendations->getItems() as $recommendation) {
                $ids[] = $recommendation->item()->identity();
            }
    
            $query = 'MATCH (user) WHERE id(user) = {userId}
            UNWIND {ids} as productId
            MATCH (product:Product)-[:DESIGNED_BY]->(designer)
            WHERE id(product) = productId
            WITH productId, designer, user
            MATCH (user)-[:BOUGHT]->(p)-[:DESIGNED_BY]->(designer)
            RETURN productId as id, count(*) as score';
    
            return Statement::create($query, ['userId' => $input->identity(), 'ids' => $ids]);
        }
    
        public function postProcess(Node $input, Recommendation $recommendation, Record $record)
        {
            $recommendation->addScore($this->name(), new SingleScore($record->get('score')));
        }
    
    我已经创建了一个存储库,在该存储库中,我在您的域中有一个功能齐全的实现:

    收到数据后更新

    一个产品和一个用户之间有多个相同类型的关系,这一事实增加了找到的模式数量的指数性

    有两种解决方案:

    将它们区分开来,并在模式末尾添加WHERE子句:

    MATCH (user) WHERE id(user) = {userId}
    UNWIND {ids} as cardId
    MATCH (reco:Card)-[:DESIGNED_BY]->(designer) WHERE id(reco) = cardId
    MATCH (user)-[:HAS_BOUGHT]->(x)
    WHERE (x)-[:DESIGNED_BY]->(designer)
    RETURN cardId as id, count(*) as sharedDesignedBy
    
    在Neo4j 3.0+中,您可以受益于
    USING JOIN
    用法,并保持与以前相同的查询:

    MATCH (user) WHERE user.id = 245
    UNWIND ids as id
    MATCH (reco:Card) WHERE id(reco) = id
    MATCH (user:User)-[:HAS_BOUGHT]->(card:Card)-[:DESIGNED_BY]->(designer:Designer)<-[:DESIGNED_BY]-(reco:Card)
    USING JOIN ON card
    RETURN id, count(card) as sharedDesignedBy
    
    匹配(用户),其中user.id=245
    将id作为id展开
    匹配(记录:卡),其中id(记录)=id
    
    匹配(用户:用户)-[:已购买]->(卡:卡)-[:设计人]->(设计师:设计师)1。是的,reco是一种产品。2.是的,因为数据模型看起来像(Product-[:DESIGNED_BY]->(Designer)3。代码基于Reco4HP模板,其中发现类的结果被传递到后处理器.我认为这不应该改变。嗨,我是Reco4HP的作者。非常酷,我从没想到会看到关于StackOverflow的问题。实际上你没有发布DiscoveryEngine的代码,你能粘贴它吗?实际上,使用你的数据集,这应该在几天内运行10ms@user125553检查这个存储库,我的回答是:谢谢!我有尝试了你的查询,速度快了一点,但不算太遗憾。即使通过Neo4j浏览器手动执行查询,也需要几秒钟。可能查询太重了。在我的笔记本电脑上,它以秒为单位运行,有1000个用户,5000个产品,每个用户的购买次数为100-200次,因此基本上它可以找到5000个产品。你能吗也许与我分享你的数据集和回购协议:graphaware.com的christophe