protected Map<String, Map<String, Object>> fieldProperties(final String analyzer,
                                                               final CustomFieldMappings customFieldMappings) {
        final ImmutableMap.Builder<String, Map<String, Object>> builder = ImmutableMap.<String, Map<String, Object>>builder()
                .put(Message.FIELD_MESSAGE, analyzedString(analyzer, false))
                // http://joda-time.sourceforge.net/api-release/org/joda/time/format/DateTimeFormat.html
                // http://www.elasticsearch.org/guide/reference/mapping/date-format.html
                .put(Message.FIELD_TIMESTAMP, typeTimeWithMillis())
                .put(Message.FIELD_GL2_ACCOUNTED_MESSAGE_SIZE, typeLong())
                .put(Message.FIELD_GL2_RECEIVE_TIMESTAMP, typeTimeWithMillis())
                .put(Message.FIELD_GL2_PROCESSING_TIMESTAMP, typeTimeWithMillis())
                .put(Message.FIELD_GL2_PROCESSING_DURATION_MS, typeInteger())
                .put(FIELD_GL2_MESSAGE_ID, notAnalyzedString())
                .put(Message.GL2_SECOND_SORT_FIELD, aliasTo(FIELD_GL2_MESSAGE_ID))
                .put(Message.FIELD_STREAMS, notAnalyzedString())
                // to support wildcard searches in source we need to lowercase the content (wildcard search lowercases search term)
                .put(Message.FIELD_SOURCE, analyzedString("analyzer_keyword", true));


        if (customFieldMappings != null) {
            customFieldMappings.stream()
                    .filter(customMapping -> !FIELDS_UNCHANGEABLE_BY_CUSTOM_MAPPINGS.contains(customMapping.fieldName())) //someone might have hardcoded reserved field mapping on MongoDB level, bypassing checks
                    .forEach(customMapping -> builder.put(customMapping.fieldName(), type(customMapping.toPhysicalType())));
        }

        //those FIELD_FULL_MESSAGE field have not been yet made reserved, so it can be added to ImmutableMap only if they do not exist in Custom Mapping
        if (customFieldMappings == null || !customFieldMappings.containsCustomMappingForField(Message.FIELD_FULL_MESSAGE)) {
            builder.put(Message.FIELD_FULL_MESSAGE, analyzedString(analyzer, false));
        }

        return builder.build();
    }
@Test
    void doesNotAllowOverridingBlacklistedFieldsWithCustomMapping() {
        IndexMapping indexMapping = new IndexMapping7();
        for (String blackListedField : FIELDS_UNCHANGEABLE_BY_CUSTOM_MAPPINGS) {
            final Map<String, Map<String, Object>> fieldProperties = indexMapping.fieldProperties("english", new CustomFieldMappings(List.of(new CustomFieldMapping(blackListedField, "geo-point"))));
            final Map<String, Object> forBlackListedField = fieldProperties.get(blackListedField);
            assertTrue(forBlackListedField == null || !forBlackListedField.get("type").equals("geo_point"));
        }
    }