I'm using Spring Data Neo4j, I have two entities, Person and PhoneNumber. Person has a 1-N relationship with PhoneNumber. I'm looking for a way to always have this relationship in a specific order.
Here's the definition of my entities (those are Groovy classes) :
#NodeEntity
class Person {
#GraphId Long id
String name
#RelatedToVia
Set<NumberRel> numbers
}
#RelationshipEntity(type = "has_number")
class NumberRel {
#GraphId Long id
#StartNode Person person
#EndNode PhoneNumber number
int sequence
}
#NodeEntity
class PhoneNumber {
#GraphId Long id
String number
}
interface PhoneNumberRepository extends GraphRepository<PhoneNumber>, CypherDslRepository<PhoneNumber> {
/* Empty */
}
interface PersonRepository extends GraphRepository<Person>, CypherDslRepository<Person> {
Person findByName(String name)
}
Then I create and save some entities :
def num01 = new PhoneNumber(number: "0101")
phoneNumberRepository.save(num01)
def num02 = new PhoneNumber(number: "0202")
phoneNumberRepository.save(num02)
def alice = new Person(
name: "Alice",
numbers: new LinkedHashSet<NumberRel>()
)
alice.numbers << new NumberRel(
person: alice,
sequence: 10,
number: num01
)
alice.numbers << new NumberRel(
person: alice,
sequence: 20,
number: num02
);
personRepository.save(alice)
And that's where I'm stuck : I would like to tell Spring - or Neo4j - to load the NumberRel relationship ordered by the value of the sequence attribute. I cannot find a way to do that.
Right now, the order is random. The following code prooves it :
(1..3).each {
println "Looking for Alice, pass ${it}"
def p = personRepository.findByName("Alice")
p.numbers.each { number ->
print "${number.sequence} "
}
println()
}
that produces this output :
Looking for Alice, pass 1
10 20
Looking for Alice, pass 2
20 10
Looking for Alice, pass 3
10 20
So my question is : is there a way to have Person.numbers be ordered by NumberRel.sequence ?
if SDN can't sort the results, send the collection into a sort first -
(1..3).each {
println "Looking for Alice, pass ${it}"
def p = personRepository.findByName("Alice")
p.numbers.sort{it.sequence}
p.numbers.each { number ->
print "${number.sequence} "
}
println()
}
Related
I need to retrieve each student with number of courses taken, for example :
count
student
5
John
8
Jenny
And I need the following JSON response from my rest end-point:
[
{
"name_student": John,
"count_course": 5,
},
{
"name_student": Jenny,
"count_course": 8,
},
]
I have made the query to fetch the required data inside the Repository:
#Query(value ="Select count(*), s.student_name from student s INNER JOIN courses c on c.student_id= s.student_id group by s.student_name", nativeQuery = true )
List<StudentDTO> getIncidentCountByOfficerKPI ()
My DTO:
#Data
#NoArgsConstructor
public class StudentDTO {
Integer count;
String StudentName;
}
Service:
public List<StudentDTO> getStudentCount() {
return p2StudentRepository.getIncidentCountByOfficerKPI ();
}
Controller:
#GetMapping("/Student-count")
public ResponseEntity<StudentDTO> getAllStudentCount() {
List<StudentDTO> selectedCount = p2StudentService.getStudentCount();
List<StudentDTO> result = new ArrayList<>();
selectedCount.forEach(i -> {
StudentDTOitem = new StudentDTO();
item.setUserName(i.getStudentName());
item.setCount(i.getCount());
result.add(item);
});
return ResponseEntity.ok(result);
}
when I test with postman, it returns to nothing like below
[]
How can I fix that?
The return type of your rest end-point is incorrect, it should be ResponseEntity<List<StudentDTO>> (since you need to place multiple StudentDTO in the response).
And to provide ResponseEntity with a response body, you can use body() method of the ResponseEntity.BodyBuilder (that's a builder-object produced by the static method ResponseEntity.ok()), or it can be done by using of the parameterized constructors of the ResponseEntity.
And since you're actually not transforming the data returned by getStudentCount() you can attach this list as the response body directly.
#GetMapping("/Student-count")
public ResponseEntity<List<StudentDTO>> getAllStudentCount() { // <- change the return type
List<StudentDTO> selectedCount = p2StudentService.getStudentCount();
return ResponseEntity.ok()
.body(selectedCount); // <- add the response body
}
Consider having a workshop table with the following records:
id
name
1
Epic Car Repairs
2
Car Care Workshop - Alpha
3
Car Care Workshop - Bravo
4
Car Care Workshop - Charlie
5
Bosch Car Service Centre
Assume all the Car Care Workshop's with id's 2, 3, 4 are part of a franchise. If I have a list of quotes submitted by all of these workshops (assume each of the 5 workshops submitted 1 quote) and assume I have a list of id's for each workshop in the franchise, how do I go about counting the quotes in linq so that all the quotes of the the franchisee workshops count as only 1?
quotes.Count() gives a value of 5
quotes.Count('somehow identify distinct franchises') should give a value of 3
First you need to decide on a way to uniquely identify distinct values, for example name up to the first dash (-). Then you can count distinct values based on this property.
Perhaps something like this;
private class Workshop
{
public Workshop(int id, string name)
{
Id = id;
Name = name;
}
public int Id { get; }
public string Name { get; }
public string UniqueName => Name.Split('-').First().Trim();
}
I like to use a helper extension method so that I can get distinct values by a specific property / method.
public static IEnumerable<T> DistinctBy<T, T2>(this ICollection<T> collection, Func<T, T2> selector)
{
return collection.GroupBy(selector).Select(x => x.First());
}
Your scenario;
[Test]
public void DistinctBy_works()
{
ICollection<Workshop> collection = new List<Workshop>();
collection.Add(new Workshop(1, "Epic Car Repairs"));
collection.Add(new Workshop(2, "Car Care Workshop - Alpha"));
collection.Add(new Workshop(3, "Car Care Workshop - Bravo"));
collection.Add(new Workshop(4, "Car Care Workshop - Charlie"));
collection.Add(new Workshop(5, "Bosch Car Service Centre"));
var result = collection.DistinctBy(x => x.UniqueName).ToList();
result.Count.Should().Be(3);
result[0].Id.Should().Be(1);
result[1].Id.Should().Be(2);
result[2].Id.Should().Be(5);
}
I have some entities which uses a slowly changing versioning by version-number without a current-version-flag. My entity looks like:
#Entity
public class MyEntity {
#Id
private long id;
private long functionalKey;
private String name;
private int version;
}
There are entries like:
{ id = 1, functionalKey = 1, name = "Test1", version = 1 }
{ id = 2, functionalKey = 18, name = "Test2", version = 1 }
{ id = 32, functionalKey = 18, name = "New Test 2", version = 2 }
How can I use Spring data to find all entries (by functionalKey) with the highest version number?
To find a single entry I can use something like MyEntity findFirstByFunctionalKeyOrderByVersionDesc(long functionalKey) or write a suitable expression statement. But I want all entities not only one! I search for something like List<MyEntity> findAllWithHighestVersionGroupedByFunctionalKey.
How is this possible?
Thanks for your support!
You cannot do that. Here is the list of things you can achieve with query methods https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation . You should use JPQL.
I want to use graphene to create many people in one go.
The document only mention the way to create one person like this:
class CreatePerson(graphene.Mutation):
class Input:
name = graphene.String()
age = graphene.Int()
ok = graphene.Boolean()
person = graphene.Field(lambda: Person)
#staticmethod
def mutate(root, args, context, info):
person = Person(name=args.get('name'), age=args.get('age'), mobile=args.get('mobile'))
ok = True
return CreatePerson(person=person, ok=ok)
are there any ways to get it done?
Instead of using a mutation that creates a list of objects, you can also call a mutation that creates one objects multiple times in one GraphQL request. This is accomplished using GraphQL Aliases:
mutation {
c001: createPerson(
name: "Donald Duck"
age: 42
) {
id
}
c002: createPerson(
name: "Daisy Duck"
age: 43
) {
id
}
c003: createPerson(
name: "Mickey Mouse"
age: 44
) {
id
}
}
I can figure out a solution base on the answer of Jan Hančič
There is a type called graphene.InputObjectType to use in this case
The solution can be
class PersonInput(InputObjectType):
name = graphene.String()
age = graphene.Int()
class CreatePeople(graphene.Mutation):
class Input:
people = graphene.List(PersonInput)
people = graphene.List(lambda: Person)
#staticmethod
def mutate(root, args, context, info):
people = [Person.objects.create(name=person.name, age=person.age) for person in args.get('people')]
return CreatePeople(people=people)
Make your mutation input a list and return a list of created people. Something like this:
class CreatePerson(graphene.Mutation):
class Input:
name = graphene.List(graphene.String)
ok = graphene.Boolean()
people = graphene.List(Person)
#staticmethod
def mutate(root, args, context, info):
people = [Person(name=name) for name in args.get('name)]
ok = True
return CreatePerson(people=people, ok=ok)
Receive a list of input, create all instances and return them all
The model node/type should be like-
class UserType(DjangoObjectType):
class Meta:
model = User
interfaces = (CustomGrapheneNode, )
filter_fields = {}
only_fields = (
'name',
'email'
)
Define Input fields
class UserInput(graphene.InputObjectType):
name = graphene.String(required=True)
password = graphene.String(required=True)
Mutation class
class CreateUser(graphene.Mutation):
users = graphene.List(UserType)
class Input:
data = graphene.List(UserInput)
Output = graphene.List(UserType)
def mutate(self, info, data):
users = []
for item in data:
user = User.objects.create(name=data['name'],
password=data['password'])
users.append(user)
return users
make this mutation callable by main schema
class Mutation():
create_user = CreateUser.Field()
the Mutation Query view will be as -
mutation{
createUser(data:[{name:"john", password:"1234"},
{name:"john", password:"1234"}]) {
user{
name
}
}
}
I've read a lot about uniqueness and constraints in Grails (but maybe not enough)
I can't make the unique constraint to work on multiple fields as explained here:
http://grails.org/doc/1.3.7/ref/Constraints/unique.html
(I'm using grails 1.3.9)
I have 2 domain classes:
class Dog {
static constraints = {
humanSsn(unique: ['name', 'breed'])
//I also tried with just 2 fields, didn't work either.
}
Integer humanSsn
String name
String breed
}
class Human {
static constraints = {
ssn(unique: true)
}
Integer ssn
String name
}
It is a legacy DB, so I cant modify the tables.
When I save a Human, I (just to test) save two dogs with the same name, breed and humanSsn
def humanoInstance = new Humano(params)
if (humanoInstance.save(flush: true)) {
def newDog = new Dog()
def newDogTwo = new Dog()
newDog.name = "n1"
newDog.breed = "b1"
newDog.humanSsn = humanInstance.ssn
println newDog.validate()
println newDog.getErrors()
newDog.save(failOnError:true)
newDogTwo.name = "n1"
newDogTwo.breed = "b1"
newDogTwo.humanSsn = humanInstance.ssn
println newDogTwo.validate()
println newDogTwo.getErrors()
newDogTwo.save(failOnError:true)
}
But it saves anyway the 2 dogs without complaining nor throwing any errors.
true
org.springframework.validation.BeanPropertyBindingResult: 0 error
true
org.springframework.validation.BeanPropertyBindingResult: 0 error
What am I doing wrong?
Thanks in advance.
it may be due to validation works on database level
and newDog.save(failOnError:true) doesnot save dog object immediately
have you try
newDog.save(flush:true)
for first dog and then
newDogTwo.save(failOnError:true)
it should work