mongoTemplate.aggregate not giving any result - mongodb

Any help or hint would be greatly appreciated it.
I am using Spring Boot with MongoDb.
I passed the filter "JohnSmithTag4" but I don't see any result even though the MongoDb document have a "tags" field with "JohnSmithTag4".
http://localhost:8080/ocrel-roles/v2/role/tag?filter="JohnSmithTag4"
mongodb database document containing the value of tags
#Override
public Page<String> getAllTags(String filter, Pageable pageable) {
List<AggregationOperation> aggregation1 = List.of(
Aggregation.unwind("tags"),
Aggregation.group("tags").addToSet("tags").as("tag"),
Aggregation.project("tag").andExclude("_id"),
Aggregation.unwind("tag"),
Aggregation.match(Criteria.where("tag").regex(Pattern.compile(StringUtils.isEmpty(filter) ? "" : "^" + filter))));
List<AggregationOperation> tagsAgg = new ArrayList<>(aggregation1);
tagsAgg.add(Aggregation.skip((long) pageable.getPageNumber() * pageable.getPageSize()));
tagsAgg.add(Aggregation.limit(pageable.getPageSize()));
List<?> result = (List<?>) mongoTemplate.aggregate(Aggregation.newAggregation(tagsAgg), RoleEntity.class, String.class)
.getRawResults().get("results");
List<String> tag = result.stream().filter(Objects::nonNull).map(Document.class::cast).filter(i -> i.get("tag") != null).map(item -> item.get("tag").toString()).collect(Collectors.toList());
List<AggregationOperation> countAggregation = new ArrayList<>(aggregation1);
countAggregation.add(Aggregation.group().count().as("count"));
return PageableExecutionUtils.getPage(tag, pageable,
() -> mongoTemplate.aggregate(Aggregation.newAggregation(countAggregation),RoleEntity.class, TagCount.class).getMappedResults().get(0).getCount());
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Document(collection = "role")
#GeneratedId(option = IdGenerationOption.UUID)
public class RoleEntity extends AuditableBaseModel {
public static final String JSON_PROPERTY_PERM_SET_TAGS = "tags";
private Set<String> tags;
}

Related

Spring-Boot RestController: Passing Id as String not working

I connected my Spring-Boot-Application to a MongoDB. The application is nothing serious, just for getting into working with spring and MongoDB.
The problem it, that my id is a String and I get an Internal Server Error, when I pass the id of a database entry, in order to get it byId...
This is my domain class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Document(collection = "songinfo")
public class SongInfo {
#Id
private String id;
private int songId;
private String songName;
private String description;
}
The Controller-Method:
#RequiredArgsConstructor
#RestController
#RequestMapping("/songsinfo")
public class SongsInfoController {
private final SongInfoService songInfoService;
#GetMapping(value = "/{id}", headers = "Accept=application/json", produces =
{MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<SongInfo> getSongInfoById(#PathVariable(value = "id") String id) {
SongInfo songInfo = songInfoService.getSongInfoById(id);
if (songInfo == null)
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
return new ResponseEntity<>(songInfo, HttpStatus.OK);
}
The SongInfoServiceImpl:*
#Override
public SongInfo getSongInfoById(String id) {
return songInfoRepository.findById(id).orElseThrow(NotFoundException::new);
}
This is the SongsInfoRepository:
public interface SongInfoRepository extends MongoRepository<SongInfo, String> {
}
Getting all songinfos from the database is working fine:
But when is pass the id from one of these entries, I get this:
What is wrong here with my implementation?
You're throwing the exception in SongInfoServiceImpl which is not handled in your SongsInfoController Class.
Solution 1: Instead of throwing the exception return null.
SongInfoServiceImpl.java
#Override
public SongInfo getSongInfoById(String id) {
return songInfoRepository.findById(id).orElse(null);
}
Solution 2: Add try catch block
SongsInfoController.java
#RequiredArgsConstructor
#RestController
#RequestMapping("/songsinfo")
public class SongsInfoController {
private final SongInfoService songInfoService;
#GetMapping(value = "/{id}",
headers = "Accept=application/json",
produces = {MediaType.APPLICATION_JSON_VALUE}
)
public ResponseEntity<SongInfo> getSongInfoById(#PathVariable(value = "id") String id) {
SongInfo songInfo = null;
try {
songInfo = songInfoService.getSongInfoById(id);
} catch(Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(songInfo, HttpStatus.OK);
}
}
I think you need to divide two problem.
Check id parameter SongsInfoController
Inside controller check your parameter is valid through log or sysout
Check getSongInfoById method in SongInfoServiceImpl
Simply getSongInfoById(8752); is get error?
I want to add comment but my reputation is under 50.
If you comment above two solution check result, then I will add additional answer.

IllegalArgumentException: NamedQuery using Spring JPA

I am using namedquery for rest api using Spring JPA. The named query is implemented in my entity class:
#Entity
#Table(name="SPECIMEN_TB")
#NamedQueries({
#NamedQuery(name="SpecimenTb.findBySpecimenNo", query="select s from SpecimenTb s where s.specimenNo = :specimenNo"),
})
public class SpecimenTb implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SPECIMEN_TB_ROWID_GENERATOR")
#Column(name="ROW_ID")
private long rowId;
#Column(name="SPECIMEN_NO", unique = true)
private String specimenNo;
My controller looks like this:
#RestController
public class RistoreController {
#Autowired
private RistoreService ristoreService;
#RequestMapping(
value = "/ristore/foundation/{specno}",
method = RequestMethod.GET,
produces = "application/json")
public ResponseEntity<SpecimenTb> getFmSpecimen(#PathVariable("specno") String specno) {
List<SpecimenTb> specimens = ristoreService.findBySpecimenNo(specno);
if (specimens == null) {
return new ResponseEntity<SpecimenTb>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<SpecimenTb>(specimens.get(0), HttpStatus.OK);
}
I have a service bean which calls JPA repository findBySpecimenNo method.
#Service
public class RistoreServiceBean implements RistoreService {
#Autowired
private SpecimenRepository specimenRepository;
#Override
public List<SpecimenTb> findAll() {
List<SpecimenTb> specimens = specimenRepository.findAll();
return specimens;
}
#Override
public List<SpecimenTb> findBySpecimenNo(String specimenNo) {
List<SpecimenTb> specimens = specimenRepository.findBySpecimenNo(specimenNo);
return specimens;
}
When I start the Spring Boot Application and type in the url "http://localhost:8080/ristore/foundation/SKM1", I got the following error:
java.lang.IllegalArgumentException: Parameter with that position [1] did not exist
What did I do wrong?
Looks like you can't use a named parameter with the #NamedQuery based on the docs I read. Have you tried with ?1 instead?
Reason that named parameter doesn't work is that you also have to add the annotation on the method parameter so Spring knows which parameter matches to what placeholder in the query.

How to reference an entity with inheritance in Spring Data REST when POSTing new entity?

I have entities with joined inheritance:
Supporter
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "supporterType")
#JsonSubTypes({
#JsonSubTypes.Type(value = PersonSupporterEntity.class, name = "PERSON"),
#JsonSubTypes.Type(value = CompanySupporterEntity.class, name = "COMPANY")
})
#DiscriminatorColumn(name="supporter_type")
#Table(name = "supporter")
public class SupporterEntity extends UpdatableEntity {
private long id;
private SupporterType supporterType;
private PartnerEntity partner;
...
}
PersonSupporter
#Entity
#DiscriminatorValue("PERSON")
#Table(name = "person_supporter")
public class PersonSupporterEntity extends SupporterEntity {
...
}
CompanySupporter
#Entity
#DiscriminatorValue("COMPANY")
#Table(name = "company_supporter")
public class CompanySupporterEntity extends SupporterEntity {
...
}
I have another entity which references SupporterEntity
#Entity
#Table(name = "contact")
public class ContactEntity extends UpdatableEntity {
private long id;
private SupporterEntity supporter;
...
#ManyToOne // same error with #OneToOne
#JoinColumn(name = "supporter_id", referencedColumnName = "id", nullable = false)
public SupporterEntity getSupporter() {
return supporter;
}
...
}
Repositories
#Transactional
#RepositoryRestResource(collectionResourceRel = "supporters", path = "supporters")
public interface SupporterEntityRepository extends JpaRepository<SupporterEntity, Long> {
#Transactional(readOnly = true)
#RestResource(path = "by-partner", rel = "by-partner")
public Page<SupporterEntity> findByPartnerName(#Param("name") String name, Pageable pageable);
}
#Transactional
#RepositoryRestResource(collectionResourceRel = "person_supporters", path = "person_supporters")
public interface PersonSupporterEntityRepository extends JpaRepository<PersonSupporterEntity, Long> {
}
#Transactional
#RepositoryRestResource(collectionResourceRel = "company_supporters", path = "company_supporters")
public interface CompanySupporterEntityRepository extends JpaRepository<CompanySupporterEntity, Long> {
}
#Transactional
#RepositoryRestResource(collectionResourceRel = "contacts", path = "contacts")
public interface ContactEntityRepository extends JpaRepository<ContactEntity, Long> {
#Transactional(readOnly = true)
#RestResource(path = "by-supporter", rel = "by-supporter")
public ContactEntity findBySupporterId(#Param("id") Long id);
}
I use Spring Boot, Spring Data REST, Spring Data JPA, Hibernate, Jackson. When I try to create a new ContactEntity with a post request like this:
{
"supporter":"/supporters/52",
"postcode":"1111",
"city":"Test City 1",
"address":"Test Address 1",
"email":"test1#email.com",
"newsletter":true
}
I get this exception:
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (VALUE_STRING), expected FIELD_NAME: missing property 'supporterType' that is to contain type id (for class com.facer.domain.supporter.SupporterEntity)
at [Source: HttpInputOverHTTP#4321c221; line: 1, column: 2] (through reference chain: com.facer.domain.supporter.ContactEntity["supporter"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148) ~[jackson-databind-2.4.4.jar:2.4.4]
After 2 days of debugging I found a way, but I kinda guessed it. So if I post it like this:
{
"supporter":{
"supporterType":"PERSON",
"id":"52"
},
"postcode":"1111",
"city":"Test City 1",
"address":"Test Address 1",
"email":"test1#email.com",
"newsletter":true
}
It works, but I don't know why. What's wrong with the other request? It works like that everywhere else when the referenced entity does not have inheritance.
Just another workaround using a RelProvider:
Do not use #JsonTypeInfo
Create a RelProvider for SupporterEntity sub-classes
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SupporterEntityRelProvider implements RelProvider {
#Override
public String getCollectionResourceRelFor(final Class<?> type) {
return "supporters";
}
#Override
public String getItemResourceRelFor(final Class<?> type) {
return "supporter";
}
#Override
public boolean supports(final Class<?> delimiter) {
return org.apache.commons.lang3.ClassUtils.isAssignable(delimiter, SupporterEntity.class);
}
}
See also:
https://jira.spring.io/browse/DATAREST-344
http://docs.spring.io/spring-hateoas/docs/current/reference/html/#configuration.at-enable
It looks like a Jackson problem. To be specific, it's the following code in com.fasterxml.jackson.databind.deser.SettableBeanProperty:
if (_valueTypeDeserializer != null) {
return _valueDeserializer.deserializeWithType(jp, ctxt, _valueTypeDeserializer);
}
return _valueDeserializer.deserialize(jp, ctxt);
Without inheritance _valueDeserializer.deserialize would be called which in turn runs some Spring code to convert the URI to a Supporter.
With inheritance _valueDeserializer.deserializeWithType is called and vanilla Jackson, of course, expects an object, not a URI.
If supporter was nullable you could first POST to /contacts and then PUT the supporter's URI to /contacts/xx/supporter. Unfortunately I am not aware of any other solution.
You should be able to workaround this by setting #JsonTypeInfo(use= JsonTypeInfo.Id.NONE) at the property/method level e.g.
Try with this:
#ManyToOne // same error with #OneToOne
#JoinColumn(name = "supporter_id", referencedColumnName = "id", nullable = false)
#JsonTypeInfo(use= JsonTypeInfo.Id.NONE)
public SupporterEntity getSupporter() {
return supporter;
}

how to insert object in mongodb

I have a class as follows:
package mongo;
import com.mongodb.BasicDBObject;
public class tweet extends BasicDBObject{
private String name;
private Integer id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
Now I am trying to insert the object of this class into mongodb:
public void connect() throws UnknownHostException
{
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
DB db = mongoClient.getDB( "test" );
tweet t=new tweet();
t.setId(100);
t.setName("Gggggg");
DBCollection Collection = null ;
Collection = db.getCollection("test");
DBObject doc = new BasicDBObject();
doc.put("first", t);
Collection.save( doc);
System.err.println(Collection.findOne());
}
But when I run this code, the object t is not inserted to the db and the system.err.println return the following:
{ "_id" : { "$oid" : "546c00efbadcd42088c8fee3"}}
How can I add the object into mongodb? Is it possible to do that?
BasicDBObject is actually a LinkedHashMap. So when you extend it, you need to put and retrieve values as you do it in a map.
So simply setting the values as attributes doesn't make sense, those attributes need to be put inside the Tweet map.
You need to modify your Tweet Class as follows:
class Tweet extends BasicDBObject{
public String getName() {
return (String)this.get("name");
}
public void setName(String name) {
this.put("name", name);
}
public Integer getId() {
return (Integer)this.get("_id");
}
public void setId(Integer id) {
this.put("_id", id);
}
}
and your main method as:
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
DB db = mongoClient.getDB( "test" );
Tweet t=new Tweet();
t.setId(100);
t.setName("Ghorbani");
DBCollection collection = null ;
collection = db.getCollection("test");
collection.save(t);
System.err.println(collection.findOne());
If you notice,collection.save(t);, the Tweet object is being directly saved. That is how it should work.
o/p:
{ "_id" : 100 , "name" : "Ghorbani"}
You seem to be trying to set an ID for your object. Usually that is something that is done automatically from Mongo. Try removing t.setId(100); and then run your code again.
Tip - try using Mongoose to manage your connection to Mongo.

a query result from a selected entity returns null

i have to retrieve the last visit of a selected patient from datatable the problem is that the query always returns bull , here is the responsible code :
public class PatientDao implements Serializable {
......
public List<String> findWithQuery(String query ,int id) {
Query q= this.getEntityManager().createQuery(query).setParameter("id",id);
return q.getResultList();
}
}
......
}
#ManagedBean
#SessionScoped
#SuppressWarnings("serial")
public class PatientCtrl implements Serializable{
......
private static Patient selectedpatient = new Patient();
private static PatientDao pDao = new PatientDao();
private static String lv ;
......
public void lastVisit()
{
this.setLv( pDao.findWithQuery("select max(v.date) from Visite v where v.patient.id = :id ",selectedpatient.getId()).toString().replaceAll("^\\[", "").replaceAll("\\]$", ""));
}
<h3>Dernière visite</h3>
<p:outputLabel value="#{patientCtrl.lv}" />
it s always null
This is a coding mistake as you are selecting Visite with a patient.id=1 and patient=p. Unless = p.id =1, you will get no results, and so null. I believe you meant to use setParameter("id",p) instead, but using v.patient.id=:id and v.patient = :p seems redundant and one should be removed anyway.