You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by rm...@apache.org on 2017/11/17 17:04:53 UTC

metron git commit: METRON-1294 IP addresses are not formatted correctly in facet and group results (merrimanr) closes apache/metron#827

Repository: metron
Updated Branches:
  refs/heads/master fd4a6d164 -> 768a6fada


METRON-1294 IP addresses are not formatted correctly in facet and group results (merrimanr) closes apache/metron#827


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/768a6fad
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/768a6fad
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/768a6fad

Branch: refs/heads/master
Commit: 768a6fada36bdb03be63052024d59ad3754d616d
Parents: fd4a6d1
Author: merrimanr <me...@gmail.com>
Authored: Fri Nov 17 11:04:41 2017 -0600
Committer: merrimanr <me...@apache.org>
Committed: Fri Nov 17 11:04:41 2017 -0600

----------------------------------------------------------------------
 .../src/app/service/search.service.ts           |  11 +-
 metron-interface/metron-rest/README.md          |  12 +-
 .../rest/controller/SearchController.java       |  15 +--
 .../metron/rest/service/SearchService.java      |   3 +-
 .../rest/service/impl/SearchServiceImpl.java    |  12 +-
 .../SearchControllerIntegrationTest.java        |  77 ++++--------
 .../elasticsearch/dao/ElasticsearchDao.java     | 115 +++++++++--------
 .../dao/ElasticsearchMetaAlertDao.java          |   8 +-
 .../dao/ElasticsearchMetaAlertDaoTest.java      |   8 +-
 .../ElasticsearchSearchIntegrationTest.java     |  15 +++
 .../apache/metron/indexing/dao/HBaseDao.java    |   7 +-
 .../apache/metron/indexing/dao/IndexDao.java    |   3 +-
 .../metron/indexing/dao/MultiIndexDao.java      |  15 +--
 .../apache/metron/indexing/dao/InMemoryDao.java |  35 +++---
 .../indexing/dao/InMemoryMetaAlertDao.java      |   7 +-
 .../indexing/dao/SearchIntegrationTest.java     | 125 +++++++++----------
 16 files changed, 197 insertions(+), 271 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-interface/metron-alerts/src/app/service/search.service.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/service/search.service.ts b/metron-interface/metron-alerts/src/app/service/search.service.ts
