-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: hibernate search across many-to-many association
PostPosted: Fri Oct 01, 2010 11:29 am 
Newbie

Joined: Sat Aug 28, 2010 2:10 pm
Posts: 6
Hello,

I would like to be able to search across many-to-many associations. As an example, I have set up a Customer-Role association...

Customer.hbm.xml
Code:
       
        <set name="roles" table="CUSTOMER_ROLES" inverse="true" cascade="all">
            <meta attribute="field-description">roles of this customer</meta>
            <key column="CUSTOMER_ID"/>
            <many-to-many class="example.Customer.model.Role" column="ROLE_ID"/>
        </set>


Role.hbm.xml
Code:
        <set name="customers" table="CUSTOMER_ROLES" lazy="false">
          <key column="ROLE_ID"/>
          <many-to-many class="example.Customer.model.Customer" column="CUSTOMER_ID"/>
        </set>


...for finding the roles of the user, this association seems to work fine, but when I try to set up the ability to search customers by role...


Customer.java
Code:
    /** persistent field */
    @ManyToMany
    @IndexedEmbedded
    private Set roles;


Role.java
Code:
    /** persistent field */
    @Field private String role;

    /** persistent field */
    @ManyToMany(mappedBy="roles")
    @ContainedIn
    private Set customers;



I've seen a number of posts that suggest I am on the right track and I've tried many permutations, but I can't seem to get this to work.

Thanks,
Ryan


Top
 Profile  
 
 Post subject: Re: hibernate search across many-to-many association
PostPosted: Mon Oct 04, 2010 4:35 am 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Hi,

what is your problem? Do you have an exception or does your search return 0 results. If you don't get any results, how does your indexing code look like? And how does your search query including the whole code between opening and closing the session look like?
Also, are you actually using hbm file for the Hibernate Core configuration or annotations?

--Hardy


Top
 Profile  
 
 Post subject: Re: hibernate search across many-to-many association
PostPosted: Tue Oct 05, 2010 7:45 pm 
Newbie

Joined: Sat Aug 28, 2010 2:10 pm
Posts: 6
Hardy,

Thanks for the reply. The issue with the previous example was that it didn't return any results. Here's the search code...

Code:
    org.hibernate.Session session = (org.hibernate.Session)entityManager.getDelegate();
    final String[] FIELD_NAMES = new String[]{"name", "address", "createdDate", "username", "roles.role", "customer.name"};
    FullTextSession fullTextSession = Search.createFullTextSession(session);
    QueryParser parser = new MultiFieldQueryParser( FIELD_NAMES, new StandardAnalyzer());
    org.apache.lucene.search.Query query=null;
    try {
      query = parser.parse(searchString);
    } catch(ParseException pe) {
      LOGGER.debug(pe.toString());
    }
    org.hibernate.search.FullTextQuery hibernateQuery = fullTextSession.createFullTextQuery(query, Customer.class);
    List results = hibernateQuery.list();


... I'm not sure what you mean by "indexing code", but I add the customers like this (which creates the index automatically unless I am mistaken)...

Code:
    cust=new Customer("someguy@gmail.com", "password", true, true, true, true, "Some Guy", new java.util.Date(), new HashSet<Role>(), new HashSet<Address>());
    entityManager.persist(cust);
    addy=new Address("SHIPTO", cust.getUsername() + " Addy", "Addy Line 1", "Addy Line 2", "Addy Line 3", "City", "State", "22345", "223-456-7890", "223-456-7891", "223-456-7892", new java.util.Date(), cust);
    entityManager.persist(addy);


...I think the problem here may have been my using xml and annotations in the same setup, so I decided to try an explicitly annotation driven example...

Customer.java
Code:
    /** persistent field */
    @ManyToMany(targetEntity=example.Customer.model.Role.class, cascade = { CascadeType.PERSIST})
    @JoinTable(name="CUSTOMER_ROLES",
      joinColumns=@JoinColumn(name="CUSTOMER_ID"),
      inverseJoinColumns=@JoinColumn(name="ROLE_ID"))
    @Type(type="java.util.Set")
    @IndexedEmbedded
    private Set roles;


