Skip to main content

Fish Species Relationship System

This document explains how the fish species relationship system works and provides real-world examples.

Overview

The system uses three main entities:

  1. FishSpecies - The main species entity
  2. FishSpeciesRelationship - Links species together with relationship types
  3. TaxonomicRank - Represents taxonomic hierarchy (Family, Genus, Species, etc.)

Relationship Types

Life Stage Relationships

Example: Steelhead → Rainbow Trout

  • Steelhead (SourceSpecies) has relationship LifeStage to Rainbow Trout (TargetSpecies)
  • Notes: "Anadromous form of rainbow trout"
  • This allows users to log "Steelhead" but the system knows it's the same species as Rainbow Trout

Example: Smolt → Salmon

  • Juvenile salmon forms can be linked to adult forms using JuvenileForm relationship

Subspecies/Variant Relationships

Example: Redear Sunfish → Bluegill

  • Redear Sunfish (SourceSpecies) has relationship Subspecies to Bluegill (TargetSpecies)
  • Notes: "Subspecies of bluegill, distinguished by red ear flap"

Example: Northern Pike → Pike

  • Can use Variant relationship for regional variants

Hybrid Relationships

Example: Hybrid Bluegill

  • Create Hybrid Bluegill as a species
  • Create two relationships:
    1. Hybrid Bluegill → Bluegill (RelationshipType: HybridParent, IsPrimaryParent: true)
    2. Hybrid Bluegill → Green Sunfish (RelationshipType: HybridParent, IsPrimaryParent: false)

Synonym Relationships

Example: Black Bass → Largemouth Bass

  • Black Bass (SourceSpecies) has relationship Synonym to Largemouth Bass (TargetSpecies)
  • This allows users to search by either name and find the same species

Taxonomic Hierarchy Example

Largemouth Bass Taxonomic Structure:

Kingdom: Animalia
└─ Phylum: Chordata
└─ Class: Actinopterygii
└─ Order: Perciformes
└─ Family: Centrarchidae (TaxonomicRank, Level: Family)
└─ Genus: Micropterus (TaxonomicRank, Level: Genus)
└─ Species: Micropterus salmoides (FishSpecies)

Usage Examples

Get all life stages of Rainbow Trout:

var rainbowTrout = context.FishSpecies
.FirstOrDefault(fs => fs.CommonName == "Rainbow Trout");

var lifeStages = context.FishSpeciesRelationships
.Where(fsr => fsr.TargetSpeciesId == rainbowTrout.Id
&& fsr.RelationshipType == SpeciesRelationshipType.LifeStage)
.Select(fsr => fsr.SourceSpecies)
.ToList();
// Returns: Steelhead, etc.

Get parent species (for subspecies):

var redearSunfish = context.FishSpecies
.FirstOrDefault(fs => fs.CommonName == "Redear Sunfish");

var parent = context.FishSpeciesRelationships
.Where(fsr => fsr.SourceSpeciesId == redearSunfish.Id
&& fsr.RelationshipType == SpeciesRelationshipType.Subspecies)
.Select(fsr => fsr.TargetSpecies)
.FirstOrDefault();
// Returns: Bluegill

Get all hybrids involving Bluegill:

var bluegill = context.FishSpecies
.FirstOrDefault(fs => fs.CommonName == "Bluegill");

var hybrids = context.FishSpeciesRelationships
.Where(fsr => fsr.TargetSpeciesId == bluegill.Id
&& fsr.RelationshipType == SpeciesRelationshipType.HybridParent)
.Select(fsr => fsr.SourceSpecies)
.ToList();
// Returns: Hybrid Bluegill, etc.

Get all synonyms:

var largemouthBass = context.FishSpecies
.FirstOrDefault(fs => fs.CommonName == "Largemouth Bass");

var synonyms = context.FishSpeciesRelationships
.Where(fsr =>
(fsr.SourceSpeciesId == largemouthBass.Id || fsr.TargetSpeciesId == largemouthBass.Id)
&& fsr.RelationshipType == SpeciesRelationshipType.Synonym)
.Select(fsr => fsr.SourceSpeciesId == largemouthBass.Id
? fsr.TargetSpecies
: fsr.SourceSpecies)
.ToList();
// Returns: Black Bass, etc.

Building a Species Tree

To show all relationships for a species:

