0
votes

The following query should return the account sorted by the number "followers" in desc order, with a limit.

It takes 2 variables as params (maxCount and limit). MaxCount refers to a numbers of followers. so if maxCount is 100, then return only those accounts that have followers less than 100.

If I run this query in a Spring Data Neo4j App. It seems to ignore maxCount constraint e.g. If I set maxCount equal to 2 and have account1 with 3 followers and account2 with 1 follower. It seems to incorrectly return both accounts when it should only return account2 which has only 1 follower.

Query

@Query("MATCH (a:Account)<-[:follows]-(b:Account) WITH a, COLLECT(b)
AS bs WHERE SIZE(bs) < {0} RETURN a ORDER BY SIZE(bs) DESC LIMIT {1}")
List<Account> findCappedSortedAccountByFollowers(int maxCount, int resultSize);

It works if I enter it directly into Neo4j Console

Perhaps this is Spring Data Bug?

SDN Version: 4.1.3.RELEASE

OGM Embedded Driver Version: 2.0.5

Small App to demonstrate problem available here

1
Can you try it with SDN 4.2.0.BUILD-SNAPSHOT and OGM 2.1.0-SNAPSHOT. You might need this: graphaware.com/neo4j/2016/09/30/upgrading-to-sdn-42.html to get set up.digx1
Did you run the sample app?Jamie White
Yep. It works with those versions.digx1
I just updated to snapshots but same results. See the modified sample appJamie White

1 Answers

2
votes

Here is your code updated to work with the new versions. Both tests pass. I've rewritten your test and model to be more in line with our documentation and examples. Remember that the OGM can persist by reachability. Hope this helps you get past your issue. I've explained how to write Transactional type tests with Spring Data here: https://stackoverflow.com/a/39887536/2271422.

build.gradle:

dependencies {
    compile "ch.qos.logback:logback-classic:1.1.7"
    compile "org.springframework.data:spring-data-neo4j:4.2.0.BUILD-SNAPSHOT"
    compile "org.springframework:spring-test:4.3.3.RELEASE"
    compile "org.neo4j:neo4j-ogm-embedded-driver:2.1.0-SNAPSHOT"
    testCompile "junit:junit:4.12"
}

src/main/java com.neo4j.relation.config:

@Configuration
@EnableNeo4jRepositories("com.neo4j.relation.repository")
@EnableTransactionManagement
public class EmbeddedNeo4JConfig {

    @Bean
    public SessionFactory sessionFactory() {
        return new SessionFactory("com.neo4j.relation.model");
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new Neo4jTransactionManager(sessionFactory());
    }
}

src/main/java com.neo4j.relation.model:

@NodeEntity
public class Account {

    @GraphId
    private Long id;

    private String name;

    @Relationship(type = "FOLLOWS",
            direction = Relationship.OUTGOING)
    private Set<Account> following = new HashSet<Account>();

    @Relationship(type = "FOLLOWS",
            direction = Relationship.INCOMING)
    private Set<Account> followers = new HashSet<Account>();

    private Account() {
    }

    private Account(String name) {
        this.name = name;
    }

    public static Account newAccountInstance(String name) {
        return new Account(name);
    }

    //Accessors
    public Long getId() {
        return id;
    }

    public Set<Account> getFollowers() {
        return followers;
    }

    public String getName() {
        return name;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Id: ").append(getId()).append(", ");
        sb.append("Name: ").append(getName()).append(", ");
        return sb.toString();
    }
}

src/main/java com.neo4j.relation.repository:

public interface AccountRepository extends GraphRepository<Account> {
    @Query("MATCH (a:Account)<-[:FOLLOWS]-(b:Account) WITH a, COLLECT(b) AS bs ORDER BY SIZE(bs) DESC RETURN a LIMIT {0}")
    List<Account> findSortedAccountByFollowers(int maxSize);

    @Query("MATCH (a:Account)<-[:FOLLOWS]-(b:Account) WITH a, COLLECT(b) AS bs WHERE SIZE(bs) <= {0} RETURN a ORDER BY SIZE(bs) DESC LIMIT {1}")
    List<Account> findCappedSortedAccountByFollowers(int maxCount, int resultSize);
}

src/test/resources logback.xml:

<configuration>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d %5p %40.40c:%4L - %m%n</pattern>
        </encoder>
    </appender>

    <logger name="org.neo4j.ogm" level="debug" />
    <logger name="org.springframework" level="warn" />
    <logger name="org.springframework.data.neo4j" level="debug" />
    <root level="debug">
        <appender-ref ref="console" />
    </root>

</configuration>

src/test/resources com.neo4j.test:

@ContextConfiguration(classes = {EmbeddedNeo4JConfig.class})
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
public class AccountTester {

    @Autowired
    AccountRepository accountRepository;

    @Test
    public void testFindSortedAccountByFollowers() {

        Account account = Account.newAccountInstance("James Martin");
        Account account2 = Account.newAccountInstance("Steve Owen");
        Account account3 = Account.newAccountInstance("Bill Gates");
        Account account4 = Account.newAccountInstance("Steve Jobs");
        account.getFollowers().add(account2);

        accountRepository.save(account);
        accountRepository.save(account3);
        accountRepository.save(account4);

        assertNotNull(account.getId());

        final Iterable<Account> all = accountRepository.findAll();

        assertEquals(4, IteratorUtils.size(all.iterator()));

        List<Account> accounts = accountRepository.findSortedAccountByFollowers(10);
        int size = accounts.size();
        assertEquals(1, size);
        assertEquals(account, accounts.get(0));
    }

    @Test
    public void testCappedSortAccountByFollowerCount() {
        Account account = Account.newAccountInstance("Steve Martin");
        Account account2 = Account.newAccountInstance("Steve Owen");
        Account account3 = Account.newAccountInstance("Bill Gates");
        Account account4 = Account.newAccountInstance("Steve Jobs");

        account.getFollowers().add(account2);
        account.getFollowers().add(account3);
        account.getFollowers().add(account4);

        account4.getFollowers().add(account3);

        accountRepository.save(account);

        List<Account> accounts = accountRepository.findCappedSortedAccountByFollowers(2, 10);
        int size = accounts.size();
        assertEquals(1, size);
        assertEquals(account4, accounts.get(0));
    }
}