Skip to content

Commit 3376cb7

Browse files
author
Jonas Kellerer
committed
feat: filter by insertions at all endpoints
1 parent 18a4a38 commit 3376cb7

21 files changed

+761
-35
lines changed

lapis2/src/main/kotlin/org/genspectrum/lapis/controller/LapisController.kt

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import org.genspectrum.lapis.controller.Delimiter.COMMA
1212
import org.genspectrum.lapis.controller.Delimiter.TAB
1313
import org.genspectrum.lapis.logging.RequestContext
1414
import org.genspectrum.lapis.model.SiloQueryModel
15+
import org.genspectrum.lapis.request.AminoAcidInsertion
1516
import org.genspectrum.lapis.request.AminoAcidMutation
1617
import org.genspectrum.lapis.request.CommonSequenceFilters
1718
import org.genspectrum.lapis.request.MutationProportionsRequest
19+
import org.genspectrum.lapis.request.NucleotideInsertion
1820
import org.genspectrum.lapis.request.NucleotideMutation
1921
import org.genspectrum.lapis.request.OrderByField
2022
import org.genspectrum.lapis.request.SequenceFiltersRequestWithFields
@@ -99,6 +101,10 @@ class LapisController(
99101
@Parameter(schema = Schema(ref = "#/components/schemas/$AMINO_ACID_MUTATIONS_SCHEMA"))
100102
@RequestParam
101103
aminoAcidMutations: List<AminoAcidMutation>?,
104+
@RequestParam
105+
nucleotideInsertions: List<NucleotideInsertion>?,
106+
@RequestParam
107+
aminoAcidInsertions: List<AminoAcidInsertion>?,
102108
@Parameter(
103109
schema = Schema(ref = "#/components/schemas/$LIMIT_SCHEMA"),
104110
description = LIMIT_DESCRIPTION,
@@ -122,6 +128,8 @@ class LapisController(
122128
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
123129
nucleotideMutations ?: emptyList(),
124130
aminoAcidMutations ?: emptyList(),
131+
nucleotideInsertions ?: emptyList(),
132+
aminoAcidInsertions ?: emptyList(),
125133
fields ?: emptyList(),
126134
orderBy ?: emptyList(),
127135
limit,
@@ -173,11 +181,23 @@ class LapisController(
173181
)
174182
@RequestParam
175183
offset: Int? = null,
184+
@Parameter(
185+
schema = Schema(ref = "#/components/schemas/$FORMAT_SCHEMA"),
186+
description = FORMAT_DESCRIPTION,
187+
)
188+
@RequestParam
189+
dataFormat: String? = null,
190+
@RequestParam
191+
nucleotideInsertions: List<NucleotideInsertion>?,
192+
@RequestParam
193+
aminoAcidInsertions: List<AminoAcidInsertion>?,
176194
): String {
177195
val request = SequenceFiltersRequestWithFields(
178196
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
179197
nucleotideMutations ?: emptyList(),
180198
aminoAcidMutations ?: emptyList(),
199+
nucleotideInsertions ?: emptyList(),
200+
aminoAcidInsertions ?: emptyList(),
181201
fields ?: emptyList(),
182202
orderBy ?: emptyList(),
183203
limit,
@@ -227,11 +247,23 @@ class LapisController(
227247
)
228248
@RequestParam
229249
offset: Int? = null,
250+
@Parameter(
251+
schema = Schema(ref = "#/components/schemas/$FORMAT_SCHEMA"),
252+
description = FORMAT_DESCRIPTION,
253+
)
254+
@RequestParam
255+
dataFormat: String? = null,
256+
@RequestParam
257+
nucleotideInsertions: List<NucleotideInsertion>?,
258+
@RequestParam
259+
aminoAcidInsertions: List<AminoAcidInsertion>?,
230260
): String {
231261
val request = SequenceFiltersRequestWithFields(
232262
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
233263
nucleotideMutations ?: emptyList(),
234264
aminoAcidMutations ?: emptyList(),
265+
nucleotideInsertions ?: emptyList(),
266+
aminoAcidInsertions ?: emptyList(),
235267
fields ?: emptyList(),
236268
orderBy ?: emptyList(),
237269
limit,
@@ -332,11 +364,17 @@ class LapisController(
332364
)
333365
@RequestParam
334366
dataFormat: String? = null,
367+
@RequestParam
368+
nucleotideInsertions: List<NucleotideInsertion>?,
369+
@RequestParam
370+
aminoAcidInsertions: List<AminoAcidInsertion>?,
335371
): LapisResponse<List<NucleotideMutationResponse>> {
336372
val mutationProportionsRequest = MutationProportionsRequest(
337373
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
338374
nucleotideMutations ?: emptyList(),
339375
aminoAcidMutations ?: emptyList(),
376+
nucleotideInsertions ?: emptyList(),
377+
aminoAcidInsertions ?: emptyList(),
340378
minProportion,
341379
orderBy ?: emptyList(),
342380
limit,
@@ -388,11 +426,17 @@ class LapisController(
388426
)
389427
@RequestParam
390428
offset: Int? = null,
429+
@RequestParam
430+
nucleotideInsertions: List<NucleotideInsertion>?,
431+
@RequestParam
432+
aminoAcidInsertions: List<AminoAcidInsertion>?,
391433
): String {
392434
val request = MutationProportionsRequest(
393435
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
394436
nucleotideMutations ?: emptyList(),
395437
aminoAcidMutations ?: emptyList(),
438+
nucleotideInsertions ?: emptyList(),
439+
aminoAcidInsertions ?: emptyList(),
396440
minProportion,
397441
orderBy ?: emptyList(),
398442
limit,
@@ -442,11 +486,17 @@ class LapisController(
442486
)
443487
@RequestParam
444488
offset: Int? = null,
489+
@RequestParam
490+
nucleotideInsertions: List<NucleotideInsertion>?,
491+
@RequestParam
492+
aminoAcidInsertions: List<AminoAcidInsertion>?,
445493
): String {
446494
val request = MutationProportionsRequest(
447495
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
448496
nucleotideMutations ?: emptyList(),
449497
aminoAcidMutations ?: emptyList(),
498+
nucleotideInsertions ?: emptyList(),
499+
aminoAcidInsertions ?: emptyList(),
450500
minProportion,
451501
orderBy ?: emptyList(),
452502
limit,
@@ -543,11 +593,17 @@ class LapisController(
543593
)
544594
@RequestParam
545595
offset: Int? = null,
596+
@RequestParam
597+
nucleotideInsertions: List<NucleotideInsertion>?,
598+
@RequestParam
599+
aminoAcidInsertions: List<AminoAcidInsertion>?,
546600
): LapisResponse<List<AminoAcidMutationResponse>> {
547601
val mutationProportionsRequest = MutationProportionsRequest(
548602
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
549603
nucleotideMutations ?: emptyList(),
550604
aminoAcidMutations ?: emptyList(),
605+
nucleotideInsertions ?: emptyList(),
606+
aminoAcidInsertions ?: emptyList(),
551607
minProportion,
552608
orderBy ?: emptyList(),
553609
limit,
@@ -599,11 +655,17 @@ class LapisController(
599655
)
600656
@RequestParam
601657
offset: Int? = null,
658+
@RequestParam
659+
nucleotideInsertions: List<NucleotideInsertion>?,
660+
@RequestParam
661+
aminoAcidInsertions: List<AminoAcidInsertion>?,
602662
): String {
603663
val mutationProportionsRequest = MutationProportionsRequest(
604664
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
605665
nucleotideMutations ?: emptyList(),
606666
aminoAcidMutations ?: emptyList(),
667+
nucleotideInsertions ?: emptyList(),
668+
aminoAcidInsertions ?: emptyList(),
607669
minProportion,
608670
orderBy ?: emptyList(),
609671
limit,
@@ -653,11 +715,17 @@ class LapisController(
653715
)
654716
@RequestParam
655717
offset: Int? = null,
718+
@RequestParam
719+
nucleotideInsertions: List<NucleotideInsertion>?,
720+
@RequestParam
721+
aminoAcidInsertions: List<AminoAcidInsertion>?,
656722
): String {
657723
val mutationProportionsRequest = MutationProportionsRequest(
658724
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
659725
nucleotideMutations ?: emptyList(),
660726
aminoAcidMutations ?: emptyList(),
727+
nucleotideInsertions ?: emptyList(),
728+
aminoAcidInsertions ?: emptyList(),
661729
minProportion,
662730
orderBy ?: emptyList(),
663731
limit,
@@ -773,11 +841,18 @@ class LapisController(
773841
)
774842
@RequestParam
775843
dataFormat: String? = null,
844+
@RequestParam
845+
nucleotideInsertions: List<NucleotideInsertion>?,
846+
@RequestParam
847+
aminoAcidInsertions: List<AminoAcidInsertion>?,
776848
): LapisResponse<List<DetailsData>> {
777849
val request = SequenceFiltersRequestWithFields(
778850
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
779851
nucleotideMutations ?: emptyList(),
780852
aminoAcidMutations ?: emptyList(),
853+
nucleotideInsertions ?: emptyList(),
854+
aminoAcidInsertions ?: emptyList(),
855+
781856
fields ?: emptyList(),
782857
orderBy ?: emptyList(),
783858
limit,
@@ -825,11 +900,17 @@ class LapisController(
825900
)
826901
@RequestParam
827902
offset: Int? = null,
903+
@RequestParam
904+
nucleotideInsertions: List<NucleotideInsertion>?,
905+
@RequestParam
906+
aminoAcidInsertions: List<AminoAcidInsertion>?,
828907
): String {
829908
val request = SequenceFiltersRequestWithFields(
830909
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
831910
nucleotideMutations ?: emptyList(),
832911
aminoAcidMutations ?: emptyList(),
912+
nucleotideInsertions ?: emptyList(),
913+
aminoAcidInsertions ?: emptyList(),
833914
fields ?: emptyList(),
834915
orderBy ?: emptyList(),
835916
limit,
@@ -876,11 +957,17 @@ class LapisController(
876957
)
877958
@RequestParam
878959
offset: Int? = null,
960+
@RequestParam
961+
nucleotideInsertions: List<NucleotideInsertion>?,
962+
@RequestParam
963+
aminoAcidInsertions: List<AminoAcidInsertion>?,
879964
): String {
880965
val request = SequenceFiltersRequestWithFields(
881966
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
882967
nucleotideMutations ?: emptyList(),
883968
aminoAcidMutations ?: emptyList(),
969+
nucleotideInsertions ?: emptyList(),
970+
aminoAcidInsertions ?: emptyList(),
884971
fields ?: emptyList(),
885972
orderBy ?: emptyList(),
886973
limit,

lapis2/src/main/kotlin/org/genspectrum/lapis/controller/SpecialProperties.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const val MIN_PROPORTION_PROPERTY = "minProportion"
66
const val FIELDS_PROPERTY = "fields"
77
const val NUCLEOTIDE_MUTATIONS_PROPERTY = "nucleotideMutations"
88
const val AMINO_ACID_MUTATIONS_PROPERTY = "aminoAcidMutations"
9+
const val NUCLEOTIDE_INSERTIONS_PROPERTY = "nucleotideInsertions"
10+
const val AMINO_ACID_INSERTIONS_PROPERTY = "aminoAcidInsertions"
911
const val ORDER_BY_PROPERTY = "orderBy"
1012
const val LIMIT_PROPERTY = "limit"
1113
const val OFFSET_PROPERTY = "offset"
@@ -16,6 +18,8 @@ val SPECIAL_REQUEST_PROPERTIES = listOf(
1618
FIELDS_PROPERTY,
1719
NUCLEOTIDE_MUTATIONS_PROPERTY,
1820
AMINO_ACID_MUTATIONS_PROPERTY,
21+
NUCLEOTIDE_INSERTIONS_PROPERTY,
22+
AMINO_ACID_INSERTIONS_PROPERTY,
1923
ORDER_BY_PROPERTY,
2024
LIMIT_PROPERTY,
2125
OFFSET_PROPERTY,

lapis2/src/main/kotlin/org/genspectrum/lapis/model/SiloFilterExpressionMapper.kt

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package org.genspectrum.lapis.model
22

33
import org.genspectrum.lapis.config.SequenceFilterFieldType
44
import org.genspectrum.lapis.config.SequenceFilterFields
5+
import org.genspectrum.lapis.request.AminoAcidInsertion
56
import org.genspectrum.lapis.request.AminoAcidMutation
67
import org.genspectrum.lapis.request.CommonSequenceFilters
8+
import org.genspectrum.lapis.request.NucleotideInsertion
79
import org.genspectrum.lapis.request.NucleotideMutation
10+
import org.genspectrum.lapis.silo.AminoAcidInsertionContains
811
import org.genspectrum.lapis.silo.AminoAcidSymbolEquals
912
import org.genspectrum.lapis.silo.And
1013
import org.genspectrum.lapis.silo.DateBetween
@@ -14,6 +17,7 @@ import org.genspectrum.lapis.silo.HasAminoAcidMutation
1417
import org.genspectrum.lapis.silo.HasNucleotideMutation
1518
import org.genspectrum.lapis.silo.IntBetween
1619
import org.genspectrum.lapis.silo.IntEquals
20+
import org.genspectrum.lapis.silo.NucleotideInsertionContains
1721
import org.genspectrum.lapis.silo.NucleotideSymbolEquals
1822
import org.genspectrum.lapis.silo.PangoLineageEquals
1923
import org.genspectrum.lapis.silo.SiloFilterExpression
@@ -68,8 +72,17 @@ class SiloFilterExpressionMapper(
6872

6973
val nucleotideMutationExpressions = sequenceFilters.nucleotideMutations.map { toNucleotideMutationFilter(it) }
7074
val aminoAcidMutationExpressions = sequenceFilters.aaMutations.map { toAminoAcidMutationFilter(it) }
71-
72-
return And(filterExpressions + nucleotideMutationExpressions + aminoAcidMutationExpressions)
75+
val nucleotideInsertionExpressions =
76+
sequenceFilters.nucleotideInsertions.map { toNucleotideInsertionFilter(it) }
77+
val aminoAcidInsertionExpressions = sequenceFilters.aminoAcidInsertions.map { toAminoAcidInsertionFilter(it) }
78+
79+
return And(
80+
filterExpressions +
81+
nucleotideMutationExpressions +
82+
aminoAcidMutationExpressions +
83+
nucleotideInsertionExpressions +
84+
aminoAcidInsertionExpressions,
85+
)
7386
}
7487

7588
private fun mapToFilterExpressionIdentifier(
@@ -314,6 +327,18 @@ class SiloFilterExpressionMapper(
314327
)
315328
}
316329

330+
private fun toNucleotideInsertionFilter(nucleotideInsertion: NucleotideInsertion): NucleotideInsertionContains {
331+
return NucleotideInsertionContains(nucleotideInsertion.position, nucleotideInsertion.insertions)
332+
}
333+
334+
private fun toAminoAcidInsertionFilter(aminoAcidInsertion: AminoAcidInsertion): AminoAcidInsertionContains {
335+
return AminoAcidInsertionContains(
336+
aminoAcidInsertion.position,
337+
aminoAcidInsertion.insertions,
338+
aminoAcidInsertion.gene,
339+
)
340+
}
341+
317342
private enum class Filter {
318343
StringEquals,
319344
PangoLineage,
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.genspectrum.lapis.request
2+
3+
import com.fasterxml.jackson.core.JsonParser
4+
import com.fasterxml.jackson.databind.DeserializationContext
5+
import com.fasterxml.jackson.databind.JsonDeserializer
6+
import org.springframework.boot.jackson.JsonComponent
7+
import org.springframework.core.convert.converter.Converter
8+
import org.springframework.stereotype.Component
9+
10+
data class AminoAcidInsertion(val position: Int, val gene: String, val insertions: String) {
11+
companion object {
12+
fun fromString(aminoAcidInsertion: String): AminoAcidInsertion {
13+
val match = AMINO_ACID_INSERTION_REGEX.find(aminoAcidInsertion)
14+
?: throw IllegalArgumentException("Invalid nucleotide mutation: $aminoAcidInsertion")
15+
16+
val matchGroups = match.groups
17+
18+
val position = matchGroups["position"]?.value?.toInt()
19+
?: throw IllegalArgumentException(
20+
"Invalid amino acid insertion: $aminoAcidInsertion: Did not find position",
21+
)
22+
23+
val gene = matchGroups["gene"]?.value
24+
?: throw IllegalArgumentException(
25+
"Invalid amino acid insertion: $aminoAcidInsertion: Did not find gene",
26+
)
27+
28+
val insertions = matchGroups["insertion"]?.value?.replace("?", ".*")
29+
?: throw IllegalArgumentException(
30+
"Invalid amino acid insertion: $aminoAcidInsertion: Did not find insertions",
31+
)
32+
33+
return AminoAcidInsertion(
34+
position,
35+
gene,
36+
insertions,
37+
)
38+
}
39+
}
40+
}
41+
42+
private val AMINO_ACID_INSERTION_REGEX =
43+
Regex(
44+
"""^ins_(?<gene>[a-zA-Z0-9_-]+):(?<position>\d+):(?<insertion>[a-zA-Z0-9?_-]+)?$""",
45+
)
46+
47+
@JsonComponent
48+
class AminoAcidInsertionDeserializer : JsonDeserializer<AminoAcidInsertion>() {
49+
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) =
50+
AminoAcidInsertion.fromString(p.valueAsString)
51+
}
52+
53+
@Component
54+
class StringToAminoAcidInsertionConverter : Converter<String, AminoAcidInsertion> {
55+
override fun convert(source: String) = AminoAcidInsertion.fromString(source)
56+
}

0 commit comments

Comments
 (0)