Class: ContentEmbedding::ImageEmbedding

Inherits:
ContentEmbedding
  • Object
show all
Defined in:
app/models/content_embedding/image_embedding.rb

Overview

ContentEmbedding STI subclass for image embeddings.

Constant Summary collapse

EMBEDDING_DIMENSIONS =

Embedding dimensions. The model(s) searched come from
ContentEmbedding::UNIFIED_MODELS (GA + draining preview).

1536

Belongs to collapse

Class Method Summary collapse

Class Method Details

.apply_published_filter(scope) ⇒ Object



99
100
101
102
# File 'app/models/content_embedding/image_embedding.rb', line 99

def apply_published_filter(scope)
  scope.joins('INNER JOIN digital_assets ON digital_assets.id = content_embeddings_images.embeddable_id')
       .where(digital_assets: { inactive: false })
end

.generate_gemini_query_embedding(query) ⇒ Array<Float>?

Generate query embedding using Gemini Embedding 2

Parameters:

  • query (String)

    Text to embed

Returns:

  • (Array<Float>, nil)

    Embedding vector or nil on error



84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'app/models/content_embedding/image_embedding.rb', line 84

def generate_gemini_query_embedding(query)
  cache_key = "query_embedding:gemini:#{EMBEDDING_DIMENSIONS}:#{Digest::SHA256.hexdigest(query.downcase.strip)[0..15]}"

  cached = Rails.cache.read(cache_key)
  return cached if cached.present?

  vector = Embedding::Gemini.embed_query(query, dimensions: EMBEDDING_DIMENSIONS)

  Rails.cache.write(cache_key, vector, expires_in: 24.hours) if vector.present?
  vector
rescue Embedding::Gemini::Error => e
  Rails.logger.error "[ImageEmbedding] Failed to generate Gemini query embedding: #{e.message}"
  nil
end

.primary_contentActiveRecord::Relation<ContentEmbedding::ImageEmbedding>

A relation of ContentEmbedding::ImageEmbeddings that are primary content. Active Record Scope

Returns:

See Also:



46
# File 'app/models/content_embedding/image_embedding.rb', line 46

scope :primary_content, -> { where(content_type: 'primary') }

.semantic_search(query, limit: 10, published_only: true) ⇒ ActiveRecord::Relation

Visual semantic search using Gemini Embedding 2 multimodal embeddings.
Enables text-to-image search (describe what you want, find matching images).

Matches the GA id and the transitional preview id (compatible geometry,
draining via ImageEmbeddingPopulationWorker). Legacy Jina v4 vectors are
excluded deliberately: Jina is a different model in an incompatible vector
space, so ranking it against a Gemini query yields meaningless scores. The
same population worker re-embeds those rows to gemini-embedding-2.

Parameters:

  • query (String)

    Natural language description of desired images

  • limit (Integer) (defaults to: 10)

    Maximum results (default: 10)

  • published_only (Boolean) (defaults to: true)

    Filter to active images (default: true)

Returns:

  • (ActiveRecord::Relation)

    Image embeddings ordered by similarity



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'app/models/content_embedding/image_embedding.rb', line 64

def semantic_search(query, limit: 10, published_only: true, **)
  return none if query.blank?

  query_embedding = generate_gemini_query_embedding(query)
  return none unless query_embedding

  scope = unified_content
          .with_unified_embedding
          .where(embedding_model: ContentEmbedding::UNIFIED_MODELS)
          .nearest_neighbors(:unified_embedding, query_embedding, distance: :cosine)

  scope = apply_published_filter(scope) if published_only

  scope.limit(limit).includes(:embeddable)
end

.unified_contentActiveRecord::Relation<ContentEmbedding::ImageEmbedding>

A relation of ContentEmbedding::ImageEmbeddings that are unified content. Active Record Scope

Returns:

See Also:



47
# File 'app/models/content_embedding/image_embedding.rb', line 47

scope :unified_content, -> { where(content_type: 'unified') }

.with_unified_embeddingActiveRecord::Relation<ContentEmbedding::ImageEmbedding>

A relation of ContentEmbedding::ImageEmbeddings that are with unified embedding. Active Record Scope

Returns:

See Also:



48
# File 'app/models/content_embedding/image_embedding.rb', line 48

scope :with_unified_embedding, -> { where.not(unified_embedding: nil) }

Instance Method Details

#embeddableImage Also known as: image

Returns:

See Also:



40
# File 'app/models/content_embedding/image_embedding.rb', line 40

belongs_to :embeddable, class_name: 'Image'