Skip to content

Commit

Permalink
feat: filter by insertions at all endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasKellerer committed Sep 13, 2023
1 parent 18a4a38 commit 3376cb7
Show file tree
Hide file tree
Showing 21 changed files with 761 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import org.genspectrum.lapis.controller.Delimiter.COMMA
import org.genspectrum.lapis.controller.Delimiter.TAB
import org.genspectrum.lapis.logging.RequestContext
import org.genspectrum.lapis.model.SiloQueryModel
import org.genspectrum.lapis.request.AminoAcidInsertion
import org.genspectrum.lapis.request.AminoAcidMutation
import org.genspectrum.lapis.request.CommonSequenceFilters
import org.genspectrum.lapis.request.MutationProportionsRequest
import org.genspectrum.lapis.request.NucleotideInsertion
import org.genspectrum.lapis.request.NucleotideMutation
import org.genspectrum.lapis.request.OrderByField
import org.genspectrum.lapis.request.SequenceFiltersRequestWithFields
Expand Down Expand Up @@ -99,6 +101,10 @@ class LapisController(
@Parameter(schema = Schema(ref = "#/components/schemas/$AMINO_ACID_MUTATIONS_SCHEMA"))
@RequestParam
aminoAcidMutations: List<AminoAcidMutation>?,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
@Parameter(
schema = Schema(ref = "#/components/schemas/$LIMIT_SCHEMA"),
description = LIMIT_DESCRIPTION,
Expand All @@ -122,6 +128,8 @@ class LapisController(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
fields ?: emptyList(),
orderBy ?: emptyList(),
limit,
Expand Down Expand Up @@ -173,11 +181,23 @@ class LapisController(
)
@RequestParam
offset: Int? = null,
@Parameter(
schema = Schema(ref = "#/components/schemas/$FORMAT_SCHEMA"),
description = FORMAT_DESCRIPTION,
)
@RequestParam
dataFormat: String? = null,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
): String {
val request = SequenceFiltersRequestWithFields(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
fields ?: emptyList(),
orderBy ?: emptyList(),
limit,
Expand Down Expand Up @@ -227,11 +247,23 @@ class LapisController(
)
@RequestParam
offset: Int? = null,
@Parameter(
schema = Schema(ref = "#/components/schemas/$FORMAT_SCHEMA"),
description = FORMAT_DESCRIPTION,
)
@RequestParam
dataFormat: String? = null,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
): String {
val request = SequenceFiltersRequestWithFields(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
fields ?: emptyList(),
orderBy ?: emptyList(),
limit,
Expand Down Expand Up @@ -332,11 +364,17 @@ class LapisController(
)
@RequestParam
dataFormat: String? = null,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
): LapisResponse<List<NucleotideMutationResponse>> {
val mutationProportionsRequest = MutationProportionsRequest(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
minProportion,
orderBy ?: emptyList(),
limit,
Expand Down Expand Up @@ -388,11 +426,17 @@ class LapisController(
)
@RequestParam
offset: Int? = null,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
): String {
val request = MutationProportionsRequest(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
minProportion,
orderBy ?: emptyList(),
limit,
Expand Down Expand Up @@ -442,11 +486,17 @@ class LapisController(
)
@RequestParam
offset: Int? = null,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
): String {
val request = MutationProportionsRequest(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
minProportion,
orderBy ?: emptyList(),
limit,
Expand Down Expand Up @@ -543,11 +593,17 @@ class LapisController(
)
@RequestParam
offset: Int? = null,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
): LapisResponse<List<AminoAcidMutationResponse>> {
val mutationProportionsRequest = MutationProportionsRequest(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
minProportion,
orderBy ?: emptyList(),
limit,
Expand Down Expand Up @@ -599,11 +655,17 @@ class LapisController(
)
@RequestParam
offset: Int? = null,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
): String {
val mutationProportionsRequest = MutationProportionsRequest(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
minProportion,
orderBy ?: emptyList(),
limit,
Expand Down Expand Up @@ -653,11 +715,17 @@ class LapisController(
)
@RequestParam
offset: Int? = null,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
): String {
val mutationProportionsRequest = MutationProportionsRequest(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
minProportion,
orderBy ?: emptyList(),
limit,
Expand Down Expand Up @@ -773,11 +841,18 @@ class LapisController(
)
@RequestParam
dataFormat: String? = null,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
): LapisResponse<List<DetailsData>> {
val request = SequenceFiltersRequestWithFields(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),

fields ?: emptyList(),
orderBy ?: emptyList(),
limit,
Expand Down Expand Up @@ -825,11 +900,17 @@ class LapisController(
)
@RequestParam
offset: Int? = null,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
): String {
val request = SequenceFiltersRequestWithFields(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
fields ?: emptyList(),
orderBy ?: emptyList(),
limit,
Expand Down Expand Up @@ -876,11 +957,17 @@ class LapisController(
)
@RequestParam
offset: Int? = null,
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
): String {
val request = SequenceFiltersRequestWithFields(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
fields ?: emptyList(),
orderBy ?: emptyList(),
limit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const val MIN_PROPORTION_PROPERTY = "minProportion"
const val FIELDS_PROPERTY = "fields"
const val NUCLEOTIDE_MUTATIONS_PROPERTY = "nucleotideMutations"
const val AMINO_ACID_MUTATIONS_PROPERTY = "aminoAcidMutations"
const val NUCLEOTIDE_INSERTIONS_PROPERTY = "nucleotideInsertions"
const val AMINO_ACID_INSERTIONS_PROPERTY = "aminoAcidInsertions"
const val ORDER_BY_PROPERTY = "orderBy"
const val LIMIT_PROPERTY = "limit"
const val OFFSET_PROPERTY = "offset"
Expand All @@ -16,6 +18,8 @@ val SPECIAL_REQUEST_PROPERTIES = listOf(
FIELDS_PROPERTY,
NUCLEOTIDE_MUTATIONS_PROPERTY,
AMINO_ACID_MUTATIONS_PROPERTY,
NUCLEOTIDE_INSERTIONS_PROPERTY,
AMINO_ACID_INSERTIONS_PROPERTY,
ORDER_BY_PROPERTY,
LIMIT_PROPERTY,
OFFSET_PROPERTY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package org.genspectrum.lapis.model

import org.genspectrum.lapis.config.SequenceFilterFieldType
import org.genspectrum.lapis.config.SequenceFilterFields
import org.genspectrum.lapis.request.AminoAcidInsertion
import org.genspectrum.lapis.request.AminoAcidMutation
import org.genspectrum.lapis.request.CommonSequenceFilters
import org.genspectrum.lapis.request.NucleotideInsertion
import org.genspectrum.lapis.request.NucleotideMutation
import org.genspectrum.lapis.silo.AminoAcidInsertionContains
import org.genspectrum.lapis.silo.AminoAcidSymbolEquals
import org.genspectrum.lapis.silo.And
import org.genspectrum.lapis.silo.DateBetween
Expand All @@ -14,6 +17,7 @@ import org.genspectrum.lapis.silo.HasAminoAcidMutation
import org.genspectrum.lapis.silo.HasNucleotideMutation
import org.genspectrum.lapis.silo.IntBetween
import org.genspectrum.lapis.silo.IntEquals
import org.genspectrum.lapis.silo.NucleotideInsertionContains
import org.genspectrum.lapis.silo.NucleotideSymbolEquals
import org.genspectrum.lapis.silo.PangoLineageEquals
import org.genspectrum.lapis.silo.SiloFilterExpression
Expand Down Expand Up @@ -68,8 +72,17 @@ class SiloFilterExpressionMapper(

val nucleotideMutationExpressions = sequenceFilters.nucleotideMutations.map { toNucleotideMutationFilter(it) }
val aminoAcidMutationExpressions = sequenceFilters.aaMutations.map { toAminoAcidMutationFilter(it) }

return And(filterExpressions + nucleotideMutationExpressions + aminoAcidMutationExpressions)
val nucleotideInsertionExpressions =
sequenceFilters.nucleotideInsertions.map { toNucleotideInsertionFilter(it) }
val aminoAcidInsertionExpressions = sequenceFilters.aminoAcidInsertions.map { toAminoAcidInsertionFilter(it) }

return And(
filterExpressions +
nucleotideMutationExpressions +
aminoAcidMutationExpressions +
nucleotideInsertionExpressions +
aminoAcidInsertionExpressions,
)
}

private fun mapToFilterExpressionIdentifier(
Expand Down Expand Up @@ -314,6 +327,18 @@ class SiloFilterExpressionMapper(
)
}

private fun toNucleotideInsertionFilter(nucleotideInsertion: NucleotideInsertion): NucleotideInsertionContains {
return NucleotideInsertionContains(nucleotideInsertion.position, nucleotideInsertion.insertions)
}

private fun toAminoAcidInsertionFilter(aminoAcidInsertion: AminoAcidInsertion): AminoAcidInsertionContains {
return AminoAcidInsertionContains(
aminoAcidInsertion.position,
aminoAcidInsertion.insertions,
aminoAcidInsertion.gene,
)
}

private enum class Filter {
StringEquals,
PangoLineage,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.genspectrum.lapis.request

import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import org.springframework.boot.jackson.JsonComponent
import org.springframework.core.convert.converter.Converter
import org.springframework.stereotype.Component

data class AminoAcidInsertion(val position: Int, val gene: String, val insertions: String) {
companion object {
fun fromString(aminoAcidInsertion: String): AminoAcidInsertion {
val match = AMINO_ACID_INSERTION_REGEX.find(aminoAcidInsertion)
?: throw IllegalArgumentException("Invalid nucleotide mutation: $aminoAcidInsertion")

val matchGroups = match.groups

val position = matchGroups["position"]?.value?.toInt()
?: throw IllegalArgumentException(
"Invalid amino acid insertion: $aminoAcidInsertion: Did not find position",
)

val gene = matchGroups["gene"]?.value
?: throw IllegalArgumentException(
"Invalid amino acid insertion: $aminoAcidInsertion: Did not find gene",
)

val insertions = matchGroups["insertion"]?.value?.replace("?", ".*")
?: throw IllegalArgumentException(
"Invalid amino acid insertion: $aminoAcidInsertion: Did not find insertions",
)

return AminoAcidInsertion(
position,
gene,
insertions,
)
}
}
}

private val AMINO_ACID_INSERTION_REGEX =
Regex(
"""^ins_(?<gene>[a-zA-Z0-9_-]+):(?<position>\d+):(?<insertion>[a-zA-Z0-9?_-]+)?$""",
)

@JsonComponent
class AminoAcidInsertionDeserializer : JsonDeserializer<AminoAcidInsertion>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) =
AminoAcidInsertion.fromString(p.valueAsString)
}

@Component
class StringToAminoAcidInsertionConverter : Converter<String, AminoAcidInsertion> {
override fun convert(source: String) = AminoAcidInsertion.fromString(source)
}
Loading

0 comments on commit 3376cb7

Please sign in to comment.