Skip to main content

10 - The Relationship Graph: 52 Types and Community Detection

The Social Graph Applied to Event Management In the world of event management, knowing the relationships between participants is not an incidental detail: it's crucial for intelligent seating, conflict prevention, and community building. Learn how to model 52 relationship types and detect communities.

What do you think about this article?

At a Glance

The Social Graph Applied to Event Management In the world of event management, knowing the relationships between participants is not an incidental detail: it's crucial for intelligent seating, conflict prevention, and community building. Learn how to model 52 relationship types and detect communities.

The Relationship Graph: 52 Types of Social Connections

When organizing an event, knowing who is attending is only half the picture. Understanding how people are connected to each other unlocks powerful features: intelligent seating arrangements, conflict detection, invitation suggestions, and community visualization. In Play The Event, we built a full social graph engine with 52 relationship types organized into 10 categories, weighted connections, automatic bidirectionality, and real-time D3.js visualization.

This article explores the entire relationship subsystem: from the domain model (RelazioneUtente, TipoRelazione, StatoRelazione) to the graph algorithms that power smart seating, friend-of-friend recommendations, and community detection.

What You'll Find in This Article

  • 52 relationship types organized into 10 semantic categories
  • Weighted connections: from -1.0 (enemy) to +1.0 (parent/child)
  • The RelationshipStrength classification system (9 levels)
  • Automatic bidirectionality and inverse type determination
  • Relationship state machine: PENDING, ACCEPTED, REJECTED
  • Community detection and conflict identification algorithms
  • Use case: graph-optimized table assignments (smart seating)
  • Friend-of-friend invitation suggestions
  • Interactive D3.js graph visualization

52 Relationship Types in 10 Categories

The TipoRelazione enum defines every possible connection between two users in Play The Event. Each type carries a weight (from -1.0 to +1.0), a display name, and is classified into one of 10 categories. The Italian enum names are preserved in the codebase to maintain domain language consistency.

Category 1: Core Family (weight 0.9 - 1.0)

The strongest positive relationships. These are immediate family bonds that carry the highest weight in seating and grouping algorithms.

Core Family Types
CORE FAMILY (weight 0.9 - 1.0)

GENITORE     (1.0)   Parent
FIGLIO       (1.0)   Child (son/daughter)
CONIUGE      (1.0)   Spouse
PARTNER      (0.95)  Partner (unmarried)
GEMELLO      (0.95)  Twin
FRATELLO     (0.9)   Brother
SORELLA      (0.9)   Sister

Category 2: Grandparents and Grandchildren (weight 0.85)

Grandparent / Grandchild Types
GRANDPARENTS & GRANDCHILDREN (weight 0.85)

NONNO              (0.85)  Grandfather
NONNA              (0.85)  Grandmother
NIPOTE_DI_NONNI    (0.85)  Grandchild

Category 3: Uncles/Aunts and Nephews/Nieces (weight 0.7)

Uncle/Aunt / Nephew/Niece Types
UNCLES/AUNTS & NEPHEWS/NIECES (weight 0.7)

ZIO              (0.7)   Uncle
ZIA              (0.7)   Aunt
NIPOTE_DI_ZII    (0.7)   Nephew / Niece

Category 4: Cousins (weight 0.3 - 0.6)

Cousin Types
COUSINS (weight 0.3 - 0.6)

CUGINO_PRIMO     (0.6)   First Cousin
CUGINO_SECONDO   (0.4)   Second Cousin
CUGINO_TERZO     (0.3)   Third Cousin

Category 5: In-Laws (weight 0.6 - 0.75)

In-Law Types
IN-LAWS (weight 0.6 - 0.75)

SUOCERO    (0.7)    Father-in-law
SUOCERA    (0.7)    Mother-in-law
GENERO     (0.75)   Son-in-law
NUORA      (0.75)   Daughter-in-law
COGNATO    (0.6)    Brother-in-law
COGNATA    (0.6)    Sister-in-law

Category 6: Godparents and Step-Family (weight 0.55 - 0.65)

