Morphia : Using Transient - mongodb

I have an object which has 3 fileds:
public class tags{
#Property("n")
private String name;
#Property("t")
private int type;
#Property("r")
private int rank;
.....
}
I am using morphia to communicate to my MongoDB.
I want to save al lthe fileds to the DB, but while retreiving I want to query only based on the 'name' and 'type' fields within my object. I have tried using the #Transient Annotation, but it completely ignores the field during load/save.

This is a very common use case.
The morphia wiki describes using filters or fluent interface: https://github.com/mongodb/morphia/wiki/Query#wiki-filter
Here's an example:
ds.createQuery(tags.class).field('name').equal('idbentley').field('type').equal(1);

If you are looking for limited query results https://github.com/mongodb/morphia/wiki/Query#wiki-ignoring-fields will cover that:
ds.createQuery(tags.class).retrievedFields(true, "name", "type").get();
Beware that you should only read these limited result sets or write back specific values. If you save them back, you will lose all the values you didn't retrieve.

Related

Spring data mongodb aggregation | custom sort based on the order of the enum attributes

I'm trying to sort a data set based on the order (ordinal) of the enum attributes. Let's say I have the following enum
public enum Status {
IN_PROGRESS,
RESOLVED,
NEW
}
Based on the above I want my dataset to be sorted as NEW, IN_PROGRESS & RESOLVED order rather than the alphabetical order of the enum name.
I'm already using aggregation to perform some filtering. So now I need a way to achieve custom sorting with my aggregation
Aggregation.newAggregation(Case.class,
match(filterCriteria),
sort(generateCustomSort()),
limit(limit),
skip(skipCount)):
I know for a fact that with JPA we have the #Enumerated annotation & we can set it to use the ordinal of the enum to sort the data. Is there something similar in Spring data MongoDB? Or with a projections can we do this?
Not possible, since MongoDB doesn't allow sorting customization (B-tree stuff).
Workaround: add to your enums numbers, store the numbers in Mongodb documents and use them to order
public enum Status {
IN_PROGRESS(2),
RESOLVED(3),
NEW(1);
private int id;
Status(int id){
this.id = id;
}
}

Simple Tagging Implementation with Spring Data JPA/Rest

I am trying to come up with a way of implementing tags for my entity that works well for me and need some help in the process. Let me write down some requirements I have in mind:
Firstly, I would like tags to show in entities as a list of strings like this:
{
"tags": ["foo", "bar"]
}
Secondly, I need to be able to retrieve a set of available tags across all entities so that users can easily choose from existing tags.
The 2nd requirement could be achieved by creating a Tag entity with the value of the Tag as the #Id. But that would make the tags property in my entity a relation that requires an extra GET operation to fetch. I could work with a getter method that resolves all the Tags and returns only a list of strings, but I see two disadvantages in that: 1. The representation as a list of strings suggests you could store tags by POSTing them in that way which is not the case. 2. The process of creating an entity requires to create all the Tags via a /tags endpoint first. That seem rather complicated for such a simple thing.
Also, I think I read somewhere that you shouldn't create a repository for an entity that isn't standalone. Would I create a Tag and only a Tag at any point in time? Nope.
I could store the tags as an #ElementCollection in my entity. In this case I don't know how to fulfill the 2nd requirement, though.
#ElementCollection
private Set<String> tags;
I made a simple test via EntityManager but it looks like I cannot query things that are not an #Entity in a result set.
#RestController
#RequestMapping("/tagList")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class TagListController implements RepresentationModelProcessor<RepositoryLinksResource> {
#PersistenceContext
private final #NonNull EntityManager entityManager;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<EntityModel<TagList>> get() {
System.out.println(entityManager.createQuery("SELECT t.tags FROM Training t").getFirstResult());
EntityModel<TagList> model = EntityModel.of(new TagList(Set.of("foo", "bar")));
model.add(linkTo(methodOn(TagListController.class).get()).withSelfRel());
return ResponseEntity.ok(model);
}
}
org.hibernate.QueryException: not an entity
Does anyone know a smart way?
The representation as a list of strings suggests you could store tags by POSTing them in that way which is not the case
This is precisely the issue with using entities as REST resource representations. They work fine until it turns out the internal representation (entity) does not match the external representation (the missing DTO).
However, it would probably make most sense performance-wise to simply use an #ElementCollection like you mentioned, because you then don't have the double join with a join table for the many-to-many association (you could also use a one-to-many association where the parent entity and the tag value are both part of the #Id to avoid a join table, but I'm not sure it's convenient to work with. Probably better to just put a UNIQUE(parent_id, TAG) constraint on the collection table, if you need it). Regarding the not an entity error, you would need to use a native query. Assuming that you have #ElementCollection #CollectionTable(name = "TAGS") #Column(name = "TAG") on tags, then SELECT DISTINCT(TAG) FROM TAGS should do the job.
(as a side note, the DISTINCT part of the query will surely introduce some performance penalty, but I would assume the result of that query is a good candidate for caching)

