Class AnnotationsWebService.AnnotationSearchResultValueObject
- Enclosing class:
AnnotationsWebService
- Author:
- tesarst
-
Constructor Summary
ConstructorsConstructorDescriptionAnnotationSearchResultValueObject(String value, String valueUri, String category, String categoryUri, Integer usageCount, String definition, List<AnnotationsWebService.OntologyTermSimpleValueObject> parents, String matchedVia, String matchedText, Long geneCount, Map<String, Integer> priorCategories, Long taxonId, String taxonCommonName, String taxonScientificName, AnnotationsWebService.ExampleUsageValueObject exampleUsage, Long priorCurationCount, AnnotationsWebService.LexicalTermMetadataValueObject sourceMetadata, AnnotationsWebService.TaxonConstraintValueObject taxonConstraint, Boolean viaSubClassOf) Creates a newAnnotationSearchResultValueObjectinstance. -
Method Summary
Modifier and TypeMethodDescriptionbooleanThe category a curator has already applied to this URI in the corpus — NOT what kind of thing the term is.URI ofcategory, on the same terms: the curated category's URI, null when unused.Definition of the term, if enriched.One representative, ACL-visible example of how this term has actually been used in the corpus — so a curator picking an unfamiliar/rare term can see it in context (e.g.Distinct genes annotated with this term (including the descendant terms walked under the request'sgeneCountMaxTermscap).The actual label or synonym text that scored the match — surfaced so the UI can render "↪ matches synonym 'Ammon's horn'" beneath the preferred label.Which Lucene field most likely produced this hit:preferred_label,exact_synonym,narrow_synonym,related_synonym,broad_synonym,alt_label(all equality tiers), orlabel_prefix,label_tokens,synonym_tokens(neighbourhood tiers).Nearest is_a OR part_of parents of this term, if enriched.Distinct-experiment counts grouped by the category that prior curators applied when tagging this URI on an experiment — e.g.Distinct experiments on which a prior curator annotated this term after being given the query string itself — the evidence?rank=commonalityorders by.Descriptive metadata for hits that came from a flat lexical source (Cellosaurus cell lines, MGI mouse strains): species, cell-line type, donor sex, strain type, and any problematic-entry flag.Common name of a gene hit's taxon (e.g.The taxon this ONTOLOGY TERM is restricted to, when the ontology declares one — OBO'sin_taxon.Taxon (species) of a gene hit — the gene'sTaxon.id.Scientific name of a gene hit's taxon (e.g.getValue()How this term was reached from the one that was asked about, on/annotations/parentsand/annotations/childrenonly.inthashCode()toString()
-
Constructor Details
-
AnnotationSearchResultValueObject
public AnnotationSearchResultValueObject(String value, String valueUri, String category, String categoryUri, Integer usageCount, @Nullable String definition, @Nullable List<AnnotationsWebService.OntologyTermSimpleValueObject> parents, @Nullable String matchedVia, @Nullable String matchedText, @Nullable Long geneCount, @Nullable Map<String, Integer> priorCategories, @Nullable Long taxonId, @Nullable String taxonCommonName, @Nullable String taxonScientificName, @Nullable AnnotationsWebService.ExampleUsageValueObject exampleUsage, @Nullable Long priorCurationCount, @Nullable AnnotationsWebService.LexicalTermMetadataValueObject sourceMetadata, @Nullable AnnotationsWebService.TaxonConstraintValueObject taxonConstraint, @Nullable Boolean viaSubClassOf) Creates a newAnnotationSearchResultValueObjectinstance.- Parameters:
value-valueUri-category- The category a curator has already applied to this URI in the corpus — NOT what kind of thing the term is. It is carried over from theCharacteristicrows the search matched, so a term nobody has tagged yet is null however obvious its kind: CLO'sahl-1 cellis as uncategorised as any Cellosaurus row, because neither has ever been used. That makes absent mean "unused", never "conflicts with the category you asked for" — a client that filters rows on category equality discards every term it would be the first to apply, which is most of a freshly loaded vocabulary. CAB lost two weeks to exactly that reading (2026-08-18): one previously-curated CLO row suppressed every other registration of the same cell line.priorCategoriesis the same signal unflattened, with the per-category counts, and is the better one to resolve on. For "what kind of thing is this", walkparentson an ontology term, or readsourceMetadata.cellLineType/strainTypeon a flat catalogue — those are declared by the source rather than inferred from curation.categoryUri- URI ofcategory, on the same terms: the curated category's URI, null when unused.usageCount-definition- Definition of the term, if enriched. Null indicates "not enriched" (the typeahead caller can lazy-load via/annotations/term?uri=X); empty string indicates "enriched, no definition known". Populated for the top-25 search hits only — seegetTerms.parents- Nearest is_a OR part_of parents of this term, if enriched. Null indicates "not enriched" (sentinel for lazy-load); empty list indicates "enriched, no parents" (e.g. top-level term). Populated for the top-25 search hits only — seegetTerms.matchedVia- Which Lucene field most likely produced this hit:preferred_label,exact_synonym,narrow_synonym,related_synonym,broad_synonym,alt_label(all equality tiers), orlabel_prefix,label_tokens,synonym_tokens(neighbourhood tiers). Back-computed by replaying the query against the term's label and indexed synonyms — seecomputeMatchAttribution.Populated for the top-25 hits only.
nullthere means UNATTRIBUTABLE, not "weak": the hit came via a field we do not probe (definition, obo_id) or a pure relevance rank. Label-level tiers are still filled in from the row's own label when the term itself could not be resolved, so a row whose label equals the query never reports null; but a SYNONYM-exact hit whose owning ontology is unloaded does, because without the term its synonyms are invisible. A client cannot therefore implement "equality tiers only" from this field alone — treatnullas unknown and fall back to label equality.matchedText- The actual label or synonym text that scored the match — surfaced so the UI can render "↪ matches synonym 'Ammon's horn'" beneath the preferred label. Equals the term's label whenmatchedViaispreferred_label. Null whenmatchedViais null.geneCount- Distinct genes annotated with this term (including the descendant terms walked under the request'sgeneCountMaxTermscap). Populated only when the caller passedincludeGeneCount=true; null otherwise. Counts include propagation through GO subClassOf descendants by default — set viaGene2GOAssociationService.countByGOTermUris.priorCategories- Distinct-experiment counts grouped by the category that prior curators applied when tagging this URI on an experiment — e.g.{"cell line": 14, "protein": 1}. Lets resolvers break ambiguous label hits by curated history: a URI tagged 14× as a cell line and 1× as a protein is almost certainly a cell line, regardless of which ontology label the query happened to match. Populated for the top-N kept hits only; null on synthetic gene-fanout rows and on responses where the lookup was skipped (e.g. /annotations/parents). An empty map means "the URI has been used in curation but never with a non-null category" (rare; carries no signal).taxonId- Taxon (species) of a gene hit — the gene'sTaxon.id. Null on ontology-term hits (taxon is a gene-only attribute). Lets a curator tell which species a returned gene belongs to without resolving the NCBI Gene URI against an external database.taxonCommonName- Common name of a gene hit's taxon (e.g. "mouse"). Null on ontology-term hits.taxonScientificName- Scientific name of a gene hit's taxon (e.g. "Mus musculus"), to disambiguate when the common name is ambiguous. Null on ontology-term hits.exampleUsage- One representative, ACL-visible example of how this term has actually been used in the corpus — so a curator picking an unfamiliar/rare term can see it in context (e.g. "wild type" under the "genotype" factor of an accessible dataset). Populated only when the caller passesincludeExampleUsage=true; null when the flag is off, on synthetic gene rows, or when the term has no accessible usage. Gate rendering client-side (e.g. only for lowusageCount).priorCurationCount- Distinct experiments on which a prior curator annotated this term after being given the query string itself — the evidence?rank=commonalityorders by.Surfaced so the ordering can be audited rather than trusted. A hit promoted on
n=1and one promoted onn=508are the same position in the list and very different claims, and only this field tells them apart. The ranker orders and never filters, so a consumer that disagrees with the corpus can always recover the term it wanted; that recovery is only possible if the strength of the evidence is visible.Populated when the per-string prior was computed for the request (i.e. under
rank=commonality); null otherwise, and null rather than 0 on rows the prior did not cover.0means the string has been written in the corpus but never for this term — a real and useful negative.⚠️ Curation history, so it carries curation's mistakes. See
AnnotationsWebService.PriorCurationValueObjectfor the fuller warning and for theagreementfigure that shows whether curators were actually consistent.sourceMetadata- Descriptive metadata for hits that came from a flat lexical source (Cellosaurus cell lines, MGI mouse strains): species, cell-line type, donor sex, strain type, and any problematic-entry flag. Null for hits from a real ontology, which carry none of this.A cell-line name on its own is not actionable — it does not say which organism the line came from, and it does not say the line is a known misidentified one. Both facts exist in the source and used to be dropped at parse time. This is descriptive metadata about the term, like
definition; it is NOT a value to annotate an experiment with.🛑 Gemma does not filter these vocabularies by species — see
AnnotationsWebService.LexicalTermMetadataValueObject. Scoping is the caller's decision, made withncbiTaxonIdin hand.taxonConstraint- The taxon this ONTOLOGY TERM is restricted to, when the ontology declares one — OBO'sin_taxon. Null for the overwhelming majority of terms, which declare no constraint.🛑 This is NOT
taxonIdand the two must not be conflated.taxonIdsays "this hit IS a gene belonging to that species"; this says "this term only APPLIES to that species". Different claims about different things, which is why it is a separate field rather than a second meaning loaded onto an existing one.Why it matters: MONDO declares
in_taxonon 3,201 terms and only 30 of them are human, so it is overwhelmingly a marker for "this term is not about your organism". Without it,MONDO:0700199 sheep lung adenocarcinomais indistinguishable from a mouse disease — right namespace, right organ, wrong species — and the category→namespace table structurally cannot catch that class, because the namespace is correct. A tag of exactly that shape reached a C57BL/6 mouse experiment on 2026-08-15.Populated for the top-N enriched hits only, like
definition. Gemma does not act on it: whether a species mismatch is a reject or a repair is the caller's decision, and MONDO'scrossSpeciesExactMatchoften supplies the counterpart to repair TO.viaSubClassOf- How this term was reached from the one that was asked about, on/annotations/parentsand/annotations/childrenonly.true= byrdfs:subClassOfalone;false= only by crossing one of the non-taxonomic relations Gemma follows;null= not computed (every other route that emits this object).Both kinds of edge are walked and the flattened list cannot be partitioned by the caller. On CHEBI the non-taxonomic set is the
part_offamily plushas_role(added inChebiOntologyService), and CHEBI asserts a compound's roles withhas_role, so on a CHEBI term this field is the structure/role split. Measured on gemma2, 2026-09-04,vancomycinwithdirect=true— 4 parents, one subClassOf and three role fillers:glycopeptide CHEBI_24396 viaSubClassOf: true antibacterial drug CHEBI_36047 viaSubClassOf: false antimicrobial agent CHEBI_33281 viaSubClassOf: false bacterial metabolite CHEBI_76969 viaSubClassOf: false
The alternative is to ask each parent for its own ancestors and test for a CHEBI role root, which costs one extra request per parent and has to pick the right root: testing
role(CHEBI_50906) over-keeps, becauseglycopeptide,peptideandorganic amino compoundall carry it throughchemical role; testingbiological role(CHEBI_24432) alone dropsantiinfective agent, which sits underapplication(CHEBI_33232). This field needs no root and no second request.It says which edge was crossed, never whether the term is worth keeping.
role,biological roleandapplicationare as uninformative aschemical entityand are all reached acrosshas_role.
-
-
Method Details
-
getValue
-
getValueUri
-
getCategory
The category a curator has already applied to this URI in the corpus — NOT what kind of thing the term is. It is carried over from theCharacteristicrows the search matched, so a term nobody has tagged yet is null however obvious its kind: CLO'sahl-1 cellis as uncategorised as any Cellosaurus row, because neither has ever been used. That makes absent mean "unused", never "conflicts with the category you asked for" — a client that filters rows on category equality discards every term it would be the first to apply, which is most of a freshly loaded vocabulary. CAB lost two weeks to exactly that reading (2026-08-18): one previously-curated CLO row suppressed every other registration of the same cell line.priorCategoriesis the same signal unflattened, with the per-category counts, and is the better one to resolve on. For "what kind of thing is this", walkparentson an ontology term, or readsourceMetadata.cellLineType/strainTypeon a flat catalogue — those are declared by the source rather than inferred from curation. -
getCategoryUri
URI ofcategory, on the same terms: the curated category's URI, null when unused. -
getUsageCount
-
getDefinition
Definition of the term, if enriched. Null indicates "not enriched" (the typeahead caller can lazy-load via/annotations/term?uri=X); empty string indicates "enriched, no definition known". Populated for the top-25 search hits only — seegetTerms. -
getParents
Nearest is_a OR part_of parents of this term, if enriched. Null indicates "not enriched" (sentinel for lazy-load); empty list indicates "enriched, no parents" (e.g. top-level term). Populated for the top-25 search hits only — seegetTerms. -
getMatchedVia
Which Lucene field most likely produced this hit:preferred_label,exact_synonym,narrow_synonym,related_synonym,broad_synonym,alt_label(all equality tiers), orlabel_prefix,label_tokens,synonym_tokens(neighbourhood tiers). Back-computed by replaying the query against the term's label and indexed synonyms — seecomputeMatchAttribution.Populated for the top-25 hits only.
nullthere means UNATTRIBUTABLE, not "weak": the hit came via a field we do not probe (definition, obo_id) or a pure relevance rank. Label-level tiers are still filled in from the row's own label when the term itself could not be resolved, so a row whose label equals the query never reports null; but a SYNONYM-exact hit whose owning ontology is unloaded does, because without the term its synonyms are invisible. A client cannot therefore implement "equality tiers only" from this field alone — treatnullas unknown and fall back to label equality. -
getMatchedText
The actual label or synonym text that scored the match — surfaced so the UI can render "↪ matches synonym 'Ammon's horn'" beneath the preferred label. Equals the term's label whenmatchedViaispreferred_label. Null whenmatchedViais null. -
getGeneCount
Distinct genes annotated with this term (including the descendant terms walked under the request'sgeneCountMaxTermscap). Populated only when the caller passedincludeGeneCount=true; null otherwise. Counts include propagation through GO subClassOf descendants by default — set viaGene2GOAssociationService.countByGOTermUris. -
getPriorCategories
Distinct-experiment counts grouped by the category that prior curators applied when tagging this URI on an experiment — e.g.{"cell line": 14, "protein": 1}. Lets resolvers break ambiguous label hits by curated history: a URI tagged 14× as a cell line and 1× as a protein is almost certainly a cell line, regardless of which ontology label the query happened to match. Populated for the top-N kept hits only; null on synthetic gene-fanout rows and on responses where the lookup was skipped (e.g. /annotations/parents). An empty map means "the URI has been used in curation but never with a non-null category" (rare; carries no signal). -
getTaxonId
Taxon (species) of a gene hit — the gene'sTaxon.id. Null on ontology-term hits (taxon is a gene-only attribute). Lets a curator tell which species a returned gene belongs to without resolving the NCBI Gene URI against an external database. -
getTaxonCommonName
Common name of a gene hit's taxon (e.g. "mouse"). Null on ontology-term hits. -
getTaxonScientificName
Scientific name of a gene hit's taxon (e.g. "Mus musculus"), to disambiguate when the common name is ambiguous. Null on ontology-term hits. -
getExampleUsage
One representative, ACL-visible example of how this term has actually been used in the corpus — so a curator picking an unfamiliar/rare term can see it in context (e.g. "wild type" under the "genotype" factor of an accessible dataset). Populated only when the caller passesincludeExampleUsage=true; null when the flag is off, on synthetic gene rows, or when the term has no accessible usage. Gate rendering client-side (e.g. only for lowusageCount). -
getPriorCurationCount
Distinct experiments on which a prior curator annotated this term after being given the query string itself — the evidence?rank=commonalityorders by.Surfaced so the ordering can be audited rather than trusted. A hit promoted on
n=1and one promoted onn=508are the same position in the list and very different claims, and only this field tells them apart. The ranker orders and never filters, so a consumer that disagrees with the corpus can always recover the term it wanted; that recovery is only possible if the strength of the evidence is visible.Populated when the per-string prior was computed for the request (i.e. under
rank=commonality); null otherwise, and null rather than 0 on rows the prior did not cover.0means the string has been written in the corpus but never for this term — a real and useful negative.⚠️ Curation history, so it carries curation's mistakes. See
AnnotationsWebService.PriorCurationValueObjectfor the fuller warning and for theagreementfigure that shows whether curators were actually consistent. -
getSourceMetadata
Descriptive metadata for hits that came from a flat lexical source (Cellosaurus cell lines, MGI mouse strains): species, cell-line type, donor sex, strain type, and any problematic-entry flag. Null for hits from a real ontology, which carry none of this.A cell-line name on its own is not actionable — it does not say which organism the line came from, and it does not say the line is a known misidentified one. Both facts exist in the source and used to be dropped at parse time. This is descriptive metadata about the term, like
definition; it is NOT a value to annotate an experiment with.🛑 Gemma does not filter these vocabularies by species — see
AnnotationsWebService.LexicalTermMetadataValueObject. Scoping is the caller's decision, made withncbiTaxonIdin hand. -
getTaxonConstraint
The taxon this ONTOLOGY TERM is restricted to, when the ontology declares one — OBO'sin_taxon. Null for the overwhelming majority of terms, which declare no constraint.🛑 This is NOT
taxonIdand the two must not be conflated.taxonIdsays "this hit IS a gene belonging to that species"; this says "this term only APPLIES to that species". Different claims about different things, which is why it is a separate field rather than a second meaning loaded onto an existing one.Why it matters: MONDO declares
in_taxonon 3,201 terms and only 30 of them are human, so it is overwhelmingly a marker for "this term is not about your organism". Without it,MONDO:0700199 sheep lung adenocarcinomais indistinguishable from a mouse disease — right namespace, right organ, wrong species — and the category→namespace table structurally cannot catch that class, because the namespace is correct. A tag of exactly that shape reached a C57BL/6 mouse experiment on 2026-08-15.Populated for the top-N enriched hits only, like
definition. Gemma does not act on it: whether a species mismatch is a reject or a repair is the caller's decision, and MONDO'scrossSpeciesExactMatchoften supplies the counterpart to repair TO. -
getViaSubClassOf
How this term was reached from the one that was asked about, on/annotations/parentsand/annotations/childrenonly.true= byrdfs:subClassOfalone;false= only by crossing one of the non-taxonomic relations Gemma follows;null= not computed (every other route that emits this object).Both kinds of edge are walked and the flattened list cannot be partitioned by the caller. On CHEBI the non-taxonomic set is the
part_offamily plushas_role(added inChebiOntologyService), and CHEBI asserts a compound's roles withhas_role, so on a CHEBI term this field is the structure/role split. Measured on gemma2, 2026-09-04,vancomycinwithdirect=true— 4 parents, one subClassOf and three role fillers:glycopeptide CHEBI_24396 viaSubClassOf: true antibacterial drug CHEBI_36047 viaSubClassOf: false antimicrobial agent CHEBI_33281 viaSubClassOf: false bacterial metabolite CHEBI_76969 viaSubClassOf: false
The alternative is to ask each parent for its own ancestors and test for a CHEBI role root, which costs one extra request per parent and has to pick the right root: testing
role(CHEBI_50906) over-keeps, becauseglycopeptide,peptideandorganic amino compoundall carry it throughchemical role; testingbiological role(CHEBI_24432) alone dropsantiinfective agent, which sits underapplication(CHEBI_33232). This field needs no root and no second request.It says which edge was crossed, never whether the term is worth keeping.
role,biological roleandapplicationare as uninformative aschemical entityand are all reached acrosshas_role. -
equals
-
hashCode
-
toString
-