index 4bbcc2d..1e97e96 100644
--- a/metron-interface/metron-alerts/src/app/service/search.service.ts
+++ b/metron-interface/metron-alerts/src/app/service/search.service.ts
@@ -43,13 +43,10 @@ export class SearchService {
     let processedKeys: string[] = [];
     let columnMetadatas: ColumnMetadata[] = [];
 
-    for (let index of Object.keys(response)) {
-      let indexMetaData = response[index];
-      for (let key of Object.keys(indexMetaData)) {
-        if (processedKeys.indexOf(key) === -1) {
-          processedKeys.push(key);
-          columnMetadatas.push(new ColumnMetadata(key, indexMetaData[key]));
-        }
+    for (let key of Object.keys(response)) {
+      if (processedKeys.indexOf(key) === -1) {
+        processedKeys.push(key);
+        columnMetadatas.push(new ColumnMetadata(key, response[key]));
       }
     }
 

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-interface/metron-rest/README.md
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/README.md b/metron-interface/metron-rest/README.md
index 724239b..e46f865 100644
--- a/metron-interface/metron-rest/README.md
+++ b/metron-interface/metron-rest/README.md
@@ -226,7 +226,6 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
 | [ `POST /api/v1/search/group`](#get-apiv1searchgroup)|
 | [ `GET /api/v1/search/findOne`](#get-apiv1searchfindone)|
 | [ `GET /api/v1/search/column/metadata`](#get-apiv1searchcolumnmetadata)|
-| [ `GET /api/v1/search/column/metadata/common`](#get-apiv1searchcolumnmetadatacommon)|
 | [ `GET /api/v1/sensor/enrichment/config`](#get-apiv1sensorenrichmentconfig)|
 | [ `GET /api/v1/sensor/enrichment/config/list/available/enrichments`](#get-apiv1sensorenrichmentconfiglistavailableenrichments)|
 | [ `GET /api/v1/sensor/enrichment/config/list/available/threat/triage/aggregators`](#get-apiv1sensorenrichmentconfiglistavailablethreattriageaggregators)|
@@ -489,18 +488,11 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
     * 404 - Document with UUID and sensor type not found
     
 ### `GET /api/v1/search/column/metadata`
-  * Description: Get column metadata for each index in the list of indicies
+  * Description: Get index column metadata for a list of sensor types with duplicates removed.  Column names and types for each sensor are retrieved from the most recent index.  Columns that exist in multiple indices with different types will default to type 'other'.
   * Input:
-      * indices - Indices
+      * sensorTypes - Sensor Types
   * Returns:
     * 200 - Column Metadata
-    
-### `GET /api/v1/search/column/metadata/common`
-  * Description: Get metadata for columns shared by the list of indices
-  * Input:
-      * indices - Indices
-  * Returns:
-    * 200 - Common Column Metadata
 
 ### `GET /api/v1/sensor/enrichment/config`
   * Description: Retrieves all SensorEnrichmentConfigs from Zookeeper

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SearchController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SearchController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SearchController.java
index e215413..2748858 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SearchController.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SearchController.java
@@ -81,17 +81,12 @@ public class SearchController {
     }
   }
 
-  @ApiOperation(value = "Get column metadata for each index in the list of indices")
+  @ApiOperation(value = "Get index column metadata for a list of sensor types with duplicates removed.  "
+      + "Column names and types for each sensor are retrieved from the most recent index.  "
+      + "Columns that exist in multiple indices with different types will default to type 'other'.")
   @ApiResponse(message = "Column Metadata", code = 200)
   @RequestMapping(value = "/column/metadata", method = RequestMethod.POST)
-  ResponseEntity<Map<String, Map<String, FieldType>>> getColumnMetadata(final @ApiParam(name = "indices", value = "Indices", required = true) @RequestBody List<String> indices) throws RestException {
-    return new ResponseEntity<>(searchService.getColumnMetadata(indices), HttpStatus.OK);
-  }
-
-  @ApiOperation(value = "Get metadata for columns shared by the list of indices")
-  @ApiResponse(message = "Common Column Metadata", code = 200)
-  @RequestMapping(value = "/column/metadata/common", method = RequestMethod.POST)
-  ResponseEntity<Map<String, FieldType>> getCommonColumnMetadata(final @ApiParam(name = "indices", value = "Indices", required = true) @RequestBody List<String> indices) throws RestException {
-    return new ResponseEntity<>(searchService.getCommonColumnMetadata(indices), HttpStatus.OK);
+  ResponseEntity<Map<String, FieldType>> getColumnMetadata(final @ApiParam(name = "sensorTypes", value = "Sensor Types", required = true) @RequestBody List<String> sensorTypes) throws RestException {
+    return new ResponseEntity<>(searchService.getColumnMetadata(sensorTypes), HttpStatus.OK);
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SearchService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SearchService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SearchService.java
index 5899765..a2f9c11 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SearchService.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SearchService.java
@@ -34,7 +34,6 @@ public interface SearchService {
   SearchResponse search(SearchRequest searchRequest) throws RestException;
   GroupResponse group(GroupRequest groupRequest) throws RestException;
   Optional<Map<String, Object>> getLatest(GetRequest request) throws RestException;
-  Map<String, Map<String, FieldType>> getColumnMetadata(List<String> indices) throws RestException;
-  Map<String, FieldType> getCommonColumnMetadata(List<String> indices) throws RestException;
+  Map<String, FieldType> getColumnMetadata(List<String> indices) throws RestException;
 
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/SearchServiceImpl.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/SearchServiceImpl.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/SearchServiceImpl.java
index 433eae3..a696f68 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/SearchServiceImpl.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/SearchServiceImpl.java
@@ -94,7 +94,7 @@ public class SearchServiceImpl implements SearchService {
   }
 
   @Override
-  public Map<String, Map<String, FieldType>> getColumnMetadata(List<String> indices) throws RestException {
+  public Map<String, FieldType> getColumnMetadata(List<String> indices) throws RestException {
     try {
       return dao.getColumnMetadata(indices);
     }
@@ -103,16 +103,6 @@ public class SearchServiceImpl implements SearchService {
     }
   }
 
-  @Override
-  public Map<String, FieldType> getCommonColumnMetadata(List<String> indices) throws RestException {
-    try {
-      return dao.getCommonColumnMetadata(indices);
-    }
-    catch(IOException ioe) {
-      throw new RestException(ioe.getMessage(), ioe);
-    }
-  }
-
   private List<String> getDefaultIndices() throws RestException {
     // Pull the indices from the cache by default
     List<String> indices = Lists.newArrayList((sensorIndexingConfigService.getAllIndices(environment.getProperty(INDEX_WRITER_NAME))));

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/SearchControllerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/SearchControllerIntegrationTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/SearchControllerIntegrationTest.java
index 2c15671..3673654 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/SearchControllerIntegrationTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/SearchControllerIntegrationTest.java
@@ -236,60 +236,37 @@ public class SearchControllerIntegrationTest extends DaoControllerTest {
             .andExpect(jsonPath("$.groupResults[0].groupResults[0].score").value(50));
 
     this.mockMvc.perform(post(searchUrl + "/column/metadata").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content("[\"bro\",\"snort\"]"))
-            .andExpect(status().isOk())
-            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
-            .andExpect(jsonPath("$.*", hasSize(2)))
-            .andExpect(jsonPath("$.bro.common_string_field").value("string"))
-            .andExpect(jsonPath("$.bro.common_integer_field").value("integer"))
-            .andExpect(jsonPath("$.bro.bro_field").value("boolean"))
-            .andExpect(jsonPath("$.bro.duplicate_field").value("date"))
-            .andExpect(jsonPath("$.snort.common_string_field").value("string"))
-            .andExpect(jsonPath("$.snort.common_integer_field").value("integer"))
-            .andExpect(jsonPath("$.snort.snort_field").value("double"))
-            .andExpect(jsonPath("$.snort.duplicate_field").value("long"));
-
-    this.mockMvc.perform(post(searchUrl + "/column/metadata/common").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content("[\"bro\",\"snort\"]"))
-            .andExpect(status().isOk())
-            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
-            .andExpect(jsonPath("$.*", hasSize(2)))
-            .andExpect(jsonPath("$.common_string_field").value("string"))
-            .andExpect(jsonPath("$.common_integer_field").value("integer"));
+        .andExpect(status().isOk())
+        .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+        .andExpect(jsonPath("$.*", hasSize(5)))
+        .andExpect(jsonPath("$.common_string_field").value("string"))
+        .andExpect(jsonPath("$.common_integer_field").value("integer"))
+        .andExpect(jsonPath("$.bro_field").value("boolean"))
+        .andExpect(jsonPath("$.snort_field").value("double"))
+        .andExpect(jsonPath("$.duplicate_field").value("other"));
 
     this.mockMvc.perform(post(searchUrl + "/column/metadata").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content("[\"bro\"]"))
-            .andExpect(status().isOk())
-            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
-            .andExpect(jsonPath("$.*", hasSize(1)))
-            .andExpect(jsonPath("$.bro.common_string_field").value("string"))
-            .andExpect(jsonPath("$.bro.common_integer_field").value("integer"))
-            .andExpect(jsonPath("$.bro.bro_field").value("boolean"))
-            .andExpect(jsonPath("$.bro.duplicate_field").value("date"));
-
-    this.mockMvc.perform(post(searchUrl + "/column/metadata/common").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content("[\"bro\"]"))
-            .andExpect(status().isOk())
-            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
-            .andExpect(jsonPath("$.*", hasSize(4)))
-            .andExpect(jsonPath("$.common_string_field").value("string"))
-            .andExpect(jsonPath("$.common_integer_field").value("integer"))
-            .andExpect(jsonPath("$.bro_field").value("boolean"))
-            .andExpect(jsonPath("$.duplicate_field").value("date"));
+          .andExpect(status().isOk())
+          .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+          .andExpect(jsonPath("$.*", hasSize(4)))
+          .andExpect(jsonPath("$.common_string_field").value("string"))
+          .andExpect(jsonPath("$.common_integer_field").value("integer"))
+          .andExpect(jsonPath("$.bro_field").value("boolean"))
+          .andExpect(jsonPath("$.duplicate_field").value("date"));
 
     this.mockMvc.perform(post(searchUrl + "/column/metadata").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content("[\"snort\"]"))
-            .andExpect(status().isOk())
-            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
-            .andExpect(jsonPath("$.*", hasSize(1)))
-            .andExpect(jsonPath("$.snort.common_string_field").value("string"))
-            .andExpect(jsonPath("$.snort.common_integer_field").value("integer"))
-            .andExpect(jsonPath("$.snort.snort_field").value("double"))
-            .andExpect(jsonPath("$.snort.duplicate_field").value("long"));
-
-    this.mockMvc.perform(post(searchUrl + "/column/metadata/common").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content("[\"snort\"]"))
-            .andExpect(status().isOk())
-            .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
-            .andExpect(jsonPath("$.*", hasSize(4)))
-            .andExpect(jsonPath("$.common_string_field").value("string"))
-            .andExpect(jsonPath("$.common_integer_field").value("integer"))
-            .andExpect(jsonPath("$.snort_field").value("double"))
-            .andExpect(jsonPath("$.duplicate_field").value("long"));
+          .andExpect(status().isOk())
+          .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+          .andExpect(jsonPath("$.*", hasSize(4)))
+          .andExpect(jsonPath("$.common_string_field").value("string"))
+          .andExpect(jsonPath("$.common_integer_field").value("integer"))
+          .andExpect(jsonPath("$.snort_field").value("double"))
+          .andExpect(jsonPath("$.duplicate_field").value("long"));
+
+    this.mockMvc.perform(post(searchUrl + "/column/metadata").with(httpBasic(user, password)).with(csrf()).contentType(MediaType.parseMediaType("application/json;charset=UTF-8")).content("[\"someindex\"]"))
+        .andExpect(status().isOk())
+        .andExpect(content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
+        .andExpect(jsonPath("$.*", hasSize(0)));
   }
 
 

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchDao.java b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchDao.java
index 61d5472..87ad7f7 100644
--- a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchDao.java
+++ b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchDao.java
@@ -34,6 +34,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import org.apache.metron.elasticsearch.utils.ElasticsearchUtils;
@@ -88,12 +89,10 @@ public class ElasticsearchDao implements IndexDao {
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   private transient TransportClient client;
   private AccessConfig accessConfig;
-  private List<String> ignoredIndices = new ArrayList<>();
 
   protected ElasticsearchDao(TransportClient client, AccessConfig config) {
     this.client = client;
     this.accessConfig = config;
-    this.ignoredIndices.add(".kibana");
   }
 
   public ElasticsearchDao() {
@@ -166,7 +165,7 @@ public class ElasticsearchDao implements IndexDao {
     if (facetFields.isPresent()) {
       Map<String, FieldType> commonColumnMetadata;
       try {
-        commonColumnMetadata = getCommonColumnMetadata(searchRequest.getIndices());
+        commonColumnMetadata = getColumnMetadata(searchRequest.getIndices());
       } catch (IOException e) {
         throw new InvalidSearchException(String.format("Could not get common column metadata for indices %s", Arrays.toString(searchRequest.getIndices().toArray())));
       }
@@ -211,7 +210,7 @@ public class ElasticsearchDao implements IndexDao {
     }
     Map<String, FieldType> commonColumnMetadata;
     try {
-      commonColumnMetadata = getCommonColumnMetadata(groupRequest.getIndices());
+      commonColumnMetadata = getColumnMetadata(groupRequest.getIndices());
     } catch (IOException e) {
       throw new InvalidSearchException(String
           .format("Could not get common column metadata for indices %s",
@@ -406,72 +405,70 @@ public class ElasticsearchDao implements IndexDao {
 
   @SuppressWarnings("unchecked")
   @Override
-  public Map<String, Map<String, FieldType>> getColumnMetadata(List<String> indices) throws IOException {
-    Map<String, Map<String, FieldType>> allColumnMetadata = new HashMap<>();
-    String[] latestIndices = getLatestIndices(indices);
-    ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> mappings = client
-            .admin()
-            .indices()
-            .getMappings(new GetMappingsRequest().indices(latestIndices))
-            .actionGet()
-            .getMappings();
-    for(Object key: mappings.keys().toArray()) {
-      String indexName = key.toString();
-
-      Map<String, FieldType> indexColumnMetadata = new HashMap<>();
-      ImmutableOpenMap<String, MappingMetaData> mapping = mappings.get(indexName);
-      Iterator<String> mappingIterator = mapping.keysIt();
-      while(mappingIterator.hasNext()) {
-        MappingMetaData mappingMetaData = mapping.get(mappingIterator.next());
-        Map<String, Map<String, String>> map = (Map<String, Map<String, String>>) mappingMetaData.getSourceAsMap().get("properties");
-        for(String field: map.keySet()) {
-          indexColumnMetadata.put(field, elasticsearchSearchTypeMap.getOrDefault(map.get(field).get("type"), FieldType.OTHER));
-        }
-      }
+  public Map<String, FieldType> getColumnMetadata(List<String> indices) throws IOException {
+    Map<String, FieldType> indexColumnMetadata = new HashMap<>();
 
-      String baseIndexName = ElasticsearchUtils.getBaseIndexName(indexName);
-      allColumnMetadata.put(baseIndexName, indexColumnMetadata);
-    }
-    return allColumnMetadata;
-  }
+    // Keep track of the last index used to inspect a field type so we can print a helpful error message on type mismatch
+    Map<String, String> previousIndices = new HashMap<>();
+    // If we have detected a field type mismatch, ignore the field going forward since the type has been set to OTHER
+    Set<String> fieldBlackList = new HashSet<>();
 
-  @SuppressWarnings("unchecked")
-  @Override
-  public Map<String, FieldType> getCommonColumnMetadata(List<String> indices) throws IOException {
-    Map<String, FieldType> commonColumnMetadata = null;
-    ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> mappings =
-            client.admin().indices().getMappings(new GetMappingsRequest().indices(getLatestIndices(indices))).actionGet().getMappings();
-    for(Object index: mappings.keys().toArray()) {
-      ImmutableOpenMap<String, MappingMetaData> mapping = mappings.get(index.toString());
-      Iterator<String> mappingIterator = mapping.keysIt();
-      while(mappingIterator.hasNext()) {
-        MappingMetaData mappingMetaData = mapping.get(mappingIterator.next());
-        Map<String, Map<String, String>> map = (Map<String, Map<String, String>>) mappingMetaData.getSourceAsMap().get("properties");
-        Map<String, FieldType> mappingsWithTypes = map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
-                e-> elasticsearchSearchTypeMap.getOrDefault(e.getValue().get("type"), FieldType.OTHER)));
-        if (commonColumnMetadata == null) {
-          commonColumnMetadata = mappingsWithTypes;
-        } else {
-          commonColumnMetadata.entrySet().retainAll(mappingsWithTypes.entrySet());
+    String[] latestIndices = getLatestIndices(indices);
+    if (latestIndices.length > 0) {
+      ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> mappings = client
+          .admin()
+          .indices()
+          .getMappings(new GetMappingsRequest().indices(latestIndices))
+          .actionGet()
+          .getMappings();
+      for (Object key : mappings.keys().toArray()) {
+        String indexName = key.toString();
+        ImmutableOpenMap<String, MappingMetaData> mapping = mappings.get(indexName);
+        Iterator<String> mappingIterator = mapping.keysIt();
+        while (mappingIterator.hasNext()) {
+          MappingMetaData mappingMetaData = mapping.get(mappingIterator.next());
+          Map<String, Map<String, String>> map = (Map<String, Map<String, String>>) mappingMetaData
+              .getSourceAsMap().get("properties");
+          for (String field : map.keySet()) {
+            if (!fieldBlackList.contains(field)) {
+              FieldType type = elasticsearchSearchTypeMap
+                  .getOrDefault(map.get(field).get("type"), FieldType.OTHER);
+              if (indexColumnMetadata.containsKey(field)) {
+                FieldType previousType = indexColumnMetadata.get(field);
+                if (!type.equals(previousType)) {
+                  String previousIndexName = previousIndices.get(field);
+                  LOG.error(String.format(
+                      "Field type mismatch: %s.%s has type %s while %s.%s has type %s.  Defaulting type to %s.",
+                      indexName, field, type.getFieldType(),
+                      previousIndexName, field, previousType.getFieldType(),
+                      FieldType.OTHER.getFieldType()));
+                  indexColumnMetadata.put(field, FieldType.OTHER);
+                  // Detected a type mismatch so ignore the field from now on
+                  fieldBlackList.add(field);
+                }
+              } else {
+                indexColumnMetadata.put(field, type);
+                previousIndices.put(field, indexName);
+              }
+            }
+          }
         }
       }
     }
-    return commonColumnMetadata;
+    return indexColumnMetadata;
   }
 
   protected String[] getLatestIndices(List<String> includeIndices) {
     Map<String, String> latestIndices = new HashMap<>();
     String[] indices = client.admin().indices().prepareGetIndex().setFeatures().get().getIndices();
     for (String index : indices) {
-      if (!ignoredIndices.contains(index)) {
-        int prefixEnd = index.indexOf(INDEX_NAME_DELIMITER);
-        if (prefixEnd != -1) {
-          String prefix = index.substring(0, prefixEnd);
-          if (includeIndices.contains(prefix)) {
-            String latestIndex = latestIndices.get(prefix);
-            if (latestIndex == null || index.compareTo(latestIndex) > 0) {
-              latestIndices.put(prefix, index);
-            }
+      int prefixEnd = index.indexOf(INDEX_NAME_DELIMITER);
+      if (prefixEnd != -1) {
+        String prefix = index.substring(0, prefixEnd);
+        if (includeIndices.contains(prefix)) {
+          String latestIndex = latestIndices.get(prefix);
+          if (latestIndex == null || index.compareTo(latestIndex) > 0) {
+            latestIndices.put(prefix, index);
           }
         }
       }

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDao.java b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDao.java
index c24ba0c..90d5410 100644
--- a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDao.java
+++ b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDao.java
@@ -574,18 +574,12 @@ public class ElasticsearchMetaAlertDao implements MetaAlertDao {
   }
 
   @Override
-  public Map<String, Map<String, FieldType>> getColumnMetadata(List<String> indices)
+  public Map<String, FieldType> getColumnMetadata(List<String> indices)
       throws IOException {
     return indexDao.getColumnMetadata(indices);
   }
 
   @Override
-  public Map<String, FieldType> getCommonColumnMetadata(List<String> indices) throws
-      IOException {
-    return indexDao.getCommonColumnMetadata(indices);
-  }
-
-  @Override
   public GroupResponse group(GroupRequest groupRequest) throws InvalidSearchException {
     // Wrap the query to hide any alerts already contained in meta alerts
     QueryBuilder qb = QueryBuilders.boolQuery()

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDaoTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDaoTest.java b/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDaoTest.java
index a1027f7..ffafe52 100644
--- a/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDaoTest.java
+++ b/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDaoTest.java
@@ -87,13 +87,7 @@ public class ElasticsearchMetaAlertDaoTest {
       }
 
       @Override
-      public Map<String, Map<String, FieldType>> getColumnMetadata(List<String> indices)
-          throws IOException {
-        return null;
-      }
-
-      @Override
-      public Map<String, FieldType> getCommonColumnMetadata(List<String> indices)
+      public Map<String, FieldType> getColumnMetadata(List<String> indices)
           throws IOException {
         return null;
       }

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/integration/ElasticsearchSearchIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/integration/ElasticsearchSearchIntegrationTest.java b/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/integration/ElasticsearchSearchIntegrationTest.java
index e7b609e..07cc708 100644
--- a/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/integration/ElasticsearchSearchIntegrationTest.java
+++ b/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/integration/ElasticsearchSearchIntegrationTest.java
@@ -93,6 +93,19 @@ public class ElasticsearchSearchIntegrationTest extends SearchIntegrationTest {
   @Multiline
   private static String snortTypeMappings;
 
+  /**
+   * {
+   * "metaalert_doc": {
+   *   "properties": {
+   *     "source:type": { "type": "string" },
+   *     "alert": { "type": "nested"}
+   *   }
+   * }
+   * }
+   */
+  @Multiline
+  private static String metaAlertTypeMappings;
+
 
   @Override
   protected IndexDao createDao() throws Exception {
@@ -134,6 +147,8 @@ public class ElasticsearchSearchIntegrationTest extends SearchIntegrationTest {
             .addMapping("bro_doc", broTypeMappings).get();
     es.getClient().admin().indices().prepareCreate("snort_index_2017.01.01.02")
             .addMapping("snort_doc", snortTypeMappings).get();
+    es.getClient().admin().indices().prepareCreate("metaalert_index")
+        .addMapping("metaalert_doc", metaAlertTypeMappings).get();
 
     BulkRequestBuilder bulkRequest = es.getClient().prepareBulk().setRefresh(true);
     JSONArray broArray = (JSONArray) new JSONParser().parse(broData);

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/HBaseDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/HBaseDao.java b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/HBaseDao.java
index 3103ea7..72f2980 100644
--- a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/HBaseDao.java
+++ b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/HBaseDao.java
@@ -260,12 +260,7 @@ public class HBaseDao implements IndexDao {
 
 
   @Override
-  public Map<String, Map<String, FieldType>> getColumnMetadata(List<String> indices) throws IOException {
-    return null;
-  }
-
-  @Override
-  public Map<String, FieldType> getCommonColumnMetadata(List<String> indices) throws IOException {
+  public Map<String, FieldType> getColumnMetadata(List<String> indices) throws IOException {
     return null;
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/IndexDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/IndexDao.java b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/IndexDao.java
index 8855a14..03d348a 100644
--- a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/IndexDao.java
+++ b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/IndexDao.java
@@ -167,6 +167,5 @@ public interface IndexDao {
     update(d, Optional.ofNullable(request.getIndex()));
   }
 
-  Map<String, Map<String, FieldType>> getColumnMetadata(List<String> indices) throws IOException;
-  Map<String, FieldType> getCommonColumnMetadata(List<String> indices) throws IOException;
+  Map<String, FieldType> getColumnMetadata(List<String> indices) throws IOException;
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/MultiIndexDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/MultiIndexDao.java b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/MultiIndexDao.java
index ed8bc95..dad08d6 100644
--- a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/MultiIndexDao.java
+++ b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/MultiIndexDao.java
@@ -88,20 +88,9 @@ public class MultiIndexDao implements IndexDao {
   }
 
   @Override
-  public Map<String, Map<String, FieldType>> getColumnMetadata(List<String> in) throws IOException {
+  public Map<String, FieldType> getColumnMetadata(List<String> in) throws IOException {
     for(IndexDao dao : indices) {
-      Map<String, Map<String, FieldType>> r = dao.getColumnMetadata(in);
-      if(r != null) {
-        return r;
-      }
-    }
-    return null;
-  }
-
-  @Override
-  public Map<String, FieldType> getCommonColumnMetadata(List<String> in) throws IOException {
-    for(IndexDao dao : indices) {
-      Map<String, FieldType> r = dao.getCommonColumnMetadata(in);
+      Map<String, FieldType> r = dao.getColumnMetadata(in);
       if(r != null) {
         return r;
       }

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/InMemoryDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/InMemoryDao.java b/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/InMemoryDao.java
index 3bce4d0..f2108de 100644
--- a/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/InMemoryDao.java
+++ b/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/InMemoryDao.java
@@ -17,13 +17,11 @@
  */
 package org.apache.metron.indexing.dao;
 
-import static org.apache.metron.common.Constants.SENSOR_TYPE;
-
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.google.common.base.Splitter;
 import com.google.common.collect.ComparisonChain;
 import com.google.common.collect.Iterables;
-import java.util.stream.Collectors;
+import java.util.Map.Entry;
 import org.apache.metron.common.Constants;
 import org.apache.metron.common.utils.JSONUtils;
 import org.apache.metron.indexing.dao.search.*;
@@ -242,25 +240,26 @@ public class InMemoryDao implements IndexDao {
     }
   }
 
-  public Map<String, Map<String, FieldType>> getColumnMetadata(List<String> indices) throws IOException {
-    Map<String, Map<String, FieldType>> columnMetadata = new HashMap<>();
-    for(String index: indices) {
-      columnMetadata.put(index, new HashMap<>(COLUMN_METADATA.get(index)));
-    }
-    return columnMetadata;
-  }
-
   @Override
-  public Map<String, FieldType> getCommonColumnMetadata(List<String> indices) throws IOException {
-    Map<String, FieldType> commonColumnMetadata = new HashMap<>();
+  public Map<String, FieldType> getColumnMetadata(List<String> indices) throws IOException {
+    Map<String, FieldType> indexColumnMetadata = new HashMap<>();
     for(String index: indices) {
-      if (commonColumnMetadata.isEmpty()) {
-        commonColumnMetadata = new HashMap<>(COLUMN_METADATA.get(index));
-      } else {
-        commonColumnMetadata.entrySet().retainAll(COLUMN_METADATA.get(index).entrySet());
+      if (COLUMN_METADATA.containsKey(index)) {
+        Map<String, FieldType> columnMetadata = COLUMN_METADATA.get(index);
+        for (Entry entry: columnMetadata.entrySet()) {
+          String field = (String) entry.getKey();
+          FieldType type = (FieldType) entry.getValue();
+          if (indexColumnMetadata.containsKey(field)) {
+            if (!type.equals(indexColumnMetadata.get(field))) {
+              indexColumnMetadata.put(field, FieldType.OTHER);
+            }
+          } else {
+            indexColumnMetadata.put(field, type);
+          }
+        }
       }
     }
-    return commonColumnMetadata;
+    return indexColumnMetadata;
   }
 
   public static void setColumnMetadata(Map<String, Map<String, FieldType>> columnMetadata) {

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/InMemoryMetaAlertDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/InMemoryMetaAlertDao.java b/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/InMemoryMetaAlertDao.java
index fad0eda..baa5416 100644
--- a/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/InMemoryMetaAlertDao.java
+++ b/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/InMemoryMetaAlertDao.java
@@ -117,17 +117,12 @@ public class InMemoryMetaAlertDao implements MetaAlertDao {
   }
 
   @Override
-  public Map<String, Map<String, FieldType>> getColumnMetadata(List<String> indices)
+  public Map<String, FieldType> getColumnMetadata(List<String> indices)
       throws IOException {
     return indexDao.getColumnMetadata(indices);
   }
 
   @Override
-  public Map<String, FieldType> getCommonColumnMetadata(List<String> indices) throws IOException {
-    return indexDao.getCommonColumnMetadata(indices);
-  }
-
-  @Override
   public Optional<Map<String, Object>> getLatestResult(GetRequest request) throws IOException {
     return indexDao.getLatestResult(request);
   }

http://git-wip-us.apache.org/repos/asf/metron/blob/768a6fad/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/SearchIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/SearchIntegrationTest.java b/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/SearchIntegrationTest.java
index d991d50..8f32946 100644
--- a/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/SearchIntegrationTest.java
+++ b/metron-platform/metron-indexing/src/test/java/org/apache/metron/indexing/dao/SearchIntegrationTest.java
@@ -190,7 +190,7 @@ public abstract class SearchIntegrationTest {
   /**
    * {
    * "facetFields": ["source:type", "ip_src_addr", "ip_src_port", "long_field", "timestamp", "latitude", "score", "is_alert"],
-   * "indices": ["bro", "snort"],
+   * "indices": ["bro", "snort", "metaalert"],
    * "query": "*",
    * "from": 0,
    * "size": 10,
@@ -323,7 +323,7 @@ public abstract class SearchIntegrationTest {
    *   }
    * ],
    * "scoreField":"score",
-   * "indices": ["bro", "snort"],
+   * "indices": ["bro", "snort", "metaalert"],
    * "query": "*"
    * }
    */
@@ -348,7 +348,7 @@ public abstract class SearchIntegrationTest {
    *     }
    *   }
    * ],
-   * "indices": ["bro", "snort"],
+   * "indices": ["bro", "snort", "metaalert"],
    * "query": "*"
    * }
    */
@@ -369,6 +369,24 @@ public abstract class SearchIntegrationTest {
   @Multiline
   public static String badGroupQuery;
 
+  /**
+   * {
+   * "groups": [
+   *   {
+   *     "field":"ip_src_addr",
+   *     "order": {
+   *       "groupOrderType": "term",
+   *       "sortOrder": "DESC"
+   *     }
+   *   }
+   * ],
+   * "indices": ["bro", "snort"],
+   * "query": "*"
+   * }
+   */
+  @Multiline
+  public static String groupByIpQuery;
+
   protected static IndexDao dao;
   protected static InMemoryComponent indexComponent;
 
@@ -572,58 +590,9 @@ public abstract class SearchIntegrationTest {
     }
     // getColumnMetadata with multiple indices
     {
-      Map<String, Map<String, FieldType>> fieldTypes = dao.getColumnMetadata(Arrays.asList("bro", "snort"));
-      Assert.assertEquals(2, fieldTypes.size());
-      Map<String, FieldType> broTypes = fieldTypes.get("bro");
-      Assert.assertEquals(12, broTypes.size());
-      Assert.assertEquals(FieldType.STRING, broTypes.get("source:type"));
-      Assert.assertEquals(FieldType.IP, broTypes.get("ip_src_addr"));
-      Assert.assertEquals(FieldType.INTEGER, broTypes.get("ip_src_port"));
-      Assert.assertEquals(FieldType.LONG, broTypes.get("long_field"));
-      Assert.assertEquals(FieldType.DATE, broTypes.get("timestamp"));
-      Assert.assertEquals(FieldType.FLOAT, broTypes.get("latitude"));
-      Assert.assertEquals(FieldType.DOUBLE, broTypes.get("score"));
-      Assert.assertEquals(FieldType.BOOLEAN, broTypes.get("is_alert"));
-      Assert.assertEquals(FieldType.OTHER, broTypes.get("location_point"));
-      Assert.assertEquals(FieldType.STRING, broTypes.get("bro_field"));
-      Assert.assertEquals(FieldType.STRING, broTypes.get("duplicate_name_field"));
-      Assert.assertEquals(FieldType.STRING, broTypes.get("guid"));
-      Map<String, FieldType> snortTypes = fieldTypes.get("snort");
-      Assert.assertEquals(12, snortTypes.size());
-      Assert.assertEquals(FieldType.STRING, snortTypes.get("source:type"));
-      Assert.assertEquals(FieldType.IP, snortTypes.get("ip_src_addr"));
-      Assert.assertEquals(FieldType.INTEGER, snortTypes.get("ip_src_port"));
-      Assert.assertEquals(FieldType.LONG, snortTypes.get("long_field"));
-      Assert.assertEquals(FieldType.DATE, snortTypes.get("timestamp"));
-      Assert.assertEquals(FieldType.FLOAT, snortTypes.get("latitude"));
-      Assert.assertEquals(FieldType.DOUBLE, snortTypes.get("score"));
-      Assert.assertEquals(FieldType.BOOLEAN, snortTypes.get("is_alert"));
-      Assert.assertEquals(FieldType.OTHER, snortTypes.get("location_point"));
-      Assert.assertEquals(FieldType.INTEGER, snortTypes.get("snort_field"));
-      Assert.assertEquals(FieldType.INTEGER, snortTypes.get("duplicate_name_field"));
-      Assert.assertEquals(FieldType.STRING, broTypes.get("guid"));
-    }
-    // getColumnMetadata with only bro
-    {
-      Map<String, Map<String, FieldType>> fieldTypes = dao.getColumnMetadata(Collections.singletonList("bro"));
-      Assert.assertEquals(1, fieldTypes.size());
-      Map<String, FieldType> broTypes = fieldTypes.get("bro");
-      Assert.assertEquals(12, broTypes.size());
-      Assert.assertEquals(FieldType.STRING, broTypes.get("bro_field"));
-    }
-    // getColumnMetadata with only snort
-    {
-      Map<String, Map<String, FieldType>> fieldTypes = dao.getColumnMetadata(Collections.singletonList("snort"));
-      Assert.assertEquals(1, fieldTypes.size());
-      Map<String, FieldType> snortTypes = fieldTypes.get("snort");
-      Assert.assertEquals(12, snortTypes.size());
-      Assert.assertEquals(FieldType.INTEGER, snortTypes.get("snort_field"));
-    }
-    // getCommonColumnMetadata with multiple Indices
-    {
-      Map<String, FieldType> fieldTypes = dao.getCommonColumnMetadata(Arrays.asList("bro", "snort"));
-      // Should only return fields in both
-      Assert.assertEquals(10, fieldTypes.size());
+      Map<String, FieldType> fieldTypes = dao.getColumnMetadata(Arrays.asList("bro", "snort"));
+      Assert.assertEquals(13, fieldTypes.size());
+      Assert.assertEquals(FieldType.STRING, fieldTypes.get("guid"));
       Assert.assertEquals(FieldType.STRING, fieldTypes.get("source:type"));
       Assert.assertEquals(FieldType.IP, fieldTypes.get("ip_src_addr"));
       Assert.assertEquals(FieldType.INTEGER, fieldTypes.get("ip_src_port"));
@@ -633,21 +602,26 @@ public abstract class SearchIntegrationTest {
       Assert.assertEquals(FieldType.DOUBLE, fieldTypes.get("score"));
       Assert.assertEquals(FieldType.BOOLEAN, fieldTypes.get("is_alert"));
       Assert.assertEquals(FieldType.OTHER, fieldTypes.get("location_point"));
-      Assert.assertEquals(FieldType.STRING, fieldTypes.get("guid"));
+      Assert.assertEquals(FieldType.STRING, fieldTypes.get("bro_field"));
+      Assert.assertEquals(FieldType.INTEGER, fieldTypes.get("snort_field"));
+      Assert.assertEquals(FieldType.OTHER, fieldTypes.get("duplicate_name_field"));
     }
-    // getCommonColumnMetadata with only bro
+    // getColumnMetadata with only bro
     {
-      Map<String, FieldType> fieldTypes = dao.getCommonColumnMetadata(Collections.singletonList("bro"));
+      Map<String, FieldType> fieldTypes = dao.getColumnMetadata(Collections.singletonList("bro"));
       Assert.assertEquals(12, fieldTypes.size());
       Assert.assertEquals(FieldType.STRING, fieldTypes.get("bro_field"));
-      Assert.assertEquals(FieldType.STRING, fieldTypes.get("duplicate_name_field"));
     }
-    // getCommonColumnMetadata with only snort
+    // getColumnMetadata with only snort
     {
-      Map<String, FieldType> fieldTypes = dao.getCommonColumnMetadata(Collections.singletonList("snort"));
+      Map<String, FieldType> fieldTypes = dao.getColumnMetadata(Collections.singletonList("snort"));
       Assert.assertEquals(12, fieldTypes.size());
       Assert.assertEquals(FieldType.INTEGER, fieldTypes.get("snort_field"));
-      Assert.assertEquals(FieldType.INTEGER, fieldTypes.get("duplicate_name_field"));
+    }
+    // getColumnMetadata with an index that doesn't exist
+    {
+      Map<String, FieldType> fieldTypes = dao.getColumnMetadata(Collections.singletonList("someindex"));
+      Assert.assertEquals(0, fieldTypes.size());
     }
     //Fields query
     {
@@ -701,7 +675,6 @@ public abstract class SearchIntegrationTest {
       List<GroupResult> trueLatitudeGroups = trueGroup.getGroupResults();
       Assert.assertEquals(2, trueLatitudeGroups.size());
 
-
       // isAlert == true && latitude == 48.5839 group
       GroupResult trueLatitudeGroup2 = trueLatitudeGroups.get(0);
       Assert.assertEquals(48.5839, Double.parseDouble(trueLatitudeGroup2.getKey()), 0.00001);
@@ -823,6 +796,32 @@ public abstract class SearchIntegrationTest {
         Assert.assertEquals("Could not execute search", ise.getMessage());
       }
     }
+    //Group by IP query
+    {
+      {
+        GroupRequest request = JSONUtils.INSTANCE.load(groupByIpQuery, GroupRequest.class);
+        GroupResponse response = dao.group(request);
+
+        // expect only 1 group for 'ip_src_addr'
+        Assert.assertEquals("ip_src_addr", response.getGroupedBy());
+
+        // there are 8 different 'ip_src_addr' values
+        List<GroupResult> groups = response.getGroupResults();
+        Assert.assertEquals(8, groups.size());
+
+        // expect dotted-decimal notation in descending order
+        Assert.assertEquals("192.168.1.8", groups.get(0).getKey());
+        Assert.assertEquals("192.168.1.7", groups.get(1).getKey());
+        Assert.assertEquals("192.168.1.6", groups.get(2).getKey());
+        Assert.assertEquals("192.168.1.5", groups.get(3).getKey());
+        Assert.assertEquals("192.168.1.4", groups.get(4).getKey());
+        Assert.assertEquals("192.168.1.3", groups.get(5).getKey());
+        Assert.assertEquals("192.168.1.2", groups.get(6).getKey());
+        Assert.assertEquals("192.168.1.1", groups.get(7).getKey());
+      }
+
+
+    }
   }
 
   @AfterClass