Neo4j 6 Spring boot 2.4 migrate driver session query - spring-boot

I am trying to migrate to neo4j 6. Whats the equivalent of this method in neo4j6?
The Result here contains: {ref=Employee.... etc, so the actual Java objects.
//org.neo4j.ogm.session.Session is autowired
#GetMapping("/companies/{companyId}/refs")
public Result getCompanyRefs(#PathVariable final String companyId)
{
String query = "MATCH (company:Company)-[ref]-(refObj) where company.id=\"" + companyId + "\" RETURN company,ref,refObj";
return this.session.query(query, Collections.emptyMap());
}
I tried with the new neo4j driver like so:
//org.neo4j.driver.Driver is autowired
#GetMapping("/persons/{personId}/refs")
public Result getPersonRefs(#PathVariable final String personId)
{
String query = "MATCH (person:Person)-[ref]-(refObj) where person.id=\"" + personId + "\" RETURN person,ref,refObj";
return this.driver.session().run(query, Collections.emptyMap());
}
but this gives a Result which is not convertable to my #Node (entity) classes. The previous version gave a Result which contained the actual java objects(mapped to classes).
the result here is:Record<{person: node<7>, ref: relationship<8>, refObj: node<0>}>
Basically the main thing is: i need the nodes mapped to java objects. But i need them via a cypher query, because i need to do some things on the (Result) Nodes before deleting the relationships between them.

so it turns out it does give back the things i need.
Result result = this.getPersonRefs(id);
result.list().forEach((entry) -> {
The problem was that for example neither entry.get("refObj").asObject(), nor asNode() actually gave back what i thought it was supposed to give back.
The Solution:
entry.get("refObj").asMap()
this gives back the actual properties of the object. Then you just need to convert it to MyClass.class with an ObjectMapper.

Related

Spring Mongo Aggregation that doesn't return duplicates values

I'm working on Spring while using Mongo as database. I have some collections and elements inside it. I'm trying to create a function that will return a random element from collection on every call but not return same element twice.
I have this in my repository class:
#Aggregation(pipeline = {"{'$match':{'typeOfAdventureHolidays':'summerCamps'}}", "
{$sample:{size:1}}"})
AggregationResults<AdventureHolidays> randomSummerCamps();
This return me a random value but also show me same value twice, while I want to avoid that.
I tried to search for some answers but I just founded a couple of ways to prevent it but to not show duplicate elements, no to not return same element twice.
So my code is like this.
Repository
#Aggregation(pipeline = {"{'$match':{'typeOfAdventureHolidays':'summerCamps'}}", "
{$sample:{size:1}}"})
AggregationResults<AdventureHolidays> randomSummerCamps();
ServiceImpl
#Override
public List<AdventureHolidays> getRandomSummerCamps() {
return Collections.singletonList(adventureHolidaysRepository.randomSummerCamps().
getMappedResults().stream().findFirst().orElse(null));
}
Controller
#GetMapping("/getRandomSummerCamps")
public String getRandomSummerCamps(Model model) {
model.addAttribute("randomSummerCamps",
adventureHolidaysService.getRandomSummerCamps());
return "randomSummerCamps";
}
If I need to provide something else I'm here, I tried to find something that will be useful but no success

Sorting a custom JPA query with pageable

So, I've already done this using the standard Spring Data JPA interface which extends PagingAndSortingRepository in order to achieve pagination and sorting for a REST API. The thing is, now I want to achieve the very same thing but now using just vanilla JPA and so far so good I managed to get my API to paginate but the sorting doesn't work at all. Every time I try to set the parameter (from a pageable object using pageable.getSort()) it ends with a query error (either if I just send a string as parameter like "name" or just send the sort object, it shows errors).
Here's some code:
My repo implementation:
#Override
public List<Project> findByAll(Pageable pageable) {
Query query = em.createQuery("SELECT project FROM Project project ORDER BY :sort");
query.setParameter("sort", pageable.getSort());
query.setMaxResults(pageable.getPageSize());
query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber());
return query.getResultList();
}
My service:
#Override
public Page<Project> findAll(Pageable pageable) {
objects = Lists.newArrayList(repository.findByAll(pageable));
PageImpl<Project> pages= new PageImpl<Project>(objects, pageable, repository.count());
return pages;
}
To be clear, I'm filling the Pageable object via URI and from the console I can say it's actually getting the data, so I assume the problem is with the repo.
Edit: This is the error I get when I replace the setParameter("sort", ...) for a hardcoded string aka query.setParameter("sort", "name"):
java.lang.NumberFormatException: For input string: "name"
And I think this method should stand for strings as well. If I use query.setParameter("sort", pageable.getSort()), the error is the same.
The order by cannot be set as a query parameter. Also, the Pageable.getSort().toString() likely won't return a string suitable for use in an order by clause as it will result in a String that represents the Order as property: ORDER, note the colon.
Here are some modifications that will work, assuming Java 8...
String order = StringUtils.collectionToCommaDelimitedString(
StreamSupport.stream(sort.spliterator(), false)
.map(o -> o.getProperty() + " " + o.getDirection())
.collect(Collectors.toList()));
Query query = em.createQuery(
String.format("SELECT project FROM Project project ORDER BY %s", order));

Update data in mongo Db using spring (MongoTemplate)

I have a collection (A) which has two fields (String, integer) in mongoDB. I want to update the collection by adding some value to sting
Ex. Lets say i have a document A[field1 : ABC,field2 : 25]. I want to update it by adding ,say 5, to it so it will look like A[field1 :ABC,field2 : 30] after updation.
The code I have used for this is as follows:
Query query = new Query();
query.addCriteria(Criteria.where("field1").is("ABC));
BeanName beanName = template.findOne(query, BeanName.class,collectionName);
if(null != beanName){
Update update = new Update();
update.set("field1", "ABC");
update.set("field2", beanName.getField2() + 5)
template.updateFirst(query, update, BeanName.class,collectionName);
}
else{
template.save(beanName, collectionName); // the value of filed1 and field 2 is populated in a bean with instance 'beanName'
}
The code is woriking fine with expected results but the performance is very slow. Is there any other efficient way for it.
I am working on large amount of data to update.
I would suggest you to use findAndModify() method, in combination with the upsert = true feature. Please find the official documentation below :
https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/

saving & updating full json document with Spring data MongoTemplate

I'm using Spring data MongoTemplate to manage mongo operations. I'm trying to save & update json full documents (using String.class in java).
Example:
String content = "{MyId": "1","code":"UG","variables":[1,2,3,4,5]}";
String updatedContent = "{MyId": "1","code":"XX","variables":[6,7,8,9,10]}";
I know that I can update code & variables independently using:
Query query = new Query(where("MyId").is("1"));
Update update1 = new Update().set("code", "XX");
getMongoTemplate().upsert(query, update1, collectionId);
Update update2 = new Update().set("variables", "[6,7,8,9,10]");
getMongoTemplate().upsert(query, update2, collectionId);
But due to our application architecture, it could be more useful for us to directly replace the full object. As I know:
getMongoTemplate().save(content,collectionId)
getMongoTemplate().save(updatedContent,collectionId)
implements saveOrUpdate functionality, but this creates two objects, do not update anything.
I'm missing something? Any approach? Thanks
You can use Following Code :
Query query = new Query();
query.addCriteria(Criteria.where("MyId").is("1"));
Update update = new Update();
Iterator<String> iterator = json.keys();
while(iterator.hasNext()) {
String key = iterator.next();
if(!key.equals("MyId")) {
Object value = json.get(key);
update.set(key, value);
}
}
mongoTemplate.updateFirst(query, update, entityClass);
There may be some other way to get keyset from json, you can use according to your convenience.
You can use BasicDbObject to get keyset.
you can get BasicDbObject using mongoTemplate.getConverter().

Spring Repository method complains about field not found

I'm using Spring Data Neo4j in my project and I'm having issues with naming conventions for repositories.
This is a simple class containing only one field and the getters/setters
#RelationshipEntity
public class ScoredRelationship
{
protected Float score;
}
and the class below extends it with other kind of fields
#RelationshipEntity(
type = RecommenderRelTypes.GOV_CONSUMER_TO_GOV_CONSUMER_SIMILARITY)
public class GovConsumerToGovConsumerSimilarity extends ScoredRelationship
{ // Other fields}
To access the relationship, I'm using the usual repository class
public interface GovConsumerToGovConsumerSimilarityRepository extends
GraphRepository<GovConsumerToGovConsumerSimilarity>
{
public Set<GovConsumerToGovConsumerSimilarity> findByScoreGreaterThan(Float value);
public Set<GovConsumerToGovConsumerSimilarity>
findByScoreGreaterThanOrderByScoreDesc(Float value);
public Set<GovConsumerToGovConsumerSimilarity>
findTopXByScoreGreaterThanOrderByScoreDesc(int limit, Float score);
}
This code compiles well. However, whenever I'm trying to use one of the methods, Spring return a series of exception or doesn't act as intended.
F.e. #findByScoreGreaterThan(0.3f) always returns an empty set. However, invoking a findAll() and printing all the scores it actually has a lot of objects with a score greater than 0.3.
In the second and third case, it always throws an exception saying
Caused by: Unknown identifier `score`.
at org.neo4j.cypher.internal.symbols.SymbolTable.evaluateType(SymbolTable.scala:60)
at org.neo4j.cypher.internal.commands.expressions.Identifier.evaluateType(Identifier.scala:51)
at org.neo4j.cypher.internal.commands.expressions.Expression.assertTypes(Expression.scala:53)
at org.neo4j.cypher.internal.pipes.SortPipe$$anonfun$assertTypes$1.apply(SortPipe.scala:34)
at org.neo4j.cypher.internal.pipes.SortPipe$$anonfun$assertTypes$1.apply(SortPipe.scala:33)
at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
at scala.collection.immutable.List.foreach(List.scala:45)
at org.neo4j.cypher.internal.pipes.SortPipe.assertTypes(SortPipe.scala:33)
at org.neo4j.cypher.internal.pipes.PipeWithSource.<init>(PipeWithSource.scala:27)
at org.neo4j.cypher.internal.pipes.SortPipe.<init>(SortPipe.scala:29)
at org.neo4j.cypher.internal.executionplan.builders.SortBuilder.apply(SortBuilder.scala:33)
at org.neo4j.cypher.internal.executionplan.ExecutionPlanImpl.prepareExecutionPlan(ExecutionPlanImpl.scala:49)
at org.neo4j.cypher.internal.executionplan.ExecutionPlanImpl.<init>(ExecutionPlanImpl.scala:33)
at org.neo4j.cypher.ExecutionEngine$$anonfun$prepare$1.apply(ExecutionEngine.scala:67)
at org.neo4j.cypher.ExecutionEngine$$anonfun$prepare$1.apply(ExecutionEngine.scala:67)
at org.neo4j.cypher.internal.LRUCache.getOrElseUpdate(LRUCache.scala:37)
at org.neo4j.cypher.ExecutionEngine.prepare(ExecutionEngine.scala:67)
at org.neo4j.cypher.ExecutionEngine.execute(ExecutionEngine.scala:59)
at org.neo4j.cypher.ExecutionEngine.execute(ExecutionEngine.scala:63)
at org.neo4j.cypher.javacompat.ExecutionEngine.execute(ExecutionEngine.java:79)
at org.springframework.data.neo4j.support.query.CypherQueryEngine.parseAndExecuteQuery(CypherQueryEngine.java:61)
How could be possible? I mean, the class obviously has the score field. Also, executing the simple #findByScoreGreaterThan(float value) doesn't throw any exception, but at the same the latter method always returns an empty set.
EDIT:
These are the queries used by Spring. Actually, they seems right
Executing cypher query: START `govConsumerToGovConsumerSimilarity`=node:__types__(className="it.cerict.recommender.persistence.neo4j.GovConsumerToGovConsumerSimilarity") WHERE `govConsumerToGovConsumerSimilarity`.`score`! > {0} RETURN `govConsumerToGovConsumerSimilarity` params {0=0.3}
Executing cypher query: START `govConsumerToGovConsumerSimilarity`=node:__types__(className="it.cerict.recommender.persistence.neo4j.GovConsumerToGovConsumerSimilarity") WHERE `govConsumerToGovConsumerSimilarity`.`score`! > {0} RETURN `govConsumerToGovConsumerSimilarity` ORDER BY score DESC params {0=0.3}
EDIT2: I've also tried to change the score type from Float to float with no further improvements.
This seems to be a bug related to Spring Data Neo4j. Looking to the query executed, it is clear that it searches for nodes, while it should search for relationships.
I changed the #findByScoreGreaterThanOrderByScoreDesc() method using the #Query annotation that specifies the following Cypher query
START `govConsumerToGovConsumerSimilarity`=rel:__rel_types__(className="it.cerict.recommender.persistence.neo4j.GovConsumerToGovConsumerSimilarity") WHERE `govConsumerToGovConsumerSimilarity`.`score`! > 0.3 RETURN `govConsumerToGovConsumerSimilarity` ORDER BY `govConsumerToGovConsumerSimilarity`.`score` DESC;

Resources