spring data neo4j #Query countQuery - neo4j

The annotation #Query in package org.springframework.data.neo4j.annotation provides certain count attributes like countQuery, countQueryName.
/**
* #return simpler count-query to be executed for #{see Pageable}-support {self} will be provided by the node-id of the current entity other parameters (e.g. {name}) by the given named params
*/
String countQuery() default "";
/**
* #return name of the named count query to be used for this annotated method, instead of Class.method.count
*/
String countQueryName() default "";
Can somebody please explain the usage of these? To be more specific I've written a query to fetch the posts on a topic. The query results will be paginated. The query below works fine and gives me the result.
#Query("start post=node:__types__(className='com.xxx.entity.Post'), topic=node({0}) match post-[:TOPIC_POST]-topic return post")
Page<Post> getPostsByTopic(Topic topic, Pageable page);
Now I also want the total number of results as well, do I have to write another query for this or is there a way to accommodate count query in this?

I think countQuery should work for you, but you still need to write the count query
If it is required that paged results return the correct total count,
the #Query annotation can be supplied with a count query in the
countQuery attribute. This query is executed separately after the
result query and its result is used to populate the totalCount
property of the returned Page.
http://docs.spring.io/spring-data/neo4j/docs/current/reference/htmlsingle/#d0e2712

Related

Returning multi value in dynamic query using neo4j client

Following the question I asked: Build a dynamic query using neo4j client
I got an answer about how can I return value dynamically using string only.
When I'm trying to use the syntax to return multi values from the query it failed,
I tried the following query:
var resQuery2 = WebApiConfig.GraphClient.Cypher
.Match("(movie:Movie {title:{title}})")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.WithParam("title", title)
.Return(() => Return.As<string>("movie, collect([person.name, head(split(lower(type(r)), '_')), r.roles])"));
I'm getting the following error:
The deserializer is running in single column mode, but the response
included multiple columns which indicates a projection instead. If
using the fluent Cypher interface, use the overload of Return that
takes a lambda or object instead of single string. (The overload with
a single string is for an identity, not raw query text: we can't map
the columns back out if you just supply raw query text.)
Is it possible to return multiple nodes using only strings?
We can't get an output like in the question you asked previously - this is due to the fact that you are asking for a Node (the movie) and a Collection of strings (the collect) and they have no common properties, or even styles of property.
Firstly, let's look at the painful way to do this:
var q = gc.Cypher
.Match("(movie:Movie)")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.Return(() => Return.As<string>("{movie:movie, roles:collect([person.name, head(split(lower(type(r)), '_')), r.roles])}"));
var results = q.Results;
Here we take the query items (movie, r, person) and create a type with them the {} around the results, and cast that to a string.
This will give you a horrible string with the Node data around the movie and then a collection of the roles:
foreach (var m in results)
{
//This is going to be painful to navigate/use
dynamic d = JsonConvert.DeserializeObject<dynamic>(m);
Console.WriteLine(d.movie);
Console.WriteLine(d.roles);
}
You'd be a lot better off doing something like:
var q = gc.Cypher
.Match("(movie:Movie)")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.Return(() => new
{
Movie = Return.As<Node<string>>("movie"),
Roles = Return.As<IEnumerable<string>>("collect([person.name, head(split(lower(type(r)), '_')), r.roles])")
});
var res = q.Results;
You could either JsonConvert.DeserializeObject<dynamic>() the Movie node, at your leisure, or write a strongly typed class.
In terms of a 'dynamic' object, I don't know how you were wanting to interact with the collect part of the return statement, if this doesn't help, you might need to update the question to show a usage expectation.

How to use parameter in the Cypher statement for variable length pattern matching?

