• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

knowledgepixels / nanodash / 20952016558

13 Jan 2026 09:42AM UTC coverage: 14.349% (-0.6%) from 14.953%
20952016558

push

github

tkuhn
fix(SearchPage): Search page tampered with cached list

546 of 5034 branches covered (10.85%)

Branch coverage included in aggregate %.

1501 of 9232 relevant lines covered (16.26%)

2.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
src/main/java/com/knowledgepixels/nanodash/page/SearchPage.java
1
package com.knowledgepixels.nanodash.page;
2

3
import java.time.Duration;
4
import java.util.ArrayList;
5
import java.util.HashMap;
6
import java.util.List;
7
import java.util.Map;
8
import java.util.Optional;
9

10
import org.apache.wicket.ajax.AjaxRequestTarget;
11
import org.apache.wicket.ajax.AjaxSelfUpdatingTimerBehavior;
12
import org.apache.wicket.extensions.ajax.markup.html.AjaxLazyLoadPanel;
13
import org.apache.wicket.markup.html.WebMarkupContainer;
14
import org.apache.wicket.markup.html.basic.Label;
15
import org.apache.wicket.markup.html.form.CheckBox;
16
import org.apache.wicket.markup.html.form.Form;
17
import org.apache.wicket.markup.html.form.RadioChoice;
18
import org.apache.wicket.markup.html.form.TextField;
19
import org.apache.wicket.model.Model;
20
import org.apache.wicket.request.mapper.parameter.PageParameters;
21
import org.nanopub.extra.services.ApiResponseEntry;
22
import org.nanopub.extra.services.QueryRef;
23
import org.slf4j.Logger;
24
import org.slf4j.LoggerFactory;
25

26
import com.google.common.collect.ArrayListMultimap;
27
import com.google.common.collect.Multimap;
28
import com.knowledgepixels.nanodash.ApiCache;
29
import com.knowledgepixels.nanodash.NanodashPreferences;
30
import com.knowledgepixels.nanodash.NanodashSession;
31
import com.knowledgepixels.nanodash.NanopubElement;
32
import com.knowledgepixels.nanodash.User;
33
import com.knowledgepixels.nanodash.Utils;
34
import com.knowledgepixels.nanodash.component.NanopubResults;
35
import com.knowledgepixels.nanodash.component.TitleBar;
36

37
/**
38
 * SearchPage allows users to search for nanopublications by URI or free text.
39
 */
