Morphia (MongoDB) Datastore "get" returns null - mongodb

So I started working with Morphia and I'm encountering a weird problem.
Here's my entity class
#Entity("movies")
#Indexes(#Index(value = "Name", fields = #Field("Name")))
#Converters(LocalDateConverter.class)
public class MovieDetails implements Serializable
{
#Id
public String Id;
public String Name;
public String Description;
public String ImageName;
public LocalDate ReleaseDate;
public String Director;
public int Duration;
public String Genres;
public String Actors;
public MovieDetails()
{
}
public MovieDetails(String id, String name, String description, String imageName, String director, String actors, LocalDate releaseDate, String genres, int duration)
{
this (name, description, imageName, director, actors, releaseDate, genres, duration);
Id = id;
}
public MovieDetails(String name, String description, String imageName, String director, String actors, LocalDate releaseDate, String genres, int duration)
{
Name = name;
Description = description;
ImageName = imageName;
Director = director;
Actors = actors;
ReleaseDate = releaseDate;
Genres = genres;
Duration = duration;
}
}
Here's my little test:
final Morphia morphia = new Morphia();
// tell Morphia where to find your classes
// can be called multiple times with different packages or classes
morphia.mapPackage("nimrodpasha.cinema.objects");
// create the Datastore connecting to the default port on the local host
final Datastore datastore =
morphia.createDatastore(SingleMongoClient.getInstance().getClient(),
Constants.DB.TICKET_DATABASE);
datastore.ensureIndexes();
//region new movie
MovieDetails movie = new MovieDetails("The Mask", "Stanley Ipkiss (Jim Carrey) is a bank clerk that is an incredibly nice man. Unfortunately," +
" he is too nice for his own good and is a pushover when it comes to confrontations. After one of the worst days of his life, he finds a mask that depicts Loki, " +
"the Norse night god of mischief. Now, when he puts it on, he becomes his inner, self: a cartoon romantic wild man. However, a small time crime boss, Dorian Tyrel (Peter Greene), " +
"comes across this character dubbed The Mask by the media. After Ipkiss's alter ego indirectly kills his friend in crime," +
" Tyrel now wants this green-faced goon destroyed.",
"MASK.jpg", "Chuck Russell", "Jim Carrey as Stanley Ipkiss/The Mask,Cameron Diaz as Tina Carlyle,Amy Yasbeck as Peggy Brandt,Joely Fisher as Maggie", new LocalDate(1994, 2, 1), "Action,Comedy,CrimeAction,Family,Fantasy", 88);
//endregion
// Clearing the db first
datastore.delete(datastore.createQuery(MovieDetails.class));
// Saving a new entity and getting the result saved id
String id = (String) datastore.save(movie).getId();
// This returns as null
MovieDetails movieRetrieved = datastore.get(MovieDetails.class, id);
// This returns with one item
List<MovieDetails> allMovies = datastore.createQuery(MovieDetails.class).asList();
When I use
datastore.get(MovieDetails.class, id)
I get null
When I use:
datastore.createQuery(MovieDetails.class).asList();
I do see my movie in the DB, with the Id used in the get function.
Tried the id in many variations... toString(), ObjectId(id), Key (The value returned from the save result).
The Id in the DB (viewed with Mongo Explorer) does show as something which isn't string (blue colored), suspicious:
Mongo Explorer item picture
Any ideas?
Edit:
* the Id is indeed a string, the cast works and it was verified using watch + instanceof
Edit 2:
* Somehow the cast from ObjectId to String passed and the Id wasnt really a String.

I would change your I'd field from String to a BSON ObjectId field which MongoDB will automagically assign on save. If you then do your get call with the ObjectId as the parameter, it should work. Using ObjectId as your ID field is highly recommended with Mongo.
I am guessing that Morphia is trying to marshall an ObjectId into your String Id field and there is a small bug somewhere. I would try calling datastore.get(Example.class, new ObjectId(id)).

After Nic Cottrell (Thanks!) answer I've had a new perspective of the problem.
As my #Id field was not assigned by me it was automaticaly assigned in the DB as ObjectId.
As I still want to use String I've simply assigned the #Id field on object creation.
Id = ObjectId.get().toString();
found the solution on:
MongoDB / Morphia saves technical id as ObjectId although it's a String in Java

Related

How to get last record in spring mongo?