Godparent, Step-Family, and Adoptive Types
GODPARENTS (weight 0.65)
PADRINO       (0.65)  Godfather
MADRINA       (0.65)  Godmother
FIGLIOCCIO    (0.65)  Godchild

STEP-FAMILY (weight 0.55 - 0.60)
PATRIGNO                 (0.6)   Stepfather
MATRIGNA                 (0.6)   Stepmother
FIGLIASTRO               (0.55)  Stepson
FIGLIASTRA               (0.55)  Stepdaughter
FRATELLASTRO             (0.55)  Stepbrother
SORELLASTRA              (0.55)  Stepsister
FRATELLO_CONSANGUINEO    (0.7)   Half-brother (blood)
SORELLA_CONSANGUINEA     (0.7)   Half-sister (blood)

ADOPTIVE FAMILY (weight 0.8 - 0.95)
GENITORE_ADOTTIVO    (0.95)  Adoptive Parent
FIGLIO_ADOTTIVO      (0.95)  Adopted Child
FRATELLO_ADOTTIVO    (0.8)   Adoptive Sibling

Category 7: Extended Family (weight 0.5 - 0.65)

Extended Family Types
EXTENDED FAMILY (weight 0.5 - 0.65)

BISNONNO      (0.65)  Great-grandparent
PRONIPOTE     (0.65)  Great-grandchild
PROZIO        (0.5)   Great-uncle / Great-aunt

Category 8: Social Relationships (weight 0.25 - 0.85)

Social Relationship Types
SOCIAL RELATIONSHIPS (weight 0.25 - 0.85)

MIGLIORE_AMICO     (0.85)  Best Friend
AMICO_STRETTO      (0.8)   Close Friend
AMICO              (0.5)   Friend
COMPAGNO_CLASSE    (0.4)   Classmate
CONOSCENTE         (0.3)   Acquaintance
COLLEGA            (0.3)   Colleague
VICINO             (0.25)  Neighbor

Category 9: Organizational Relationships (weight 0.3 - 0.7)

These types cover both corporate and association contexts, enabling Play The Event to serve company events, charity galas, and association meetings equally well.

Organizational Relationship Types
CORPORATE (weight 0.3 - 0.6)
DIPENDENTE       (0.4)   Employee
RESPONSABILE     (0.5)   Manager
DIRIGENTE        (0.6)   Executive / Director
COLLABORATORE    (0.35)  Collaborator
CONSULENTE       (0.35)  Consultant
STAGISTA         (0.3)   Intern

ASSOCIATIONS (weight 0.35 - 0.7)
ASSOCIATO            (0.4)   Associate / Member
MEMBRO_DIRETTIVO     (0.55)  Board Member
PRESIDENTE           (0.7)   President
VICEPRESIDENTE       (0.65)  Vice President
SEGRETARIO           (0.6)   Secretary
TESORIERE            (0.6)   Treasurer
CONSIGLIERE          (0.5)   Counselor / Advisor
VOLONTARIO           (0.35)  Volunteer

Category 10: Negative and Neutral (weight -1.0 to 0.0)

Negative and Neutral Types
NEGATIVE RELATIONSHIPS (weight -1.0 to -0.2)
EX_CONIUGE     (-0.3)   Ex-Spouse
EX_PARTNER     (-0.2)   Ex-Partner
RIVALE         (-0.5)   Rival
NEMICO         (-1.0)   Enemy

NEUTRAL (weight 0.0)
SCONOSCIUTO    (0.0)    Unknown / Stranger

Why Negative Weights Matter

Negative relationships are not just metadata. They drive real features: the conflict detection algorithm flags when enemies or rivals are seated at the same table, and the smart seating engine actively separates people with negative connections. An ex-couple at the same wedding table is exactly the kind of disaster this system prevents.

Weight and Relationship Strength

Every relationship type carries a numeric weight between -1.0 and +1.0. This weight is not just a label: it is used in graph algorithms for community detection, seating optimization, and invitation ranking. The RelationshipStrength enum maps continuous weights into 9 discrete levels.

