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

felleslosninger / einnsyn-backend / 22318855984

23 Feb 2026 06:12PM UTC coverage: 85.005% (+0.03%) from 84.977%
22318855984

push

github

web-flow
Merge pull request #606 from felleslosninger/EIN-4789-konvertering-reindeksering-av-gamle-lagret-soek-3

EIN-4789: Bugfix, LegacySearchConverter

2516 of 3387 branches covered (74.28%)

Branch coverage included in aggregate %.

7648 of 8570 relevant lines covered (89.24%)

3.74 hits per line

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

93.09
src/main/java/no/einnsyn/backend/common/search/SearchQueryService.java
1
package no.einnsyn.backend.common.search;
2

3
import co.elastic.clients.elasticsearch._types.FieldValue;
4
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
5
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
6
import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
7
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
8
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQuery;
9
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField;
10
import java.time.LocalDate;
11
import java.time.LocalDateTime;
12
import java.time.ZoneId;
13
import java.time.ZonedDateTime;
14
import java.time.format.DateTimeFormatter;
15
import java.time.format.DateTimeParseException;
16
import java.time.temporal.ChronoUnit;
17
import java.util.ArrayList;
18
import java.util.LinkedHashSet;
19
import java.util.List;
20
import no.einnsyn.backend.authentication.AuthenticationService;
21
import no.einnsyn.backend.common.exceptions.models.BadRequestException;
22
import no.einnsyn.backend.common.exceptions.models.EInnsynException;
23
import no.einnsyn.backend.common.queryparameters.models.FilterParameters;
24
import no.einnsyn.backend.entities.enhet.EnhetService;
25
import org.springframework.stereotype.Service;
26
import org.springframework.util.StringUtils;
27

