Skip to content

Commit

Permalink
search service with structured data (beta)
Browse files Browse the repository at this point in the history
  • Loading branch information
kadhonn committed Sep 8, 2024
1 parent f28aff4 commit 010c352
Show file tree
Hide file tree
Showing 18 changed files with 453 additions and 146 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ object KeyFilters {
.sortedBy { it.first }
}

private fun doesKeyMatchFilter(
fun doesKeyMatchFilter(
key: Key,
keyFilter: Key
): Boolean {
Expand All @@ -36,7 +36,7 @@ object KeyFilters {
return true
}

private fun containsVariant(key: Key, variant: Variant): Boolean {
fun containsVariant(key: Key, variant: Variant): Boolean {
if (variant.variantValue == "*") {
return doesContainVariantName(key, variant.variantName)
}
Expand All @@ -54,8 +54,17 @@ object KeyFilters {
}

private fun doesContainVariantName(key: Key, variantName: String): Boolean {
for (selfVariant in key.variants) {
if (variantName == selfVariant.variantName) {
for (variant in key.variants) {
if (variantName == variant.variantName) {
return true
}
}
return false
}

fun doesContainVariantValue(key: Key, variantName: String, variantValues: List<String>): Boolean {
for (value in variantValues) {
if (containsVariant(key, Variant(variantName, value))) {
return true
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ class KeySelector private constructor(
fun builder(propertyName: String): KeySelectorBuilder {
return KeySelectorBuilder(propertyName)
}

fun builder(key: Key): KeySelectorBuilder {
val builder = KeySelectorBuilder(key.name)
key.variants.forEach { builder.thenVariant(it) }
return builder
}
}

class KeySelectorBuilder internal constructor(private val propertyName: String) {
Expand All @@ -79,6 +85,11 @@ class KeySelector private constructor(
return this
}

fun thenVariant(variant: Variant): KeySelectorBuilder {
variants.add(Pair(variant.variantName, listOf(variant.variantValue)))
return this
}

fun build(): KeySelector {
return KeySelector(propertyName, variants.toList())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class EventService @Autowired constructor(
listOf(
FilterQueryEntryDTO(SemanticKeys.LOCATION_NAME),
FilterQueryEntryDTO(SemanticKeys.LOCATION_CITY),
FilterQueryEntryDTO(SemanticKeys.CONCERT_BANDLIST, true),
FilterQueryEntryDTO(SemanticKeys.CONCERT_BANDLIST),
)
)
)
Expand All @@ -133,7 +133,7 @@ class EventService @Autowired constructor(

fun getSources(): List<String> {
val allSources =
caller.getFiltersFor(FilterQueryDTO(listOf(FilterQueryEntryDTO(SemanticKeys.SOURCES, true))))
caller.getFiltersFor(FilterQueryDTO(listOf(FilterQueryEntryDTO(SemanticKeys.SOURCES))))
return allSources[SemanticKeys.SOURCES]!!
.map { normalize(it) }
.distinct()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package base.boudicca.query

import base.boudicca.model.structured.Key
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException
Expand Down Expand Up @@ -43,42 +44,46 @@ abstract class HasTwoChildren(

abstract class FieldAndTextExpression(
private val name: String,
private val fieldName: String,
keyFilter: String,
private val text: String,
) : Expression {

fun getFieldName(): String {
return fieldName
private val keyFilter = parseKeyFilter(keyFilter)

fun getKeyFilter(): Key {
return keyFilter
}

fun getText(): String {
return text
}

override fun toString(): String {
return "$name('$fieldName','$text')"
return "$name('${keyFilter.toKeyString()}','$text')"
}
}

abstract class FieldExpression(
private val name: String,
private val fieldName: String,
keyFilter: String,
) : Expression {

fun getFieldName(): String {
return fieldName
private val keyFilter = parseKeyFilter(keyFilter)

fun getKeyFilter(): Key {
return keyFilter
}

override fun toString(): String {
return "$name('$fieldName')"
return "$name('${keyFilter.toKeyString()}')"
}
}

abstract class DateExpression(
private val name: String,
dateFieldName: String,
dateKeyFilter: String,
dateText: String,
) : FieldAndTextExpression(name, dateFieldName, dateText) {
) : FieldAndTextExpression(name, dateKeyFilter, dateText) {

private val date: LocalDate

Expand All @@ -95,31 +100,42 @@ abstract class DateExpression(
}

override fun toString(): String {
return "$name('${getFieldName()}','${date.format(DateTimeFormatter.ISO_LOCAL_DATE)}')"
return "$name('${getKeyFilter().toKeyString()}','${date.format(DateTimeFormatter.ISO_LOCAL_DATE)}')"
}
}

abstract class AbstractDurationExpression(
private val name: String,
private val startDateField: String,
private val endDateField: String,
startDateKeyFilter: String,
endDateKeyFilter: String,
private val duration: Number,
) : Expression {

fun getStartDateField(): String {
return startDateField
private val startDateKeyFilter = parseKeyFilter(startDateKeyFilter)
private val endDateKeyFilter = parseKeyFilter(endDateKeyFilter)

fun getStartDateKeyFilter(): Key {
return startDateKeyFilter
}

fun getEndDateField(): String {
return endDateField
fun getEndDateKeyFilter(): Key {
return endDateKeyFilter
}

fun getDuration(): Number {
return duration
}

override fun toString(): String {
return "$name('${startDateField}','${endDateField}',${duration})"
return "$name('${startDateKeyFilter.toKeyString()}','${endDateKeyFilter.toKeyString()}',${duration})"
}
}

private fun parseKeyFilter(keyFilter: String): Key {
try {
return Key.parse(keyFilter)
} catch (e: IllegalArgumentException) {
throw QueryException("invalid keyfilter", e)
}
}

Expand All @@ -138,24 +154,24 @@ class NotExpression(
) : HasOneChild("NOT", child)

class ContainsExpression(
fieldName: String,
keyFilter: String,
text: String,
) : FieldAndTextExpression("CONTAINS", fieldName, text)
) : FieldAndTextExpression("CONTAINS", keyFilter, text)

class EqualsExpression(
fieldName: String,
keyFilter: String,
text: String,
) : FieldAndTextExpression("EQUALS", fieldName, text)
) : FieldAndTextExpression("EQUALS", keyFilter, text)

class BeforeExpression(
dateFieldName: String,
dateKeyFilter: String,
dateText: String,
) : DateExpression("BEFORE", dateFieldName, dateText)
) : DateExpression("BEFORE", dateKeyFilter, dateText)

class AfterExpression(
dateFieldName: String,
dateKeyFilter: String,
dateText: String,
) : DateExpression("AFTER", dateFieldName, dateText)
) : DateExpression("AFTER", dateKeyFilter, dateText)

class DurationShorterExpression(
startDateField: String,
Expand All @@ -170,5 +186,5 @@ class DurationLongerExpression(
) : AbstractDurationExpression("DURATIONLONGER", startDateField, endDateField, duration)

class HasFieldExpression(
fieldName: String,
) : FieldExpression("HASFIELD", fieldName)
keyFilter: String,
) : FieldExpression("HASFIELD", keyFilter)
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package base.boudicca.query

import base.boudicca.SemanticKeys
import base.boudicca.keyfilters.KeySelector
import base.boudicca.model.Entry
import base.boudicca.model.structured.VariantConstants
import base.boudicca.model.structured.selectKey
import base.boudicca.model.toStructuredEntry
import java.time.Instant
import java.time.OffsetDateTime
import java.time.ZoneId
Expand All @@ -19,7 +23,7 @@ object Utils {

fun order(entries: Collection<Entry>, dateCache: ConcurrentHashMap<String, OffsetDateTime>): List<Entry> {
return entries.toList()
.map { Pair(it, getStartDate(it[SemanticKeys.STARTDATE], dateCache)) }
.map { Pair(it, getStartDate(it, dateCache)) }
.sortedWith(
Comparator
.comparing<Pair<Entry, OffsetDateTime>, OffsetDateTime> { it.second }
Expand All @@ -29,12 +33,22 @@ object Utils {
}

private fun getStartDate(
dateText: String?,
entry: Entry,
startDateCache: ConcurrentHashMap<String, OffsetDateTime>
): OffsetDateTime {
if (dateText == null) {
val optionalDateText = entry.toStructuredEntry().selectKey(
KeySelector.builder(SemanticKeys.STARTDATE).thenVariant(
VariantConstants.FORMAT_VARIANT_NAME,
listOf(
VariantConstants.FormatVariantConstants.DATE_FORMAT_NAME,
VariantConstants.FormatVariantConstants.TEXT_FORMAT_NAME
)
).build()
)
if (optionalDateText.isEmpty) {
return Instant.ofEpochMilli(0).atOffset(ZoneOffset.MIN)
}
val dateText = optionalDateText.get().second
if (startDateCache.containsKey(dateText)) {
return startDateCache[dateText]!!
}
Expand Down
Loading

0 comments on commit 010c352

Please sign in to comment.