RelationshipStrength Classification

Strength Weight Range Example Types
VERY_STRONG 0.8 to 1.0 Parent, Spouse, Best Friend
STRONG 0.6 to 0.79 Uncle, In-laws, Godfather
MODERATE 0.4 to 0.59 Friend, First Cousin, Manager
WEAK 0.2 to 0.39 Acquaintance, Colleague, Intern
VERY_WEAK 0.01 to 0.19 (custom overrides only)
NEUTRAL 0.0 Unknown / Stranger
SLIGHTLY_NEGATIVE -0.3 to -0.01 Ex-Partner, Ex-Spouse
NEGATIVE -0.6 to -0.31 Rival
VERY_NEGATIVE -1.0 to -0.61 Enemy

The getStrength() method on TipoRelazione maps each type to its strength level. This classification is used throughout the application: the D3.js graph renders thicker edges for stronger connections, the seating algorithm gives higher priority to VERY_STRONG bonds, and the conflict detector triggers warnings only for NEGATIVE and VERY_NEGATIVE links.

Strength Classification Logic
public RelationshipStrength getStrength() {
    if (weight >= 0.8) return VERY_STRONG;
    if (weight >= 0.6) return STRONG;
    if (weight >= 0.4) return MODERATE;
    if (weight >= 0.2) return WEAK;
    if (weight >  0)   return VERY_WEAK;
    if (weight == 0)   return NEUTRAL;
    if (weight >= -0.3) return SLIGHTLY_NEGATIVE;
    if (weight >= -0.6) return NEGATIVE;
    return VERY_NEGATIVE;
}

// Weight can also be manually overridden:
public void regolaPeso(double nuovoPeso) {
    if (nuovoPeso < -1.0 || nuovoPeso > 1.0) {
        throw new IllegalArgumentException(
            "Weight must be between -1.0 and 1.0"
        );
    }
    this.peso = nuovoPeso;
}

Automatic Bidirectionality

One of the most important design decisions in the relationship graph is automatic bidirectionality detection. When a user declares a relationship, the system automatically determines whether it is symmetric (both parties share the same type) or directional (each side has a different type).

Symmetric Relationships

Symmetric relationships are the same in both directions. If Alice is the CONIUGE (spouse) of Bob, then Bob is also the CONIUGE of Alice. The system stores a single record and marks it as bidirectional.

Symmetric Relationship Types
SYMMETRIC (bidirectional = true, inverse = same type)

CONIUGE, PARTNER, GEMELLO
FRATELLO, SORELLA, FRATELLASTRO, SORELLASTRA
FRATELLO_CONSANGUINEO, SORELLA_CONSANGUINEA
FRATELLO_ADOTTIVO
CUGINO_PRIMO, CUGINO_SECONDO, CUGINO_TERZO
COGNATO, COGNATA
AMICO, AMICO_STRETTO, MIGLIORE_AMICO
CONOSCENTE, COLLEGA, VICINO, COMPAGNO_CLASSE
RIVALE, NEMICO, SCONOSCIUTO

Directional Relationships

Directional relationships have a distinct inverse type. The system automatically computes the inverse so that both parties see the correct label.

Inverse Type Determination
DIRECTIONAL (bidirectional = false, inverse auto-computed)

GENITORE       <-->  FIGLIO
NONNO / NONNA  <-->  NIPOTE_DI_NONNI
ZIO / ZIA      <-->  NIPOTE_DI_ZII
SUOCERO/A      <-->  GENERO / NUORA
PADRINO/MADRINA <--> FIGLIOCCIO
PATRIGNO       <-->  FIGLIASTRO
MATRIGNA       <-->  FIGLIASTRA
GENITORE_ADOTTIVO <--> FIGLIO_ADOTTIVO
BISNONNO       <-->  PRONIPOTE