Role.java
Code:
    /** persistent field */
    @Column(name="ENABLED")
    @Field
    private String role;

    /** persistent field */
    @ManyToMany(mappedBy="roles", targetEntity=example.Customer.model.Customer.class)
    @JoinTable(name="CUSTOMER_ROLES",
      joinColumns=@JoinColumn(name="ROLE_ID"),
      inverseJoinColumns=@JoinColumn(name="CUSTOMER_ID"))
    @ContainedIn
    private Set customers;


...and I got an error at startup ...

Code:
Caused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn: example.Customer.model.Role.customers
   at org.hibernate.cfg.annotations.CollectionBinder.bind(CollectionBinder.java:476)
   at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1897)
   at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:762)
   at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:726)
   at org.hibernate.cfg.AnnotationConfiguration.processArtifactsOfType(AnnotationConfiguration.java:636)
   at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:359)
   at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1206)
   at org.hibernate.ejb.Ejb3Configuration.buildMappings(Ejb3Configuration.java:1449)


... the join table is important (to me) for the role functionality, so I'm moving on to another example with addresses....

Customer.java
Code:
    /** persistent field */
    @OneToMany(mappedBy="customer", targetEntity=example.Customer.model.Address.class, cascade = { CascadeType.ALL}, fetch=FetchType.EAGER)
    @IndexedEmbedded
    @Type(type="java.util.Set")
    private Set addresses;


Address.java
Code:
    /** persistent field */
    @Column(name="NAME")
    @Field private String name;

    /** persistent field */
    @ManyToOne
    @ContainedIn
    @JoinColumn(name="CUSTOMER_ID")
    private example.Customer.model.Customer customer;


... I'm also having problems with this one. For example, searching for the word "addy" should return the someguy@gmail.com customer (e.g. the 'customer.name' is "someguy@gmail.com Addy" for the address), but it does not. I can see the addy entry in the Address index (using luke), so I know it's there. I'm still not sure what I'm doing wrong.

Thanks,
Ryan


Top
 Profile  
 
 Post subject: Re: hibernate search across many-to-many association
PostPosted: Wed Oct 06, 2010 4:06 am 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Hi,

since your are searching the Customer index and Address is embedded you need to search the field addresses.name. I cannot see that in your search.

Some note:
  • You cannot and don't have to use physical mapping annotations (annotations describing properties of the underlying database, like @JoinTable on the non owning side of a relation
  • It should work fine to have all Hibernate Core related configuration in hbm files and only add the Search specific annotations into the entities
  • With indexing code I meant the code to generate the initial index. Often you have existing data in the database which needs to be indexed first. In your case you are creating new entities and rely on the automatic indexing functionality of Search. That's fine as well


Top
 Profile  
 
 Post subject: Re: hibernate search across many-to-many association
PostPosted: Wed Oct 06, 2010 10:49 am 
Newbie

Joined: Sat Aug 28, 2010 2:10 pm
Posts: 6
Hardy,

Once again, thanks for your insight. I added the addresses.name to the search...

Code:
    org.hibernate.Session session = (org.hibernate.Session)entityManager.getDelegate();
    final String[] FIELD_NAMES = new String[]{"name", "address", "createdDate", "username", "roles.role", "customer.name", "addresses.name"};
    FullTextSession fullTextSession = Search.createFullTextSession(session);
    QueryParser parser = new MultiFieldQueryParser( FIELD_NAMES, new StandardAnalyzer());


... but I still don't get any results for the "addy" query. I noticed looking at the Customer index there is no "addresses" entry...

Image

... I think all of my hibernate configurations are set up correctly as I can successfully search for all fields already in the Customer index. I'm just having issues with any associated fields (like the addresses).

Thanks,
Ryan


Top
 Profile  
 
 Post subject: Re: hibernate search across many-to-many association
PostPosted: Wed Oct 06, 2010 11:21 am 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Code:
cust=new Customer("someguy@gmail.com", "password", true, true, true, true, "Some Guy", new java.util.Date(), new HashSet<Role>(), new HashSet<Address>());
    entityManager.persist(cust);
    addy=new Address("SHIPTO", cust.getUsername() + " Addy", "Addy Line 1", "Addy Line 2", "Addy Line 3", "City", "State", "22345", "223-456-7890", "223-456-7891", "223-456-7892", new java.util.Date(), cust);
    entityManager.persist(addy);


