Skip to content

Commit

Permalink
Re-added Analyser.newRetrieverForDescriptors method as requested by @…
Browse files Browse the repository at this point in the history
…lucaro.

Signed-off-by: Ralph Gasser <[email protected]>
  • Loading branch information
ppanopticon committed Mar 26, 2024
1 parent f3b77fb commit 7e049b1
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import kotlin.reflect.KClass
*
* @author Luca Rossetto
* @author Ralph Gasser
* @version 1.1.0
* @version 1.2.0
*/
interface Analyser<C: ContentElement<*>, D: Descriptor> {
/** The [KClass]es of the [ContentElement] accepted by this [Analyser]. */
Expand Down Expand Up @@ -56,7 +56,8 @@ interface Analyser<C: ContentElement<*>, D: Descriptor> {
/**
* Generates and returns a new [Retriever] instance for this [Analyser].
*
* Some [Analyser]s may not come with their own [Retriever], in which case the implementation of this method should throw an [UnsupportedOperationException]
* This is the base-case, every [Analyser] should support this operation unless the [Analyser] is not meant to be used for retrieval at all,
* in which case the implementation of this method should throw an [UnsupportedOperationException]
*
* @param field The [Schema.Field] to create an [Retriever] for.
* @param query The [Query] to use with the [Retriever].
Expand All @@ -70,14 +71,34 @@ interface Analyser<C: ContentElement<*>, D: Descriptor> {
/**
* Generates and returns a new [Retriever] instance for this [Analyser].
*
* Some [Analyser]s may not come with their own [Retriever], in which case the implementation of this method should throw an [UnsupportedOperationException]
* Some [Analyser]s may not come with their own [Retriever] or may not support generating a [Retriever] from [Content].
* In both chases, the implementation of this method should throw an [UnsupportedOperationException].
*
* @param field The [Schema.Field] to create an [Retriever] for.
* @param content An array of [Content] elements to use with the [Retriever]
* @param context The [QueryContext] to use with the [Retriever]
*
* @return A new [Retriever] instance for this [Analyser]
* @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Retriever] instance.
* @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Retriever] instance from [Content].
*/
fun newRetrieverForContent(field: Schema.Field<C, D>, content: Collection<C>, context: QueryContext): Retriever<C, D> {
throw UnsupportedOperationException("This Analyser does not support the creation of a retriever from a collection of descriptors.")
}

/**
* Generates and returns a new [Retriever] instance for this [Analyser] from the provided [Collection] of [Descriptor]s.
*
* Some [Analyser]s may not come with their own [Retriever] or may not support generating a [Retriever] from a [Descriptor].
* In both chases, the implementation of this method should throw an [UnsupportedOperationException].
*
* @param field The [Schema.Field] to create an [Retriever] for.
* @param descriptors An array of [Descriptor] elements to use with the [Retriever]
* @param context The [QueryContext] to use with the [Retriever]
*
* @return A new [Retriever] instance for this [Analyser]
* @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Retriever] instance from [Descriptor]s.
*/
fun newRetrieverForContent(field: Schema.Field<C, D>, content: Collection<C>, context: QueryContext): Retriever<C, D>
fun newRetrieverForDescriptors(field: Schema.Field<C, D>, descriptors: Collection<D>, context: QueryContext): Retriever<C, D> {
throw UnsupportedOperationException("This Analyser does not support the creation of a retriever from a collection of descriptors.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,22 @@ class Schema(val name: String = "vitrivr", val connection: Connection) : Closeab
*/
fun getRetrieverForContent(content: Collection<C>, queryContext: QueryContext): Retriever<C, D> = this.analyser.newRetrieverForContent(this, content, queryContext)

/**
* Returns a [Retriever] instance for this [Schema.Field].
*
* @param descriptor The [Descriptor] that should be used with the [Retriever].
* @return [Retriever] instance.
*/
fun getRetrieverForDescriptor(descriptor: D, queryContext: QueryContext): Retriever<C, D> = this.getRetrieverForDescriptors(listOf(descriptor), queryContext)

/**
* Returns a [Retriever] instance for this [Schema.Field].
*
* @param descriptors The [Descriptor] element(s) that should be used with the [Retriever].
* @return [Retriever] instance.
*/
fun getRetrieverForDescriptors(descriptors: Collection<D>, queryContext: QueryContext): Retriever<C, D> = this.analyser.newRetrieverForDescriptors(this, descriptors, queryContext)

/**
* Returns the [DescriptorInitializer] for this [Schema.Field].
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ data class ProximityQuery<T : Value<*>>(
val order: SortOrder = SortOrder.ASC,

/** The number of results that should be returned by this [ProximityQuery]. */
val k: Int = 1000,
val k: Long = 1000L,

/** Flag indicating, whether [VectorDescriptor] should be returned as well. */
val fetchVector: Boolean = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,26 +84,36 @@ class AverageColor : Analyser<ImageContent, FloatVectorDescriptor> {
/**
* Generates and returns a new [AverageColorRetriever] instance for this [AverageColor].
*
* Invoking this method involves converting the provided [ImageContent] and the [QueryContext] into a [ProximityQuery] that can be used to retrieve
* similar [ImageContent] elements.
*
* Invoking this method involves converting the provided [FloatVectorDescriptor] into a [ProximityQuery] that can be used to retrieve similar [ImageContent] elements.
*
* @param field The [Schema.Field] to create an [Retriever] for.
* @param content An array of [Content] elements to use with the [Retriever]
* @param descriptors An array of [FloatVectorDescriptor] elements to use with the [Retriever]
* @param context The [QueryContext] to use with the [Retriever]
*/
override fun newRetrieverForContent(field: Schema.Field<ImageContent, FloatVectorDescriptor>, content: Collection<ImageContent>, context: QueryContext): AverageColorRetriever {
override fun newRetrieverForDescriptors(field: Schema.Field<ImageContent, FloatVectorDescriptor>, descriptors: Collection<FloatVectorDescriptor>, context: QueryContext): AverageColorRetriever {
require(field.analyser == this) { "The field '${field.fieldName}' analyser does not correspond with this analyser. This is a programmer's error!" }

/* Prepare query parameters. */
val k = context.getProperty(field.fieldName, "limit")?.toIntOrNull() ?: 1000
val k = context.getProperty(field.fieldName, "limit")?.toLongOrNull() ?: 1000L
val fetchVector = context.getProperty(field.fieldName, "returnDescriptor")?.toBooleanStrictOrNull() ?: false
val vector = this.analyse(content).first().vector

/* Return retriever. */
return this.newRetrieverForQuery(field, ProximityQuery(value = vector, k = k, fetchVector = fetchVector), context)
return this.newRetrieverForQuery(field, ProximityQuery(value = descriptors.first().vector, k = k, fetchVector = fetchVector), context)
}

/**
* Generates and returns a new [AverageColorRetriever] instance for this [AverageColor].
*
* Invoking this method involves converting the provided [ImageContent] and the [QueryContext] into a [FloatVectorDescriptor]
* that can be used to retrieve similar [ImageContent] elements.
*
* @param field The [Schema.Field] to create an [Retriever] for.
* @param content An array of [Content] elements to use with the [Retriever]
* @param context The [QueryContext] to use with the [Retriever]
*/
override fun newRetrieverForContent(field: Schema.Field<ImageContent, FloatVectorDescriptor>, content: Collection<ImageContent>, context: QueryContext): AverageColorRetriever =
this.newRetrieverForDescriptors(field, this.analyse(content), context)

/**
* Performs the [AverageColor] analysis on the provided [List] of [ImageContent] elements.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package org.vitrivr.engine.base.features.external.implementations.clip

import org.vitrivr.engine.base.features.external.ExternalAnalyser
import org.vitrivr.engine.base.features.external.common.ExternalWithFloatVectorDescriptorAnalyser
import org.vitrivr.engine.base.features.external.implementations.dino.DINORetriever
import org.vitrivr.engine.core.context.IndexContext
import org.vitrivr.engine.core.context.QueryContext
import org.vitrivr.engine.core.model.content.element.ContentElement
import org.vitrivr.engine.core.model.content.element.ImageContent
import org.vitrivr.engine.core.model.content.element.TextContent
import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor
import org.vitrivr.engine.core.model.metamodel.Analyser
import org.vitrivr.engine.core.model.metamodel.Schema
import org.vitrivr.engine.core.model.query.Query
import org.vitrivr.engine.core.model.query.proximity.ProximityQuery
Expand Down Expand Up @@ -74,6 +76,25 @@ class CLIP : ExternalWithFloatVectorDescriptorAnalyser<ContentElement<*>>() {
return CLIPRetriever(field, query as ProximityQuery<Value.Float>, context)
}

/**
* Generates and returns a new [DINORetriever] instance for this [CLIP].
*
* @param field The [Schema.Field] to create an [Retriever] for.
* @param descriptors An array of [FloatVectorDescriptor] elements to use with the [Retriever]
* @param context The [QueryContext] to use with the [Retriever]
*
* @return A new [Retriever] instance for this [Analyser]
* @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Retriever] instance.
*/
override fun newRetrieverForDescriptors(field: Schema.Field<ContentElement<*>, FloatVectorDescriptor>, descriptors: Collection<FloatVectorDescriptor>, context: QueryContext): CLIPRetriever {
/* Prepare query parameters. */
val k = context.getProperty(field.fieldName, "limit")?.toLongOrNull() ?: 1000L
val fetchVector = context.getProperty(field.fieldName, "returnDescriptor")?.toBooleanStrictOrNull() ?: false

/* Return retriever. */
return this.newRetrieverForQuery(field, ProximityQuery(value = descriptors.first().vector, k = k, fetchVector = fetchVector), context)
}

/**
* Generates and returns a new [Retriever] instance for this [CLIP].
*
Expand All @@ -87,13 +108,11 @@ class CLIP : ExternalWithFloatVectorDescriptorAnalyser<ContentElement<*>>() {
override fun newRetrieverForContent(field: Schema.Field<ContentElement<*>, FloatVectorDescriptor>, content: Collection<ContentElement<*>>, context: QueryContext): Retriever<ContentElement<*>, FloatVectorDescriptor> {
val host = field.parameters[HOST_PARAMETER_NAME] ?: HOST_PARAMETER_DEFAULT

/* Prepare query parameters. */
val vector = analyse(content.first(), host)
val k = context.getProperty(field.fieldName, "limit")?.toIntOrNull() ?: 1000
val fetchVector = context.getProperty(field.fieldName, "returnDescriptor")?.toBooleanStrictOrNull() ?: false
/* Extract vectors from content. */
val vectors = content.map { analyse(it, host) }

/* Return retriever. */
return this.newRetrieverForQuery(field, ProximityQuery(value = vector.vector, k = k, fetchVector = fetchVector), context)
return this.newRetrieverForDescriptors(field, vectors, context)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,25 @@ class DINO : ExternalWithFloatVectorDescriptorAnalyser<ImageContent>() {
return DINORetriever(field, query as ProximityQuery<Value.Float>, context)
}

/**
* Generates and returns a new [DINORetriever] instance for this [DINO].
*
* @param field The [Schema.Field] to create an [Retriever] for.
* @param descriptors An array of [FloatVectorDescriptor] elements to use with the [Retriever]
* @param context The [QueryContext] to use with the [Retriever]
*
* @return A new [Retriever] instance for this [Analyser]
* @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Retriever] instance.
*/
override fun newRetrieverForDescriptors(field: Schema.Field<ImageContent, FloatVectorDescriptor>, descriptors: Collection<FloatVectorDescriptor>, context: QueryContext): DINORetriever {
/* Prepare query parameters. */
val k = context.getProperty(field.fieldName, "limit")?.toLongOrNull() ?: 1000L
val fetchVector = context.getProperty(field.fieldName, "returnDescriptor")?.toBooleanStrictOrNull() ?: false

/* Return retriever. */
return this.newRetrieverForQuery(field, ProximityQuery(value = descriptors.first().vector, k = k, fetchVector = fetchVector), context)
}

/**
* Generates and returns a new [DINORetriever] instance for this [DINO].
*
Expand All @@ -84,13 +103,11 @@ class DINO : ExternalWithFloatVectorDescriptorAnalyser<ImageContent>() {
require(field.analyser == this) { "The field '${field.fieldName}' analyser does not correspond with this analyser. This is a programmer's error!" }
val host = field.parameters[HOST_PARAMETER_NAME] ?: HOST_PARAMETER_DEFAULT

/* Prepare query parameters. */
val vector = analyse(content.first(), host)
val k = context.getProperty(field.fieldName, "limit")?.toIntOrNull() ?: 1000
val fetchVector = context.getProperty(field.fieldName, "returnDescriptor")?.toBooleanStrictOrNull() ?: false
/* Extract vectors from content. */
val vectors = content.map { analyse(it, host) }

/* Return retriever. */
return this.newRetrieverForQuery(field, ProximityQuery(value = vector.vector, k = k, fetchVector = fetchVector), context)
return this.newRetrieverForDescriptors(field, vectors, context)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ class ASR : Analyser<ContentElement<*>, StringDescriptor> {
return FulltextRetriever(field, query, context)
}

/**
* Generates and returns a new [FulltextRetriever] instance for this [ASR].
*
* @param field The [Schema.Field] to create an [Retriever] for.
* @param descriptors An array of [StringDescriptor] elements to use with the [Retriever]
* @param context The [QueryContext] to use with the [Retriever]
* @return [FulltextRetriever]
*/
override fun newRetrieverForDescriptors(field: Schema.Field<ContentElement<*>, StringDescriptor>, descriptors: Collection<StringDescriptor>, context: QueryContext): Retriever<ContentElement<*>, StringDescriptor> {
require(field.analyser == this) { "The field '${field.fieldName}' analyser does not correspond with this analyser. This is a programmer's error!" }

/* Prepare query parameters and return retriever. */
val limit = context.getProperty(field.fieldName, "limit")?.toLongOrNull() ?: 1000L
return this.newRetrieverForQuery(field, SimpleFulltextQuery(value = descriptors.first().value, limit = limit), context)
}

/**
* Generates and returns a new [FulltextRetriever] instance for this [ASR].
*
Expand All @@ -64,10 +80,9 @@ class ASR : Analyser<ContentElement<*>, StringDescriptor> {
override fun newRetrieverForContent(field: Schema.Field<ContentElement<*>, StringDescriptor>, content: Collection<ContentElement<*>>, context: QueryContext): Retriever<ContentElement<*>, StringDescriptor> {
require(field.analyser == this) { "The field '${field.fieldName}' analyser does not correspond with this analyser. This is a programmer's error!" }

/* Prepare query parameters. */
/* Prepare query parameters and return retriever. */
val text = content.filterIsInstance<TextContent>().firstOrNull() ?: throw IllegalArgumentException("No text content found in the provided content.")
val limit = context.getProperty(field.fieldName, "limit")?.toLongOrNull() ?: 1000L

return this.newRetrieverForQuery(field, SimpleFulltextQuery(value = Value.String(text.content), limit = limit), context)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ class OCR : Analyser<ContentElement<*>, StringDescriptor> {
return FulltextRetriever(field, query, context)
}

/**
* Generates and returns a new [FulltextRetriever] instance for this [OCR].
*
* @param field The [Schema.Field] to create an [Retriever] for.
* @param descriptors An array of [StringDescriptor] elements to use with the [Retriever]
* @param context The [QueryContext] to use with the [Retriever]
* @return [FulltextRetriever]
*/
override fun newRetrieverForDescriptors(field: Schema.Field<ContentElement<*>, StringDescriptor>, descriptors: Collection<StringDescriptor>, context: QueryContext): Retriever<ContentElement<*>, StringDescriptor> {
require(field.analyser == this) { "The field '${field.fieldName}' analyser does not correspond with this analyser. This is a programmer's error!" }

/* Prepare query parameters and return retriever. */
val limit = context.getProperty(field.fieldName, "limit")?.toLongOrNull() ?: 1000L
return this.newRetrieverForQuery(field, SimpleFulltextQuery(value = descriptors.first().value, limit = limit), context)
}

/**
* Generates and returns a new [FulltextRetriever] instance for this [OCR].
*
Expand Down
Loading

0 comments on commit 7e049b1

Please sign in to comment.