// Implementation in RelazioneUtente:
private TipoRelazione determinaTipoInverso(TipoRelazione tipo) {
    return switch (tipo) {
        case GENITORE          -> FIGLIO;
        case FIGLIO            -> GENITORE;
        case NONNO, NONNA      -> NIPOTE_DI_NONNI;
        case PADRINO, MADRINA  -> FIGLIOCCIO;
        case GENITORE_ADOTTIVO -> FIGLIO_ADOTTIVO;
        case FIGLIO_ADOTTIVO   -> GENITORE_ADOTTIVO;
        case BISNONNO          -> PRONIPOTE;
        case PRONIPOTE         -> BISNONNO;
        default                -> tipo; // Symmetric
    };
}

Ambiguous Inverses

Some directional types have ambiguous inverses. For example, NIPOTE_DI_NONNI (grandchild) could be the inverse of either NONNO (grandfather) or NONNA (grandmother). In these cases, determinaTipoInverso() returns null, and the system requires the other party to explicitly specify the relationship type when accepting the connection request.

Relationship State Machine

Relationships in Play The Event are not created instantly. They follow a bilateral workflow similar to social network connection requests: one user sends a request, and the other must accept or reject it.

Relationship State Machine
RELATIONSHIP STATE MACHINE (StatoRelazione)

  User A sends request
         │
         ▼
  ┌──────────┐
  │ PENDING  │  Initial state (request sent)
  └────┬─────┘
       │
       ├── accetta(userBId) ──────▶ ACCEPTED  (active, visible in graph)
       │
       └── rifiuta(userBId) ──────▶ REJECTED  (inactive, preserved)
                                        │
                                        ▼
                               resetRichiesta()
                                        │
                                        ▼
                                    PENDING  (can be re-sent)

  RULES:
  ├── Only the RECIPIENT can accept or reject
  ├── Only PENDING requests can be accepted/rejected
  ├── REJECTED requests can be reset and re-sent
  ├── Only ACCEPTED relationships appear in the graph
  └── State tracked with timestamps for auditing

The RelazioneUtente aggregate root enforces all state transition rules. The accetta() and rifiuta() methods validate that the caller is the intended recipient of the request and that the current state is PENDING. Any violation throws an exception, keeping the domain invariants intact.

State Transition Implementation
// Only the recipient can accept
public void accetta(Long utenteIdRisposta) {
    validaDestinatario(utenteIdRisposta);

    if (this.stato != StatoRelazione.PENDING) {
        throw new IllegalStateException(
            "Can only accept PENDING requests. Current: " + stato
        );
    }

    this.stato = StatoRelazione.ACCEPTED;
    this.rispostaDataIl = Instant.now();
}

// Recipient validation
private void validaDestinatario(Long utenteId) {
    Long destinatarioId = getDestinatarioId();
    if (!destinatarioId.equals(utenteId)) {
        throw new IllegalArgumentException(
            "Only the recipient (ID: " + destinatarioId +
            ") can respond. Provided: " + utenteId
        );
    }
}

Optimistic Locking

The RelazioneUtente entity uses a @Version field for optimistic locking. If two concurrent requests try to modify the same relationship (e.g., accept and reject at the same time), the second one will fail with an OptimisticLockException, ensuring data consistency without database-level locking.

Community Detection

With the social graph in place, the system can automatically identify communities: clusters of people who are densely connected to each other. This is essential for event features like automatic table grouping, activity suggestions, and understanding the social dynamics of the guest list.

Community Detection Algorithm
COMMUNITY DETECTION IN PLAY THE EVENT

INPUT:  Social graph G = (Users, Relationships)
        Only ACCEPTED relationships with weight > 0
FILTER: isFamilyRelation() || isSocialRelation()

ALGORITHM: Modified Label Propagation

1. INITIALIZATION
   Each user starts with a unique community label
   communityOf(user) = user.id

2. ITERATION (until convergence)
   For each user U:
     neighbors = getAcceptedRelationships(U)
     weightedVotes = {}
     for each neighbor N in neighbors:
       label = communityOf(N)
       weightedVotes[label] += relationship.weight
     communityOf(U) = argmax(weightedVotes)

3. MERGE SMALL COMMUNITIES
   Communities with < 3 members are merged
   with the closest neighboring community

