-->
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.  [ 2 posts ] 
Author Message
 Post subject: Sort by function value
PostPosted: Mon Apr 16, 2012 10:03 am 
Newbie

Joined: Wed Apr 13, 2011 2:59 pm
Posts: 5
Is it possible to sort results by the return value of a function like in Solr? http://solr.pl/en/2011/02/28/sorting-by ... solr-1297/

I profit by the occasion to say a big thank you to the HSearch team for the Conditional Indexing feature. It's key for our project.

--
Jordi


Top
 Profile  
 
 Post subject: Re: Sort by function value
PostPosted: Thu May 03, 2012 8:49 am 
Hibernate Team
Hibernate Team

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

ramot wrote:
Is it possible to sort results by the return value of a function like in Solr? http://solr.pl/en/2011/02/28/sorting-by ... solr-1297/

ATM Hibernate Search does not offer functions as described in your link. If you think it should, you could open a Jira in HSEARCH.
That said, you can always write your own custom FieldComparator to achieve this. This could look like this (pasting some unit test code):

Code:
public class SortTest extends SearchTestCase {

   private static FullTextSession fullTextSession;

   public void setUp() throws Exception {
      super.setUp();
      fullTextSession = Search.getFullTextSession( openSession() );
      createTestNumbers();
   }

   public void tearDown() throws Exception {
      // check for ongoing transaction which is an indicator that something went wrong
      // don't call the cleanup methods in this case. Otherwise the original error get swallowed
      if ( !fullTextSession.getTransaction().isActive() ) {
         deleteTestNumbers();
         fullTextSession.close();
      }
      super.tearDown();
   }

   @SuppressWarnings("unchecked")
   public void testCustomFieldComparatorAscendingSort() {
      Transaction tx = fullTextSession.beginTransaction();

      Query query = new MatchAllDocsQuery();
      FullTextQuery hibQuery = fullTextSession.createFullTextQuery( query, NumberHolder.class );
      Sort sort = new Sort( new SortField( "sum", new SumFieldComparatorSource() ) );
      hibQuery.setSort( sort );
      List<NumberHolder> result = hibQuery.list();
      assertNotNull( result );
      assertEquals( "Wrong number of test results.", 4, result.size() );

      int previousSum = 0;
      for ( NumberHolder n : result ) {
         assertTrue( "Documents should be ordered by increasing sum", previousSum < n.getSum() );
         previousSum = n.getSum();
      }

      tx.commit();
   }

   @SuppressWarnings("unchecked")
   public void testCustomFieldComparatorDescendingSort() {
      Transaction tx = fullTextSession.beginTransaction();

      Query query = new MatchAllDocsQuery();
      FullTextQuery hibQuery = fullTextSession.createFullTextQuery( query, NumberHolder.class );
      Sort sort = new Sort( new SortField( "sum", new SumFieldComparatorSource(), true ) );
      hibQuery.setSort( sort );
      List<NumberHolder> result = hibQuery.list();
      assertNotNull( result );
      assertEquals( "Wrong number of test results.", 4, result.size() );

      int previousSum = 100;
      for ( NumberHolder n : result ) {
         assertTrue( "Documents should be ordered by decreasing sum", previousSum > n.getSum() );
         previousSum = n.getSum();
      }

      tx.commit();
   }

   /**
    * Helper method creating test data for number holder.
    */
   private void createTestNumbers() {
      Transaction tx = fullTextSession.beginTransaction();
      NumberHolder holder = new NumberHolder( 1, 1 );
      fullTextSession.save( holder );
      holder = new NumberHolder( 1, 10 );
      fullTextSession.save( holder );
      holder = new NumberHolder( 1, 5 );
      fullTextSession.save( holder );
      holder = new NumberHolder( 3, 2 );
      fullTextSession.save( holder );
      tx.commit();
      fullTextSession.clear();
   }

   private void deleteTestNumbers() {
      Transaction tx = fullTextSession.beginTransaction();
      fullTextSession.createQuery( "delete " + NumberHolder.class.getName() ).executeUpdate();
      tx.commit();
      fullTextSession.clear();
   }

   protected Class<?>[] getAnnotatedClasses() {
      return new Class[] {
            NumberHolder.class
      };
   }

   @Entity
   @Indexed
   public static class NumberHolder {
      @Id
      @GeneratedValue
      int id;

      @Field(analyze = Analyze.NO)
      int num1;

      @Field(analyze = Analyze.NO)
      int num2;

      public NumberHolder(int num1, int num2) {
         this.num1 = num1;
         this.num2 = num2;
      }

      public int getSum() {
         return num1 + num2;
      }

      @Override
      public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append( "NumbersHolder" );
         sb.append( "{id=" ).append( id );
         sb.append( ", num1=" ).append( num1 );
         sb.append( ", num2=" ).append( num2 );
         sb.append( '}' );
         return sb.toString();
      }

      @SuppressWarnings("unused")
      private NumberHolder() {
      }
   }

   public static class SumFieldComparatorSource extends FieldComparatorSource {
      @Override
      public FieldComparator<?> newComparator(String fieldName, int numHits, int sortPos, boolean reversed)
            throws IOException {
         return new SumFieldComparator( numHits, "num1", "num2" );
      }
   }

   public static class SumFieldComparator extends FieldComparator<Integer> {
      private final String field1;
      private final String field2;
      private final int[] field1Values;
      private final int[] field2Values;

      private int[] currentReaderValuesField1;
      private int[] currentReaderValuesField2;
      private int bottom;


      public SumFieldComparator(int numHits, String field1, String field2) {
         this.field1 = field1;
         this.field2 = field2;
         this.field1Values = new int[numHits];
         this.field2Values = new int[numHits];
      }

      @Override
      public int compare(int slot1, int slot2) {
         final int v1 = field1Values[slot1] + field2Values[slot1];
         final int v2 = field1Values[slot2] + field2Values[slot2];

         return compareValues( v1, v2 );
      }


      private int compareValues(int v1, int v2) {
         if ( v1 > v2 ) {
            return 1;
         }
         else if ( v1 < v2 ) {
            return -1;
         }
         else {
            return 0;
         }
      }

      @Override
      public int compareBottom(int doc) {
         int v = currentReaderValuesField1[doc] + currentReaderValuesField2[doc];
         return compareValues( bottom, v );
      }

      @Override
      public void copy(int slot, int doc) {
         int v1 = currentReaderValuesField1[doc];
         field1Values[slot] = v1;

         int v2 = currentReaderValuesField2[doc];
         field2Values[slot] = v2;
      }

      @Override
      public void setNextReader(IndexReader reader, int docBase) throws IOException {
         currentReaderValuesField1 = FieldCache.DEFAULT
               .getInts( reader, field1, FieldCache.DEFAULT_INT_PARSER, false );
         currentReaderValuesField2 = FieldCache.DEFAULT
               .getInts( reader, field2, FieldCache.DEFAULT_INT_PARSER, false );
      }

      @Override
      public void setBottom(final int bottom) {
         this.bottom = field1Values[bottom] + field2Values[bottom];
      }

      @Override
      public Integer value(int slot) {
         return field1Values[slot] + field2Values[slot];
      }
   }
}


Have a look at the source code for org.apache.lucene.search.FieldComparator as well


ramot wrote:
I profit by the occasion to say a big thank you to the HSearch team for the Conditional Indexing feature. It's key for our project.


Thanks, glad to hear :-)

--Hardy


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 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:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.