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:
- FishSpecies - The main species entity
- FishSpeciesRelationship - Links species together with relationship types
- TaxonomicRank - Represents taxonomic hierarchy (Family, Genus, Species, etc.)
Relationship Types
Life Stage Relationships
Example: Steelhead → Rainbow Trout
- Steelhead (SourceSpecies) has relationship
LifeStageto 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
JuvenileFormrelationship
Subspecies/Variant Relationships
Example: Redear Sunfish → Bluegill
- Redear Sunfish (SourceSpecies) has relationship
Subspeciesto Bluegill (TargetSpecies) - Notes: "Subspecies of bluegill, distinguished by red ear flap"
Example: Northern Pike → Pike
- Can use
Variantrelationship for regional variants
Hybrid Relationships
Example: Hybrid Bluegill
- Create Hybrid Bluegill as a species
- Create two relationships:
- Hybrid Bluegill → Bluegill (RelationshipType:
HybridParent, IsPrimaryParent: true) - Hybrid Bluegill → Green Sunfish (RelationshipType:
HybridParent, IsPrimaryParent: false)
- Hybrid Bluegill → Bluegill (RelationshipType:
Synonym Relationships
Example: Black Bass → Largemouth Bass
- Black Bass (SourceSpecies) has relationship
Synonymto 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
Querying Related Species
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
- Always create the parent/base species first before creating relationships
- Use high confidence (1.0) for well-established relationships
- Use lower confidence for uncertain or debated relationships
- Add detailed notes explaining the relationship, especially for life stages
- Set IsValidSpecies = false for synonym entries that shouldn't appear in main species lists
- Use TaxonomicRank for proper scientific classification
- 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);