You don't seem to set the association from the customer side. You always have to update the association from both sides.
In fact I would recommend to create the Customer and the Address and then call Customer.setAddress(address) and then just persist the customer. addresses is configured as CascadeType.ALL anyways.

Also, if you turn set the Hibernate debug level to trace you should be able to see in the log files what gets indexed.

--Hardy


Top
 Profile  
 
 Post subject: Re: hibernate search across many-to-many association
PostPosted: Wed Oct 06, 2010 12:15 pm 
Newbie

Joined: Sat Aug 28, 2010 2:10 pm
Posts: 6
Hardy,

Per your suggestion, I created the Customer/Address and then set the address on the Customer, then persisted the Customer only...

Code:
    cust=new Customer("someguy@gmail.com", "password", true, true, true, true, "Some Guy", new java.util.Date(), new HashSet<Role>(), new HashSet<Address>());
    Address addy=new Address("SHIPTO", cust.getUsername() + " Addy", "Addy Line 1", "Addy Line 2", "Addy Line 3", "City", "State", "22345", "223-456-7890", "223-456-7891", "223-456-7892", new java.util.Date(), cust);
    cust.getAddresses().add(addy);
    entityManager.persist(cust);


... in this case (as before), the cust and addy are persisted to the tables, but I still don't get any search results for "addy". I also still do not see an entry for "addresses" in the Customer index. For good measure, here is my persistence.xml....

Code:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
  <persistence-unit name="plus">
    <properties>
      <property name="hibernate.jdbc.use_scrollable_resultset" value="true"/>
      <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
      <property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.FSDirectoryProvider"/>
      <property name="hibernate.search.default.indexBase" value="/var/lucene/indexes"/>
      <class>example.Role</class>
      <class>example.Customer</class>
      <class>example.Address</class>
      <!-- Not needed with HA 3.3 -->
      <property name="hibernate.ejb.event.post-insert" value="org.hibernate.search.event.FullTextIndexEventListener"/>
      <property name="hibernate.ejb.event.post-update" value="org.hibernate.search.event.FullTextIndexEventListener"/>
      <property name="hibernate.ejb.event.post-delete" value="org.hibernate.search.event.FullTextIndexEventListener"/>
      <property name="hibernate.ejb.event.post-collection-recreate" value="org.hibernate.search.event.FullTextIndexEventListener"/>
      <property name="hibernate.ejb.event.post-collection-remove" value="org.hibernate.search.event.FullTextIndexEventListener"/>
      <property name="hibernate.ejb.event.post-collection-update" value="org.hibernate.search.event.FullTextIndexEventListener"/>
      <property name="hibernate.ejb.interceptor" value="com.inftropy.plus.interceptors.CreateAndUpdateSession" />
    </properties>
  </persistence-unit>
</persistence>



... I'm using hibernate annotations v3.5.1-Final, so I also tried removing the hibernate.ejb.event.* entries (the result was the same).

Thanks,
Ryan


Top
 Profile  
 
 Post subject: Re: hibernate search across many-to-many association
PostPosted: Thu Oct 07, 2010 12:05 pm 
Newbie

Joined: Sat Aug 28, 2010 2:10 pm
Posts: 6
Hardy,

Found it! I did not understand that I needed to explicitly set the type of object contained in the Set, e.g....

Bad Customer.java
Code:
    /** persistent field */
    @OneToMany(mappedBy="customer", targetEntity=example.Customer.model.Address.class, cascade = { CascadeType.ALL}, fetch=FetchType.EAGER)
    @IndexedEmbedded
    private Set addresses;


Good Customer.java
Code:
    /** persistent field */
    @OneToMany(mappedBy="customer", targetEntity=example.Customer.model.Address.class, cascade = { CascadeType.ALL}, fetch=FetchType.EAGER)
    @IndexedEmbedded
    private Set<Address> addresses;


... This was not necessary in typical hibernate queries, so I overlooked it, once I made this change, the addresses fields showed up in the Customer index!

Image

... and of course I can successfully query the addresses.id and addresses.name field.

Thanks so much for your guidance! I had so many things wrong with my initial implementation I'm sure I would not have reached a satisfactory conclusion without your help.

Best,
Ryan


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
cron
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.