I have a transaction class which stores the each transaction of a customer,Following are the fields in this class.
class Transaction{
#Id
private String id;
private Date date;
private String customerId;
private double openBalance;
private double transctionAmount;
private double finalAmount;
}
I need to fetch only the last inserted record of a customer (let say for customerId = cust123).
I defined following function in repository.
public interface TranscationRepository extends MongoRepository<Transaction, String> {
Optional<Transaction> findTopByCustomerIdOrderByIdDesc(String id);
}
This method giving last entry not by customerId but overall. I tried few modifications to it but did not get success.
I know I can findAllByCustomer but I don't want to pull huge list of transaction which is of no use in this use case. What is correct signature in spring mongo to get last inserted record by a field? I am ok to use custom #Query also.
Thank you.

I am getting Weird output in console when fetching Data from Database

I am trying to fetch some data ,I am using H2 data base. The model is
#Entity
public class Employee {
#Id
private long empid;
private String empname;
private Date empdoj;
private double emptotalsalary;
private String emptype;
The service class is Where I used the methods
#Autowired
EmployeeRepository repo;
public int countAllEmployees() {
return (int)repo.count();
}
public List<Employee> getPermanentEmployees(String employeetype){
Employee filter = new Employee();
filter.setEmptype(employeetype);
Example<Employee> example = Example.of(filter);
List<Employee> emplist = (List<Employee>)repo.findAll();
return emplist;
}
public List<Employee> getEmployeeNames(){
Employee filter = new Employee();
filter.setEmpname("tha");
ExampleMatcher matcher = ExampleMatcher.matching().withStringMatcher(StringMatcher.CONTAINING).withIgnoreCase();
Example<Employee> example = Example.of(filter,matcher);
return (List<Employee>)repo.findAll();
}
In Console I am getting this output
Employee Count
5
Get List of permanent Employees
[com.example.demo.Employee#416c1b0, com.example.demo.Employee#a302f30, com.example.demo.Employee#4af44f2a, com.example.demo.Employee#47994b51, com.example.demo.Employee#143fe09c]
Get List of all Employees Ending with Tha
com.example.demo.Employee#416c1b0
com.example.demo.Employee#a302f30
com.example.demo.Employee#4af44f2a
com.example.demo.Employee#47994b51
com.example.demo.Employee#143fe09c
I dont understand why this is coming.please help.
I suppose by weird you mean the text like com.example.demo.Employee#143fe09c
Somewhere in your code, you are printing the Employee records. eg: System.out.println(employee) System.out.println uses the object's toString() method to print the object. Since you did not provide it in your Employee class it uses the toString method from superclass of Employee which is Object.
As mentioned in the doc, it prints the classname followed by # and the hash.
https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#toString--
Returns a string representation of the object. In general, the
toString method returns a string that "textually represents" this
object. The result should be a concise but informative representation
that is easy for a person to read. It is recommended that all
subclasses override this method.
The toString method for class Object returns a string consisting of
the name of the class of which the object is an instance, the at-sign
character `#', and the unsigned hexadecimal representation of the hash
code of the object. In other words, this method returns a string equal
to the value of:
getClass().getName() + '#' + Integer.toHexString(hashCode())
Just override the toString method in Employee class to generate text representation of the Employee object. Your IDE already provides a nice way to generate this method.
#Override
public String toString() {
return "Employee{" + "empid=" + empid +
", empname=" + empname + "'" +
'}';
}

Designing mongodb schema with searchable nested arrays.

I'm new to Mongodb coming from relational databases and I'd also like to point out I'm using SpringBoot with JPA. If I were to build an automotive classified site where I would have thousands of Users and 100s of thousands of listings, how would I go about setting up the schema? I've read some articles that say normalizing nosql data is bad practices.
Anyhow lets say we have the following structure.
User
id
name
email
Cars
id
make
model
year
I would need to be able to list many cars with the User and what i've seen in my examples is it creates a nested array of cars within User. This would work great for user accounts where I'd like to provide the user with all their cars
Where I get a bit confused is with the cars. The cars need to be able to be searched very quickly and would not need the user info right away. In a sql db I would typically do a search against the cars (year, make, model) and grab the user later on if I needed it.
In mongodb, do you create a User document that contains a nested car array? or do you somehow create 2 documents that are both automatically maintained and search against the car document for performance reasons?
Sample code
#Document(collection = "person")
public class Person {
#Id
private String id;
private String firstName;
private String lastName;
// #DBRef(lazy = true)
private List<Listing> listings;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
#Document(collection = "listing")
public class Listing {
#Id
public String id;
public String year;
public String make;
public String model;
public String trim;
public Listing(String year, String make, String model, String trim) {
this.year = year;
this.make = make;
this.model = model;
this.trim = trim;
}
}
#Override
public void run(String... args) throws Exception {
repository.deleteAll();
List<Listing> listings = new ArrayList<>();
Listing listing = new Listing("2008", "Ford", "Focus", "SE");
//listingRepository.save(listing);
listings.add(listing);
Person person = new Person("Alice", "Smith");
person.setListings(listings);
// save a couple of customers
repository.save(person);
person = new Person("Bob", "Smith");
listings = new ArrayList<>();
listings.add(new Listing("2018", "Chrysler", "300", "S"));
person.setListings(listings);
repository.save(person);
// fetch all customers
System.out.println("Customers found with findAll():");
System.out.println("-------------------------------");
for (Person _person : repository.findAll()) {
System.out.println(_person);
}
System.out.println();
// fetch an individual customer
System.out.println("Person found with findByFirstName('Alice'):");
System.out.println("--------------------------------");
System.out.println(repository.findByFirstName("Alice"));
System.out.println("Persons found with findByLastName('Smith'):");
System.out.println("--------------------------------");
for (Person _person : repository.findByLastName("Smith")) {
System.out.println(_person);
}
List<Listing> _listings = listingRepository.findAll();
System.out.println("listings " + _listings.size());
_listings.forEach(v -> {
System.out.println(v.toString());
});
}
Going by your entity model,I think what you are looking for is analogous to Many to Many/One to Many relationship in a relational database. So you can go for One way Embedding or Two way Embedding in MongoDb.
For One way embedding, You can create a Car collection like below:
db.carCollection.insertMany([{
_id:1,
make: 'porcha',
model:'qwerty',
year:'2018'
},
{
_id:2,
make: 'ferrara',
model:'uiop',
year:'2018'
}])
You can then go on to create user collection as below:
db.userCollection.insert({
_id:1,
user:'Tom',
email:'tom#tom.com',
car_ids:[1,2]
})
The car_ids is an array which will hold the ids of cars that belong to the user.
You can fetch the cars belonging to an user as(using findOne to fetch the user. Search parameter should be an unique id. I am considering email to be unique here.Ideally it should be user's id) :
var user=db.userCollection.findOne({email:'tom#tom.com'})
db.carCollection.find({_id:{$in:user.car_ids}})
This will fetch you all the cars per user
For fetching cars only you can simply do:
db.carCollection.find({})
For Two way embedding you can have similar array (as in user collection) inside cars collection so that each car can be identified to its user.

How can I store a Java HashMap<String,Double> in mongodb using Morphia?

I have a user defined class CostMatrix this class contains two properties and data type for both properties is HashMap. I am using Morphia to communicate with mongodb so my entity class looks like this
#Entity(value = "CostMatrix",noClassnameStored = false)
public class CostMatrix {
#Id
private String id;
private HashMap<String,Double> distances;
private HashMap<String,Double> durations;
public CostMatrix(){}
public CostMatrix(String id, HashMap<String,Double>distances, HashMap<String,Double>durations) {
this.id = id;
this.distances = distances;
this.durations = durations;
}
I am unable to store object properly into database object is stored any how but when I retrieve its just returns id and class name any thoughts would be appreciated.
if you don't want to have any class/package names in your collection just put the noClassnameStored flag to true.
#Entity(value = "CostMatrix",noClassnameStored =**true**)
As for the saving part, do you fill some values into your maps? The mapper will ignore null values and empty lists.

Spring mongodb get ID of inserted item after Save

I am working with Spring MongoDb.
I create various entities using insert method:
http://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/MongoOperations.html#insert-java.lang.Object-
However, all methods return void. I need to return the ObjectId of the inserted document.
What is the best way to get it?
This is pretty interesting and thought I would share. I just figured out the solution for this with the help of BatScream comment above:
You would create an object and insert it into your MongoDB:
Animal animal = new Animal();
animal.setName(name);
animal.setCat(cat);
mongoTemplate.insert(animal);
Your animal class looks like this with getters and settings for all fields:
public class Animal {
#Id
#JsonProperty
private String id;
#JsonProperty
private String name;
#JsonProperty
private String cat;
public String getId() {
return id;
}
}
AFTER you have done the insert under mongoTemplate.insert(animal);, you can actually call the method animal.getId() and it will return back the ObjectId that was created.
I had the same problem with #AlanH that animal.getId() is null. And then I just realized my id field had been set as a final field with a wither method. So of course getId() is null since the id field is immutable and the wither method returns a new object with id.
if this is the case: use animal = template.insert(animal).