Using #containedIn and includePaths - hibernate-search

Using hibernate search version v4.5.0, I am tring to use something like
#IndexedEmbedded(depth = 0, includePaths = {"id", "name"})
instead of
#IndexedEmbedded(depth = 1)
In cases where I dont need all the details of the associated class, but just one or two of the fields. The problem Im having is that the #ContainedIn on the other side of the relationship now seems to have no effect. I thought the point of "includePaths" was to allow you to index info beyond the scope of the depth specified. If so, how do I keep the primary entity index in synch when the associated entity changes?
Worse still I find that setting includePaths to seemingly unrelated entries also breaks my #containedIn logic.
e.g. "Person" object contains a set of "Card" objects.
public class Person {
...
#IndexedEmbedded(depth = 1)
private Set<Card> cards = new HashSet<Card>(0);
...
}
"Card" objects are of type "Suit".
public class Card {
...
private Person person;
#Field
private String cardName;//e.g. jack, queen, king
#IndexedEmbedded(depth = 0, includePaths = {"id"})//????????????
private Suit suit;//e.g. hearts, diamonds
...
#ContainedIn
public Person getPerson() {
return this.person;
}
}
With the above code, a CRUD operation to card is reflected only on the Card index itself, but NOT reflected in the Person index who owns that card. However, if I change the seemingly unrelated annotation on Suit from
#IndexedEmbedded(depth = 0, includePaths = {"id"})
to
#IndexedEmbedded(depth = 1, includePaths = {"id"})
Then both Card and Person entites are updated.

"includePaths" does not index info beyond the given "depth", so you need to at least have "depth=1" for "includePaths" to work.
Since you use "includePaths", only the paths you specified will be indexed, which I think is what you want.
Page 66 (physical page 58) in this document explained that in detail.

Related

Using #IndexingDependency derivedFrom and a property bridge