In our project , PEPOLEs are connected by KNOWS relationships. We need to query one’s friends in depth n which n is a parameter inputed by user.We use Spring Data Neo4j to implement it.
public interface PeopleRepository extends GraphRepository<People>
{
#Query("MATCH (startnode{name:{name}})-[:KNOWS*1..{depth}]-(remote_friend) RETURN remote_friend.name");
List<People> getFriendsInDepth(#Param("name") String name, #Param("depth") Integer depth);
}
The above codes won’t work. But if I replace {depth} parameter with a fixed Integer value as follows:
#Query("MATCH (startnode{name:{name}})-[:KNOWS*1..2]-(remote_friend) RETURN remote_friend.name");
List<People> getFriendsInDepth(#Param("name") String name, #Param("depth") Integer depth);
it works. I know the problem is caused by the depth parameter. But I have tried a lot of methods to replace {depth}, for example: toInt({depth}), it still won’t work. Is there anyone know how to use the parameter in the Cypher statement for variable length pattern matching?
Cypher does not allow you to parameterize the depth of a relationship, hence #Query won't support it either.
If you use Spring Data Neo4j 4, then perhaps you can translate your #Query to a set of org.neo4j.ogm.cypher.Filter.
Then you can use the Session.loadAll methods which accept Filters as well as a depth.
MusicIntegrationTest contains a couple of Filter examples.

How to write dynamic search query for Spring-Data-Neo4j

I am implementing search on node attributes. Suppose I have a millions node of User with attributes name, location, income.
n:user (name:"abc",location:"xyz", income:"123")
n:user (name:"def",location:"ghj", income:"1877")
I want to search User either with name or location or income or the combination of attributes like name and location etc. I can perform this search with simple cypher query
#Query("MATCH(n:user{location:{xyz},}) RETURN n") or
#Query("MATCH(n:userr{location:{xyz},name:{abc}}) RETURN n")
But i don't want to do like that, i just want to pass the search parameter and create search query at runtime with the requested parameter. In neo4template class, got this method query(String statement, Map<String,Object> params) Now i am clueless, any detailed answer would be appreciated, as i am new to neo4j.
Unfortunately, parameter maps cannot be used in MATCH patterns.
So I believe you cannot use universal query like:
#Query("MATCH(n:user{0}) RETURN n")
public List<User> complexFind(Map<String, Object> props);
I'd suggest building query string dynamically, using string concatenation, and executing it later on with smth like that:
neo4template.query(YourQueryStringBuilder.build(search_params), other_params);

How to get total number of db-hits from Cypher query within a Java code?

I am trying to get total number of db-hits from my Cypher query. For some reason I always get 0 when calling this:
String query = "PROFILE MATCH (a)-[r]-(b)-[p]-(c)-[q]-(a) RETURN a,b,c";
Result result = database.execute(query);
while (result.hasNext()) {
result.next();
}
System.out.println(result.getExecutionPlanDescription().getProfilerStatistics().getDbHits());
The database seems to be ok. Is there something wrong about the way of reaching such value?
ExecutionPlanDescription is a tree like structure. Most likely the top element does not directly hit the database by itself, e.g. a projection.
So you need to write a recursive function using ExecutionPlanDescription.getChildren() to drill to the individual parts of the query plan. E.g. if one of the children (or sub*-children) is a plan of type Expand you can use plan.getProfilerStatistics().getDbHits().

how to get solr results in given order specified in query

I have framed query to submit to solr which is of following format.
id:95154 OR id:68209 OR id:89482 OR id:94233 OR id:112481 OR id:93843
i want to get records according to order from starting. say i need to get document with id 95154 document first then id 68209 next and so on. but its not happening right now its giving last id 93843 first and some times random.i am using solr in grails 2.1 and my solr version is 1.4.0. here is sample way i am getting documents from solr
def server = solrService.getServer('provider')
SolrQuery sponsorSolrQuery = new SolrQuery(solarQuery)
def queryResponse = server.query(sponsorSolrQuery);
documentsList = queryResponse.getResults()
As #injecteer mentions, there is nothing built-in to Lucene to consider the sequence of clauses in a boolean query, but:
You are able to apply boosts to each term, and as long as the field is a basic field (meaning, not a TextField), the boosts will apply cleanly to give you a decent sort by score.
id:95154^6 OR id:68209^5 OR id:89482^4 OR id:94233^3 OR id:112481^2 OR id:93843
there's no such thing in Lucene (I strongly assume, that in Solr as well). In Lucene you can sort the results based on contents of documents' fields, but not on the order of clauses in a query.
that means, that you have to sort the results yourself:
documentsList = queryResponse.getResults()
def sordedByIdOrder = solarQueryAsList.collect{ id -> documentList.find{ it.id == id } }

Resources