OUTPUT: Map<CommunityId, Set<UserId>>

EXAMPLE (Wedding with 120 guests):
├── Community 1: Bride's family (28 people)
├── Community 2: Groom's family (25 people)
├── Community 3: University friends (15 people)
├── Community 4: Work colleagues bride (12 people)
├── Community 5: Work colleagues groom (10 people)
├── Community 6: Childhood friends (8 people)
└── Unassigned: loose connections (22 people)

The algorithm leverages the relationship weights: a family cluster (weights 0.7-1.0) will form a much tighter community than a group of acquaintances (weight 0.3). The result is used by the seating optimizer to ensure that each table has a cohesive social group rather than random assignments.

Conflict Detection

Negative relationships are not just passive metadata. The system actively scans the guest list for potential conflicts using the isNegative() method on TipoRelazione.

Conflict Detection Logic
CONFLICT DETECTION

SCAN: For all pairs (A, B) in event participants
WHERE: relationship(A, B).weight < 0

SEVERITY LEVELS:
├── CRITICAL (weight < -0.6)
│   Types: NEMICO
│   Action: Flag to organizer, prevent same table
│
├── HIGH (weight -0.3 to -0.6)
│   Types: RIVALE
│   Action: Warn organizer, suggest separation
│
└── MODERATE (weight -0.01 to -0.3)
    Types: EX_CONIUGE, EX_PARTNER
    Action: Notify organizer, recommend distance

OUTPUT: List<ConflictReport>
├── userA, userB
├── relationshipType
├── severity (CRITICAL | HIGH | MODERATE)
├── recommendation (text)
└── resolved (boolean, set by organizer)

Transitive Conflicts

The system also detects transitive conflicts: if A is enemies with B, and B is best friends with C, placing A next to C could create social tension even though A and C have no direct negative relationship. The algorithm assigns a reduced conflict score to such indirect connections based on the path weight product.

Use Case: Smart Seating

One of the most powerful features enabled by the relationship graph is smart seating: automatically assigning guests to tables in a way that maximizes social cohesion and minimizes conflicts. This is a real optimization problem that organizers of weddings and large events spend hours solving manually.

Smart Seating Algorithm
SMART SEATING OPTIMIZATION

INPUT:
├── participants: List<UserId> (confirmed guests)
├── tables: List<Table> (capacity per table)
├── relationships: Graph<UserId, Relationship>
├── communities: Map<CommunityId, Set<UserId>>
└── constraints: organizer-defined rules

OBJECTIVE: Maximize total table cohesion score
  score(table) = SUM(weight(A, B)) for all pairs in table
  totalScore   = SUM(score(table)) for all tables

HARD CONSTRAINTS:
├── No NEMICO pairs at the same table
├── Table capacity not exceeded
├── Organizer-pinned assignments respected
└── Accessibility requirements met

SOFT CONSTRAINTS (weighted penalties):
├── Keep families together (community bonus +2.0)
├── Separate EX_CONIUGE / EX_PARTNER (penalty -3.0)
├── Mix communities slightly (diversity bonus +0.5)
└── Balance table sizes (variance penalty -0.2)

ALGORITHM: Greedy + Local Search
1. Seed each table with a community core
2. Assign remaining guests by max affinity
3. Local search: swap pairs to improve score
4. Check hard constraints after each swap
5. Repeat until no improvement found

EXAMPLE OUTPUT (Wedding, 120 guests, 12 tables):
Table 1:  Bride's parents + close family    (score: 18.4)
Table 2:  Groom's parents + close family    (score: 17.8)
Table 3:  Bride's extended family           (score: 12.1)
Table 4:  Groom's extended family           (score: 11.5)
Table 5:  University friends (mixed)        (score: 9.2)
...
Table 12: Loose connections + colleagues    (score: 4.3)
Conflicts avoided: 3 (2 ex-couples, 1 rival pair)

Why This Matters

