Project

General

Profile

1
package eu.dnetlib.functionality.index.client.clients.solr;
2

    
3
import java.io.IOException;
4
import java.util.ArrayList;
5
import java.util.HashMap;
6
import java.util.List;
7
import java.util.Map;
8

    
9
import com.google.common.collect.BiMap;
10
import com.google.common.collect.Maps;
11
import eu.dnetlib.data.provision.index.rmi.BrowsingRow;
12
import eu.dnetlib.data.provision.index.rmi.GroupResult;
13
import eu.dnetlib.data.provision.index.rmi.IndexServiceException;
14
import eu.dnetlib.functionality.cql.CqlValueTransformerMap;
15
import eu.dnetlib.functionality.index.client.AbstractIndexClient;
16
import eu.dnetlib.functionality.index.client.IndexClient;
17
import eu.dnetlib.functionality.index.client.IndexClientException;
18
import eu.dnetlib.functionality.index.client.response.BrowseEntry;
19
import eu.dnetlib.functionality.index.client.response.BrowseValueEntry;
20
import eu.dnetlib.functionality.index.client.response.LookupResponse;
21
import eu.dnetlib.functionality.index.model.Any.ValueType;
22
import eu.dnetlib.functionality.index.query.*;
23
import eu.dnetlib.functionality.index.solr.cql.SolrTypeBasedCqlValueTransformerMapFactory;
24
import eu.dnetlib.functionality.index.utils.MetadataReference;
25
import org.apache.commons.logging.Log;
26
import org.apache.commons.logging.LogFactory;
27
import org.apache.solr.client.solrj.SolrClient;
28
import org.apache.solr.client.solrj.SolrServerException;
29
import org.apache.solr.client.solrj.impl.CloudSolrClient;
30
import org.apache.solr.client.solrj.request.LukeRequest;
31
import org.apache.solr.client.solrj.response.LukeResponse;
32
import org.apache.solr.client.solrj.response.LukeResponse.FieldInfo;
33
import org.apache.solr.client.solrj.response.QueryResponse;
34

    
35
/**
36
 * The Class SolrIndexClient.
37
 */
