private static SortField.Type getSortType(FieldMetadata fieldMetadata) {
   TypeMetadata fieldType = fieldMetadata.getType();
   String type = MetadataUtils.getSuperConcreteType(fieldType).getName();
   if (Types.STRING.equals(type)
       || Types.ANY_URI.equals(type)
       || Types.BOOLEAN.equals(type)
       || Types.BASE64_BINARY.equals(type)
       || Types.QNAME.equals(type)
       || Types.HEX_BINARY.equals(type)
       || Types.DURATION.equals(type)) {
     return SortField.Type.STRING_VAL; // STRING does not work well for 'long' strings.
   } else if (Types.INT.equals(type)
       || Types.INTEGER.equals(type)
       || Types.POSITIVE_INTEGER.equals(type)
       || Types.NON_POSITIVE_INTEGER.equals(type)
       || Types.NON_NEGATIVE_INTEGER.equals(type)
       || Types.NEGATIVE_INTEGER.equals(type)
       || Types.UNSIGNED_INT.equals(type)) {
     return SortField.Type.INT;
   } else if (Types.DECIMAL.equals(type) || Types.DOUBLE.equals(type)) {
     return SortField.Type.DOUBLE;
   } else if (Types.DATE.equals(type) || Types.DATETIME.equals(type) || Types.TIME.equals(type)) {
     return SortField.Type.STRING;
   } else if (Types.UNSIGNED_SHORT.equals(type) || Types.SHORT.equals(type)) {
     return SortField.Type.SHORT;
   } else if (Types.UNSIGNED_LONG.equals(type) || Types.LONG.equals(type)) {
     return SortField.Type.LONG;
   } else if (Types.FLOAT.equals(type)) {
     return SortField.Type.FLOAT;
   } else if (Types.BYTE.equals(type) || Types.UNSIGNED_BYTE.equals(type)) {
     return SortField.Type.BYTE;
   } else {
     throw new UnsupportedOperationException("No support for field typed as '" + type + "'");
   }
 }
 @Override
 public StorageResults visit(OrderBy orderBy) {
   TypedExpression field = orderBy.getExpression();
   if (field instanceof Field) {
     FieldMetadata fieldMetadata = ((Field) field).getFieldMetadata();
     SortField sortField =
         new SortField(
             fieldMetadata.getName(),
             getSortType(fieldMetadata),
             orderBy.getDirection() == OrderBy.Direction.DESC);
     query.setSort(new Sort(sortField));
     return null;
   } else {
     throw new NotImplementedException(
         "No support for order by for full text search on non-field.");
   }
 }
 @Override
 public List<DataRecord> visit(Alias alias) {
   alias.getTypedExpression().accept(this);
   FieldMetadata aliasField =
       new SimpleTypeFieldMetadata(
           explicitProjection,
           false,
           lastField.isMany(),
           lastField.isMandatory(),
           alias.getAliasName(),
           lastField.getType(),
           Collections.<String>emptyList(),
           Collections.<String>emptyList(),
           Collections.<String>emptyList(),
           StringUtils.EMPTY);
   ValueBuilder previousValueBuilder = recordProjection.remove(lastField);
   if (previousValueBuilder == null) {
     AggregateValueBuilder previous = aggregateProjection.remove(lastField);
     aggregateProjection.put(aliasField, previous);
   } else {
     recordProjection.put(aliasField, previousValueBuilder);
   }
   return records;
 }
 @Override
 public StorageResults visit(Select select) {
   // TMDM-4654: Checks if entity has a composite PK.
   Set<ComplexTypeMetadata> compositeKeyTypes = new HashSet<ComplexTypeMetadata>();
   // TMDM-7496: Search should include references to reused types
   Collection<ComplexTypeMetadata> types =
       new HashSet<ComplexTypeMetadata>(select.accept(new SearchTransitiveClosure()));
   for (ComplexTypeMetadata type : types) {
     if (type.getKeyFields().size() > 1) {
       compositeKeyTypes.add(type);
     }
   }
   if (!compositeKeyTypes.isEmpty()) {
     StringBuilder message = new StringBuilder();
     Iterator it = compositeKeyTypes.iterator();
     while (it.hasNext()) {
       ComplexTypeMetadata compositeKeyType = (ComplexTypeMetadata) it.next();
       message.append(compositeKeyType.getName());
       if (it.hasNext()) {
         message.append(',');
       }
     }
     throw new FullTextQueryCompositeKeyException(message.toString());
   }
   // Removes Joins and joined fields.
   List<Join> joins = select.getJoins();
   if (!joins.isEmpty()) {
     Set<ComplexTypeMetadata> joinedTypes = new HashSet<ComplexTypeMetadata>();
     for (Join join : joins) {
       joinedTypes.add(join.getRightField().getFieldMetadata().getContainingType());
     }
     for (ComplexTypeMetadata joinedType : joinedTypes) {
       types.remove(joinedType);
     }
     List<TypedExpression> filteredFields = new LinkedList<TypedExpression>();
     for (TypedExpression expression : select.getSelectedFields()) {
       if (expression instanceof Field) {
         FieldMetadata fieldMetadata = ((Field) expression).getFieldMetadata();
         if (joinedTypes.contains(fieldMetadata.getContainingType())) {
           TypeMapping mapping =
               mappings.getMappingFromDatabase(fieldMetadata.getContainingType());
           filteredFields.add(
               new Alias(
                   new StringConstant(StringUtils.EMPTY),
                   mapping.getUser(fieldMetadata).getName()));
         } else {
           filteredFields.add(expression);
         }
       } else {
         filteredFields.add(expression);
       }
     }
     selectedFields.clear();
     selectedFields.addAll(filteredFields);
   }
   // Handle condition
   Condition condition = select.getCondition();
   if (condition == null) {
     throw new IllegalArgumentException("Expected a condition in select clause but got 0.");
   }
   // Create Lucene query (concatenates all sub queries together).
   FullTextSession fullTextSession = Search.getFullTextSession(session);
   Query parsedQuery = select.getCondition().accept(new LuceneQueryGenerator(types));
   // Create Hibernate Search query
   Set<Class> classes = new HashSet<Class>();
   for (ComplexTypeMetadata type : types) {
     String className = ClassCreator.getClassName(type.getName());
     try {
       ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
       classes.add(contextClassLoader.loadClass(className));
     } catch (ClassNotFoundException e) {
       throw new RuntimeException("Could not find class '" + className + "'.", e);
     }
   }
   FullTextQuery fullTextQuery =
       fullTextSession.createFullTextQuery(
           parsedQuery, classes.toArray(new Class<?>[classes.size()]));
   // Very important to leave this null (would disable ability to search across different types)
   fullTextQuery.setCriteriaQuery(null);
   fullTextQuery.setSort(Sort.RELEVANCE); // Default sort (if no order by specified).
   query =
       EntityFinder.wrap(
           fullTextQuery,
           (HibernateStorage) storage,
           session); // ensures only MDM entity objects are returned.
   // Order by
   for (OrderBy current : select.getOrderBy()) {
     current.accept(this);
   }
   // Paging
   Paging paging = select.getPaging();
   paging.accept(this);
   pageSize = paging.getLimit();
   boolean hasPaging = pageSize < Integer.MAX_VALUE;
   if (!hasPaging) {
     return createResults(query.scroll(ScrollMode.FORWARD_ONLY));
   } else {
     return createResults(query.list());
   }
 }