Manual seating for a 120-person wedding typically takes 3-5 hours of negotiation between the couple and families. The smart seating algorithm produces an optimized first draft in under 2 seconds, which the organizer can then fine-tune with drag-and-drop adjustments. Each manual change is validated against the conflict rules in real time.

Invitation Suggestions

The relationship graph also powers friend-of-friend recommendations. When building a guest list, the system suggests people that the organizer might have forgotten, based on their connections to already-invited guests.

Invitation Suggestion Algorithm
INVITATION SUGGESTIONS (Friend-of-Friend)

INPUT:
├── invitedGuests: Set<UserId>
├── organizerId: UserId
└── eventType: EventType (WEDDING, PARTY, CORPORATE, ...)

ALGORITHM:
1. For each invited guest G:
     connections = getAcceptedRelationships(G)
     for each connection C not in invitedGuests:
       score(C) += relationship(G, C).weight

2. For each candidate C:
     mutualConnections = count of invited guests connected to C
     avgWeight = average weight of those connections
     score(C) = mutualConnections * avgWeight

3. BOOST by relationship to organizer:
     if organizer knows C directly:
       score(C) *= 1.5

4. FILTER by event type:
     WEDDING  → boost family (isFamilyRelation * 1.3)
     PARTY    → boost friends (isSocialRelation * 1.2)
     CORPORATE → boost colleagues (isOrganizationalRelation * 1.4)

5. SORT by score DESC, return top 20

EXAMPLE (Wedding, 80 already invited):
Suggested:
├── Mario R.  (score: 4.2) - cousin of bride, friend of 3 guests
├── Laura B.  (score: 3.8) - colleague + friend of 5 guests
├── Andrea M. (score: 3.1) - university friend of 4 guests
└── ... (17 more suggestions)

This feature transforms the guest list creation from a memory exercise into a data-driven process. The organizer still makes all final decisions, but the system ensures no important connection is accidentally overlooked.

D3.js Visualization

All of this data comes alive through an interactive D3.js force-directed graph rendered in the frontend. Users can explore their social network, see communities highlighted with colors, and interact with the graph in real time.

D3.js Graph Configuration
D3.JS FORCE-DIRECTED GRAPH

NODES (circles):
├── Size: proportional to connection count
├── Color: by community (auto-detected)
├── Border: gold for organizer, white for others
├── Label: user name (shown on hover)
└── Icon: relationship category icon

EDGES (lines):
├── Thickness: proportional to |weight|
├── Color:
│   ├── Green gradient  → weight > 0.6 (strong positive)
│   ├── Blue gradient   → weight 0.2 - 0.6 (moderate)
│   ├── Gray            → weight 0.0 - 0.2 (weak)
│   └── Red gradient    → weight < 0 (negative)
├── Style:
│   ├── Solid           → ACCEPTED
│   └── Dashed          → PENDING
└── Arrow: shown for directional relationships

FORCES:
├── charge: -300 (repulsion between nodes)
├── link distance: inversely proportional to weight
│   strongBond (w>0.8)  → distance 50px
│   weakBond   (w<0.3)  → distance 200px
├── center: pulls graph toward viewport center
└── collision: prevents node overlap (radius + 10px)

INTERACTIONS:
├── Drag: reposition nodes (fixes position)
├── Hover: highlight node + direct connections
├── Click: show relationship details panel
├── Zoom: mousewheel zoom + pan
├── Filter: toggle by category (family, social, work)
└── Search: find and focus on a specific person

The visualization is particularly useful during event planning. The organizer can see at a glance which social clusters exist, where the bridges between groups are, and which participants are isolated (few or no connections). This information directly feeds into table assignments and activity group creation.

Performance Optimization

For events with 200+ participants, rendering all relationships at once would create visual clutter. The graph implements progressive disclosure: initially showing only STRONG and VERY_STRONG connections, with a slider control that lets the user expand to weaker relationships. The D3 simulation runs on a Web Worker to avoid blocking the main UI thread.

The RelazioneUtente Aggregate Root

The entire relationship subsystem is anchored by the RelazioneUtente entity, which serves as the aggregate root. It encapsulates all the business logic discussed in this article: type validation, weight management, bidirectionality, state transitions, and recipient enforcement.