Create index in correct collection

I annotate a document with #Index(unique = true) like so:
public class ADocumentWithUniqueIndex {
private static final long serialVersionUID = 1L;
#Indexed(unique = true)
private String iAmUnique;
public String getiAmUnique() {
return iAmUnique;
}
public void setiAmUnique(String iAmUnique) {
this.iAmUnique = iAmUnique;
}
}
When saving the object, I specify a custom collection:
MongoOperations mongoDb = ...
mongoDb.save(document, "MyCollection");
As a result I get:
A new document in "MyCollection"
An index in the collection "ADocumentWithUniqueIndex"
How can I create the index in "MyCollection" instead without having to explicitly specify it in the annotation?
BACKGROUND:
The default collection name is too ambiguous in our use case. We cannot guarantee, that there wouldn't be two documents with the same name but in different packages. So we added the package name to the collection.
Mapping a document to a collection is dealt with in an infrastructure component.
The implementation details like collection name etc. shouldn't leak into the individual documents.
I understand this is a bit of an "abstraction on top of an abstraction" smell but required since we had to support MongoDb and Windows Azure blob storage. Not anymore though...
This seemed like a fairly standard approach to hide the persistence details in a infrastructure component. Any comments on the approach appreciated as well.
It's kind of unusual to define the collection for an object to be stored and then expect the index annotations to work. There's a few options you have here:
Use #Document on ADocumentWithUniqueIndex and configure the collection name manually. This will cause all objects of that class to be persisted into that collection of course.
Manually create indexes via MongoOperations.indexOps() into the collections you'd like to use. This would be more consistent to your approach of manually determining the collection name during persistence operations.

Save form data to multi-value field on MongoDB using Play2.2.2

I'm experimenting with play (v2.2.2), and I have it connected to MongoDB (v2.4.6) using jackson.
I have a models.Role class with the following attributes:
#Id
#ObjectId
public String id;
public String name;
public ArrayList<String> permissions;
On the template (roles.scala.html), I can easily get the list of permissions to be printed on the HTML, but when I try to add a new role passing a single permission as a string in the form as an #InputText field, it does not get recorded in MongoDB. I suppose that it is because play/scala is trying to assign a simple String to an ArrayList<String>.
Any ideas on the propper approach? Maybe I should do some logic on the create() method under Role class?

Spring Data JPA finder for dynamic fields as Map

My requirement is to have few custom fields in the domain objects. These fields may vary as per the clients.
We are using Spring Data JPA to execute finders. Spring data implicitly provides finders for static fields of the domain and can also handle finders for the fields in the object graph.
I want to know if there is a way to find data on the custom fields? Can someone suggest me a strategy to achieve the same. Below is the sample of my domain class.
public class Employee{
private String name;
private String age;
private Map customeFields; (May vary as per client)
}
I was thinking of overriding QueryLookupStrategy and create my CustomJpaQuery on lines of PartTreeJpaQuery to achieve it. Is there any better approach? Does spring data jpa provides an easy mechanism to override query creation mechanism?
If you are using hibernate (not sure about other JPA implementations) you may add methods with #Query annotations like this:
#Query("select e from Employee as e where e.customeFields[:key] = :value")
List<Employee> findSomeHow(#Param("key") String key, #Param("value") String value)