1 | package eu.clarin.cmdi.vlo.pages; |
---|
2 | |
---|
3 | import eu.clarin.cmdi.vlo.FacetConstants; |
---|
4 | import eu.clarin.cmdi.vlo.Resources; |
---|
5 | import eu.clarin.cmdi.vlo.VloPageParameters; |
---|
6 | import eu.clarin.cmdi.vlo.config.VloConfig; |
---|
7 | import eu.clarin.cmdi.vlo.dao.AutoCompleteDao; |
---|
8 | import eu.clarin.cmdi.vlo.importer.FacetConceptMapping.FacetConcept; |
---|
9 | import eu.clarin.cmdi.vlo.importer.VLOMarshaller; |
---|
10 | import fiftyfive.wicket.basic.TruncatedLabel; |
---|
11 | import java.io.UnsupportedEncodingException; |
---|
12 | import java.net.URL; |
---|
13 | import java.net.URLEncoder; |
---|
14 | import java.util.ArrayList; |
---|
15 | import java.util.HashMap; |
---|
16 | import java.util.Iterator; |
---|
17 | import java.util.List; |
---|
18 | import java.util.Map; |
---|
19 | |
---|
20 | import org.apache.solr.client.solrj.SolrServerException; |
---|
21 | import org.apache.solr.client.solrj.response.FacetField; |
---|
22 | import org.apache.solr.common.SolrDocument; |
---|
23 | import org.apache.wicket.request.mapper.parameter.PageParameters; |
---|
24 | import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteTextField; |
---|
25 | import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable; |
---|
26 | import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator; |
---|
27 | import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn; |
---|
28 | import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; |
---|
29 | import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider; |
---|
30 | import org.apache.wicket.markup.html.WebMarkupContainer; |
---|
31 | import org.apache.wicket.markup.html.basic.Label; |
---|
32 | import org.apache.wicket.markup.html.basic.MultiLineLabel; |
---|
33 | import org.apache.wicket.markup.html.form.Button; |
---|
34 | import org.apache.wicket.markup.html.form.Form; |
---|
35 | import org.apache.wicket.markup.html.link.BookmarkablePageLink; |
---|
36 | import org.apache.wicket.markup.html.link.ExternalLink; |
---|
37 | import org.apache.wicket.markup.repeater.Item; |
---|
38 | import org.apache.wicket.markup.repeater.data.GridView; |
---|
39 | import org.apache.wicket.markup.repeater.data.IDataProvider; |
---|
40 | import org.apache.wicket.model.CompoundPropertyModel; |
---|
41 | import org.apache.wicket.model.IModel; |
---|
42 | import org.apache.wicket.model.ResourceModel; |
---|
43 | import org.apache.wicket.protocol.http.RequestUtils; |
---|
44 | import org.apache.wicket.request.Url; |
---|
45 | import org.apache.wicket.request.cycle.RequestCycle; |
---|
46 | |
---|
47 | public class FacetedSearchPage extends BasePage { |
---|
48 | |
---|
49 | private static final long serialVersionUID = 1L; |
---|
50 | |
---|
51 | private final SearchPageQuery query; |
---|
52 | private final static AutoCompleteDao autoCompleteDao = new AutoCompleteDao(); |
---|
53 | private final static String facetConceptsFile = VloConfig.getFacetConceptsFile(); |
---|
54 | private final static Map<String, FacetConcept> facetNameMap = VLOMarshaller.getFacetConceptMapping(facetConceptsFile).getFacetConceptMap(); |
---|
55 | |
---|
56 | public FacetedSearchPage() { |
---|
57 | this(new VloPageParameters()); |
---|
58 | } |
---|
59 | |
---|
60 | /** |
---|
61 | * @param parameters Page parameters |
---|
62 | * @throws SolrServerException |
---|
63 | */ |
---|
64 | public FacetedSearchPage(final VloPageParameters parameters) { |
---|
65 | super(parameters); |
---|
66 | query = new SearchPageQuery(parameters); |
---|
67 | addSearchBox(); |
---|
68 | addFacetColumns(); |
---|
69 | addSearchResults(); |
---|
70 | addSearchServiceForm(); |
---|
71 | } |
---|
72 | |
---|
73 | @SuppressWarnings("serial") |
---|
74 | private class SearchBoxForm extends Form<SearchPageQuery> { |
---|
75 | |
---|
76 | private final AutoCompleteTextField<String> searchBox; |
---|
77 | |
---|
78 | /* |
---|
79 | * Add multiline list of selected facet values |
---|
80 | */ |
---|
81 | private void addFacetOverview() { |
---|
82 | |
---|
83 | // get a map of the facets currently selected |
---|
84 | Map<String, String> selectedFacets; |
---|
85 | selectedFacets = query.getFilterQueryMap(); |
---|
86 | |
---|
87 | // create an interator for walking over the facets |
---|
88 | Iterator<Map.Entry<String, String>> entries |
---|
89 | = selectedFacets.entrySet().iterator(); |
---|
90 | |
---|
91 | /* |
---|
92 | * Wicket label to be used to show the list of facets that have been |
---|
93 | * selected. |
---|
94 | */ |
---|
95 | MultiLineLabel facetOverview; |
---|
96 | |
---|
97 | // walk over the facets |
---|
98 | if (!entries.hasNext()) { |
---|
99 | // not a single facet has been selected |
---|
100 | facetOverview = new MultiLineLabel("facetOverview", |
---|
101 | "No facets values selected"); |
---|
102 | } else { |
---|
103 | // at least one facet has been selected |
---|
104 | |
---|
105 | String string = "Selected facet values: "; |
---|
106 | |
---|
107 | // start building the the multiline label here |
---|
108 | String[] facetFields; |
---|
109 | int i = 0, lineLength = 0, |
---|
110 | maxLineLength = VloConfig.getFacetOverviewLength(); |
---|
111 | Boolean hasPrevious = false; |
---|
112 | |
---|
113 | /* |
---|
114 | * Get the facet fields. We need them in order to display |
---|
115 | * the values in the overview in the same order as their |
---|
116 | * respective facets are listed in the boxes. Store multiple |
---|
117 | * lines in one string. |
---|
118 | */ |
---|
119 | facetFields = VloConfig.getFacetFields(); |
---|
120 | |
---|
121 | while (i < facetFields.length) { |
---|
122 | |
---|
123 | // check if facet field is in selected facets map |
---|
124 | if (selectedFacets.containsKey(facetFields[i])) { |
---|
125 | String value = selectedFacets.get(facetFields[i]); |
---|
126 | lineLength = lineLength + value.length(); |
---|
127 | |
---|
128 | if (hasPrevious) { |
---|
129 | string = string.concat(", "); |
---|
130 | } |
---|
131 | |
---|
132 | if (lineLength > maxLineLength) { |
---|
133 | string = string.concat("\n"); |
---|
134 | lineLength = 0; |
---|
135 | hasPrevious = false; |
---|
136 | } |
---|
137 | |
---|
138 | string = string.concat(value); |
---|
139 | hasPrevious = true; |
---|
140 | } |
---|
141 | i++; |
---|
142 | } |
---|
143 | |
---|
144 | // create a new wicket multi line label |
---|
145 | facetOverview = new MultiLineLabel("facetOverview", string); |
---|
146 | } |
---|
147 | |
---|
148 | // finally, merge the label to the form |
---|
149 | this.add(facetOverview); |
---|
150 | } |
---|
151 | |
---|
152 | public SearchBoxForm(String id, SearchPageQuery query) { |
---|
153 | super(id, new CompoundPropertyModel<SearchPageQuery>(query)); |
---|
154 | add(new ExternalLink("vloHomeLink", VloConfig.getHomeUrl())); |
---|
155 | |
---|
156 | searchBox = new AutoCompleteTextField<String>("searchQuery") { |
---|
157 | @Override |
---|
158 | protected Iterator<String> getChoices(String input) { |
---|
159 | return autoCompleteDao.getChoices(input).iterator(); |
---|
160 | } |
---|
161 | }; |
---|
162 | |
---|
163 | add(searchBox); |
---|
164 | Button submit = new Button("searchSubmit"); |
---|
165 | add(submit); |
---|
166 | |
---|
167 | // merge link to help menu page |
---|
168 | String helpUrl = VloConfig.getHelpUrl(); |
---|
169 | ExternalLink helpLink = new ExternalLink("helpLink", helpUrl, "help"); |
---|
170 | add(helpLink); |
---|
171 | |
---|
172 | VloPageParameters param; |
---|
173 | param = new VloPageParameters(query.getPageParameters()); |
---|
174 | |
---|
175 | final RequestCycle reqCycle = getRequestCycle(); |
---|
176 | final Url reqUrl = Url.parse(reqCycle.urlFor(ShowResultPage.class, param.convert())); |
---|
177 | String thisURL = reqCycle.getUrlRenderer().renderFullUrl(reqUrl); |
---|
178 | |
---|
179 | try { |
---|
180 | thisURL = URLEncoder.encode(thisURL, "UTF-8"); |
---|
181 | } catch (UnsupportedEncodingException e) { |
---|
182 | } |
---|
183 | |
---|
184 | String feedbackFormUrl = VloConfig.getFeedbackFromUrl() + thisURL; |
---|
185 | ExternalLink feedbackLink = new ExternalLink("feedbackLink", feedbackFormUrl, "found an error?"); |
---|
186 | add(feedbackLink); |
---|
187 | |
---|
188 | addFacetOverview(); |
---|
189 | } |
---|
190 | |
---|
191 | @Override |
---|
192 | protected void onSubmit() { |
---|
193 | SearchPageQuery query = getModelObject(); |
---|
194 | PageParameters param = query.getPageParameters(); |
---|
195 | |
---|
196 | VloPageParameters newParam = new VloPageParameters(); |
---|
197 | |
---|
198 | setResponsePage(FacetedSearchPage.class, newParam); |
---|
199 | } |
---|
200 | } |
---|
201 | |
---|
202 | private void addSearchBox() { |
---|
203 | add(new SearchBoxForm("searchForm", query)); |
---|
204 | } |
---|
205 | |
---|
206 | @SuppressWarnings("serial") |
---|
207 | private void addFacetColumns() { |
---|
208 | GridView<FacetField> facetColumns = new GridView<FacetField>("facetColumns", (IDataProvider<FacetField>) new SolrFacetDataProvider(query.getSolrQuery() |
---|
209 | .getCopy())) { |
---|
210 | @Override |
---|
211 | protected void populateItem(Item<FacetField> item) { |
---|
212 | String facetName = ((FacetField) item.getDefaultModelObject()).getName(); |
---|
213 | String descriptionTooltip = ""; |
---|
214 | if (facetNameMap.containsKey(facetName)) { |
---|
215 | descriptionTooltip = facetNameMap.get(facetName).getDescription(); |
---|
216 | } |
---|
217 | item.add(new FacetBoxPanel("facetBox", item.getModel(), descriptionTooltip).create(query)); |
---|
218 | } |
---|
219 | |
---|
220 | @Override |
---|
221 | protected void populateEmptyItem(Item<FacetField> item) { |
---|
222 | item.add(new Label("facetBox", "")); |
---|
223 | } |
---|
224 | }; |
---|
225 | facetColumns.setColumns(2); |
---|
226 | add(facetColumns); |
---|
227 | } |
---|
228 | |
---|
229 | @SuppressWarnings("serial") |
---|
230 | private void addSearchResults() { |
---|
231 | List<IColumn<SolrDocument, String>> columns; |
---|
232 | columns = new ArrayList<IColumn<SolrDocument, String>>(); |
---|
233 | columns.add(new AbstractColumn<SolrDocument, String>(new ResourceModel(Resources.NAME)) { |
---|
234 | |
---|
235 | @Override |
---|
236 | public void populateItem(Item<ICellPopulator<SolrDocument>> cellItem, String componentId, IModel<SolrDocument> rowModel) { |
---|
237 | cellItem.add(new DocumentLinkPanel(componentId, rowModel, query)); |
---|
238 | } |
---|
239 | }); |
---|
240 | columns.add(new AbstractColumn<SolrDocument, String>(new ResourceModel(Resources.DESCRIPTION)) { |
---|
241 | |
---|
242 | @Override |
---|
243 | public void populateItem(Item<ICellPopulator<SolrDocument>> cellItem, String componentId, IModel<SolrDocument> rowModel) { |
---|
244 | String descriptionText = (String) rowModel.getObject().getFirstValue(FacetConstants.FIELD_DESCRIPTION); |
---|
245 | cellItem.add(new TruncatedLabel(componentId, 100, descriptionText)); |
---|
246 | } |
---|
247 | }); |
---|
248 | AjaxFallbackDefaultDataTable<SolrDocument, String> searchResultList = new AjaxFallbackDefaultDataTable<SolrDocument, String>("searchResults", columns, (ISortableDataProvider<SolrDocument, String>) new SolrDocumentDataProvider(query.getSolrQuery().getCopy()), 30); |
---|
249 | |
---|
250 | add(searchResultList); |
---|
251 | } |
---|
252 | |
---|
253 | /** |
---|
254 | * Add contentSearch form (FCS) |
---|
255 | * |
---|
256 | * @param solrDocument |
---|
257 | */ |
---|
258 | private void addSearchServiceForm() { |
---|
259 | |
---|
260 | BookmarkablePageLink link; |
---|
261 | link = new BookmarkablePageLink("link", FacetedSearchPage.class); |
---|
262 | link.add(new Label("naar deze pagina")); |
---|
263 | |
---|
264 | // get values for cql endpoint substitution |
---|
265 | String cqlEndpointFilter = VloConfig.getCqlEndpointFilter(); |
---|
266 | String cqlEndpointAlternative = VloConfig.getCqlEndpointAlternative(); |
---|
267 | |
---|
268 | final WebMarkupContainer contentSearchContainer = new WebMarkupContainer("contentSearch"); |
---|
269 | add(contentSearchContainer); |
---|
270 | |
---|
271 | // get all documents with an entry for the FCS |
---|
272 | SearchServiceDataProvider dataProvider = new SearchServiceDataProvider(query.getSolrQuery()); |
---|
273 | if (dataProvider.size() > 0 && dataProvider.size() <= 200) { // at least one and not more than x records with FCS endpoint in result set? |
---|
274 | try { |
---|
275 | // building map (CQL endpoint -> List of resource IDs) |
---|
276 | HashMap<String, List<String>> aggregationContextMap = new HashMap<String, List<String>>(); |
---|
277 | |
---|
278 | int offset = 0; |
---|
279 | int fetchSize = 1000; |
---|
280 | int totalResults = (int) dataProvider.size(); |
---|
281 | while (offset < totalResults) { |
---|
282 | Iterator<SolrDocument> iter = dataProvider.iterator(offset, fetchSize); |
---|
283 | while (iter.hasNext()) { |
---|
284 | SolrDocument document = iter.next(); |
---|
285 | String id = document.getFirstValue(FacetConstants.FIELD_ID).toString(); |
---|
286 | String fcsEndpoint = document.getFirstValue(FacetConstants.FIELD_SEARCH_SERVICE).toString(); |
---|
287 | if (aggregationContextMap.containsKey(fcsEndpoint)) { |
---|
288 | aggregationContextMap.get(fcsEndpoint).add(id); |
---|
289 | } else { |
---|
290 | List<String> idArray = new ArrayList<String>(); |
---|
291 | idArray.add(id); |
---|
292 | |
---|
293 | // substitute endpoint |
---|
294 | if (cqlEndpointFilter.length() == 0) { |
---|
295 | // no substitution |
---|
296 | } else { |
---|
297 | // check for the need to substitute |
---|
298 | } |
---|
299 | |
---|
300 | if (cqlEndpointFilter.equals(cqlEndpointFilter)) { |
---|
301 | // no substitution, take the value from the record |
---|
302 | aggregationContextMap.put(fcsEndpoint, idArray); |
---|
303 | } else { |
---|
304 | // substitution, take the alternative url |
---|
305 | aggregationContextMap.put(cqlEndpointAlternative, idArray); |
---|
306 | } |
---|
307 | } |
---|
308 | } |
---|
309 | |
---|
310 | offset += fetchSize; |
---|
311 | } |
---|
312 | |
---|
313 | // merge HTML form to container |
---|
314 | String labelString; |
---|
315 | if (totalResults == 1) { |
---|
316 | labelString = "Plain text search via Federated Content Search (supported by one resource in this result set)"; |
---|
317 | } else { |
---|
318 | labelString = "Plain text search via Federated Content Search (supported by " + totalResults |
---|
319 | + " resources in this result set)"; |
---|
320 | } |
---|
321 | Label contentSearchLabel = new Label("contentSearchForm", HtmlFormCreator.getContentSearchForm( |
---|
322 | aggregationContextMap, labelString)); |
---|
323 | contentSearchLabel.setEscapeModelStrings(false); |
---|
324 | contentSearchContainer.add(contentSearchLabel); |
---|
325 | // contentSearchContainer.merge(link); |
---|
326 | } catch (UnsupportedEncodingException uee) { |
---|
327 | contentSearchContainer.setVisible(false); |
---|
328 | } |
---|
329 | } else { |
---|
330 | contentSearchContainer.setVisible(false); |
---|
331 | } |
---|
332 | } |
---|
333 | } |
---|