Project

General

Profile

1
package eu.dnetlib.data.search.app;
2

    
3
import eu.dnetlib.api.data.IndexService;
4
import eu.dnetlib.api.data.IndexServiceException;
5
import eu.dnetlib.api.data.SearchService;
6
import eu.dnetlib.api.data.SearchServiceException;
7
import eu.dnetlib.api.enabling.ISLookUpService;
8
import eu.dnetlib.common.rmi.UnimplementedException;
9
import eu.dnetlib.data.search.app.plan.FieldRewriteRule;
10
import eu.dnetlib.data.search.app.plan.QueryRewriteRule;
11
import eu.dnetlib.data.search.solr.SolrResultSet;
12
import eu.dnetlib.data.search.transform.Transformer;
13
import eu.dnetlib.data.search.transform.config.SearchRegistry;
14
import eu.dnetlib.data.search.transform.formatter.Formatter;
15
import eu.dnetlib.domain.ActionType;
16
import eu.dnetlib.domain.EPR;
17
import eu.dnetlib.domain.ResourceType;
18
import eu.dnetlib.domain.data.FormattedSearchResult;
19
import eu.dnetlib.domain.data.SearchResult;
20
import eu.dnetlib.domain.data.SuggestiveResult;
21
import eu.dnetlib.domain.enabling.Notification;
22
import gr.uoa.di.driver.app.DriverServiceImpl;
23
import gr.uoa.di.driver.enabling.issn.NotificationListener;
24
import gr.uoa.di.driver.enabling.resultset.ResultSet;
25
import gr.uoa.di.driver.enabling.resultset.ResultSetFactory;
26
import gr.uoa.di.driver.util.ServiceLocator;
27
import io.micrometer.core.instrument.Timer;
28
import io.micrometer.prometheus.PrometheusMeterRegistry;
29
import org.apache.log4j.Logger;
30
import org.apache.solr.client.solrj.SolrServerException;
31
import org.springframework.beans.factory.annotation.Autowired;
32
import org.w3c.dom.Document;
33
import org.w3c.dom.Node;
34
import org.xml.sax.InputSource;
35

    
36
import javax.xml.parsers.DocumentBuilder;
37
import javax.xml.parsers.DocumentBuilderFactory;
38
import javax.xml.xpath.XPath;
39
import javax.xml.xpath.XPathConstants;
40
import javax.xml.xpath.XPathExpression;
41
import javax.xml.xpath.XPathFactory;
42
import java.io.OutputStream;
43
import java.io.StringReader;
44
import java.util.*;
45

    
46
//import eu.dnetlib.utils.cql.CqlException;
47

    
48
public class SearchServiceImpl extends DriverServiceImpl
49
        implements SearchService {
50

    
51
    private static Logger logger = Logger.getLogger(SearchServiceImpl.class);
52
    //@Deprecated
53
    //private static Logger tlogger = Logger.getLogger("eu.dnetlib.data.search.app.Timer");
54

    
55
    private String mdFormat = "DMF";
56
    private String indexLayout = "index";
57

    
58
    private ServiceLocator<IndexService> indexLocator = null;
59
    private ServiceLocator<ISLookUpService> lookUpServiceServiceLocator = null;
60
    private ResultSetFactory rsFactory = null;
61

    
62
    private SearchRegistry transformerFactory = null;
63

    
64
    private List<QueryRewriteRule> queryRules = null;
65
    private List<String> fieldQueryRules = null;
66

    
67
    private Map<String, FieldRewriteRule> fieldRules = null;
68
    private boolean enableBrowseCache = false;
69

    
70
    private SearchServiceBlackboardHandler blackboardNotificationHandler = null;
71

    
72
    @Autowired
73
    private PrometheusMeterRegistry registry;
74

    
75

    
76
    //private CQLParser cqlParser = null;
77
    @Override
78
    public void init() {
79
        super.init();
80

    
81
        String serviceId = this.getServiceEPR().getParameter("serviceId");
82

    
83
        this.subscribe(
84
                ActionType.UPDATE,
85
                ResourceType.ANY.SEARCHSERVICERESOURCETYPE,
86
                serviceId,
87
                "RESOURCE_PROFILE/BODY/BLACKBOARD/LAST_REQUEST",
88
                new NotificationListener() {
89

    
90
                    @Override
91
                    public void processNotification(Notification notification) {
92
                        blackboardNotificationHandler.notified(
93
                                notification.getSubscriptionId(),
94
                                notification.getTopic(),
95
                                notification.getIsId(),
96
                                notification.getMessage());
97
                    }
98
                });
99

    
100
        try {
101
            String searchProfile = lookUpServiceServiceLocator.getService().getResourceProfile(serviceId);
102

    
103
            if (searchProfile != null) {
104
                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
105
                dbf.setNamespaceAware(true);
106
                DocumentBuilder db = dbf.newDocumentBuilder();
107
                Document doc = db.parse(new InputSource(new StringReader(searchProfile)));
108

    
109

    
110
                XPathFactory factory = XPathFactory.newInstance();
111
                XPath xpath = factory.newXPath();
112

    
113
                XPathExpression searchMdFormatExpression = xpath.compile("//SERVICE_PROPERTIES/PROPERTY[@key='mdformat']");
114
                Node node = (Node) searchMdFormatExpression.evaluate(doc,XPathConstants.NODE);
115

    
116
                if (node != null){
117
                    String profileMdFormat = node.getAttributes().getNamedItem("value").getTextContent();
118
                    if (profileMdFormat != null) {
119
                        //logger.debug("mdformat in properties " + mdFormat );
120
                        logger.info("Setting mdformat to '" + profileMdFormat + "'");
121
                        mdFormat = profileMdFormat;
122
                    }
123
                }
124
            }
125

    
126
        } catch (Exception e) {
127
            logger.error("Fail to load search service profile with id " + serviceId + " from IS.", e);
128
        }
129

    
130
    }
131

    
132
    @Override
133
    public SuggestiveResult suggestiveSearch(String query) throws SearchServiceException {
134
        throw new UnimplementedException();
135
    }
136

    
137
    @Override
138
    @Deprecated
139
    public SearchResult search(String text, String transformer, String locale, int page, int size) throws SearchServiceException {
140
        return searchNrefine(text, transformer, null, locale, page, size, null);
141
    }
142

    
143
    @Override
144
    @Deprecated
145
    public SearchResult refine(String text, String transformer, String locale, Collection<String> fields) throws SearchServiceException {
146
        return searchNrefine(text, null, transformer, locale, 1, -1, fields);
147
    }
148

    
149
    @Override
150
    @Deprecated
151
    public SearchResult searchNrefine(String text, String searchTransformer, String browseTransformer,
152
                                      String locale, int page, int size, Collection<String> fields) throws SearchServiceException {
153

    
154
        //logger.info("deprecated searchNrefine > from: " + page + " to:" + size);
155
        //TODO check locale
156
        //logger.debug("Search transformer " + searchTransformer);
157
        Transformer sTransformer = transformerFactory.getTransformer(searchTransformer, Locale.getDefault());
158
        Transformer oldRefineTransformer = transformerFactory.getTransformer("results_openaire_browse", Locale.getDefault());
159

    
160
        //logger.debug("Refine transformer " + browseTransformer);
161
        //Transformer rTranformer = transformerFactory.getTransformer(refineTransformer, Locale.getDefault());
162

    
163
        List<String> refineFields = null;
164
        if (fields!=null) {
165
            refineFields = new ArrayList<String>(fields);
166
        }
167

    
168
        return newSearch(text, locale, refineFields, new ArrayList<String>(), page, size, "", sTransformer, oldRefineTransformer, true);
169
    }
170

    
171
    @Override
172
    public FormattedSearchResult search(String queryText, String transformerName, String format, String locale, int page, int size)
173
            throws SearchServiceException {
174
        return searchNrefine(queryText, transformerName, null, format, locale, page, size, null);
175
    }
176

    
177
    @Override
178
    public FormattedSearchResult refine(String queryText, String refineTransformer, String format, String locale, Collection<String> fields) throws SearchServiceException {
179
        return searchNrefine(queryText, null, refineTransformer, format, locale, 0, -1, fields);
180
    }
181

    
182
    @Override
183
    public FormattedSearchResult searchNrefine(String queryText,
184
                                               String searchTransformer, String refineTransformer, String format,
185
                                               String locale, int page, int size, Collection<String> fields)  throws SearchServiceException {
186

    
187

    
188
        //logger.info("searchNrefine > from: " + page + " to:" + size);
189
        //TODO check locale
190
        FormattedSearchResult formattedSearchResult = null;
191

    
192
        //logger.debug("Search transformer " + searchTransformer);
193
        Transformer sTransformer = transformerFactory.getTransformer(searchTransformer, Locale.getDefault());
194
        Transformer oldRefineTransformer = transformerFactory.getTransformer("results_openaire_browse", Locale.getDefault());
195

    
196
        //logger.debug("Refine transformer " + refineTransformer);
197
        //Transformer rTranformer = transformerFactory.getTransformer(refineTransformer, Locale.getDefault());
198

    
199
        List<String> refineFields = null;
200
        if (fields!=null) {
201
            refineFields = new ArrayList<String>(fields);
202
        }
203
        SearchResult searchResult = newSearch(queryText, locale, refineFields, new ArrayList<String>(), page, size, format, sTransformer, oldRefineTransformer, true);
204

    
205
        Formatter formatter = transformerFactory.getFormatter(format); // formatter cannot be returned as null
206
        try {
207
            formattedSearchResult = new FormattedSearchResult(formatter.format(searchResult), searchResult.getTotal());
208

    
209
        } catch (Exception e) {
210
            logger.error("Error formating search results.", e);
211
        }
212

    
213
        return formattedSearchResult;
214
    }
215

    
216

    
217
    public SearchResult newSearch (String text, String locale, List<String> refinefields, List<String> fieldQueries,
218
                                   int from, int to, String format, Transformer transformer, Transformer oldRefineTransformer,
219
                                   boolean oldPaging) throws SearchServiceException {
220
        logger.info("newSearch > from: " + from + " to:" + to);
221
        long startTime = System.nanoTime();
222

    
223
        Timer.Sample timer = Timer.start(registry);
224

    
225
        IndexService index = getIndexLocator().getService();
226

    
227
        EPR epr = null;
228
        ResultSet<String> rs = null;
229

    
230
        List<String> browseResults = null;
231
        List<String> searchResults = null;
232

    
233
        String query = rewrite(text);
234
        enhanceFieldQueries(fieldQueries);
235
        logger.info("Performing query " + query + "' and fields " + fieldQueries + " and refine " + refinefields);
236

    
237
        try {
238
            //TODO see parser and maybe delete!
239
            //query = new CQLParser().parse(query).toCQL();
240
            String eprQuery = createEprQuery(query, refinefields, fieldQueries);
241

    
242
            epr = index.getBrowsingStatistics(eprQuery, "all", mdFormat, indexLayout);
243

    
244
            if (epr == null){
245
                throw new SearchServiceException("Something really strange happened there! Index returned null result set id.");
246
            }
247

    
248
            //get the locale TODO do we need this?
249
            //String correctLocale = getCorrectLocale(locale);
250
            //StringTokenizer tokenizer = new StringTokenizer(correctLocale, "_");
251
            //Locale requestLocale = new Locale(tokenizer.nextToken(), tokenizer.nextToken());
252

    
253
            rs = rsFactory.createResultSet(epr);
254

    
255

    
256
            Map<String, List<String>> list = null;
257
            if (oldPaging) {
258
                list = ((SolrResultSet)rs).newGet(from-1, to, format, transformer, oldRefineTransformer);
259

    
260
            } else {
261
                list = ((SolrResultSet)rs).newGet(from, to, format, transformer, oldRefineTransformer);
262
            }
263

    
264
            searchResults = list.get("search");
265
            browseResults = list.get("refine");
266

    
267
        } catch (IndexServiceException ise) {
268
            logger.error("Error getting refine results.", ise);
269
            throw new SearchServiceException("Error getting refine results.", ise);
270

    
271
        } finally {
272
            timer.stop(registry.timer("search.server.response.duration"));
273
/*
274
            histogram.observe(System.currentTimeMillis()-startTime);
275
*/
276
        }
277

    
278
        long estimatedTime = System.nanoTime() - startTime;
279

    
280
        logger.debug("Search time " + estimatedTime/1000000 +  " milliseconds for query " + query +
281
                " and fields " + fieldQueries + " and refine " + refinefields + " from: "+ from + " and size " + to);
282

    
283
        //logger.info("Returned results for NEW search query '" + query + "' and fields " + fieldQueries + " and refine " + refinefields);;
284
        rs.close();
285

    
286
        return new SearchResult(query, Locale.getDefault().toString(), rs.size(), from, to, searchResults, browseResults, refinefields);
287
    }
288

    
289
    public void cursorSearch(String text, List<String> refinefields, List<String> fieldQueries,
290
                             String format, Transformer transformer, OutputStream os) throws SearchServiceException {
291

    
292
        long startTime = System.nanoTime();
293

    
294
        IndexService index = getIndexLocator().getService();
295

    
296

    
297
        EPR epr = null;
298
        ResultSet<String> rs = null;
299

    
300
        String query = rewrite(text);
301
        enhanceFieldQueries(fieldQueries);
302
        logger.info("Performing cursor query " + query + "' and fields " + fieldQueries + " and refine " + refinefields);
303
        logger.debug("Performing cursor query " + query + "' and fields " + fieldQueries + " and refine " + refinefields);
304

    
305

    
306
        try {
307
            String eprQuery = createEprQuery(query, refinefields, fieldQueries);
308
            epr = index.getBrowsingStatistics(eprQuery, "all", mdFormat, indexLayout);
309

    
310
            if (epr == null) {
311
                throw new SearchServiceException("Something really strange happened there! Index returned null result set id.");
312
            }
313

    
314
            rs = rsFactory.createResultSet(epr);
315

    
316
            ((SolrResultSet)rs).cursorGet(transformer,2000, os);
317

    
318
        } catch (IndexServiceException ise) {
319
            logger.error("Error getting cursor results.", ise);
320
            throw new SearchServiceException("Error getting cursor results.", ise);
321

    
322
        } catch (SolrServerException sse) {
323
            logger.error("Error getting cursor results.", sse);
324
            throw new SearchServiceException("Error getting cursor results.", sse);
325
        }
326

    
327
        long estimatedTime = System.nanoTime() - startTime;
328
        logger.debug("Cursor search time " + estimatedTime/1000000 +  " milliseconds for query " + query +
329
                " and fields " + fieldQueries + " and refine " + refinefields);
330

    
331
        rs.close();
332
    }
333

    
334

    
335
    private String rewrite(String query) {
336
        if (queryRules != null) {
337
            for (QueryRewriteRule queryRule: queryRules) {
338
                if (logger.isDebugEnabled()) {
339
                    logger.debug("Apply rule " + query);
340
                }
341
                query = queryRule.apply(query);
342
                if (logger.isDebugEnabled()) {
343
                    logger.debug("Rewritten query is " + query);
344
                }
345
            }
346
        }
347
        return query;
348
    }
349

    
350
    private void enhanceFieldQueries(List<String> fieldQueries) {
351
        if (fieldQueries != null && fieldQueryRules != null && !fieldQueryRules.isEmpty()) {
352
            fieldQueries.addAll(fieldQueryRules);
353
        }
354
    }
355

    
356
    public void setFieldRules(Collection<FieldRewriteRule> fieldRules) {
357
        this.fieldRules = new HashMap<String, FieldRewriteRule>();
358
        for (FieldRewriteRule rule : fieldRules) {
359
            String key = rule.getFieldName();
360
            if (this.fieldRules.containsKey(key)) {
361
                logger.warn("Multiple rules for field " + key);
362
                logger.warn("Keeping last rule " + rule.getName());
363
            }
364
            this.fieldRules.put(key, rule);
365
        }
366
    }
367

    
368
    public static String createEprQuery(String query, List<String> refineFields, List<String> fieldQueries) {
369
        StringBuffer queryBuffer = new StringBuffer();
370
        queryBuffer.append("query=");
371

    
372
        StringBuffer facetsBuffer = new StringBuffer();
373
        facetsBuffer.append("&groupby=");
374

    
375
        StringBuffer fqBuffer = new StringBuffer();
376
        fqBuffer.append("&fq=");
377

    
378
        if (query != null) { //TODO consider exception?
379
            queryBuffer.append(query);
380
        }
381

    
382
        if(refineFields != null) {
383
            for (Iterator<String> iterator = refineFields.iterator(); iterator.hasNext(); ) {
384
                facetsBuffer.append(iterator.next());
385
                if (iterator.hasNext()) {
386
                    facetsBuffer.append(",");
387
                }
388
            }
389
        }
390

    
391
        if(fieldQueries != null) {
392
            for (Iterator<String> iterator = fieldQueries.iterator(); iterator.hasNext(); ) {
393
                fqBuffer.append(iterator.next());
394
                if (iterator.hasNext()) {
395
                    fqBuffer.append(",");
396
                }
397
            }
398
        }
399

    
400
        return queryBuffer.append(facetsBuffer.toString()).append(fqBuffer.toString()).toString();
401
    }
402

    
403

    
404
    //TODO: I wish to remove this. This was only made (quick and dirty - only the enhanceFieldQueries(fieldQueries) is missing
405
    //from newSearch() after a last time request for the portal to show all the publications and the deletedbyinference ones.
406
    //I did not want to pass a parameter since I do not know if we are going to keep it. This is for a tech meeting showcase.
407
    //If we want to keep this I need to redesign.
408

    
409
    public SearchResult newSearchWithoutFieldQueries (String text, String locale, List<String> refinefields, List<String> fieldQueries,
410
                                                      int from, int to, String format, Transformer transformer, Transformer oldRefineTransformer,
411
                                                      boolean oldPaging) throws SearchServiceException {
412
        logger.info("non filtered search for...  > from: " + from + " to:" + to);
413
        long startTime = System.nanoTime();
414

    
415
        IndexService index = getIndexLocator().getService();
416

    
417
        EPR epr = null;
418
        ResultSet<String> rs = null;
419

    
420
        List<String> browseResults = null;
421
        List<String> searchResults = null;
422

    
423
        String query = rewrite(text);
424
        logger.info("Performing query " + query + "' and fields " + fieldQueries + " and refine " + refinefields);
425

    
426
        try {
427
            //TODO see parser and maybe delete!
428
            //query = new CQLParser().parse(query).toCQL();
429
            String eprQuery = createEprQuery(query, refinefields, fieldQueries);
430

    
431
            epr = index.getBrowsingStatistics(eprQuery, "all", mdFormat, indexLayout);
432

    
433
            if (epr == null) {
434
                throw new SearchServiceException("Something really strange happened there! Index returned null result set id.");
435
            }
436

    
437
            //get the locale TODO do we need this?
438
            //String correctLocale = getCorrectLocale(locale);
439
            //StringTokenizer tokenizer = new StringTokenizer(correctLocale, "_");
440
            //Locale requestLocale = new Locale(tokenizer.nextToken(), tokenizer.nextToken());
441

    
442
            rs = rsFactory.createResultSet(epr);
443

    
444
            Map<String, List<String>> list = null;
445
            if (oldPaging) {
446
                list = ((SolrResultSet)rs).newGet(from-1, to, format, transformer, oldRefineTransformer);
447

    
448
            } else {
449
                list = ((SolrResultSet)rs).newGet(from, to, format, transformer, oldRefineTransformer);
450
            }
451

    
452

    
453
            searchResults = list.get("search");
454
            browseResults = list.get("refine");
455

    
456
        } catch (IndexServiceException ise) {
457
            logger.error("Error getting refine results.", ise);
458
            throw new SearchServiceException("Error getting refine results.", ise);
459

    
460
        }
461

    
462
        long estimatedTime = System.nanoTime() - startTime;
463
        logger.debug("Search time " + estimatedTime/1000000 +  " milliseconds for query " + query +
464
                " and fields " + fieldQueries + " and refine " + refinefields + " from: "+ from + " and size " + to);
465

    
466
        //logger.info("Returned results for NEW search query '" + query + "' and fields " + fieldQueries + " and refine " + refinefields);;
467
        rs.close();
468
        return new SearchResult(query, Locale.getDefault().toString(), rs.size(), from, to, searchResults, browseResults, refinefields);
469
    }
470

    
471

    
472
    public String getMdFormat() {
473
        return mdFormat;
474
    }
475

    
476
    public void setMdFormat(String mdFormat) {
477
        this.mdFormat = mdFormat;
478
    }
479

    
480
    public ServiceLocator<IndexService> getIndexLocator() {
481
        return indexLocator;
482
    }
483

    
484
    public void setIndexLocator(ServiceLocator<IndexService> indexLocator) {
485
        this.indexLocator = indexLocator;
486
    }
487

    
488
    public ResultSetFactory getRsFactory() {
489
        return rsFactory;
490
    }
491

    
492
    public void setRsFactory(ResultSetFactory rsFactory) {
493
        this.rsFactory = rsFactory;
494
    }
495

    
496
    public Collection<FieldRewriteRule> getFieldRules() {
497
        return fieldRules.values();
498
    }
499

    
500
    public List<QueryRewriteRule> getQueryRules() {
501
        return queryRules;
502
    }
503

    
504
    public void setQueryRules(List<QueryRewriteRule> queryRules) {
505
        this.queryRules = queryRules;
506
    }
507

    
508
    public boolean isEnableBrowseCache() {
509
        return enableBrowseCache;
510
    }
511

    
512
    public void setEnableBrowseCache(boolean enableBrowseCache) {
513
        this.enableBrowseCache = enableBrowseCache;
514
    }
515

    
516
    public SearchRegistry getTransformerFactory() {
517
        return transformerFactory;
518
    }
519

    
520
    public void setTransformerFactory(SearchRegistry transformerFactory) { this.transformerFactory = transformerFactory; }
521

    
522
    public String getIndexLayout() {
523
        return indexLayout;
524
    }
525

    
526
    public void setIndexLayout(String indexLayout) {
527
        this.indexLayout = indexLayout;
528
    }
529

    
530
    public SearchServiceBlackboardHandler getBlackboardNotificationHandler() {
531
        return blackboardNotificationHandler;
532
    }
533

    
534
    public void setBlackboardNotificationHandler(SearchServiceBlackboardHandler blackboardNotificationHandler) {
535
        this.blackboardNotificationHandler = blackboardNotificationHandler;
536
    }
537

    
538
    public void setLookUpServiceServiceLocator(ServiceLocator<ISLookUpService> lookUpServiceServiceLocator) {
539
        this.lookUpServiceServiceLocator = lookUpServiceServiceLocator;
540
    }
541

    
542
    public List<String> getFieldQueryRules() {
543
        return fieldQueryRules;
544
    }
545

    
546
    public void setFieldQueryRules(List<String> fieldQueryRules) {
547
        this.fieldQueryRules = fieldQueryRules;
548
    }
549
}
(2-2/2)