Morphia storing empty values - mongodb

I am using Morphia+MongoDB as a backend for a simple CRUD input mask.
I prepare the framework the simplest possible way, creating a new MongoClient and initializing Morphia like that:
Morphia morphia = new Morphia();
morphia.mapPackage("it.trew.omg.model");
Both instances are injected into a DAO:
public class ClientiDao extends BasicDAO<Cliente, String>
My entity is still pretty simple:
#Entity("clienti")
public class Cliente {
#Id ObjectId id;
String name;
String address;
String city;
String state;
String email;
public Cliente() {
}
// getters+setters
}
Let's say I create a Cliente by just filling the name inside my form.
When a controller calls the save method, the operation is successful:
getClientiDao().save(cliente);
But when I query the 'clienti' collection from the mongo console I get this:
{ "_id" : ObjectId("547edf630364677dd2f911b8"), "className" : "it.trew.omg.model.Cliente", "name" : "Fabio Bozzo", "indirizzo" : "", "citta" : "", "cap" : "", "provincia" : "", "stato" : "", "email" : "", "telefono" : "", "fax" : "", "note" : "" }
I thought that empty fields would not have been inserted. Is there something wrong?
Versions are:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.12.4</version>
</dependency>
<dependency>
<groupId>org.mongodb.morphia</groupId>
<artifactId>morphia</artifactId>
<version>0.108</version>
</dependency>

Empty fields mean null. Those fields have "" values and so are persisted. If you don't want them persisted, they'd need to be null.

Related

Spring data mongo: Projection not working when path includes a key of a HashMap

Problem
When I try to project a value which is inside a java.util.Map then I get below exception. But when I run the generated shell query in roboMongo then It works. I would be very grateful if somebody could point out the problem.
org.springframework.data.mapping.context.InvalidPersistentPropertyPath: No property Germany found on com.nntn.corona.snapshot.repo.model.StatWithDelta!
Query Code In Java
spring boot parent: 2.0.5.RELEASE
Criteria matchCriteria = Criteria.where("timestamp").gte(startDate);
MatchOperation match = Aggregation.match(matchCriteria);
SortOperation sort = sort(new Sort(Sort.Direction.ASC, "timestamp"));
// #formatter:off
ProjectionOperation projection = project()
.andExpression("timestamp").as("timestamp")
.andExpression("countries.germany.total").as("total")
.andExpression("countries.germany.today").as("today");
// #formatter:on
Aggregation aggregation = newAggregation(match, sort,projection);
AggregationResults<Document> result = mongoTemplate.aggregate(aggregation, SnapshotEntry.class,
Document.class);
return result.getMappedResults();
Data Model
Java representation
#Document(collection = "snpashots")
public class SnapshotEntry {
#Id
private String id;
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
#Temporal(TemporalType.TIMESTAMP)
private DateTime timestamp;
private Map<String, StatWithDelta> countries;
private StatEntity total;
private StatEntity today;
private String source;
private String previousRecordId;
#Data
#NoArgsConstructor
#AllArgsConstructor
public class StatWithDelta {
private StatEntity total;
private StatEntity today;
}
}
Json representation
{
"_id" : "21-03-2020",
"timestamp" : ISODate("2020-03-21T09:26:00.965Z"),
"countries" : {
"germany" : {
"total" : {
"born" : NumberLong(81008),
"dead" : NumberLong(3255),
"sick" : NumberLong(30000)
},
"today" : {
"born" : NumberLong(50),
"dead" : NumberLong(10),
"sick" : NumberLong(12)
}
}
},
"_class" : "com.nntn.snapshot.repo.model.SnapshotEntry"
}
The problem is in TypedAggregation. This is a special aggregation where Spring holds information of the input aggregation type.
To avoid it, use raw aggregation (as if you run in MongoDB shell) this way:
AggregationResults<SnapshotEntry> result = mongoTemplate.aggregate(aggregation,
mongoTemplate.getCollectionName(SnapshotEntry.class),
SnapshotEntry.class);