RelazioneUtente Entity Structure
RELAZIONE_UTENTE (Aggregate Root)

FIELDS:
├── id: Long (auto-generated)
├── utenteId1: Long (first user)
├── utenteId2: Long (second user)
├── tipoRelazione: TipoRelazione (enum, 52 types)
├── peso: Double (-1.0 to 1.0, auto-set from type)
├── bidirezionale: Boolean (auto-determined)
├── tipoRelazioneInversa: TipoRelazione (auto-computed)
├── stato: StatoRelazione (PENDING / ACCEPTED / REJECTED)
├── richiestaInviataDaUtenteId: Long (requester)
├── richiestaInviataIl: Instant (request timestamp)
├── rispostaDataIl: Instant (response timestamp)
├── note: String (max 500 chars)
├── creatoIl: Instant (audit)
├── aggiornatoIl: Instant (audit)
└── versione: Long (optimistic locking)

DATABASE INDEXES:
├── idx_relazione_utente1 (utenteId1)
├── idx_relazione_utente2 (utenteId2)
├── idx_relazione_tipo (tipoRelazione)
└── idx_relazione_peso (peso)

UNIQUE CONSTRAINT:
└── uk_coppia_utenti (utenteId1, utenteId2)

FACTORY METHOD:
  RelazioneUtente.crea(userId1, userId2, TipoRelazione.AMICO)
  → auto-sets: peso=0.5, bidirezionale=true,
    tipoRelazioneInversa=AMICO, stato=PENDING

INVARIANTS:
├── Users cannot have a relationship with themselves
├── Duplicate pairs are prevented by unique constraint
├── Weight must be between -1.0 and 1.0
├── Only the recipient can accept/reject
└── Only PENDING requests can change state

In the Next Article

The relationship graph is one of the most distinctive features of Play The Event, turning a simple guest list into a rich social network that powers intelligent automation. In the next article, we will explore another cross-cutting concern: how the platform handles multilingual support across 7 languages, including entity translations, UI localization, and locale-aware formatting.

Key Takeaways

  • 52 relationship types in 10 categories cover family, social, organizational, and negative connections
  • Weights from -1.0 to +1.0 drive graph algorithms for seating, suggestions, and conflict detection
  • RelationshipStrength maps continuous weights into 9 discrete levels used across the UI
  • Automatic bidirectionality: symmetric types (spouse, friend) vs. directional types (parent/child)
  • State machine (PENDING, ACCEPTED, REJECTED) ensures bilateral consent for all connections
  • Community detection identifies social clusters for intelligent table grouping
  • Conflict detection prevents ex-couples and rivals from being seated together
  • Smart seating algorithm optimizes table assignments using graph weights and community data
  • D3.js force-directed graph provides interactive visualization with filtering and progressive disclosure

Frequently asked questions

What is the purpose of a relationship graph in an event management system?

A relationship graph helps to understand how people are connected to each other, unlocking features such as intelligent seating arrangements, conflict detection, invitation suggestions, and community visualization.

How many categories of relationships does the system define, and what are they called?

The system defines 10 categories of relationships, including Core Family, Grandparents and Grandchildren, Uncles/Aunts and Nephews/Nieces, Cousins, In-Laws, Godparents and Step-Family, Extended Family, Social Relationships, Organizational Relationships, and Negative and Neutral.

What is automatic bidirectionality in the context of relationship graphs?

Automatic bidirectionality detection is a feature that determines whether a user-declared relationship is symmetric (the same type for both parties) or directional (each side has a different type), with the system automatically computing the inverse type when necessary.

What is the purpose of community detection in an event management system?

Community detection identifies clusters of people who are densely connected to each other, which is essential for features like automatic table grouping, activity suggestions, and understanding social dynamics.

Commenti

Caricamento commenti...

Sign in to comment

Discuss this article in the community

Join the Telegram group to discuss with other developers, ask questions and share your experiences.

Did you like this article?

Explore other content on the blog or discover my projects