40
public class SearchPage extends NanodashPage {
41

42
    private static final Logger logger = LoggerFactory.getLogger(SearchPage.class);
×
43

44
    /**
45
     * The mount path for this page.
46
     */
47
    public static final String MOUNT_PATH = "/search";
48

49
    /**
50
     * {@inheritDoc}
51
     */
52
    @Override
53
    public String getMountPath() {
54
        return MOUNT_PATH;
×
55
    }
56

57
    private TextField<String> searchField;
58
    private CheckBox filterUser;
59
    private Model<String> progress;
60

61
    private Map<String, String> pubKeyMap;
62
    private RadioChoice<String> pubkeySelection;
63

64
    /**
65
     * Constructor for SearchPage.
66
     *
67
     * @param parameters Page parameters containing the search query, filter option, and public key.
68
     */
69
    public SearchPage(final PageParameters parameters) {
70
        super(parameters);
×
71

72
        add(new TitleBar("titlebar", this, "search"));
×
73

74
        final String searchText = parameters.get("query").toString();
×
75
        final Boolean filterCheck = Boolean.valueOf(parameters.get("filter").toString());
×
76
        final String pubkey = parameters.get("pubkey").toString();
×
77

78
        Form<?> form = new Form<Void>("form") {
×
79

80
            protected void onSubmit() {
81
                String searchText = searchField.getModelObject().trim();
×
82
                Boolean filterCheck = filterUser.getModelObject();
×
83
                String pubkey = pubkeySelection.getModelObject();
×
84
                PageParameters params = new PageParameters();
×
85
                params.set("query", searchText);
×
86
                params.set("filter", filterCheck);
×
87
                if (pubkey != null) params.set("pubkey", pubkey);
×
88
                setResponsePage(SearchPage.class, params);
×
89
            }
×
90
        };
91
        add(form);
×
92

93
        form.add(searchField = new TextField<String>("search", Model.of(searchText)));
×
94
        WebMarkupContainer ownFilter = new WebMarkupContainer("own-filter");
×
95
        ownFilter.add(filterUser = new CheckBox("filter", Model.of(filterCheck)));
×
96
        NanodashSession session = NanodashSession.get();
×
97
        ownFilter.setVisible(!NanodashPreferences.get().isReadOnlyMode() && session.getUserIri() != null);
×
98
        ArrayList<String> pubKeyList = new ArrayList<>();
×
99
        if (session.getUserIri() != null) {
×
100
            pubKeyMap = new HashMap<>();
×
101
            String lKeyShort = Utils.getShortPubkeyhashLabel(session.getPubkeyString(), session.getUserIri());
×
102
            pubKeyList.add(lKeyShort);
×
103
            pubKeyMap.put(lKeyShort, session.getPubkeyString());
×
104
            for (String pk : User.getPubkeyhashes(session.getUserIri(), null)) {
×
105
                String keyShort = Utils.getShortPubkeyhashLabel(pk, session.getUserIri());
×
106
                if (!pubKeyMap.containsKey(keyShort)) {
×
107
                    pubKeyList.add(keyShort);
×
108
                    pubKeyMap.put(keyShort, pk);
×
109
                }
110
            }
×
111
        }
112

113
        pubkeySelection = new RadioChoice<String>("pubkeygroup", Model.of(pubkey), pubKeyList);
×
114
        if (!pubKeyList.isEmpty() && pubkeySelection.getModelObject() == null) {
×
115
            pubkeySelection.setDefaultModelObject(pubKeyList.get(0));
×
116
        }
117
        ownFilter.add(pubkeySelection);
×
118

119
        form.add(ownFilter);
×
120

121
        // TODO: Progress bar doesn't update at the moment:
122
        progress = new Model<>();
×
123
        final Label progressLabel = new Label("progress", progress);
×
124
        progressLabel.setOutputMarkupId(true);
×
125
        progressLabel.add(new AjaxSelfUpdatingTimerBehavior(Duration.ofMillis(1000)));
×
126
        add(progressLabel);
×
127

128
        if (searchText == null || searchText.isEmpty()) {
×
129
            add(new Label("nanopubs", "Enter a search term above."));
×
130
        } else {
131
            add(new AjaxLazyLoadPanel<NanopubResults>("nanopubs") {
×
132

133
                @Override
134
                public NanopubResults getLazyLoadComponent(String markupId) {
135
                    Multimap<String, String> nanopubParams = ArrayListMultimap.create();
×
136
                    List<ApiResponseEntry> nanopubResults = new ArrayList<>();
×
137
                    String s = searchText;
×
138
                    if (s != null) {
×
139
                        s = s.trim();
×
140
                        if (s.matches("https?://[^\\s]+")) {
×
141
                            logger.info("URI QUERY: {}", s);
×
142
                            nanopubParams.put("ref", s);
×
143
                            if (Boolean.TRUE.equals(filterCheck)) {
×
144
                                String pubkey = pubKeyMap.get(pubkeySelection.getModelObject());
×
145
                                logger.info("Filter for PUBKEY: {}", pubkey);
×
146
                                nanopubParams.put("pubkey", pubkey);
×
147
                            }
148
                            try {
149
                                // nanopubResults = ApiAccess.getAll("find_nanopubs_with_uri", nanopubParams).getData();
150
                                nanopubResults = ApiCache.retrieveResponseSync(new QueryRef("find-uri-references", nanopubParams), false).getData();
×
151
                            } catch (Exception ex) {
×
152
                                logger.error("Error while running the query for URI", ex);
×
153
                            }
×
154
//                                                        nanopubResults = ApiAccess.getRecent("find_nanopubs_with_uri", nanopubParams, progress);
155
                        } else {
156
                            String freeTextQuery = getFreeTextQuery(s);
×
157
                            if (!freeTextQuery.isEmpty()) {
×
158
                                logger.info("FREE TEXT QUERY: {}", freeTextQuery);
×
159
                                nanopubParams.put("query", freeTextQuery);
×
160
                                if (filterCheck != null && Boolean.TRUE.equals(filterCheck)) {
×
161
                                    String pubkey = pubKeyMap.get(pubkeySelection.getModelObject());
×
162
                                    logger.info("Filter for PUBKEY: {}", pubkey);
×
163
                                    nanopubParams.put("pubkey", pubkey);
×
164
                                }
165
                                try {
166
                                    // nanopubResults = ApiAccess.getAll("find_nanopubs_with_text", nanopubParams).getData();
167
                                    nanopubResults = ApiCache.retrieveResponseSync(new QueryRef("fulltext-search-on-labels", nanopubParams), false).getData();
×
168
                                } catch (Exception ex) {
×
169
                                    logger.error("Error during search", ex);
×
170
                                }
×
171
                                logger.info("Results in: {}", nanopubResults.size());
×
172
//                                                                nanopubResults = ApiAccess.getRecent("find_nanopubs_with_text", nanopubParams, progress);
173
                            }
174
                        }
175
                    }
176
                    nanopubResults = new ArrayList<>(nanopubResults);
×
177
                    nanopubResults.sort(new ApiResponseEntry.DataComparator());
×
178
                    List<String> nanopubIds = new ArrayList<>();
×
179
                    while (!nanopubResults.isEmpty() && nanopubIds.size() < 100) {
×
180
                        String npUri = nanopubResults.remove(0).get("np");
×
181
                        if (!nanopubIds.contains(npUri)) nanopubIds.add(npUri);
×
182
                    }
×
183
                    progress.setObject("");
×
184
                    if (nanopubIds.isEmpty()) progress.setObject("nothing found");
×
185
                    List<NanopubElement> nanopubs = new ArrayList<>();
×
186
                    for (String id : nanopubIds) nanopubs.add(NanopubElement.get(id));
×
187
                    return NanopubResults.fromList(markupId, nanopubs, 20);
×
188
                }
189

190
                @Override
191
                protected void onContentLoaded(NanopubResults content, Optional<AjaxRequestTarget> target) {
192
                    super.onContentLoaded(content, target);
×
193
                    if (target.isPresent()) {
×
194
                        target.get().appendJavaScript("updateElements();");
×
195
                    }
196
                }
×
197

198
            });
199

200
        }
201
    }
×
202

203
    private static String getFreeTextQuery(String searchText) {
204
        String freeTextQuery = "";
×
205
        String previous = "AND";
×
206
        String preprocessed = "";
×
207
        boolean inQuote = true;
×
208
        for (String s : searchText.replaceAll("\\(\\s+", "(").replaceAll("\\s+\\)", ")").replaceAll("@", "").split("\"")) {
×
209
            inQuote = !inQuote;
×
210
            if (inQuote) {
×
211
                s = "\\\"" + String.join("@", s.split("[^\\p{L}0-9\\-_]+")) + "\\\"";
×
212
            }
213
            preprocessed += s;
×
214
        }
215
        preprocessed = preprocessed.trim();
×
216
        for (String s : preprocessed.split("[^\\p{L}0-9\\-_\\(\\)@\\\"\\\\]+")) {
×
217
            if (s.matches("[0-9].*")) continue;
×
218
            if (!s.matches("AND|OR|\\(+|\\)+|\\(?NOT")) {
×
219
                if (s.toLowerCase().matches("and|or|not")) {
×
220
                    // ignore lower-case and/or/not
221
                    continue;
×
222
                }
223
                if (!previous.matches("AND|OR|\\(?NOT")) {
×
224
                    freeTextQuery += " AND";
×
225
                }
226
            }
227
            freeTextQuery += " " + s.toLowerCase();
×
228
            previous = s;
×
229
        }
230
        freeTextQuery = freeTextQuery.replaceAll("@", " ").trim();
×
231
        return freeTextQuery;
×
232
    }
233

234
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc