org.exoplatform.social.addons.search.PeopleElasticUnifiedSearchServiceConnector.java Source code

Java tutorial

Introduction

Here is the source code for org.exoplatform.social.addons.search.PeopleElasticUnifiedSearchServiceConnector.java

Source

/* 
* Copyright (C) 2003-2016 eXo Platform SAS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see http://www.gnu.org/licenses/ .
*/
package org.exoplatform.social.addons.search;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import org.exoplatform.addons.es.client.ElasticSearchingClient;
import org.exoplatform.addons.es.search.ElasticSearchException;
import org.exoplatform.addons.es.search.ElasticSearchFilter;
import org.exoplatform.addons.es.search.ElasticSearchServiceConnector;
import org.exoplatform.commons.api.search.data.SearchContext;
import org.exoplatform.commons.api.search.data.SearchResult;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.social.core.identity.model.Identity;
import org.exoplatform.social.core.identity.model.Profile;
import org.exoplatform.social.core.manager.IdentityManager;
import org.exoplatform.social.core.service.LinkProvider;

public class PeopleElasticUnifiedSearchServiceConnector extends ElasticSearchServiceConnector {

    private static final Log LOG = ExoLogger.getLogger(PeopleElasticUnifiedSearchServiceConnector.class);

    private IdentityManager identityManager;

    private Map<String, String> sortMapping;

    public PeopleElasticUnifiedSearchServiceConnector(InitParams initParams, ElasticSearchingClient client,
            IdentityManager identityManager) {
        super(initParams, client);
        this.identityManager = identityManager;

        sortMapping = new HashMap<>();
        sortMapping.put("date", "lastUpdatedDate");
        sortMapping.put("title", "name");
    }

    @Override
    protected String getSourceFields() {
        List<String> fields = new ArrayList<>();
        fields.add("name");
        fields.add("firstName");
        fields.add("lastName");
        fields.add("position");
        fields.add("skills");

        List<String> sourceFields = new ArrayList<>();
        for (String sourceField : fields) {
            sourceFields.add("\"" + sourceField + "\"");
        }

        return StringUtils.join(sourceFields, ",");
    }

    protected String buildFilteredQuery(String query, Collection<String> sites, List<ElasticSearchFilter> filters,
            int offset, int limit, String sort, String order) {
        StringBuilder esQuery = new StringBuilder();
        esQuery.append("{\n");
        esQuery.append("     \"from\" : " + offset + ", \"size\" : " + limit + ",\n");

        //Score are always tracked, even with sort
        //https://www.impl.co/guide/en/elasticsearch/reference/current/search-request-sort.html#_track_scores
        esQuery.append("     \"track_scores\": true,\n");
        esQuery.append("     \"sort\" : [\n");
        esQuery.append("       { \""
                + (StringUtils.isNotBlank(sortMapping.get(sort)) ? sortMapping.get(sort) : "_score") + "\" : ");
        esQuery.append("{\"order\" : \"" + (StringUtils.isNotBlank(order) ? order : "desc") + "\"}}\n");
        esQuery.append("     ],\n");
        esQuery.append("     \"_source\": [" + getSourceFields() + "],");
        esQuery.append("     \"query\": {\n");
        esQuery.append("        \"filtered\" : {\n");
        esQuery.append("            \"query\" : {\n");
        esQuery.append("                \"query_string\" : {\n");
        esQuery.append("                    \"fields\" : [" + getFields() + "],\n");
        esQuery.append("                    \"query\" : \"" + query + "\"\n");
        esQuery.append("                }\n");
        esQuery.append("            }\n");
        esQuery.append("        }\n");
        esQuery.append("     },\n");
        esQuery.append("     \"highlight\" : {\n");
        esQuery.append("       \"pre_tags\" : [\"<strong>\"],\n");
        esQuery.append("       \"post_tags\" : [\"</strong>\"],\n");
        esQuery.append("       \"fields\" : {\n");
        for (int i = 0; i < getSearchFields().size(); i++) {
            esQuery.append("         \"" + getSearchFields().get(i)
                    + "\" : {\"fragment_size\" : 150, \"number_of_fragments\" : 3}");
            if (i < this.getSearchFields().size() - 1) {
                esQuery.append(",");
            }
            esQuery.append("\n");
        }
        esQuery.append("       }\n");
        esQuery.append("     }\n");
        esQuery.append("}");

        LOG.debug("Search Query request to ES : {} ", esQuery);

        return esQuery.toString();
    }

    protected Collection<SearchResult> buildResult(String jsonResponse, SearchContext context) {

        LOG.debug("Search Query response from ES : {} ", jsonResponse);

        Collection<SearchResult> results = new ArrayList<>();
        JSONParser parser = new JSONParser();

        Map json;
        try {
            json = (Map) parser.parse(jsonResponse);
        } catch (ParseException e) {
            throw new ElasticSearchException("Unable to parse JSON response", e);
        }

        // TODO check if response is successful
        JSONObject jsonResult = (JSONObject) json.get("hits");
        JSONArray jsonHits = (JSONArray) jsonResult.get("hits");

        for (Object jsonHit : jsonHits) {
            Identity identity = identityManager.getIdentity(((JSONObject) jsonHit).get("_id").toString(), true);
            Profile profile = identity.getProfile();
            if (identity.isDeleted()) {
                continue;
            }

            Double score = (Double) ((JSONObject) jsonHit).get("_score");

            //
            StringBuilder sb = new StringBuilder();

            //
            if (profile.getEmail() != null) {
                sb.append(profile.getEmail());
            }

            //
            List<Map> phones = (List<Map>) profile.getProperty(Profile.CONTACT_PHONES);
            if (phones != null && phones.size() > 0) {
                sb.append(" - " + phones.get(0).get("value"));
            }

            //
            if (profile.getProperty(Profile.GENDER) != null) {
                sb.append(" - " + profile.getProperty(Profile.GENDER));
            }

            results.add(
                    new SearchResult(profile.getUrl(), profile.getFullName(), profile.getPosition(), sb.toString(),
                            profile.getAvatarUrl() != null ? profile.getAvatarUrl()
                                    : LinkProvider.PROFILE_DEFAULT_AVATAR_URL,
                            profile.getCreatedTime(),
                            // score must not be null as "track_scores"
                            // is part of the query
                            score.longValue()));
        }

        return results;

    }

}