View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.solr.search;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Spliterator;
26  import java.util.function.Consumer;
27  import java.util.stream.Collectors;
28  import java.util.stream.Stream;
29  import java.util.stream.StreamSupport;
30  
31  import org.apache.logging.log4j.LogManager;
32  import org.apache.logging.log4j.Logger;
33  import org.apache.solr.client.solrj.SolrClient;
34  import org.apache.solr.client.solrj.SolrQuery;
35  import org.apache.solr.client.solrj.SolrServerException;
36  import org.apache.solr.client.solrj.response.QueryResponse;
37  import org.apache.solr.common.SolrDocument;
38  import org.apache.solr.common.SolrDocumentList;
39  import org.apache.solr.common.params.ModifiableSolrParams;
40  import org.apache.solr.common.params.SolrParams;
41  import org.jdom2.Document;
42  import org.mycore.parsers.bool.MCRCondition;
43  import org.mycore.parsers.bool.MCROrCondition;
44  import org.mycore.parsers.bool.MCRSetCondition;
45  import org.mycore.services.fieldquery.MCRQuery;
46  
47  import jakarta.servlet.http.HttpServletRequest;
48  
49  /**
50   * Some solr search utils.
51   * 
52   * @author Matthias Eichner
53   */
54  public abstract class MCRSolrSearchUtils {
55  
56      private static final Logger LOGGER = LogManager.getLogger(MCRQLSearchUtils.class);
57  
58      /**
59       * Returns the first document.
60       * 
61       * @param solrClient solr server connection
62       * @param query solr query
63       * @return first solr document or null
64       * @throws SolrServerException communication with the solr server failed in any way
65       */
66      public static SolrDocument first(SolrClient solrClient, String query) throws SolrServerException, IOException {
67          ModifiableSolrParams p = new ModifiableSolrParams();
68          p.set("q", query);
69          p.set("rows", 1);
70          QueryResponse response = solrClient.query(p);
71          return response.getResults().isEmpty() ? null : response.getResults().get(0);
72      }
73  
74      @SuppressWarnings("rawtypes")
75      public static SolrQuery getSolrQuery(MCRQuery query, Document input, HttpServletRequest request) {
76          int rows = query.getNumPerPage();
77          List<String> returnFields = query.getReturnFields();
78          MCRCondition condition = query.getCondition();
79          HashMap<String, List<MCRCondition>> table;
80  
81          if (condition instanceof MCRSetCondition) {
82              table = MCRConditionTransformer.groupConditionsByIndex((MCRSetCondition) condition);
83          } else {
84              // if there is only one condition its no set condition. we don't need to group
85              LOGGER.warn("Condition is not SetCondition.");
86              table = new HashMap<>();
87  
88              ArrayList<MCRCondition> conditionList = new ArrayList<>();
89              conditionList.add(condition);
90  
91              table.put("metadata", conditionList);
92  
93          }
94  
95          boolean booleanAnd = !(condition instanceof MCROrCondition<?>);
96          SolrQuery mergedSolrQuery = MCRConditionTransformer.buildMergedSolrQuery(query.getSortBy(), false, booleanAnd,
97              table, rows, returnFields);
98          String qt = input.getRootElement().getAttributeValue("qt");
99          if (qt != null) {
100             mergedSolrQuery.setParam("qt", qt);
101         }
102 
103         String mask = input.getRootElement().getAttributeValue("mask");
104         if (mask != null) {
105             mergedSolrQuery.setParam("mask", mask);
106             mergedSolrQuery.setParam("_session", request.getParameter("_session"));
107         }
108         return mergedSolrQuery;
109     }
110 
111     /**
112      * Returns a list of ids found by the given query. Returns an empty list
113      * when nothing is found.
114      * 
115      * @param solrClient solr server connection
116      * @param query solr query
117      * @return list of id's
118      */
119     public static List<String> listIDs(SolrClient solrClient, String query) {
120         ModifiableSolrParams p = new ModifiableSolrParams();
121         p.set("q", query);
122         p.set("fl", "id");
123         return stream(solrClient, p).map(doc -> doc.getFieldValue("id").toString()).collect(Collectors.toList());
124     }
125 
126     /**
127      * Creates a stream of SolrDocument's.
128      * 
129      * @param solrClient the client to query
130      * @param params solr parameter
131      * @return stream of solr documents
132      */
133     public static Stream<SolrDocument> stream(SolrClient solrClient, SolrParams params) {
134         return stream(solrClient, params, true, 1000);
135     }
136 
137     public static Stream<SolrDocument> stream(SolrClient solrClient, SolrParams params, boolean parallel,
138         int rowsPerRequest) {
139         SolrDocumentSpliterator solrDocumentSpliterator = new SolrDocumentSpliterator(solrClient, params, 0,
140             rowsPerRequest);
141         return StreamSupport.stream(solrDocumentSpliterator, parallel);
142     }
143 
144     /**
145      * Spliterator for solr documents.
146      */
147     public static class SolrDocumentSpliterator implements Spliterator<SolrDocument> {
148 
149         protected SolrClient solrClient;
150 
151         protected SolrParams params;
152 
153         protected long start;
154 
155         protected long rows;
156 
157         protected Long size;
158 
159         protected QueryResponse response;
160 
161         public SolrDocumentSpliterator(SolrClient solrClient, SolrParams params, long start, long rows) {
162             this(solrClient, params, start, rows, null);
163         }
164 
165         public SolrDocumentSpliterator(SolrClient solrClient, SolrParams params, long start, long rows, Long size) {
166             this.solrClient = solrClient;
167             this.params = params;
168             this.start = start;
169             this.rows = rows;
170             this.size = size;
171         }
172 
173         @Override
174         public int characteristics() {
175             return Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
176         }
177 
178         @Override
179         public long estimateSize() {
180             if (this.size == null) {
181                 ModifiableSolrParams sizeParams = new ModifiableSolrParams(this.params);
182                 sizeParams.set("start", 0);
183                 sizeParams.set("rows", 0);
184                 try {
185                     QueryResponse response = solrClient.query(sizeParams);
186                     this.size = response.getResults().getNumFound();
187                 } catch (SolrServerException | IOException e) {
188                     throw new IllegalStateException(e);
189                 }
190             }
191             return this.size;
192         }
193 
194         @Override
195         public void forEachRemaining(Consumer<? super SolrDocument> action) {
196             if (action == null) {
197                 throw new NullPointerException();
198             }
199             ModifiableSolrParams p = new ModifiableSolrParams(params);
200             p.set("rows", (int) rows);
201             long start = this.start, size = estimateSize(), fetched = 0;
202             while (fetched < size) {
203                 p.set("start", (int) (start + fetched));
204                 response = query(p);
205                 SolrDocumentList results = response.getResults();
206                 for (SolrDocument doc : results) {
207                     action.accept(doc);
208                 }
209                 fetched += results.size();
210             }
211         }
212 
213         protected QueryResponse query(SolrParams params) {
214             try {
215                 return solrClient.query(params);
216             } catch (SolrServerException | IOException e) {
217                 throw new IllegalStateException(e);
218             }
219         }
220 
221         @Override
222         public boolean tryAdvance(Consumer<? super SolrDocument> action) {
223             if (action == null) {
224                 throw new NullPointerException();
225             }
226             long i = start, size = estimateSize();
227             if (size > 0) {
228                 if (response == null) {
229                     ModifiableSolrParams p = new ModifiableSolrParams(params);
230                     p.set("start", (int) i);
231                     p.set("rows", (int) rows);
232                     response = query(p);
233                 }
234                 action.accept(response.getResults().get(response.getResults().size() - (int) size));
235                 this.start = i + 1;
236                 this.size -= 1;
237                 return true;
238             }
239             return false;
240         }
241 
242         @Override
243         public Spliterator<SolrDocument> trySplit() {
244             long s = estimateSize(), i = start, l = rows;
245             if (l >= s) {
246                 return null;
247             }
248             this.size = l;
249             return new SolrDocumentSpliterator(solrClient, params, i + l, l, s - l);
250         }
251     }
252 
253 }