28
@Service
29
@SuppressWarnings("java:S1192") // Allow string literals
30
public class SearchQueryService {
31

32
  public enum DateBoundary {
3✔
33
    NONE,
6✔
34
    START_OF_DAY,
6✔
35
    END_OF_DAY
6✔
36
  }
37

38
  private static final List<String> allowedEntities =
4✔
39
      List.of("Journalpost", "Saksmappe", "Moetemappe", "Moetesak");
2✔
40
  public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
2✔
41
  private static final ZoneId NORWEGIAN_ZONE = ZoneId.of("Europe/Oslo");
4✔
42

43
  private final AuthenticationService authenticationService;
44
  private final EnhetService enhetService;
45

46
  public SearchQueryService(
47
      AuthenticationService authenticationService, EnhetService enhetService) {
2✔
48
    this.authenticationService = authenticationService;
3✔
49
    this.enhetService = enhetService;
3✔
50
  }
1✔
51

52
  private String toIsoDateTime(String dateString, DateBoundary boundary) {
53
    if (dateString == null) {
2!
54
      return null;
×
55
    }
56

57
    ZonedDateTime zonedDateTime;
58

59
    // DateTime
60
    if (dateString.contains("T")) {
4✔
61
      // Try parsing zoned first; if no zone/offset is present, assume system default zone
62
      try {
63
        zonedDateTime = ZonedDateTime.parse(dateString);
3✔
64
      } catch (DateTimeParseException e) {
×
65
        var localDateTime = LocalDateTime.parse(dateString);
×
66
        zonedDateTime = localDateTime.atZone(NORWEGIAN_ZONE);
×
67
      }
1✔
68
    }
69

70
    // Date (no timestamp)
71
    else {
72
      zonedDateTime = LocalDate.parse(dateString).atStartOfDay(NORWEGIAN_ZONE);
5✔
73
      // Adjust to start or end of day if needed
74
      zonedDateTime =
1✔
75
          switch (boundary) {
3!
76
            case START_OF_DAY -> zonedDateTime.withHour(0).withMinute(0).withSecond(0).withNano(0);
×
77
            case END_OF_DAY ->
78
                zonedDateTime.withHour(23).withMinute(59).withSecond(59).withNano(999_999_999);
10✔
79
            case NONE -> zonedDateTime;
1✔
80
          };
81
    }
82

83
    return zonedDateTime.format(FORMATTER);
4✔
84
  }
85

86
  /**
87
   * Resolve IDs from identifiers like orgnummer, email, ...
88
   *
89
   * @param enhetIdentifiers the list of identifiers to resolve
90
   * @return the list of resolved Enhet IDs
91
   * @throws BadRequestException if an Enhet is not found
92
   */
93
  private List<String> resolveEnhetIds(List<String> enhetIdentifiers) throws BadRequestException {
94
    var enhetIds = new ArrayList<String>(enhetIdentifiers.size());
6✔
95
    for (var identifier : enhetIdentifiers) {
10✔
96
      var enhetId = enhetService.resolveId(identifier);
5✔
97
      if (enhetId == null) {
2!
98
        throw new BadRequestException("Enhet not found: " + identifier);
×
99
      }
100
      enhetIds.add(enhetId);
4✔
101
    }
1✔
102
    return enhetIds;
2✔
103
  }
104

105
  /**
106
   * Adds a filter to the bool query.
107
   *
108
   * @param bqb the bool query builder
109
   * @param propertyName the name of the property to filter on
110
   * @param list the list of values to filter by
111
   */
112
  private void addFilter(BoolQuery.Builder bqb, String propertyName, List<String> list) {
113
    if (list != null && !list.isEmpty()) {
5!
114
      var fieldValueList = list.stream().map(FieldValue::of).toList();
6✔
115
      bqb.filter(
6✔
116
          TermsQuery.of(tqb -> tqb.field(propertyName).terms(tqfb -> tqfb.value(fieldValueList)))
12✔
117
              ._toQuery());
3✔
118
    }
119
  }
1✔
120

121
  /**
122
   * Adds a must-not clause to the bool query.
123
   *
124
   * @param bqb the bool query builder
125
   * @param propertyName the name of the property to exclude
126
   * @param list the list of values to exclude
127
   */
128
  private void addMustNot(BoolQuery.Builder bqb, String propertyName, List<String> list) {
129
    if (list != null && !list.isEmpty()) {
5!
130
      var fieldValueList = list.stream().map(FieldValue::of).toList();
6✔
131
      bqb.mustNot(
6✔
132
          TermsQuery.of(tqb -> tqb.field(propertyName).terms(tqfb -> tqfb.value(fieldValueList)))
12✔
133
              ._toQuery());
3✔
134
    }
135
  }
1✔
136

137
  /**
138
   * Build a ES Query from the given search parameters.
139
   *
140
   * @param filterParameters the filter parameters
141
   * @return the bool query builder
142
   * @throws EInnsynException if an error occurs
143
   */
144
  public BoolQuery.Builder getQueryBuilder(FilterParameters filterParameters)
145
      throws EInnsynException {
146
    return getQueryBuilder(filterParameters, false);
5✔
147
  }
148

149
  /**
150
   * Build a ES Query from the given search parameters.
151
   *
152
   * @param filterParameters the filter parameters
153
   * @param uncensored whether to exclude sensitive fields or not
154
   * @return the bool query builder
155
   * @throws EInnsynException if an error occurs
156
   */
157
  public BoolQuery.Builder getQueryBuilder(FilterParameters filterParameters, boolean uncensored)
158
      throws EInnsynException {
159
    var rootBoolQueryBuilder = new BoolQuery.Builder();
4✔
160

161
    // Filter by entity. We don't want unexpected entities (Innsynskrav, Downloads, ...), so we'll
162
    // always filter by entities.
163
    if (filterParameters.getEntity() != null) {
3✔
164
      addFilter(rootBoolQueryBuilder, "type", filterParameters.getEntity());
7✔
165
    } else {
166
      addFilter(rootBoolQueryBuilder, "type", allowedEntities);
5✔
167
    }
168

169
    // Exclude hidden enhets and unaccessible documents
170
    if (!uncensored) {
2✔
171
      var authenticatedEnhetId = authenticationService.getEnhetId();
4✔
172
      var authenticatedSubtreeIdList = enhetService.getSubtreeIdList(authenticatedEnhetId);
5✔
173

174
      // Filter hidden enhets that the user is not authenticated for
175
      var hiddenEnhetList = enhetService.findHidden();
4✔
176
      var hiddenIdList =
1✔
177
          hiddenEnhetList.stream()
2✔
178
              .map(e -> e.getId())
3✔
179
              .filter(e -> !authenticatedSubtreeIdList.contains(e))
1!
180
              .toList();
2✔
181
      if (!hiddenIdList.isEmpty()) {
3!
182
        addMustNot(rootBoolQueryBuilder, "administrativEnhetTransitive", hiddenIdList);
×
183
      }
184
    }
185

186
    // Exclude unaccessible documents
187
    if (!uncensored) {
2✔
188
      var authenticatedEnhetId = authenticationService.getEnhetId();
4✔
189
      var accessibleAfterBoolQueryBuilder = new BoolQuery.Builder();
4✔
190
      accessibleAfterBoolQueryBuilder.minimumShouldMatch("1");
4✔
191

192
      // Allow documents with a valid accessibleAfter
193
      accessibleAfterBoolQueryBuilder.should(
4✔
194
          RangeQuery.of(r -> r.date(d -> d.field("accessibleAfter").lte("now")))._toQuery());
15✔
195

196
      // If logged in, allow documents with a valid administrativEnhet
197
      if (authenticatedEnhetId != null) {
2✔
198
        var authenticatedEnhetFieldValues = List.of(FieldValue.of(authenticatedEnhetId));
4✔
199
        accessibleAfterBoolQueryBuilder.should(
7✔
200
            new TermsQuery.Builder()
201
                .field("administrativEnhetTransitive")
5✔
202
                .terms(new TermsQueryField.Builder().value(authenticatedEnhetFieldValues).build())
4✔
203
                .build()
1✔
204
                ._toQuery());
3✔
205
      }
206

207
      rootBoolQueryBuilder.filter(accessibleAfterBoolQueryBuilder.build()._toQuery());
8✔
208
    }
209

210
    // Filter by search query
211
    var queryString = filterParameters.getQuery();
3✔
212
    if (StringUtils.hasText(queryString)) {
3✔
213
      rootBoolQueryBuilder.must(
3✔
214
          uncensored
2✔
215
              ? getSearchStringQuery(
8✔
216
                  queryString,
217
                  List.of(
3✔
218
                      "search_id",
219
                      "search_innhold",
220
                      "search_innhold_SENSITIV",
221
                      "search_tittel^3",
222
                      "search_tittel_SENSITIV^3"),
223
                  3.0f,
224
                  1.0f)
225
              : getSearchStringQuery(
7✔
226
                  queryString,
227
                  List.of("search_id", "search_innhold_SENSITIV", "search_tittel_SENSITIV^3"),
4✔
228
                  List.of("search_id", "search_innhold", "search_tittel^3"),
3✔
229
                  3.0f,
230
                  2.0f));
231
    }
232

233
    // Filter by tittel
234
    if (filterParameters.getTittel() != null) {
3✔
235
      for (var tittel : filterParameters.getTittel()) {
11✔
236
        if (StringUtils.hasText(tittel)) {
3!
237
          rootBoolQueryBuilder.filter(
3✔
238
              uncensored
2!
239
                  ? getSearchStringQuery(tittel, List.of("search_tittel", "search_tittel_SENSITIV"))
×
240
                  : getSearchStringQuery(
5✔
241
                      tittel, List.of("search_tittel_SENSITIV"), List.of("search_tittel")));
3✔
242
        }
243
      }
1✔
244
    }
245

246
    // Filter by skjermingshjemmel
247
    if (filterParameters.getSkjermingshjemmel() != null) {
3✔
248
      for (var skjermingshjemmel : filterParameters.getSkjermingshjemmel()) {
11✔
249
        if (StringUtils.hasText(skjermingshjemmel)) {
3!
250
          rootBoolQueryBuilder.filter(
5✔
251
              getSearchStringQuery(skjermingshjemmel, List.of("skjerming.skjermingshjemmel")));
4✔
252
        }
253
      }
1✔
254
    }
255

256
    // Filter by korrespondansepartNavn
257
    if (filterParameters.getKorrespondansepartNavn() != null) {
3✔
258
      for (var korrespondansepartNavn : filterParameters.getKorrespondansepartNavn()) {
11✔
259
        if (StringUtils.hasText(korrespondansepartNavn)) {
3!
260
          rootBoolQueryBuilder.filter(
5✔
261
              getSearchStringQuery(
3✔
262
                  korrespondansepartNavn,
263
                  List.of("korrespondansepart.korrespondansepartNavn_SENSITIV"),
2✔
264
                  List.of("korrespondansepart.korrespondansepartNavn")));
1✔
265
        }
266
      }
1✔
267
    }
268

269
    // Filter by saksaar
270
    addFilter(rootBoolQueryBuilder, "saksaar", filterParameters.getSaksaar());
6✔
271

272
    // Filter by sakssekvensnummer
273
    addFilter(rootBoolQueryBuilder, "sakssekvensnummer", filterParameters.getSakssekvensnummer());
6✔
274

275
    // Filter by saksnummer
276
    addFilter(rootBoolQueryBuilder, "saksnummer", filterParameters.getSaksnummer());
6✔
277

278
    // Filter by journalpostnummer
279
    addFilter(rootBoolQueryBuilder, "journalpostnummer", filterParameters.getJournalpostnummer());
6✔
280

281
    // Filter by journalsekvensnummer
282
    addFilter(
5✔
283
        rootBoolQueryBuilder, "journalsekvensnummer", filterParameters.getJournalsekvensnummer());
1✔
284

285
    // Filter by moetesaksaar
286
    addFilter(rootBoolQueryBuilder, "møtesaksår", filterParameters.getMoetesaksaar());
6✔
287

288
    // Filter by moetesakssekvensnummer
289
    addFilter(
5✔
290
        rootBoolQueryBuilder,
291
        "møtesakssekvensnummer",
292
        filterParameters.getMoetesakssekvensnummer());
1✔
293

294
    // Matches against administrativEnhet or children
295
    if (filterParameters.getAdministrativEnhet() != null) {
3✔
296
      var enhetList = resolveEnhetIds(filterParameters.getAdministrativEnhet());
5✔
297
      addFilter(rootBoolQueryBuilder, "administrativEnhetTransitive", enhetList);
5✔
298
    }
299

300
    // Exact matches against administrativEnhet
301
    if (filterParameters.getAdministrativEnhetExact() != null) {
3✔
302
      var enhetList = resolveEnhetIds(filterParameters.getAdministrativEnhetExact());
5✔
303
      addFilter(rootBoolQueryBuilder, "administrativEnhet", enhetList);
5✔
304
    }
305

306
    // Exclude documents from given administrativEnhet or children
307
    if (filterParameters.getExcludeAdministrativEnhet() != null) {
3✔
308
      var enhetList = resolveEnhetIds(filterParameters.getExcludeAdministrativEnhet());
5✔
309
      addMustNot(rootBoolQueryBuilder, "administrativEnhetTransitive", enhetList);
5✔
310
    }
311

312
    // Exclude documents from given administrativEnhet
313
    if (filterParameters.getExcludeAdministrativEnhetExact() != null) {
3✔
314
      var enhetList = resolveEnhetIds(filterParameters.getExcludeAdministrativEnhetExact());
5✔
315
      addMustNot(rootBoolQueryBuilder, "administrativEnhet", enhetList);
5✔
316
    }
317

318
    // Filter by publisertDatoTo
319
    if (filterParameters.getPublisertDatoTo() != null) {
3✔
320
      var date = toIsoDateTime(filterParameters.getPublisertDatoTo(), DateBoundary.END_OF_DAY);
6✔
321
      rootBoolQueryBuilder.filter(
5✔
322
          RangeQuery.of(r -> r.date(d -> d.field("publisertDato").lte(date)))._toQuery());
16✔
323
    }
324

325
    // Filter by publisertDatoFrom
326
    if (filterParameters.getPublisertDatoFrom() != null) {
3✔
327
      var date = toIsoDateTime(filterParameters.getPublisertDatoFrom(), DateBoundary.NONE);
6✔
328
      rootBoolQueryBuilder.filter(
5✔
329
          RangeQuery.of(r -> r.date(d -> d.field("publisertDato").gte(date)))._toQuery());
16✔
330
    }
331

332
    // Filter by oppdatertDatoTo
333
    if (filterParameters.getOppdatertDatoTo() != null) {
3✔
334
      var date = toIsoDateTime(filterParameters.getOppdatertDatoTo(), DateBoundary.END_OF_DAY);
6✔
335
      rootBoolQueryBuilder.filter(
5✔
336
          RangeQuery.of(r -> r.date(d -> d.field("oppdatertDato").lte(date)))._toQuery());
16✔
337
    }
338

339
    // Filter by oppdatertDatoFrom
340
    if (filterParameters.getOppdatertDatoFrom() != null) {
3✔
341
      var date = toIsoDateTime(filterParameters.getOppdatertDatoFrom(), DateBoundary.NONE);
6✔
342
      rootBoolQueryBuilder.filter(
5✔
343
          RangeQuery.of(r -> r.date(d -> d.field("oppdatertDato").gte(date)))._toQuery());
16✔
344
    }
345

346
    // Filter by dokumentetsDatoTo
347
    if (filterParameters.getDokumentetsDatoTo() != null) {
3✔
348
      var date = toIsoDateTime(filterParameters.getDokumentetsDatoTo(), DateBoundary.END_OF_DAY);
6✔
349
      rootBoolQueryBuilder.filter(
5✔
350
          RangeQuery.of(r -> r.date(d -> d.field("dokumentetsDato").lte(date)))._toQuery());
16✔
351
    }
352

353
    // Filter by dokumentetsDatoFrom
354
    if (filterParameters.getDokumentetsDatoFrom() != null) {
3✔
355
      var date = toIsoDateTime(filterParameters.getDokumentetsDatoFrom(), DateBoundary.NONE);
6✔
356
      rootBoolQueryBuilder.filter(
5✔
357
          RangeQuery.of(r -> r.date(d -> d.field("dokumentetsDato").gte(date)))._toQuery());
16✔
358
    }
359

360
    // Filter by journaldatoTo
361
    if (filterParameters.getJournaldatoTo() != null) {
3✔
362
      var date = toIsoDateTime(filterParameters.getJournaldatoTo(), DateBoundary.END_OF_DAY);
6✔
363
      rootBoolQueryBuilder.filter(
5✔
364
          RangeQuery.of(r -> r.date(d -> d.field("journaldato").lte(date)))._toQuery());
16✔
365
    }
366

367
    // Filter by journaldatoFrom
368
    if (filterParameters.getJournaldatoFrom() != null) {
3✔
369
      var date = toIsoDateTime(filterParameters.getJournaldatoFrom(), DateBoundary.NONE);
6✔
370
      rootBoolQueryBuilder.filter(
5✔
371
          RangeQuery.of(r -> r.date(d -> d.field("journaldato").gte(date)))._toQuery());
16✔
372
    }
373

374
    // Filter by moetedatoTo
375
    if (filterParameters.getMoetedatoTo() != null) {
3✔
376
      var date = toIsoDateTime(filterParameters.getMoetedatoTo(), DateBoundary.END_OF_DAY);
6✔
377
      rootBoolQueryBuilder.filter(
5✔
378
          RangeQuery.of(r -> r.date(d -> d.field("moetedato").lte(date)))._toQuery());
16✔
379
    }
380

381
    // Filter by moetedatoFrom
382
    if (filterParameters.getMoetedatoFrom() != null) {
3✔
383
      var date = toIsoDateTime(filterParameters.getMoetedatoFrom(), DateBoundary.NONE);
6✔
384
      rootBoolQueryBuilder.filter(
5✔
385
          RangeQuery.of(r -> r.date(d -> d.field("moetedato").gte(date)))._toQuery());
16✔
386
    }
387

388
    // Filter by standardDatoTo
389
    if (filterParameters.getStandardDatoTo() != null) {
3✔
390
      var date = toIsoDateTime(filterParameters.getStandardDatoTo(), DateBoundary.END_OF_DAY);
6✔
391
      rootBoolQueryBuilder.filter(
5✔
392
          RangeQuery.of(r -> r.date(d -> d.field("standardDato").lte(date)))._toQuery());
16✔
393
    }
394

395
    // Filter by standardDatoFrom
396
    if (filterParameters.getStandardDatoFrom() != null) {
3✔
397
      var date = toIsoDateTime(filterParameters.getStandardDatoFrom(), DateBoundary.NONE);
6✔
398
      rootBoolQueryBuilder.filter(
5✔
399
          RangeQuery.of(r -> r.date(d -> d.field("standardDato").gte(date)))._toQuery());
16✔
400
    }
401

402
    // Filter by fulltext
403
    if (filterParameters.getFulltext() != null) {
3✔
404
      rootBoolQueryBuilder.filter(
5✔
405
          TermQuery.of(tqb -> tqb.field("fulltext").value(filterParameters.getFulltext()))
9✔
406
              ._toQuery());
3✔
407
    }
408

409
    // Filter by journalposttype
410
    if (filterParameters.getJournalposttype() != null) {
3✔
411
      addFilter(rootBoolQueryBuilder, "journalposttype", filterParameters.getJournalposttype());
6✔
412
    }
413

414
    // Get specific IDs
415
    addFilter(rootBoolQueryBuilder, "id", filterParameters.getIds());
6✔
416

417
    return rootBoolQueryBuilder;
2✔
418
  }
419

420
  /**
421
   * /** Get a sensitive query that handles uncensored/censored searches.
422
   *
423
   * @param queryString the query string to search for
424
   * @param sensitiveFields the list of sensitive fields
425
   * @param nonSensitiveFields the list of non-sensitive fields
426
   * @return the constructed query
427
   */
428
  private static Query getSearchStringQuery(
429
      String queryString, List<String> sensitiveFields, List<String> nonSensitiveFields) {
430
    return getSearchStringQuery(queryString, sensitiveFields, nonSensitiveFields, 1.0f, 1.0f);
7✔
431
  }
432

433
  /**
434
   * Get a sensitive query that handles uncensored/censored searches.
435
   *
436
   * @param queryString the search query string
437
   * @param sensitiveFields the list of sensitive field names to search in
438
   * @param nonSensitiveFields the list of non-sensitive field names to search in
439
   * @param exactBoost the boost factor for exact matches
440
   * @param looseBoost the boost factor for loose matches
441
   * @return the constructed query
442
   */
443
  private static Query getSearchStringQuery(
444
      String queryString,
445
      List<String> sensitiveFields,
446
      List<String> nonSensitiveFields,
447
      float exactBoost,
448
      float looseBoost) {
449
    var boolQueryBuilder = new BoolQuery.Builder();
4✔
450
    boolQueryBuilder.minimumShouldMatch("1");
4✔
451

452
    // Match sensitive fields for documents from the past year only
453
    // Round to start of day to ensure consistent query hashing for preference-based shard routing
454
    var lastYear =
1✔
455
        ZonedDateTime.now(NORWEGIAN_ZONE)
2✔
456
            .truncatedTo(ChronoUnit.DAYS)
2✔
457
            .minusYears(1)
2✔
458
            .format(FORMATTER);
2✔
459
    var gteLastYear =
2✔
460
        RangeQuery.of(r -> r.date(d -> d.field("publisertDato").gte(lastYear)))._toQuery();
15✔
461

462
    // For recent documents, evaluate the query against the union of sensitive and non-sensitive
463
    // fields so negation semantics (e.g. -word) are applied across both field groups.
464
    var recentFields = new LinkedHashSet<String>(nonSensitiveFields);
5✔
465
    recentFields.addAll(sensitiveFields);
4✔
466
    var recentDocumentsQuery =
6✔
467
        new BoolQuery.Builder()
468
            .filter(gteLastYear)
3✔
469
            .must(
1✔
470
                getSearchStringQuery(
3✔
471
                    queryString, List.copyOf(recentFields), exactBoost, looseBoost))
3✔
472
            .build();
2✔
473

474
    // For older (or missing publisertDato) documents, only evaluate non-sensitive fields.
475
    var olderDocumentsQuery =
6✔
476
        new BoolQuery.Builder()
477
            .mustNot(gteLastYear)
5✔
478
            .must(getSearchStringQuery(queryString, nonSensitiveFields, exactBoost, looseBoost))
4✔
479
            .build();
2✔
480
    boolQueryBuilder.should(b -> b.bool(recentDocumentsQuery));
9✔
481
    boolQueryBuilder.should(b -> b.bool(olderDocumentsQuery));
9✔
482

483
    return boolQueryBuilder.build()._toQuery();
4✔
484
  }
485

486
  /**
487
   * A direct wrapper around SearchQueryParser that doesn't consider sensitive fields.
488
   *
489
   * @param searchString the search string
490
   * @param fields the fields to search in
491
   * @return the constructed query
492
   */
493
  private static Query getSearchStringQuery(String searchString, List<String> fields) {
494
    return SearchQueryParser.parse(searchString, fields);
4✔
495
  }
496

497
  /**
498
   * A direct wrapper around SearchQueryParser that doesn't consider sensitive fields.
499
   *
500
   * @param searchString the search query string
501
   * @param fields the list of field names to search in
502
   * @param exactBoost the boost factor for exact matches
503
   * @param looseBoost the boost factor for loose matches
504
   * @return the constructed query
505
   */
506
  private static Query getSearchStringQuery(
507
      String searchString, List<String> fields, float exactBoost, float looseBoost) {
508
    return SearchQueryParser.parse(searchString, fields, exactBoost, looseBoost);
6✔
509
  }
510
}
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