Spring Boot: Get an object from MongoDB document?

I am using Spring Boot 2.1.3.RELEASE and MongoDB. And I am looking for a way to retrieve a part of a document.
Repository:
#Repository
public interface CompanyRepository extends MongoRepository<Company, String> {
}
Object:
#Data
#Document
public class Company {
public GeneralInfo info;
public Map<String, List<Employee>> officeIdEmployeeMap;
#Data
public class GeneralInfo {
#Id
public String companyId;
public String name;
}
#Data
public class Employee {
public String firstName;
public String lastName;
}
}
I need to get only GeneralInfo objects and then if some conditions are true get the List<Employee> from the officeIdEmployeeMap, not the whole map.
Can this be done by MongoRepository?
This is possible with MongoTemplate.
finalString officeName = "IT";
final Query query = new Query();
query.addCriteria(Criteria.where("officeIdEmployeeMap." + officeName).exists(true));
query.fields().exclude("officeIdEmployeeMap");
final Company company = mongoTemplate.findOne(query, Company.class, "collection");
This will return the Company object but only GeneralInfo will be set.
In CompanyRepository add the following method:
#Query(value="{}", fields = "{'info':1}")
public List<Company> getCompanies();
Call this when you need to get only General Info otherwise, call findAll() to get all details. You may write a new Query in the repository restricting the selected fields and optionally the criteria.
In case you want to achieve both the scenarios (get GeneralInfo only and get both) in the same query conditionally, instead of annotation create a query like below:
#Autowired
private MongoTemplate mongoTemplate;
Query query = new Query();
boolean someCondition = true; // Put your condition here
if(someCondition) {
query.fields().exclude("officeIdEmployeeMap");
}
return mongoTemplate.find(query, Company.class);
Assuming your company data in the database looks like this:
{ "_id" : ObjectId("5d3afb75b2ebf8a9ec2f906f"), "info" : { "companyId" : "1", "name" : "abc" }, "officeIdEmployeeMap" : { "g1" : [ { "firstName" : "A", "lastName" : "B" }, { "firstName" : "C", "lastName" : "D" } ] } }

MongoDB with Spring Boot and document containing similar fields

I'm current trying to access an object in my MongoDB database.
My object is stored this format in db:
{
"_id" : ObjectId("some object id"), // mongodb gives this id
"my_id" : "Given id by myself",
"url" : "Some string data"
}
Myobj class:
#Document(collection = "MYOBJ")
public class Myobj {
#Id
private ObjectId _id;
private String my_id;
private String url;
// getters and setters and other methods
}
I want to fetch this object using my_id field. In my repository I have these:
public interface MyobjRepository extends MongoRepository<Myobj, String> {
Myobj findBy_id(ObjectId _id);
Myobj findByMy_id(String my_id);
}
But it fails to build, it gives me this error:
No property my found for type Myobj! Did you mean 'url'?
I suppose it cannot differantiate between my_id and _id. How can I solve this issue without changing my object in the database?
Without findByMy_id method it was working. That method causes compile error.
Problem is that you inside interface MyobjRepository set String as Id of that template MongoRepository<Myobj, String>, and you need to set ObjectId like MongoRepository<Myobj, ObjectId> because you said in your Myobj class that you will be use ObjectId _id for #Id
Am I right ?
#Document(collection = "MYOBJ")
public class Myobj {
private ObjectId _id;
#Id
private String my_id;
private String url;
}
public interface MyobjRepository extends MongoRepository<Myobj, String> {
Myobj findBy_id(ObjectId _id);
Myobj findByMy_id(String my_id);
}

ClassCastException using Morphia

