💕

AI Matching System

Vector embeddings, Erotic DNA profiles, GeoMatch (LocalLove) en cosine similarity voor de perfecte matches

Fastify 5 API PostgreSQL 16 + Drizzle ORM Pinecone / Weaviate Vectors React 19 + TanStack Query v5

🛠️ Tech Stack

Backend

Fastify 5 API — POST /ai/match, vector queries via Pinecone/Weaviate SDK

🗄️

Database

PostgreSQL 16 + Drizzle ORM — profiles, kink preferences, geo coords

🔐

Auth

Lucia Auth — sessie-validatie op alle matching endpoints

🧠

AI Engine

Pinecone of Weaviate vector store — embedding similarity + collaborative filtering

🤖 AI Matching Engine

Het matching systeem combineert twee technieken: cosine similarity op vector embeddings voor profielvergelijking en collaborative filtering op gedragspatronen van gebruikers.

🔢 Vector Embeddings

Elk Erotic DNA profiel wordt omgezet naar een high-dimensional embedding vector en opgeslagen in Pinecone of Weaviate. ANN-search vindt de meest gelijkende profielen in milliseconden.

Embedding dimensies

1536-dim OpenAI embeddings op samengesteld profieltekst (kinks, mood, intent, bio)

Vector store

Pinecone (managed) of Weaviate (self-hosted) — beide supported via adapter pattern

Similarity metric

Cosine similarity — schaalbaar, rotatie-invariant, ideaal voor semantische vergelijking

🔄 Collaborative Filtering

Gedragsdata (likes, skips, chats, streams bekeken) wordt gebruikt om patronen te vinden tussen vergelijkbare gebruikers — "users like you also liked..."

Signalen

Likes, super-likes, chat-initiaties, subscription conversies, stream watch-time

Model

Matrix factorization (ALS) — periodiek getraind, resultaten gecached in Redis

Refresh cycle

Model hertraining elke 24u, real-time signals verwerkt via event queue

🎯 Matching Dimensies

Vijf kernfactoren bepalen de compatibiliteitsscore. Elk profiel wordt bijgewerkt wanneer de gebruiker zijn DNA Quiz invult of voorkeuren wijzigt.

🧬

Erotic DNA

Uniek profiel op basis van de DNA Quiz

🔗

Kink Preferences

Gemeenschappelijke kink-categorieën

🎭

Mood

Realtime mood-status (flirty, chill, intense…)

📍

GeoMatch

Locatie-based — LocalLove radius

🎯

Intent

Wat zoekt de gebruiker? (casual, serieus, fans)

Compatibiliteitsscore Gewichten

🧬 Erotic DNA Profile 30%
🔗 Kink Preferences 25%
🎭 Mood Compatibility 20%
📍 Location Proximity 15%
🎯 Intent Match 10%

🧬 Erotic DNA Profile

De DNA Quiz genereert een uniek profiel van 12 dimensies. Dit profiel vormt de kern van het matching algoritme en wordt omgezet naar een embedding vector.

Energie & Dynamiek

  • • Dominant ↔ Submissive spectrum
  • • Initiator ↔ Responder
  • • Intensity niveau (1–10)

Stijl & Sfeer

  • • Romantic ↔ Raw spectrum
  • • Playful ↔ Serious
  • • Fantasy vs. Reality focus

Kink Categorieën

  • • 40+ kink-tags (weighted)
  • • Soft / Hard limits aangegeven
  • • Anonieme consent-opslag
// Drizzle ORM — DNA profile schema (PostgreSQL 16)
export const dnaProfiles = pgTable('dna_profiles', {
  userId: uuid().primaryKey().references(() => users.id),
  embeddingVector: vector(1536), // pgvector extension
  kinkTags: text().array(), // ['bondage','roleplay',...]
  dominanceScore: integer(), // 0-100
  intensityScore: integer(), // 0-100
  moodCurrent: text(), // realtime mood
  intent: text(), // 'casual'|'fan'|'connect'
  updatedAt: timestamp().defaultNow(),
});

📍 LocalLove — GeoMatch

Proximity-based matching met instelbare radius. Geo-coördinaten worden opgeslagen als PostGIS geometry in PostgreSQL 16 voor efficiënte afstandsberekeningen.

🏘️

Buurt

5 km

Hyperlocal — zelfde wijk of dorp

STANDAARD
🏙️

Stad

25 km

Regio — stad en omgeving

🗺️

Provincie

100 km

Breed — hele regio of provincie

🔒 Privacy Controls

  • • Exacte locatie nooit gedeeld — alleen afstand
  • • Fuzzy location: coördinaten afgerond op 1km grid
  • • Ghost Mode: verschijn niet in GeoMatch
  • • Locatie op elk moment uitschakelbaar
// PostGIS distance query via Drizzle
const nearby = await db.execute(sql`
  SELECT u.id, ST_Distance(
    l.coords, ST_Point(${lon}, ${lat})
  ) AS dist_km FROM users u
  JOIN locations l ON l.user_id = u.id
  WHERE dist_km <= ${radiusKm}
  ORDER BY dist_km ASC LIMIT 50
`);

🔌 Fastify 5 API — POST /ai/match

Eén unified endpoint retourneert gerangschikte matches met compatibiliteitsscores. Intern worden vector-query, collaborative filtering en GeoFilter gecombineerd.

POST /ai/match Lucia Auth vereist
// Request body
{
  "radiusKm": 25,
  "mood": "flirty",
  "intent": "casual",
  "limit": 20,
  "excludeIds": ["usr_abc", "usr_def"]
}
// Response 200 — gerangschikte matches
{
  "matches": [
    {
      "userId": "usr_xyz",
      "displayName": "Luna",
      "compatibilityScore": 0.91, // 0.0–1.0
      "scoreBreakdown": {
        "dna": 0.94, "kinks": 0.88,
        "mood": 0.95, "geo": 0.82, "intent": 1.0
      },
      "distanceKm": 7.4,
      "mood": "flirty",
      "isOnline": true
    }
  ],
  "totalCandidates": 234,
  "processingMs": 48
}
⚛️

TanStack Query v5 — useMatches hook

const useMatches = (filters: MatchFilters) => {
  return useMutation({
    mutationFn: (body) =>
      fetch('/ai/match', {
        method: 'POST',
        body: JSON.stringify(body),
      }).then(r => r.json()),
    onSuccess: (data) => {
      queryClient.setQueryData(['matches'], data.matches);
    },
  });
};

🚀 Matching Flow

1

DNA Quiz

Erotic DNA profiel aanmaken via quiz

2

Embedding

Profiel → 1536-dim vector (Pinecone)

3

ANN Search

Top-K kandidaten via cosine similarity

4

GeoFilter

Verwijder matches buiten gekozen radius

5

Re-ranking

Collaborative filtering score toepassen

6

Response

Gerangschikte lijst met score breakdown