org.icgc.dcc.portal.util.ElasticsearchResponseUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.icgc.dcc.portal.util.ElasticsearchResponseUtils.java

Source

/*
 * Copyright (c) 2014 The Ontario Institute for Cancer Research. All rights reserved.                             
 *                                                                                                               
 * This program and the accompanying materials are made available under the terms of the GNU Public License v3.0.
 * You should have received a copy of the GNU General Public License along with                                  
 * this program. If not, see <http://www.gnu.org/licenses/>.                                                     
 *                                                                                                               
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY                           
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES                          
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT                           
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,                                
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED                          
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;                               
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER                              
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN                         
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.icgc.dcc.portal.util;

import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
import static java.util.Collections.emptyMap;
import static javax.ws.rs.core.Response.status;
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
import static lombok.AccessLevel.PRIVATE;
import static org.icgc.dcc.portal.model.IndexModel.FIELDS_MAPPING;

import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.ws.rs.WebApplicationException;

import lombok.NoArgsConstructor;
import lombok.val;
import lombok.extern.slf4j.Slf4j;

import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.index.get.GetField;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHitField;
import org.icgc.dcc.portal.model.IndexModel.Kind;
import org.icgc.dcc.portal.model.Query;

import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.primitives.Longs;

/**
 * Provides methods to retrieve values from SearchHit
 */
@Slf4j
@NoArgsConstructor(access = PRIVATE)
public final class ElasticsearchResponseUtils {

    /**
     * Returns first value of the list as a String
     */
    public static String getString(Object values) {
        if (values == null) {
            return null;
        }

        if (values instanceof Iterable<?>) {
            val iterable = (Iterable<?>) values;
            return Iterables.isEmpty(iterable) ? null : Iterables.get(iterable, 0).toString();
        }

        if (values instanceof String) {
            return values.toString();
        }

        return null;
    }

    public static List<String> toStringList(Object value) {
        if (null == value) {
            return null;
        }

        if (value instanceof List<?>) {
            // There might be a performance issue here. In the event that we already know 'value' is a List<String>, maybe we
            // should just cast it to List<String> directly. Alternatively, we could check the size of the list then,
            // depending on the size, either directly cast it for a big collection or transform it for a small collection.
            return newArrayList(transform((List<?>) value, v -> v.toString()));
        }

        if (value instanceof Iterable<?>) {
            return newArrayList(transform((Iterable<?>) value, v -> v.toString()));
        }

        return newArrayList(value.toString());
    }

    public static Long getLong(Object value) {
        if (null == value) {
            return null;
        }

        if (value instanceof Long) {
            return (Long) value;
        }

        if (value instanceof Float) {
            return ((Float) value).longValue();
        }

        if (value instanceof Integer) {
            return Long.valueOf((Integer) value);
        }

        if (value instanceof Iterable<?>) {
            val iterable = (Iterable<?>) value;
            return Iterables.isEmpty(iterable) ? null : Longs.tryParse(Iterables.get(iterable, 0).toString());
        }

        return Longs.tryParse(value.toString());
    }

    public static Boolean getBoolean(Object values) {
        val defaultValue = false;

        if (values == null) {
            return defaultValue;
        }

        if (values instanceof Boolean) {
            return (Boolean) values;
        }

        if (values instanceof Iterable<?>) {
            val iterable = (Iterable<?>) values;
            return Iterables.isEmpty(iterable) ? defaultValue : Boolean.TRUE.equals(Iterables.get(iterable, 0));
        }

        return defaultValue;
    }

    private static void processConsequences(Map<String, Object> map, Query query) {
        if (query.hasInclude("consequences")) {
            log.debug("Copying transcripts to consequences...");
            map.put("consequences", map.get("transcript"));
            if (!query.hasInclude("transcripts")) {
                log.debug("Removing transcripts...");
                map.remove("transcript");
            }
        }
    }

    public static Map<String, Object> createMapFromSearchFields(Map<String, SearchHitField> fields) {
        val result = Maps.<String, Object>newHashMap();
        for (val field : fields.entrySet()) {
            result.put(field.getKey(), field.getValue().getValues());
        }

        return result;
    }

    public static Map<String, Object> createMapFromGetFields(Map<String, GetField> fields) {
        val result = Maps.<String, Object>newHashMap();
        for (val field : fields.entrySet()) {
            result.put(field.getKey(), field.getValue().getValues());
        }

        return result;
    }

    public static Map<String, Object> createResponseMap(GetResponse response, Query query, Kind kind) {
        val map = createMapFromGetFields(response.getFields());
        map.putAll(processSource(response.getSource(), query, kind));

        return map;
    }

    public static Map<String, Object> createResponseMap(SearchHit response, Query query, Kind kind) {
        val map = createMapFromSearchFields(response.getFields());
        map.putAll(processSource(response.getSource(), query, kind));

        return map;
    }

    public static void checkResponseState(String id, GetResponse response, Kind kind) {
        if (!response.isExists()) {
            val type = kind.getId().substring(0, 1).toUpperCase() + kind.getId().substring(1);
            log.info("{} {} not found.", type, id);

            val message = format("{\"code\": 404, \"message\":\"%s %s not found.\"}", type, id);
            throw new WebApplicationException(status(NOT_FOUND).entity(message).build());
        }
    }

    public static GetResponse sanityCheck(GetResponse response, String indexType, String id) {
        if (response.isExists()) {
            return response;
        }

        log.info("ID {} was NOT found in {} index type.", id, indexType);

        val message = format("{\"code\": 404, \"message\":\"ID %s was NOT found.\"}", id);
        throw new WebApplicationException(status(NOT_FOUND).entity(message).build());
    }

    private static Map<String, Object> processSource(Map<String, Object> source, Query query, Kind kind) {
        if (source == null) {
            return emptyMap();
        }

        val result = flatternMap(source, query, kind);
        processConsequences(result, query);

        return result;
    }

    public static Map<String, Object> flatternMap(Map<String, Object> source) {
        if (source == null) {
            return emptyMap();
        }

        return flatternMap(Optional.empty(), source, null, null);
    }

    public static Map<String, Object> flatternMap(Map<String, Object> source, Query query, Kind kind) {
        if (source == null) {
            return emptyMap();
        }

        return flatternMap(Optional.empty(), source, kind, query);
    }

    @SuppressWarnings("unchecked")
    private static Map<String, Object> flatternMap(Optional<String> prefix, Map<String, Object> source, Kind kind,
            Query query) {
        val result = Maps.<String, Object>newHashMap();

        for (val entry : source.entrySet()) {
            val fieldName = resolvePrefix(prefix, entry.getKey());
            if (entry.getValue() instanceof Map && !isSkip(fieldName, query, kind)) {
                result.putAll(flatternMap(Optional.of(entry.getKey()), (Map<String, Object>) entry.getValue(), kind,
                        query));
            } else {
                result.put(fieldName, entry.getValue());
            }
        }

        return result;
    }

    /**
     * Some fields are maps and the client expects them to be a map. This methods resolves those maps and prevents them
     * from been 'flattern' further
     */
    private static boolean isSkip(String fieldName, Query query, Kind kind) {
        if (kind == null || query == null) {
            return false;
        }

        return (FIELDS_MAPPING.get(kind).containsValue(fieldName) || query.hasInclude(fieldName));
    }

    private static String resolvePrefix(Optional<String> prefix, String field) {
        return prefix.isPresent() ? format("%s.%s", prefix.get(), field) : field;
    }
}