I'm trying to get a collection of MongoDB and convert the records into javabeans using Morphia, but when I try to get the collection of objects (see below in App code), a cast error appears:
Exception in thread "main" java.lang.ClassCastException: com.mongodb.BasicDBObject cannot be cast to com.homework.Score
Document
{
"_id" : 19,
"name" : "Student 01",
"scores" : [
{
"type" : "exam",
"score" : 44.51211101958831
},
{
"type" : "quiz",
"score" : 0.6578497966368002
},
{
"type" : "homework",
"score" : 93.36341655949683
},
{
"type" : "homework",
"score" : 49.43132782777443
}
]
}
Student
#Entity("students")
public class Student {
#Id
private int id;
#Property("name")
private String name;
#Property("scores")
private List<Score> scores;
gets and sets
}
Score
#Embedded
public class Score {
#Property("type")
private String type;
#Property("score")
private double score;
gets and sets
}
App
private static MongoClient client = new MongoClient();
private static final Morphia morphia = new Morphia();
private static Datastore datastore;
private static Query<Student> query;
public static void main(String[] args) {
morphia.mapPackage("com.homework");
datastore = morphia.createDatastore(client, "school");
datastore.ensureIndexes();
query = datastore.createQuery(Student.class);
List<Student> students = query.asList();
List<Score> scoresCurr;
Score score1;
Score score2;
int idx;
for (Student s : students) {
scoresCurr = s.getScores();
score1 = scoresCurr.get(2); <<<< exception occurs here
score2 = scoresCurr.get(3);
idx = score1.getScore() < score2.getScore() ? 2 : 3;
scoresCurr.remove(idx);
s.setScores(scoresCurr);
datastore.save(s);
}
client.close();
}
This is the similar behavior other people have also experienced.
Can't find a codec for class , Morphia , Spring.
You can file a bug report here if you feel this is not the right behavior.
https://github.com/mongodb/morphia/issues
Okay now coming to the solution. You can fix it two ways.
Solution 1
You can remove the #Embedded annotation from score pojo and
Replace
#Property("scores")
private List<Score> scores;
with
#Embedded("scores")
private List<Score> scores;
Solution 2
Remove the #property annotation for scores field in Student pojo.

Spring data - search Documents by search string and two parameters

I will search workingBook documents in my mongoDB database by a search string and two parameters: project and tenant.
Is there a possibility to do this?
This is my current Spring data query but it does not work:
#Query("{$text : { $search : ?0 } }")
List<WorkingBook> findWorkingBookByProjectAndTenantAndSearchString(final Project project, final Tenant tenant,
final String searchString);
Thats a sample document:
db.WorkingBook.find().pretty()
{
"_id" : ObjectId("589f2af42f7a97b95842afc6"),
"_class" : "my.domain.dao.domain.WorkingBook",
"dateTimeFrom" : ISODate("2017-02-11T15:16:33Z"),
"dateTimeUntil" : ISODate("2017-02-11T16:17:33Z"),
"categoryType" : "SERVICE",
"workingText" : "That's a working book text ",
"creationDateTime" : ISODate("2017-02-11T15:17:08.550Z"),
"user" : DBRef("User", ObjectId("5589929b887dc1fdb501cdba")),
"project" : DBRef("Project", ObjectId("5899eb0dbfb41f1382eac15a")),
"tenant" : DBRef("Tenant", ObjectId("58500aed747a6cddb55ba094"))
}
Assuming you have id field like below in both Project and Tenant classes.
#Id
private String id;
and DBRef like below in WorkingBook
#DBRef
private Project project;
#DBRef
private Tenant tenant;
With #Query annotation
Pass the project and tenant id along with search string.
#Query("{$text : { $search : ?0 }, 'project' : ?1, 'tenant' : ?2 }")
List<WorkingBook> findWorkingBookByProjectAndTenantAndSearchString(final String searchString, final String projectId, final String tenantId);
Without #Query annotation
You can define the the below method in repository.
List<WorkingBook> findByProjectAndTenant(final TextCriteria searchString, final String projectId, final String tenantId);
Calling Code
TextCriteria textCriteria = new TextCriteria().matching(searchString);
List<WorkingBook> workingBookList = workingBookRepository.findByProjectAndTenant(textCriteria, projectId, tenantId);
Spring Data does not combine the query from an annotation with a query derived from the method name. So if you want to combine the search query with what would otherwise generated from the name you have to do it yourself and put it in the annotation.