-->
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: Natural Sort Order for Hibernate Shard
PostPosted: Wed Apr 30, 2008 7:02 am 
Newbie

Joined: Wed Apr 30, 2008 6:00 am
Posts: 1
If anyone who don't understand what is "natural sort order", here is a good post talking about this problem:
http://www.codinghorror.com/blog/archives/001018.html

Obviously, both hibernate and hibernate shard are using ASCII order at the moment. It turns out the sorted results are not really human friendly. User could sort the result again after getting the results, which is unnecessary. In hibernate shard, we can acctually add an algorithm to make the sorted results in a natural sort order.

The algorithm I am using is from here:
http://www.davekoelle.com/alphanum.html

To use the algorithm, two classes need to be modified. First in ExitOperationUtils.java, when the getPropertyValue method called, we want it to return an Object instead of a Comparable object.

Code:
public static Object getPropertyValue(Object obj, String propertyName) {
    try {
      StringBuilder propertyPath = new StringBuilder();
      for(int i=0; i < propertyName.length(); i++) {
        String s = propertyName.substring(i,i+1);
        if (i == 0 || propertyName.charAt(i-1) == '.') {
          propertyPath.append(StringUtil.capitalize(s));
        } else {
          propertyPath.append(s);
        }
      }
      String[] methods = ("get" + propertyPath.toString().replaceAll("\\.", ".get")).split("\\.");
      Object root = obj;
      for (String method : methods) {
        Method m = findPotentiallyPrivateMethod(root.getClass(), method);
        m.setAccessible(true);
        root = m.invoke(root);
        if (root == null) {
          break;
        }
      }
      return root;
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
      throw new RuntimeException(e);
    }
  }


Other methods don't need to be changed in this class.
Then we can use the algorithm to sort in OrderExitOperation.java. Only the apply method need to be changed:

Code:
  public List<Object> apply(List<Object> results) {
    List<Object> nonNullList = ExitOperationUtils.getNonNullList(results);
    Comparator<Object> comparator = new Comparator<Object>() {
     
      private final boolean isDigit(char ch)
      {
          return ch >= 48 && ch <= 57;
      }

      /** Length of string is passed in for improved efficiency (only need to calculate it once) **/
      private final String getChunk(String s, int slength, int marker)
      {
          StringBuilder chunk = new StringBuilder();
          char c = s.charAt(marker);
          chunk.append(c);
          marker++;
          if (isDigit(c))
          {
              while (marker < slength)
              {
                  c = s.charAt(marker);
                  if (!isDigit(c))
                      break;
                  chunk.append(c);
                  marker++;
              }
          } else
          {
              while (marker < slength)
              {
                  c = s.charAt(marker);
                  if (isDigit(c))
                      break;
                  chunk.append(c);
                  marker++;
              }
          }
          return chunk.toString();
      }
     
      public int compare(Object o1, Object o2) {
        if (o1 == o2) {
          return 0;
        }
        Object o1Value = ExitOperationUtils.getPropertyValue(o1, propertyName);
        Object o2Value = ExitOperationUtils.getPropertyValue(o2, propertyName);
        if (o1Value == null) {
          return -1;
        }
        if( o1Value.getClass().getSimpleName().equals("String")&& o2Value.getClass().getSimpleName().equals("String")){
          String v1 = (String)o1Value;
          String v2 = (String)o2Value;
         
          int thisMarker = 0;
          int thatMarker = 0;
          int s1Length = v1.length();
          int s2Length = v2.length();

          while (thisMarker < s1Length && thatMarker < s2Length)
          {
              String thisChunk = getChunk(v1, s1Length, thisMarker);
              thisMarker += thisChunk.length();

              String thatChunk = getChunk(v2, s2Length, thatMarker);
              thatMarker += thatChunk.length();

              // If both chunks contain numeric characters, sort them numerically
              int result = 0;
              if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0)))
              {
                  // Simple chunk comparison by length.
                  int thisChunkLength = thisChunk.length();
                  result = thisChunkLength - thatChunk.length();
                  // If equal, the first different number counts
                  if (result == 0)
                  {
                      for (int i = 0; i < thisChunkLength; i++)
                      {
                          result = thisChunk.charAt(i) - thatChunk.charAt(i);
                          if (result != 0)
                          {
                              return result;
                          }
                      }
                  }
              } else
              {
                  result = thisChunk.toLowerCase().compareTo(thatChunk.toLowerCase());
              }

              if (result != 0)
                  return result;
          }
          return s1Length - s2Length;
        }
       
        Comparable<Object> v01 = (Comparable<Object>) o1Value;
        Comparable<Object> v02 = (Comparable<Object>) o2Value;
        return v01.compareTo(v02);
      }
    };

    Collections.sort(nonNullList, comparator);
    if (order.toString().endsWith("desc")) {
      Collections.reverse(nonNullList);
    }

    return nonNullList;
  }


In the code above, I ignored the case of the strings. You can check the ignoreCase attribute of the Order object to determine if you should ignore the case.

I suggest the Hibernate team to consider add this sort option to the whole project. Because as the post said, in most situations, people don't want the results to be sorted according to the ASCII, but human readable.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 02, 2008 7:56 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
I am sorry, what are you trying to sort exactly?

_________________
Emmanuel


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.