38
public class SolrIndexClient extends AbstractIndexClient implements IndexClient {
39

    
40
	/** The client. */
41
	private CloudSolrClient client;
42

    
43
	private SolrIndexQueryFactory solrIndexQueryFactory;
44

    
45
	/** The query response factory. */
46
	private QueryResponseFactory<QueryResponse> queryResponseFactory;
47

    
48
	private SolrTypeBasedCqlValueTransformerMapFactory tMapFactory;
49

    
50
	private static final Log log = LogFactory.getLog(SolrIndexClient.class);
51

    
52
	/**
53
	 * The Constructor.
54
	 *
55
	 * @param format
56
	 *            the format
57
	 * @param layout
58
	 *            the layout
59
	 * @param interpretation
60
	 *            the interpretation
61
	 * @param serviceProperties
62
	 *            the service properties
63
	 * @param tMapFactory
64
	 */
65
	public SolrIndexClient(final String format, final String layout, final String interpretation, final Map<String, String> serviceProperties,
66
			final SolrIndexQueryFactory indexQueryFactory, final QueryResponseFactory<QueryResponse> queryResponseFactory,
67
			final SolrTypeBasedCqlValueTransformerMapFactory tMapFactory) {
68
		super(format, layout, interpretation, serviceProperties);
69

    
70
		log.debug(String.format("Created a new instance of the index of type %s-%s-%s", format, layout, interpretation));
71
		this.solrIndexQueryFactory = indexQueryFactory;
72
		this.queryResponseFactory = queryResponseFactory;
73
		this.tMapFactory = tMapFactory;
74

    
75
	}
76

    
77
	/**
78
	 * Do delete.
79
	 *
80
	 * @param query
81
	 *            the CQL query
82
	 * @return true, if do delete
83
	 * @throws IndexServiceException
84
	 *             the index service exception
85
	 */
86
	@Override
87
	public long delete(final String query) throws IndexClientException {
88
		try {
89
			log.debug("delete by query: " + query);
90
			MetadataReference mdRef = new MetadataReference(getFormat(), getLayout(), getInterpretation());
91
			SolrIndexQuery translatedQuery = (SolrIndexQuery) solrIndexQueryFactory.getIndexQuery(QueryLanguage.CQL, query, this, mdRef);
92
			String tquery = translatedQuery.getQuery();
93
			translatedQuery.setQueryLimit(0);
94

    
95
			SolrIndexQueryResponse rsp = new SolrIndexQueryResponse(client.query(translatedQuery));
96
			QueryResponseParser responseParser = queryResponseFactory.getQueryResponseParser(rsp, mdRef);
97
			long total = responseParser.getNumFound();
98
			client.deleteByQuery(tquery);
99
			client.commit();
100
			return total;
101
		} catch (Exception e) {
102
			throw new IndexClientException("unable to run delete by query: " + query, e);
103
		}
104
	}
105

    
106
	/**
107
	 * {@inheritDoc}
108
	 *
109
	 * @throws IndexClientException
110
	 *
111
	 * @see eu.dnetlib.functionality.index.client.IndexClient#browse(String, List, int)
112
	 */
113
	@Override
114
	public List<BrowseEntry> browse(final String query, final List<String> browseFields, final int max) throws IndexClientException {
115
		MetadataReference mdRef = new MetadataReference(getFormat(), getLayout(), getInterpretation());
116
		SolrIndexQuery translatedQuery = buildBrowseQuery(query, browseFields, max, mdRef);
117
		return executeBrowseQuery(query, translatedQuery, mdRef, browseFields);
118
	}
119

    
120
	/**
121
	 * {@inheritDoc}
122
	 *
123
	 * @throws IndexClientException
124
	 *
125
	 * @see eu.dnetlib.functionality.index.client.IndexClient#browse(String, List, int, List)
126
	 */
127
	@Override
128
	public List<BrowseEntry> browse(final String query, final List<String> browseFields, final int max, final List<String> filterQuery)
129
			throws IndexClientException {
130
		MetadataReference mdRef = new MetadataReference(getFormat(), getLayout(), getInterpretation());
131
		SolrIndexQuery translatedQuery = buildBrowseQuery(query, browseFields, max, mdRef);
132
		if (filterQuery != null) {
133
			log.debug("Filter Query:");
134
			for (String fq : filterQuery) {
135
				translatedQuery.addFilterQuery(fq);
136
				log.debug("- " + fq);
137
			}
138
		}
139
		return executeBrowseQuery(query, translatedQuery, mdRef, browseFields);
140

    
141
	}
142

    
143
	public SolrIndexQuery buildBrowseQuery(final String query, final List<String> browseFields, final int max, final MetadataReference mdRef)
144
			throws IndexClientException {
145
		log.debug("Browse request for the index collection for query:" + query);
146

    
147
		SolrIndexQuery translatedQuery = (SolrIndexQuery) solrIndexQueryFactory.getIndexQuery(QueryLanguage.CQL, query, this, mdRef);
148
		translatedQuery.setFacet(true);
149
		if (browseFields != null) {
150
			List<String> browsableFields = solrIndexQueryFactory.getBrowsableFields(browseFields, mdRef);
151
			log.debug("Browsing fields:");
152
			for (String field : browsableFields) {
153
				translatedQuery.addFacetField(field);
154
				log.debug("- " + field);
155

    
156
			}
157
			translatedQuery.setFacetLimit(max);
158
			log.debug("max number of browsing field :" + max);
159
		}
160
		return translatedQuery;
161
	}
162

    
163
	private List<BrowseEntry> executeBrowseQuery(final String originalQuery,
164
			final SolrIndexQuery query,
165
			final MetadataReference mdRef,
166
			final List<String> browseFields) throws IndexClientException {
167
		try {
168
			SolrIndexQueryResponse response = new SolrIndexQueryResponse(client.query(query));
169
			QueryResponseParser responseParser = queryResponseFactory.getQueryResponseParser(response, mdRef);
170
			List<BrowsingRow> results = responseParser.getBrowsingResults();
171
			List<BrowseEntry> out = convertBrowseEntry(browseFields, results, responseParser.getAliases());
172
			return out;
173
		} catch (SolrServerException | IOException e) {
174
			throw new IndexClientException("Error on executing a query " + originalQuery, e);
175
		}
176
	}
177

    
178
	/**
179
	 * Creates the connection.
180
	 *
181
	 * @return the string
182
	 * @throws IndexClientException
183
	 *             the index client exception
184
	 */
185
	private String getUrl() throws IndexClientException {
186
		String address = serviceProperties.get(ZK_ADDRESS);
187
		if (address == null) { throw new IndexClientException("Unable to load a solr client, missing zk address"); }
188
		return address;
189
	}
190

    
191
	/**
192
	 * Gets the client.
193
	 *
194
	 * @return the client
195
	 * @throws IndexClientException
196
	 *             the index client exception
197
	 */
198
	public SolrClient getClient() throws IndexClientException {
199
		if (this.client == null) {
200
			String url = getUrl();
201
			log.debug("create new Client " + url);
202
			client = new CloudSolrClient.Builder()
203
					.withZkHost(url)
204
					.build();
205
			client.setDefaultCollection(String.format("%s-%s-%s", getFormat(), getLayout(), getInterpretation()));
206
			try {
207
				client.ping();
208
			} catch (Exception e) {
209
				throw new IndexClientException("oops something went wrong", e);
210
			}
211
		}
212
		return client;
213
	}
214

    
215
	/**
216
	 * Sets the client.
217
	 *
218
	 * @param client
219
	 *            the client
220
	 */
221
	public void setClient(final CloudSolrClient client) {
222
		this.client = client;
223
	}
224

    
225
	@Override
226
	public LookupResponse lookup(final String query, final List<String> filterQuery, final int from, final int to) throws IndexClientException {
227
		log.debug("lookup request for the index collection for query:" + query);
228
		MetadataReference mdRef = new MetadataReference(getFormat(), getLayout(), getInterpretation());
229
		SolrIndexQuery translatedQuery = (SolrIndexQuery) solrIndexQueryFactory.getIndexQuery(QueryLanguage.CQL, query, this, mdRef);
230
		translatedQuery.setQueryOffset(from);
231
		translatedQuery.setQueryLimit(to - from + 1);
232
		if (filterQuery != null) {
233
			for (String fq : filterQuery) {
234
				translatedQuery.addFilterQuery(fq);
235
			}
236
		}
237

    
238
		try {
239
			SolrIndexQueryResponse response = new SolrIndexQueryResponse(client.query(translatedQuery));
240
			QueryResponseParser responseParser = queryResponseFactory.getQueryResponseParser(response, mdRef);
241

    
242
			return new LookupResponse(responseParser);
243
		} catch (SolrServerException | IOException e) {
244
			throw new IndexClientException("Error on executing a query " + query, e);
245
		}
246

    
247
	}
248

    
249
	@Override
250
	public CqlValueTransformerMap getCqlValueTransformerMap(final MetadataReference mdRef) throws IndexClientException {
251
		try {
252
			return this.tMapFactory.getIt(readFieldNamesAndTypes(mdRef.toString()));
253
		} catch (Exception e) {
254
			throw new IndexClientException(e);
255

    
256
		}
257
	}
258

    
259
	@Override
260
	public IndexQueryFactory getIndexQueryFactory() {
261
		return solrIndexQueryFactory;
262
	}
263

    
264
	@Override
265
	public void stop() throws IndexClientException {
266
		try {
267
			log.debug("shutdown client: " + serviceProperties.get(ZK_ADDRESS));
268
			client.close();
269
		} catch (Throwable e) {
270
			throw new IndexClientException(e);
271
		}
272
	}
273

    
274
	private List<BrowseEntry> convertBrowseEntry(final List<String> browseFields, final List<BrowsingRow> results, final BiMap<String, String> aliases) {
275

    
276
		Map<String, BrowseEntry> mapResult = new HashMap<String, BrowseEntry>();
277
		for (BrowsingRow row : results) {
278
			for (GroupResult groupResult : row.getGroupResult()) {
279
				String name = groupResult.getName();
280
				List<BrowseValueEntry> valuesEntry;
281
				BrowseEntry entry;
282
				if (mapResult.containsKey(name)) {
283
					entry = mapResult.get(name);
284
					valuesEntry = entry.getValues();
285
					if (valuesEntry == null) {
286
						valuesEntry = new ArrayList<BrowseValueEntry>();
287
						entry.setValues(valuesEntry);
288
					}
289

    
290
				} else {
291
					entry = new BrowseEntry();
292
					entry.setField(name);
293
					entry.setLabel(name);
294
					valuesEntry = new ArrayList<BrowseValueEntry>();
295
					entry.setValues(valuesEntry);
296
					mapResult.put(name, entry);
297
				}
298
				String value = groupResult.getValue();
299
				int count = groupResult.getCount();
300
				BrowseValueEntry entryValue = new BrowseValueEntry(value, count);
301
				valuesEntry.add(entryValue);
302
			}
303
		}
304
		List<BrowseEntry> out = new ArrayList<BrowseEntry>();
305
		for (String b : browseFields) {
306
			String inverse = null;
307
			if (aliases != null) {
308
				inverse = aliases.get(b) != null ? aliases.get(b) : aliases.inverse().get(b);
309
			}
310
			if (mapResult.containsKey(b)) {
311
				out.add(mapResult.get(b));
312
			} else if (mapResult.containsKey(inverse) == true) {
313
				BrowseEntry data = mapResult.get(inverse);
314
				data.setField(b);
315
				out.add(data);
316
			}
317
		}
318
		return out;
319
	}
320

    
321
	private Map<String, ValueType> readFieldNamesAndTypes(final String coreName) throws SolrServerException, IOException, IndexClientException {
322
		// final SolrServer client = cloudServer.getSolrServer(coreName);
323
		final LukeRequest request = new LukeRequest();
324
		request.setShowSchema(true);
325

    
326
		// cloudServer.setDefaultCollection(coreName);
327
		request.setNumTerms(0);
328
		final LukeResponse response = request.process(getClient());
329
		final Map<String, FieldInfo> fieldInfos = response.getFieldInfo();
330
		final Map<String, LukeResponse.FieldTypeInfo> fieldTypeInfos = response.getFieldTypeInfo();
331
		final Map<String, ValueType> result = Maps.newHashMap();
332
		for (FieldInfo fieldInfo : fieldInfos.values()) {
333
			LukeResponse.FieldTypeInfo fieldTypeInfo = fieldTypeInfos.get(fieldInfo.getType());
334
			final String fieldName = fieldTypeInfo.getName().toLowerCase();
335
			final ValueType fieldType = resolveSolrTypeClassName(fieldName);
336
			result.put(fieldInfo.getName(), fieldType);
337
		}
338
		return result;
339
	}
340

    
341
	private ValueType resolveSolrTypeClassName(final String solrTypeName) {
342
		if (solrTypeName.contains("LongField")) {
343
			return ValueType.LONG;
344
		} else if (solrTypeName.contains("IntField")) {
345
			return ValueType.LONG;
346
		} else if (solrTypeName.contains("short")) {
347
			return ValueType.LONG;
348
		} else if (solrTypeName.contains("float")) {
349
			return ValueType.DOUBLE;
350
		} else if (solrTypeName.contains("double")) {
351
			return ValueType.DOUBLE;
352
		} else if (solrTypeName.contains("date")) {
353
			return ValueType.DATETIME;
354
		} else {
355
			return ValueType.STRING;
356
		}
357
	}
358

    
359
}
(1-1/2)