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, null, 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<>(), 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> specialFacets, 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, specialFacets, 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> specialFacets, 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, specialFacets, 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> specialFacets, 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
        StringBuffer sfBuffer = new StringBuffer();
379
        sfBuffer.append("&sf=");
380

    
381
        if (query != null) { //TODO consider exception?
382
            queryBuffer.append(query);
383
        }
384

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

    
394
        if(specialFacets != null) {
395
            for (Iterator<String> iterator = specialFacets.iterator(); iterator.hasNext(); ) {
396
                sfBuffer.append(iterator.next());
397
                if (iterator.hasNext()) {
398
                    sfBuffer.append(",");
399
                }
400
            }
401
        }
402
       // logger.debug("special buffer " + sfBuffer.toString());
403

    
404
        if(fieldQueries != null) {
405
            for (Iterator<String> iterator = fieldQueries.iterator(); iterator.hasNext(); ) {
406
                fqBuffer.append(iterator.next());
407
                if (iterator.hasNext()) {
408
                    fqBuffer.append(",");
409
                }
410
            }
411
        }
412

    
413
        return queryBuffer.append(facetsBuffer.toString()).append(sfBuffer.toString()).append(fqBuffer.toString()).toString();
414
    }
415

    
416

    
417
    //TODO: I wish to remove this. This was only made (quick and dirty - only the enhanceFieldQueries(fieldQueries) is missing
418
    //from newSearch() after a last time request for the portal to show all the publications and the deletedbyinference ones.
419
    //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.
420
    //If we want to keep this I need to redesign.
421

    
422
    public SearchResult newSearchWithoutFieldQueries (String text, String locale, List<String> refinefields, List<String> specialFacets,
423
                                                      List<String> fieldQueries, int from, int to, String format, Transformer transformer,
424
                                                      Transformer oldRefineTransformer, boolean oldPaging) throws SearchServiceException {
425
        logger.info("non filtered search for...  > from: " + from + " to:" + to);
426
        long startTime = System.nanoTime();
427

    
428
        IndexService index = getIndexLocator().getService();
429

    
430
        EPR epr = null;
431
        ResultSet<String> rs = null;
432

    
433
        List<String> browseResults = null;
434
        List<String> searchResults = null;
435

    
436
        String query = rewrite(text);
437
        logger.info("Performing query '" + query + "' and fields " + fieldQueries + " and refine " + refinefields);
438

    
439
        try {
440
            //TODO see parser and maybe delete!
441
            //query = new CQLParser().parse(query).toCQL();
442
            String eprQuery = createEprQuery(query, refinefields, specialFacets, fieldQueries);
443

    
444
            epr = index.getBrowsingStatistics(eprQuery, "all", mdFormat, indexLayout);
445

    
446
            if (epr == null) {
447
                throw new SearchServiceException("Something really strange happened there! Index returned null result set id.");
448
            }
449

    
450
            //get the locale TODO do we need this?
451
            //String correctLocale = getCorrectLocale(locale);
452
            //StringTokenizer tokenizer = new StringTokenizer(correctLocale, "_");
453
            //Locale requestLocale = new Locale(tokenizer.nextToken(), tokenizer.nextToken());
454

    
455
            rs = rsFactory.createResultSet(epr);
456

    
457
            Map<String, List<String>> list = null;
458
            if (oldPaging) {
459
                list = ((SolrResultSet)rs).newGet(from-1, to, format, transformer, oldRefineTransformer);
460

    
461
            } else {
462
                list = ((SolrResultSet)rs).newGet(from, to, format, transformer, oldRefineTransformer);
463
            }
464

    
465

    
466
            searchResults = list.get("search");
467
            browseResults = list.get("refine");
468

    
469
        } catch (IndexServiceException ise) {
470
            logger.error("Error getting refine results.", ise);
471
            throw new SearchServiceException("Error getting refine results.", ise);
472

    
473
        }
474

    
475
        long estimatedTime = System.nanoTime() - startTime;
476
        logger.debug("Search time " + estimatedTime/1000000 +  " milliseconds for query " + query +
477
                " and fields " + fieldQueries + " and refine " + refinefields + " from: "+ from + " and size " + to);
478

    
479
        //logger.info("Returned results for NEW search query '" + query + "' and fields " + fieldQueries + " and refine " + refinefields);;
480
        rs.close();
481
        return new SearchResult(query, Locale.getDefault().toString(), rs.size(), from, to, searchResults, browseResults, refinefields);
482
    }
483

    
484

    
485
    public String getMdFormat() {
486
        return mdFormat;
487
    }
488

    
489
    public void setMdFormat(String mdFormat) {
490
        this.mdFormat = mdFormat;
491
    }
492

    
493
    public ServiceLocator<IndexService> getIndexLocator() {
494
        return indexLocator;
495
    }
496

    
497
    public void setIndexLocator(ServiceLocator<IndexService> indexLocator) {
498
        this.indexLocator = indexLocator;
499
    }
500

    
501
    public ResultSetFactory getRsFactory() {
502
        return rsFactory;
503
    }
504

    
505
    public void setRsFactory(ResultSetFactory rsFactory) {
506
        this.rsFactory = rsFactory;
507
    }
508

    
509
    public Collection<FieldRewriteRule> getFieldRules() {
510
        return fieldRules.values();
511
    }
512

    
513
    public List<QueryRewriteRule> getQueryRules() {
514
        return queryRules;
515
    }
516

    
517
    public void setQueryRules(List<QueryRewriteRule> queryRules) {
518
        this.queryRules = queryRules;
519
    }
520

    
521
    public boolean isEnableBrowseCache() {
522
        return enableBrowseCache;
523
    }
524

    
525
    public void setEnableBrowseCache(boolean enableBrowseCache) {
526
        this.enableBrowseCache = enableBrowseCache;
527
    }
528

    
529
    public SearchRegistry getTransformerFactory() {
530
        return transformerFactory;
531
    }
532

    
533
    public void setTransformerFactory(SearchRegistry transformerFactory) { this.transformerFactory = transformerFactory; }
534

    
535
    public String getIndexLayout() {
536
        return indexLayout;
537
    }
538

    
539
    public void setIndexLayout(String indexLayout) {
540
        this.indexLayout = indexLayout;
541
    }
542

    
543
    public SearchServiceBlackboardHandler getBlackboardNotificationHandler() {
544
        return blackboardNotificationHandler;
545
    }
546

    
547
    public void setBlackboardNotificationHandler(SearchServiceBlackboardHandler blackboardNotificationHandler) {
548
        this.blackboardNotificationHandler = blackboardNotificationHandler;
549
    }
550

    
551
    public void setLookUpServiceServiceLocator(ServiceLocator<ISLookUpService> lookUpServiceServiceLocator) {
552
        this.lookUpServiceServiceLocator = lookUpServiceServiceLocator;
553
    }
554

    
555
    public List<String> getFieldQueryRules() {
556
        return fieldQueryRules;
557
    }
558

    
559
    public void setFieldQueryRules(List<String> fieldQueryRules) {
560
        this.fieldQueryRules = fieldQueryRules;
561
    }
562
}
(2-2/2)