I would like to use hibernate search's #IndexingDependency with a PropertyBridge but I can't seem to make it work.
I get this error :
Hibernate ORM mapping: type 'com.something.Person': path '.currentStatus':
failures:
- HSEARCH700020: Unable to find the inverse side of the association on type
'com.something.Person' at path '.currentStatus<no value extractors>'. Hibernate Search
needs this information in order to reindex 'com.something.Person' when
'com.something.Status' is modified. You can solve this error by defining the inverse
side of this association, either with annotations specific to your integration
(#OneToMany(mappedBy = ...) in Hibernate ORM) or with the Hibernate Search
#AssociationInverseSide annotation. Alternatively, if you do not need to reindex
'com.something.Person' when 'com.something.Status' is modified, you can disable
automatic reindexing with #IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW)
Not sure if I'm doing something wrong or if what I'm trying to do isn't possible. Thank for the help.
Here are the files involved.
Person.class
#Entity
#Table
#Indexed
public class Person {
#OneToMany(mappedBy = "patient", cascade = CascadeType.ALL)
private Set<Status> status = new HashSet<>();
#Transient
#StatusBinding(fieldName = "currentStatus")
#IndexingDependency(derivedFrom = #ObjectPath(#PropertyValue(propertyName = "status")))
public Status getCurrentStatus() {
return this.status.stream()
.filter(it -> it.getDate().isAfter(LocalDate.now()))
.max(Comparator.comparing(Status::getDate))
.orElse(null);
}
}
StatusBinding.class
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.FIELD})
#PropertyMapping(processor = #PropertyMappingAnnotationProcessorRef(type = StatusBinding.Processor.class))
#Documented
public #interface StatusBinding {
String fieldName() default "";
class Processor implements PropertyMappingAnnotationProcessor<StatusBinding> {
#Override
public void process(PropertyMappingStep mapping, StatusBindingannotation, PropertyMappingAnnotationProcessorContext context) {
StatusBinderbinder = new StatusBinder();
if (!annotation.fieldName().isBlank()) binder.setFieldName(annotation.fieldName());
mapping.binder(binder);
}
}
}
StatusBinder.class
public class StatusBinder implements PropertyBinder {
#Setter private String fieldName = "mainStatus";
#Override
public void bind(PropertyBindingContext context) {
context.dependencies()
.use("status")
.use("date")
.use("note");
IndexSchemaObjectField mainStatusField = context.indexSchemaElement().objectField(this.fieldName);
context.bridge(Status.class, new StatusBridge(
mainStatusField.toReference(),
mainStatusField.field("status", context.typeFactory().asString()).toReference(),
mainStatusField.field("date", context.typeFactory().asLocalDate()).toReference(),
mainStatusField.field("note", context.typeFactory().asString()).toReference()
));
}
private static class StatusBrige implements PropertyBridge<Status> {
private final IndexObjectFieldReference mainStatusField;
private final IndexFieldReference<String> statusField;
private final IndexFieldReference<LocalDate> dateField;
private final IndexFieldReference<String> noteField;
public StatusBrige(
IndexObjectFieldReference mainStatusField,
IndexFieldReference<String> statusField,
IndexFieldReference<LocalDate> dateField,
IndexFieldReference<String> noteField
) {
this.mainStatusField = mainStatusField;
this.statusField = statusField;
this.dateField = dateField;
this.noteField = noteField;
}
#Override
public void write(DocumentElement target, Status mainStatus, PropertyBridgeWriteContext context) {
DocumentElement statutElement = target.addObject(this.mainStatusField);
statutElement.addValue(this.statusField, mainStatus.getStatus);
statutElement.addValue(this.dateField, mainStatus.getDate());
statutElement.addValue(this.noteField, mainStatus.getNote());
}
}
}
Problem
When a Status entity is modified, Hibernate Search doesn't know how to retrieve the corresponding Person having that Status as its currentStatus.
Solution
Assuming the currentStatus is always contained in status, and since Status.patient is the inverse side of the Person.status association, you should only need to add this:
#Transient
#StatusBinding(fieldName = "currentStatus")
#IndexingDependency(derivedFrom = #ObjectPath(#PropertyValue(propertyName = "status")))
// ADD THIS ANNOTATION
#AssociationInverseSide(
inversePath = #ObjectPath(#PropertyValue(propertyName = "patient"))
)
public Status getCurrentStatus() {
// ...
}
Why?
I'll try to explain this, but it's a bit complex, so bear with me.
Derived properties and the inverse side of associations are related concepts: they share the common purpose of allowing Hibernate Search to perform automatic reindexing.
However, they are still separate concepts, and Hibernate Search is not able to infer one from the other.
With #IndexingDependency(derivedFrom), you are defining what the computation of currentStatus depends on:
#IndexingDependency(derivedFrom = #ObjectPath(#PropertyValue(propertyName = "status")))
public Status getCurrentStatus() {
This tells Hibernate Search that currentStatus will change whenever the status property changes. With that information, Hibernate Search is able to determine that whenever you call person.getStatus().remove(...) or person.getStatus().add(...) (for example), your Person entity needs reindexing, because currentStatus is indexed, and it probably changed.
In your custom binder, you're also defining dependencies:
context.dependencies()
.use("status")
.use("date")
.use("note");
This tells Hibernate Search that whenever the status, date, and note properties of a Status entity change, the Person having that Status as its currentStatus will need reindexing.
However... what Hibernate Search doesn't know is how to retrieve the person having that Status as its currentStatus.
It may know how to retrieve all persons having that Status in their status set, but that's a different thing, isn't it? Hibernate Search doesn't know that currentStatus is actually one of the elements contained in the status property. For all it knows, getCurrentStatus() could very well be doing this: status.iterator().next().getParentStatus(). Then the current status wouldn't be included in Person#status, and it's unclear if myStatus.getPatient() could return a Person whose currentStatus is myStatus.
So you need to tell Hibernate Search explicitly: "from a given Status myStatus, if you retrieve the value of myStatus.getPatient(), you get the Person whose currentStatus property may point back to myStatus". That's exactly what #AssociationInverseSide is for.

Class Design - Object Oriented Programming Question

This was asked during an interview.
There are different manufacturers of buses. Each bus has got different models and each model has only 2 variants. So different manufacturers have different models with only 2 variants. The interviewer asked me to design a standalone program with just classes. She mentioned that I should not think about databases and I didn't have to code them. For example, it could be a console based program with inputs and outputs.
The manufacturers, models and variants information should be held in memory (hard-coded values were fine for this standalone program). She wanted to observe the classes and my problem solving approach.
She told me to focus on implementing three APIs or methods for this system.
The first one was to get information about a particular bus. Input would be manufacturer name, model name and variant name. Given these three values, the information about a particular bus such as its price, model, year, etc should be shown to the client.
The second API would be to compare two buses and the output would be to list the features side by side, probably in a tabular format. Input would be the same as the one for the first API i.e. manufacturer name, model name and variant name for both the buses.
The third one would be to search the buses by price (>= price) and get the list of buses which satisfy the condition.
She also added that the APIs should be scalable and I should design the solution with this condition on my mind.
This is how I designed the classes:
class Manufacturer {
private String name;
private Set<Model> models;
// some more properties related to manufacturer
}
class Model {
private String name;
private Integer year;
private Set<Variant> variants;
// some more properties related to model
}
class Variant {
private String name;
private BigDecimal price;
// some more properties related to variant
}
class Bus {
private String manufacturerName;
private String modelName;
private String variantName;
private Integer year;
private BigDecimal price;
// some more additional properties as required by client
}
class BusService {
// The first method
public Bus getBusInformation(String manufacturerName, String modelName, String variantName) throws Exception {
Manufacturer manufacturer = findManufacturer(manufacturerName);
//if(manufacturer == null) throw a valid exception
Model model = findModel(manufacturer);
// if(model == null) throw a valid exception
Variant variant = findVariant(model);
// if(variant == null) throw a valid exception
return createBusInformation(manufacturer, model, variant);
}
}
She stressed that there were only 2 variants and there wouldn't be any more variants and it should be scalable. After going through the classes, she said she understood my approach and I didn't have to implement the other APIs/methods. I realized that she wasn't impressed with the way I designed them.
It would be helpful to understand the mistake I made so that I could learn from it.
I interpreted your 3 requirements a bit differently (and I may be wrong). But it sounds like the overall desire is to be able to perform different searches against all Models, correct?
Also, sounds to me that as all Variants are Models. I suspect different variants would have different options, but nothing to confirm that. If so, a variant is just a subclass of a particular model. However, if variants end up having the same set of properties, then variant isn't anything more than an additional descriptor to the model.
Anyway, going on my suspicions, I'd have made Model the center focus, and gone with:
(base class)
abstract class Model {
private Manufacturer manufacturer;
private String name;
private String variant;
private Integer year;
private BigDecimal price;
// some more properties related to model
}
(manufacturer variants)
abstract class AlphaModel {
AlphaModel() {
this.manufacturer = new Manufacturer() { name = "Alpha" }
}
// some more properties related to this manufacturer
}
abstract class BetaModel {
BetaModel() {
this.manufacturer = new Manufacturer() { name = "Beta" }
}
// some more properties related to this manufacturer
}
(model variants)
abstract class AlphaBus : AlphaModel {
AlphaBus() {
this.name = "Super Bus";
}
// some more properties related to this model
}
abstract class BetaTruck : BetaModel {
BetaTruck() {
this.name = "Big Truck";
}
// some more properties related to this model
}
(actual instances)
class AlphaBusX : AlphaBus {
AlphaBusX() {
this.variant = "X Edition";
}
// some more properties exclusive to this variant
}
class AlphaBusY : AlphaBus {
AlphaBusY() {
this.variant = "Y Edition";
}
// some more properties exclusive to this variant
}
class BetaTruckF1 : BetaTruck {
BetaTruckF1() {
this.variant = "Model F1";
}
// some more properties exclusive to this variant
}
class BetaTruckF2 : BetaTruck {
BetaTruckF2() {
this.variant = "Model F2";
}
// some more properties exclusive to this variant
}
Then finally:
var data = new Set<Model> {
new AlphaBusX(),
new AlphaBusY(),
new BetaTruckF1(),
new BetaTruckF2()
}
API #1:
var result = data.First(x => x.manufacturer.name = <manufactuer>
&& x.name = <model>
&& x.variant = <variant>);
API #2:
var result1 = API#1(<manufacturer1>, <model1>, <variant1>);
var result2 = API#1(<manufacturer2>, <model2>, <variant2>);
API #3:
var result = data.Where(x => x.price >= <price>);
I would say your representation of the Bus class is severely limited, Variant, Model, Manufacturer should be hard links to the classes and not strings. Then a get for the name of each.
E.G from the perspective of Bus bus1 this.variant.GetName() or from the outside world. bus1.GetVariant().name
By limiting your bus to strings of it's held pieces, you're forced to do a lookup even when inside the bus class, which performs much slower at scale than a simple memory reference.
In terms of your API (while I don't have an example), your one way to get bus info is limited. If the makeup of the bus changes (variant changes, new component classes are introduced), it requires a decent rewrite of that function, and if other functions are written similarly then all of those two.
It would require some thought but a generic approach to this that can dynamically grab the info based on the input makes it easier to add/remove component pieces later on. This will be the are your interviewer was focusing on most in terms of advanced technical&language skills. Implementing generics, delegates, etc. here in the right way can make future upkeep of your API a lot easier. (Sorry I don't have an example)
I wouldn't say your approach here is necessarily bad though, the string member variables are probably the only major issue.

GWT RequestFactory with Set sub-collections

I have a little problem with RequestFactory regarding persistence of children collections in the shape of Set . I am using gwt 2.5 with requestfactory, and Hibernate4/Spring3 at the backend. I am using the open-session-in-view filter by Spring so that collections can be persisted after findByID in the save method of my DAO. My problem is everything seems to work ok when children collections are based on List , but when they are based on Set , not all of the items from the client reach the server aparently.
My code looks like this:
-The root entity IndicationTemplate:
#Entity
#Table (name = "vdasIndicationTemplate")
#org.hibernate.annotations.Table ( appliesTo = "vdasIndicationTemplate", indexes =
{#Index (name = "xieIndicationTemplateCreateUser", columnNames= {"createUserID"}),
#Index (name = "xieIndicationTemplateModifyUser", columnNames= {"modifyUserID"})})
public class IndicationTemplate extends AbstractEditable <Integer> implements IEntity <Integer>, IDateable, IDescriptable {
//...
private Set <ProposalTemplate> proposalTemplates = null;
//...
#OneToMany (fetch = FetchType.LAZY, mappedBy = "indicationTemplate"
, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.DETACH})
public Set <ProposalTemplate> getProposalTemplates () {
return proposalTemplates;
}
public void setProposalTemplates (Set <ProposalTemplate> proposalTemplates) {
this.proposalTemplates = proposalTemplates;
}
//...
}
-The child entity ProposalTemplate of course has the opposite ManyToOne mapping and has 3 sub-collections as well of the same sort with 3 different entities.
-Client-side proxy for root entity:
#ProxyFor (value = IndicationTemplate.class, locator = PersistenceEntityLocator.class)
public interface IIndicationTemplateProxy extends IEntityProxy, IDeletableProxy, IDescriptableProxy {
//....
Set <IProposalTemplateProxy> getProposalTemplates ();
void setProposalTemplates (Set <IProposalTemplateProxy> proposalTemplateProxy);
}
-On the client, i render the attributes of root entity and also the list of children entity. Then the user can update them, and the changes are stored back into the collection like this:
Set <IProposalTemplateProxy> newList = getElementsFromUiSomehow (); //these elements can be new or just the old ones with some changes
indicationTemplate.getProposalTemplates ().clear ();
indicationTemplate.getProposalTemplates ().addAll (newList);
-And then at some point:
requestContext.saveIndicationTemplate ((IIndicationTemplateProxy) entityProxy)
.fire (new Receiver <IIndicationTemplateProxy> ()
-The RequestContext looks something like:
#Service (value = TemplateService.class, locator = SpringServiceLocator.class)
public interface ITemplateRequestContext extends RequestContext {
/** saves (creates or updates) one given indication template */
Request <IIndicationTemplateProxy> saveIndicationTemplate (IIndicationTemplateProxy indicationTemplate);
//....
}
The problem is only 1 child entity is added per request to the collection server-side. For example, indicationTemplate has 2 proposalTemplates, and i add 4 more, then on the server-side saveIndicationTemplate the entity contains only 3 instead of 6. If happens no matter how many entities i have previously and how many i add, i only get 1 more than before on the server. I did check the proxy object right before firing the requestContext method and it is fully loaded, with all of its children. And finally the weirdest thing is, if i replace Set per List (and all subsequent changes), everything works sweet!
May there be any problem why RF fails to transfer all the changes to the server when using Sets instead of Lists?? Btw, i do prefer Sets in this case, so that is why i am asking.
Anyone?
Thanks for helping!
I assume you are hitting this error. It is a known gwt bug which is still unfixed.
https://code.google.com/p/google-web-toolkit/issues/detail?id=6354&q=set&colspec=ID%20Type%20Status%20Owner%20Milestone%20Summary%20Stars
try to use list instead of set and it should be fine.

Wicket - Wrapped collection Model "transformation"

I have a domain object which has a collection of primitive values, which represent the primary keys of another domain object ("Person").
I have a Wicket component that takes IModel<List<Person>>, and allows you to view, remove, and add Persons to the list.
I would like to write a wrapper which implements IModel<List<Person>>, but which is backed by a PropertyModel<List<Long>> from the original domain object.
View-only is easy (Scala syntax for brevity):
class PersonModel(wrappedModel: IModel[List[Long]]) extends LoadableDetachableModel[List[Person]] {
#SpringBean dao: PersonDao =_
def load: List[Person] = {
// Returns a collection of Persons for each id
wrappedModel.getObject().map { id: Long =>
dao.getPerson(id)
}
}
}
But how might I write this to allow for adding and removing from the original List of Longs?
Or is a Model not the best place to do this translation?
Thanks!
You can do something like this:
class PersonModel extends Model<List<Person>> {
private transient List<Person> cache;
private IModel<List<String>> idModel;
public PersonModel( IModel<List<String>> idModel ) {
this.idModel = idModel;
}
public List<Person> getObject() {
if ( cache == null ) {
cache = convertIdsToPersons( idModel.getObject() );
return cache;
}
public void setObject( List<Person> ob ) {
cache = null;
idModel.setObject( convertPersonsToIds( ob ) );
}
}
This isn't very good code but it shows the general idea. One thing you need to consider is how this whole thing will be serialised between requests, you might be better off extending LoadableDetachableModel instead.
Another thing is the cache: it's there to avoid having to convert the list every time getObject() is called within a request. You may or may not need it in practice (depends on a lot of factors, including the speed of the conversion), but if you use it, it means that if something else is modifying the underlying collection, the changes may not be picked up by this model.
I'm not quite sure I understand your question and I don't understand the syntax of Scala.
But, to remove an entity from a list, you can provide a link that simply removes it using your dao. You must be using a repeater to populate your Person list so each repeater entry will have its own Model which can be passed to the deletion link.
Take a look at this Wicket example that uses a link with a repeater to select a contact. You just need to adapt it to delete your Person instead of selecting it.
As for modifying the original list of Longs, you can use the ListView.removeLink() method to get a link component that removes an entry from the backing list.

How else to ensure the order of a #ManyToMany List?

Consider:
#Entity
public class M {
#ManyToMany
private List<E> es = new ArrayList<E>();
private E newE;
public M(E newE) {
this.newE = newE;
es.add(newE);
}
Cannot I assert(m.newE == m.getEs(0))?
I could only if after rebooting the app/PU is:
public List<E> getEs() {
final int indexOf = es.indexOf(newE);
if (indexOf != 0) {
es.remove(indexOf);
es.add(0, newE);
}
return Collections.unmodifiableList(es);
}
However this burdensome code is even inefficient as it forces loading the E entitities from the PU before they may actually be needed. Any alternative that works?
Cannot I assert(m.newE == m.getEs(0))?
No, if there are any objects in the #ManyToMany, newE will be added at the end of the list. Also, I believe the list will be lazy-loaded before the insert, so your concern about things being inefficient doesn't really apply.
If you're only concerned about the ordering of elements when loaded from the persistence unit, #OrderBy will do the trick.
#ManyToMany
#Orderby("someproperty ASC")
private List<E> es = new ArrayList<E>();
If you also want to maintain order at runtime while adding elements, you'll have to implement compareTo() or a separate Comparator, then use Collections.sort(), or use a naturally sorted collection like SortedSet.
JPA also provides an #OrderColumn if you want the order maintained.
See this link
Otherwise changes to the order of the elements of the list are not considered a persistent change.