private static Object getReferencedId(DataRecord next, ReferenceFieldMetadata field) {
   DataRecord record = (DataRecord) next.get(field);
   if (record != null) {
     Collection<FieldMetadata> keyFields = record.getType().getKeyFields();
     if (keyFields.size() == 1) {
       return record.get(keyFields.iterator().next());
     } else {
       List<Object> compositeKeyValues = new ArrayList<Object>(keyFields.size());
       for (FieldMetadata keyField : keyFields) {
         compositeKeyValues.add(record.get(keyField));
       }
       return compositeKeyValues;
     }
   } else {
     return StringUtils.EMPTY;
   }
 }
 @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());
   }
 }
  private StorageResults createResults(final List list) {
    CloseableIterator<DataRecord> iterator;
    if (selectedFields.isEmpty()) {
      iterator = new ListIterator(mappings, storageClassLoader, list.iterator(), callbacks);
    } else {
      iterator =
          new ListIterator(mappings, storageClassLoader, list.iterator(), callbacks) {
            @Override
            public DataRecord next() {
              final DataRecord next = super.next();
              final ComplexTypeMetadata explicitProjectionType =
                  new ComplexTypeMetadataImpl(StringUtils.EMPTY, Storage.PROJECTION_TYPE, false);
              final DataRecord nextRecord =
                  new DataRecord(explicitProjectionType, UnsupportedDataRecordMetadata.INSTANCE);
              VisitorAdapter<Void> visitor =
                  new VisitorAdapter<Void>() {
                    private String aliasName;

                    @Override
                    public Void visit(Field field) {
                      FieldMetadata fieldMetadata = field.getFieldMetadata();
                      TypeMapping mapping =
                          mappings.getMappingFromDatabase(fieldMetadata.getContainingType());
                      if (mapping != null && mapping.getUser(fieldMetadata) != null) {
                        fieldMetadata = mapping.getUser(fieldMetadata);
                      }
                      Object value;
                      if (fieldMetadata instanceof ReferenceFieldMetadata) {
                        value = getReferencedId(next, (ReferenceFieldMetadata) fieldMetadata);
                      } else {
                        value = next.get(fieldMetadata);
                      }
                      if (aliasName != null) {
                        SimpleTypeMetadata fieldType =
                            new SimpleTypeMetadata(
                                XMLConstants.W3C_XML_SCHEMA_NS_URI,
                                fieldMetadata.getType().getName());
                        fieldMetadata =
                            new SimpleTypeFieldMetadata(
                                explicitProjectionType,
                                false,
                                false,
                                false,
                                aliasName,
                                fieldType,
                                Collections.<String>emptyList(),
                                Collections.<String>emptyList(),
                                Collections.<String>emptyList(),
                                StringUtils.EMPTY);
                        explicitProjectionType.addField(fieldMetadata);
                      } else {
                        explicitProjectionType.addField(fieldMetadata);
                      }
                      nextRecord.set(fieldMetadata, value);
                      return null;
                    }

                    @Override
                    public Void visit(StringConstant constant) {
                      if (aliasName != null) {
                        SimpleTypeMetadata fieldType =
                            new SimpleTypeMetadata(
                                XMLConstants.W3C_XML_SCHEMA_NS_URI, Types.STRING);
                        FieldMetadata fieldMetadata =
                            new SimpleTypeFieldMetadata(
                                explicitProjectionType,
                                false,
                                false,
                                false,
                                aliasName,
                                fieldType,
                                Collections.<String>emptyList(),
                                Collections.<String>emptyList(),
                                Collections.<String>emptyList(),
                                StringUtils.EMPTY);
                        explicitProjectionType.addField(fieldMetadata);
                        nextRecord.set(fieldMetadata, constant.getValue());
                      } else {
                        throw new IllegalStateException(
                            "Expected an alias for a constant expression.");
                      }
                      return null;
                    }

                    @Override
                    public Void visit(Count count) {
                      if (aliasName != null) {
                        SimpleTypeMetadata fieldType =
                            new SimpleTypeMetadata(
                                XMLConstants.W3C_XML_SCHEMA_NS_URI, count.getTypeName());
                        FieldMetadata fieldMetadata =
                            new SimpleTypeFieldMetadata(
                                explicitProjectionType,
                                false,
                                false,
                                false,
                                aliasName,
                                fieldType,
                                Collections.<String>emptyList(),
                                Collections.<String>emptyList(),
                                Collections.<String>emptyList(),
                                StringUtils.EMPTY);
                        explicitProjectionType.addField(fieldMetadata);
                        nextRecord.set(fieldMetadata, list.size());
                      }
                      return null;
                    }

                    @Override
                    public Void visit(Alias alias) {
                      aliasName = alias.getAliasName();
                      {
                        alias.getTypedExpression().accept(this);
                      }
                      aliasName = null;
                      return null;
                    }

                    private Void handleMetadataField(MetadataField field) {
                      SimpleTypeMetadata fieldType =
                          new SimpleTypeMetadata(
                              XMLConstants.W3C_XML_SCHEMA_NS_URI, field.getTypeName());
                      String fieldName = aliasName == null ? field.getFieldName() : aliasName;
                      SimpleTypeFieldMetadata aliasField =
                          new SimpleTypeFieldMetadata(
                              explicitProjectionType,
                              false,
                              false,
                              false,
                              fieldName,
                              fieldType,
                              Collections.<String>emptyList(),
                              Collections.<String>emptyList(),
                              Collections.<String>emptyList(),
                              StringUtils.EMPTY);
                      explicitProjectionType.addField(aliasField);
                      nextRecord.set(aliasField, field.getReader().readValue(next));
                      return null;
                    }

                    @Override
                    public Void visit(Timestamp timestamp) {
                      return handleMetadataField(timestamp);
                    }

                    @Override
                    public Void visit(TaskId taskId) {
                      return handleMetadataField(taskId);
                    }

                    @Override
                    public Void visit(StagingStatus stagingStatus) {
                      return handleMetadataField(stagingStatus);
                    }

                    @Override
                    public Void visit(StagingError stagingError) {
                      return handleMetadataField(stagingError);
                    }

                    @Override
                    public Void visit(StagingSource stagingSource) {
                      return handleMetadataField(stagingSource);
                    }

                    @Override
                    public Void visit(StagingBlockKey stagingBlockKey) {
                      return handleMetadataField(stagingBlockKey);
                    }

                    @Override
                    public Void visit(Type type) {
                      FieldMetadata fieldMetadata = type.getField().getFieldMetadata();
                      SimpleTypeMetadata fieldType =
                          new SimpleTypeMetadata(XMLConstants.W3C_XML_SCHEMA_NS_URI, Types.STRING);
                      SimpleTypeFieldMetadata aliasField =
                          new SimpleTypeFieldMetadata(
                              explicitProjectionType,
                              false,
                              false,
                              false,
                              aliasName,
                              fieldType,
                              Collections.<String>emptyList(),
                              Collections.<String>emptyList(),
                              Collections.<String>emptyList(),
                              StringUtils.EMPTY);
                      explicitProjectionType.addField(aliasField);
                      DataRecord dataRecord = (DataRecord) next.get(fieldMetadata.getName());
                      if (dataRecord != null) {
                        nextRecord.set(aliasField, dataRecord.getType().getName());
                      } else {
                        nextRecord.set(aliasField, StringUtils.EMPTY);
                      }
                      return null;
                    }
                  };
              for (TypedExpression selectedField : selectedFields) {
                selectedField.accept(visitor);
              }
              return nextRecord;
            }
          };
    }
    return new FullTextStorageResults(pageSize, query.getResultSize(), iterator);
  }