var species = context.FishSpecies
.Include(fs => fs.SourceRelationships)
.ThenInclude(fsr => fsr.TargetSpecies)
.Include(fs => fs.TargetRelationships)
.ThenInclude(fsr => fsr.SourceSpecies)
.FirstOrDefault(fs => fs.Id == speciesId);

Data Seeding Examples

Example 1: Steelhead and Rainbow Trout

// Create Rainbow Trout
var rainbowTrout = new FishSpecies
{
CommonName = "Rainbow Trout",
ScientificName = "Oncorhynchus mykiss",
Description = "Freshwater trout species"
};

// Create Steelhead
var steelhead = new FishSpecies
{
CommonName = "Steelhead",
ScientificName = "Oncorhynchus mykiss",
Description = "Anadromous form of rainbow trout",
HasDistinctLifeStages = true,
LifeStageNotes = "Anadromous form that migrates to ocean"
};

// Create relationship
var relationship = new FishSpeciesRelationship
{
SourceSpecies = steelhead,
TargetSpecies = rainbowTrout,
RelationshipType = SpeciesRelationshipType.LifeStage,
Notes = "Anadromous form of rainbow trout",
Confidence = 1.0
};

Example 2: Bluegill Variants

// Create Bluegill (parent)
var bluegill = new FishSpecies
{
CommonName = "Bluegill",
ScientificName = "Lepomis macrochirus",
Description = "Common sunfish species"
};

// Create Redear Sunfish (subspecies)
var redearSunfish = new FishSpecies
{
CommonName = "Redear Sunfish",
ScientificName = "Lepomis microlophus",
Description = "Subspecies of bluegill with red ear flap"
};

// Create relationship
var relationship = new FishSpeciesRelationship
{
SourceSpecies = redearSunfish,
TargetSpecies = bluegill,
RelationshipType = SpeciesRelationshipType.Subspecies,
Notes = "Distinguished by red ear flap",
Confidence = 1.0
};

Example 3: Hybrid Bluegill

// Create Bluegill
var bluegill = new FishSpecies { CommonName = "Bluegill", ... };

// Create Green Sunfish
var greenSunfish = new FishSpecies { CommonName = "Green Sunfish", ... };

// Create Hybrid Bluegill
var hybridBluegill = new FishSpecies
{
CommonName = "Hybrid Bluegill",
Description = "Hybrid between bluegill and green sunfish"
};

// Create relationships
var relationship1 = new FishSpeciesRelationship
{
SourceSpecies = hybridBluegill,
TargetSpecies = bluegill,
RelationshipType = SpeciesRelationshipType.HybridParent,
IsPrimaryParent = true
};

var relationship2 = new FishSpeciesRelationship
{
SourceSpecies = hybridBluegill,
TargetSpecies = greenSunfish,
RelationshipType = SpeciesRelationshipType.HybridParent,
IsPrimaryParent = false
};

Best Practices

  1. Always create the parent/base species first before creating relationships
  2. Use high confidence (1.0) for well-established relationships
  3. Use lower confidence for uncertain or debated relationships
  4. Add detailed notes explaining the relationship, especially for life stages
  5. Set IsValidSpecies = false for synonym entries that shouldn't appear in main species lists
  6. Use TaxonomicRank for proper scientific classification
  7. Link to Genus and Family for better organization and queries

Query Patterns

Find all variants/subspecies of a species:

var variants = context.FishSpeciesRelationships
.Where(fsr => fsr.TargetSpeciesId == parentId
&& (fsr.RelationshipType == SpeciesRelationshipType.Subspecies
|| fsr.RelationshipType == SpeciesRelationshipType.Variant))
.Select(fsr => fsr.SourceSpecies);

Find parent species:

var parent = context.FishSpeciesRelationships
.Where(fsr => fsr.SourceSpeciesId == childId
&& (fsr.RelationshipType == SpeciesRelationshipType.ParentSpecies
|| fsr.RelationshipType == SpeciesRelationshipType.Subspecies))
.Select(fsr => fsr.TargetSpecies)
.FirstOrDefault();

Get all species in a genus:

var genusId = context.TaxonomicRanks
.FirstOrDefault(tr => tr.Level == TaxonomicLevel.Genus
&& tr.Name == "Micropterus")?.Id;

var species = context.FishSpecies
.Where(fs => fs.GenusId == genusId);