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

knowledgepixels / nanodash / 24121036693

08 Apr 2026 06:18AM UTC coverage: 16.243% (+0.05%) from 16.194%
24121036693

push

github

tkuhn
fix: remove AjaxSelfUpdatingTimerBehavior causing repeated GET requests on search page

Closes #432

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

790 of 6002 branches covered (13.16%)

Branch coverage included in aggregate %.

1978 of 11039 relevant lines covered (17.92%)

2.45 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.util.ArrayList;
4
import java.util.HashMap;
5
import java.util.List;
6
import java.util.Map;
7
import java.util.Optional;
8

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

118
        form.add(ownFilter);
×
119

120
        progress = new Model<>();
×
121
        final Label progressLabel = new Label("progress", progress);
×
122
        add(progressLabel);
×
123

124
        if (searchText == null || searchText.isEmpty()) {
×
125
            add(new Label("nanopubs", "Enter a search term above."));
×
126
        } else {
127
            add(new AjaxLazyLoadPanel<NanopubResults>("nanopubs") {
×
128

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

186
                @Override
187
                protected void onContentLoaded(NanopubResults content, Optional<AjaxRequestTarget> target) {
188
                    super.onContentLoaded(content, target);
×
189
                    if (target.isPresent()) {
×
190
                        target.get().appendJavaScript("updateElements();");
×
191
                    }
192
                }
×
193

194
            });
195

196
        }
197
    }
×
198

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

230
}
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