README
¶
modware-annotation
gRPC microservice for managing biological annotations and feature annotations in dictyBase. Backed by ArangoDB.
On every create, update, or delete mutation the service publishes a protobuf-serialized event to NATS so that downstream services can react without polling.
- Running the Server
- NATS Events
- gRPC API
- Tagged Annotation Service Methods
- Feature Annotation Service Methods
- Connect with Go
- Create an Annotation
- Get an Annotation
- Update an Annotation
- Delete an Annotation
- List Annotations with Pagination and Filters
- Filtering
- Annotation Groups
- Autocomplete Annotation
- Search Annotation
- Create a Feature Annotation
- Get a Feature Annotation
- Update a Feature Annotation
- Tag Operations on Feature Annotations
- List Feature Annotations by Publication
- ArangoDB Schema
Running the Server
Two gRPC servers are available: start-server for tagged annotations (port 9560) and start-feature-server for feature annotations (port 9570). See --help on each command for the full flag list.
# Tagged annotation server
modware-annotation start-server \
--port 9560 \
--arangodb-database annotation \
--arangodb-user root \
--arangodb-pass secret \
--arangodb-host arangodb \
--arangodb-port 8529 \
--nats-host nats \
--nats-port 4222
# Feature annotation server
modware-annotation start-feature-server \
--port 9570 \
--arangodb-database annofeature \
--arangodb-user root \
--arangodb-pass secret \
--arangodb-host arangodb \
--arangodb-port 8529 \
--nats-host nats \
--nats-port 4222
Core Flags
Common to all commands.
| Flag | Default | Description |
|---|---|---|
--log-format |
json |
Logging output format (json or text) |
--log-level |
error |
Log level (debug, warn, error, fatal, panic) |
Server Flags (both start-server and start-feature-server)
| Flag | Default | Env Variable | Description |
|---|---|---|---|
--port |
9560 / 9570 |
— | gRPC server listen port |
--arangodb-database, --db |
annotation / annofeature |
ARANGODB_DATABASE |
ArangoDB database name |
--arangodb-user |
— | ARANGODB_USER |
ArangoDB user |
--arangodb-pass |
— | ARANGODB_PASS |
ArangoDB password |
--arangodb-host |
— | ARANGODB_SERVICE_HOST |
ArangoDB host |
--arangodb-port |
— | ARANGODB_SERVICE_PORT |
ArangoDB port |
--is-secure |
false |
— | Use TLS for ArangoDB |
--nats-host |
— | NATS_SERVICE_HOST |
NATS host |
--nats-port |
— | NATS_SERVICE_PORT |
NATS port |
ArangoDB Collection Flags — Tagged Annotations
Control the names of collections, edges, and graphs used by start-server. Defaults match the dictyBase production configuration.
| Flag | Default | Description |
|---|---|---|
--anno-collection |
annotation |
Document collection for annotations |
--annogroup-collection |
annotation_group |
Document collection for annotation groups |
--annoterm-collection |
annotation_cvterm |
Edge collection linking annotation → ontology term |
--annover-collection |
annotation_version |
Edge collection linking old → new annotation versions |
--annoterm-graph |
annotation_tag |
Named graph over annotation_cvterm edges |
--annover-graph |
annotation_history |
Named graph over annotation_version edges |
ArangoDB Collection Flags — Feature Annotations
Control the names of collections, edges, and graphs used by start-feature-server.
| Flag | Default | Description |
|---|---|---|
--feature-collection |
feature |
Document collection for feature annotations |
--pub-collection |
publication |
Document collection for publications |
--edge-collection |
feature_publication |
Edge collection linking feature → publication |
--feature-graph |
feature_pub_graph |
Named graph over feature_publication edges |
Ontology Flags
These flags configure which ontologies are loaded into ArangoDB and used by the tagged annotation service. The referenced ontologies must be loaded before the service starts (see load-ontologies command).
| Flag | Default | Description |
|---|---|---|
--term-collection |
cvterm |
Collection for ontology terms |
--rel-collection |
cvterm_relationship |
Edge collection for term relationships |
--cv-collection |
cv |
Collection for ontology metadata |
--obograph |
obograph |
Named graph for the ontology graph |
# Load ontologies before starting the server
modware-annotation load-ontologies \
--arangodb-database annotation \
--arangodb-user root \
--arangodb-pass secret \
--arangodb-host arangodb \
--obograph path/to/obograph.json
NATS Events
Tagged Annotations
| Operation | NATS Subject | Payload |
|---|---|---|
| Create annotation | AnnotationService.Create |
TaggedAnnotation |
| Update annotation | AnnotationService.Update |
TaggedAnnotation |
| Delete annotation | AnnotationService.Delete |
TaggedAnnotation |
Feature Annotations
| Operation | NATS Subject | Payload |
|---|---|---|
| Create feature annotation | FeatureAnnotationService.Create |
FeatureAnnotation |
| Update feature annotation | FeatureAnnotationService.Update |
FeatureAnnotation |
| Add / set / remove tags | FeatureAnnotationService.Update |
FeatureAnnotation |
Deserialize using proto.Unmarshal with the generated types from github.com/dictyBase/go-genproto/dictybaseapis/annotation or github.com/dictyBase/go-genproto/dictybaseapis/feature_annotation.
gRPC API
Full protobuf definitions: dictybaseapis/annotation.proto and dictybaseapis/feature_annotation.proto.
Tagged Annotation Service Methods
| Method | Request | Response | Description |
|---|---|---|---|
GetAnnotation |
AnnotationId |
TaggedAnnotation |
Retrieve an annotation by ID |
GetEntryAnnotation |
EntryAnnotationRequest |
TaggedAnnotation |
Retrieve an annotation by entry ID |
ListAnnotations |
ListParameters |
TaggedAnnotationCollection |
Paginated annotation listing with filters |
CreateAnnotation |
NewTaggedAnnotation |
TaggedAnnotation |
Create a new annotation |
UpdateAnnotation |
TaggedAnnotationUpdate |
TaggedAnnotation |
Update an existing annotation |
DeleteAnnotation |
DeleteAnnotationRequest |
Empty |
Delete an annotation |
CreateAnnotationGroup |
AnnotationIdList |
TaggedAnnotationGroup |
Create a group from multiple annotations |
GetAnnotationGroup |
GroupEntryId |
TaggedAnnotationGroup |
Retrieve an annotation group by ID |
AddToAnnotationGroup |
AnnotationGroupId |
TaggedAnnotationGroup |
Add annotations to an existing group |
DeleteAnnotationGroup |
GroupEntryId |
Empty |
Delete an annotation group |
ListAnnotationGroups |
ListGroupParameters |
TaggedAnnotationGroupCollection |
Paginated group listing with filters |
GetAnnotationTag |
TagRequest |
AnnotationTag |
Retrieve an ontology tag by ID |
AutocompleteAnnotation |
AnnotationAutocompleteParameters |
AnnotationSuggestionCollection |
Suggest strain synonyms and genotypes for partial text |
SearchAnnotation |
AnnotationSearchParameters |
AnnotationSearchResultCollection |
Search strain annotations across 10 fields and 4 score bands |
OboJSONFileUpload |
stream FileUploadRequest |
FileUploadResponse |
Stream-upload an OBO JSON ontology file |
Autocomplete Annotation
The AutocompleteAnnotation method returns short suggestions for a partial strain synonym or a partial strain genotype. The request is an AnnotationAutocompleteParameters message. The response is an AnnotationSuggestionCollection message.
Request fields:
| Field | Value | Behavior |
|---|---|---|
data.type |
annotation |
The resource name of the request. |
data.attributes.query |
3 characters or more | The service trims the text, converts it to lower case, and removes accents. |
data.attributes.limit |
5 by default, 50 maximum | The service uses 5 when the value is 0. The service rejects a value above 50 with InvalidArgument. |
Search behavior:
- The service searches the
synonymandgenotypetags of thedicty_annotationontology. - The service searches strain entries only. The entry identifier starts with
DBS. - The service uses active annotations only. The
is_obsoleteattribute isfalse. - The service ignores annotations with other tags, for example
description. - The service returns one suggestion for each strain. It keeps the suggestion with the best score.
- The service accepts a whole-value prefix and an allele prefix inside a multi-allele genotype value. The query
axeaand the querysadaboth match the genotypeaxeA1, axeB1, sadA-. - The service accepts a small typographical error. A fuzzy match uses an n-gram similarity of 0.3 or more.
Order of results:
- A prefix match comes before a fuzzy match. A prefix match has a score of 1000 or more.
- A fuzzy match has a score less than 1000.
- The metadata gives the number of suggestions in
meta.totaland the effective limit inmeta.limit.
Response fields of one AnnotationSuggestion message:
| Field | Meaning |
|---|---|
entry_id |
The strain identifier of the match, for example DBS0236126. |
field |
The tag of the match. The value is ANNOTATION_SEARCH_FIELD_SYNONYM or ANNOTATION_SEARCH_FIELD_GENOTYPE. |
display_text |
The annotation value. |
score |
The rank of the match. |
The method returns an empty collection and no error when no annotation matches. The method does not return NotFound for an empty result.
Search Annotation
The SearchAnnotation method searches 10 strain annotation fields. The request is an AnnotationSearchParameters message. The response is an AnnotationSearchResultCollection message.
Request fields:
| Field | Value | Behavior |
|---|---|---|
data.type |
annotation |
The resource name of the request. |
data.attributes.query |
2 characters or more | The service trims the text, converts it to lower case, and removes accents. The service rejects a query that has only stopwords. |
data.attributes.limit |
50 by default, 50 maximum | The service uses 50 when the value is 0. The service rejects a value below 0 or above 50 with InvalidArgument. |
Searched strain fields:
The service searches 10 strain fields across active strain annotations (DBS... entry identifiers):
- 6 annotation value fields:
synonym(dicty_annotationontology)genotype(dicty_annotationontology, whole-value and interior allele prefix)systematic_name(dicty_annotationontology, tagsystematic name)genetic_modification(dicty_annotationontology, tagmutant type)mutagenesis_method(Dd Mutagenesis Methodontology, tagmutagenesis method)public_note(dicty_annotationontology, tagpublic note, token and phrase match)
- 4 ontology term fields (term label matched through a 1-hop inbound traversal):
phenotype(Dicty Phenotypesontology)environment(Dicty Environmentontology)assay(Dictyostelium Assayontology)characteristic(dicty_strain_characteristicsontology)
Search behavior:
- The service searches strain entries only. The entry identifier starts with
DBS. - The service uses active annotations only. The
is_obsoleteattribute isfalse. - The service uses active ontology terms only. The
deprecatedattribute isfalse. - The service ignores annotations with other tags. It also ignores non-strain annotations (
DDB_G...,DBP...). - The service returns one result for each strain. It keeps the result with the best score.
- The service tolerates missing ontologies. If an ontology is not loaded, its branches return zero results and other branches continue.
- The service accepts a whole-value prefix and an allele prefix inside a multi-allele genotype value.
- The service accepts a small typographical error. A fuzzy match uses an n-gram similarity of 0.3 or more.
Order of results (4 score bands):
- A prefix match comes first. A prefix match has a score of 1000 or more (
1000 + BM25(d)). - A phrase match comes second. A phrase match has a score between 500 and 750 (
500 + BM25(d)). - A token match comes third. A token match has a score between 250 and 500 (
250 + BM25(d)). - A fuzzy match comes fourth. A fuzzy match has a score less than 250 (
BM25(d)). - The metadata gives the number of results in
meta.totaland the effective limit inmeta.limit.
Response fields of one AnnotationSearchResult message:
| Field | Meaning |
|---|---|
entry_id |
The strain identifier of the match, for example DBS0236126. |
field |
The field enum of the match, for example ANNOTATION_SEARCH_FIELD_PHENOTYPE. |
display_text |
The matched annotation value or ontology term label. |
score |
The rank of the match. |
The method returns an empty collection and no error when no annotation matches. The method does not return NotFound for an empty result.
Feature Annotation Service Methods
| Method | Request | Response | Description |
|---|---|---|---|
CreateFeatureAnnotation |
NewFeatureAnnotation |
FeatureAnnotation |
Create a new feature annotation |
GetFeatureAnnotation |
FeatureAnnotationId |
FeatureAnnotation |
Retrieve a feature annotation by ID |
GetFeatureAnnotationByName |
FeatureName |
FeatureAnnotation |
Retrieve a feature annotation by name |
UpdateFeatureAnnotation |
FeatureAnnotationUpdate |
FeatureAnnotation |
Update an existing feature annotation |
DeleteFeatureAnnotation |
DeleteFeatureAnnotationRequest |
Empty |
Delete a feature annotation |
AddTag |
AddTagRequest |
FeatureAnnotation |
Add a single tag to a feature |
AddTags |
AddTagsRequest |
FeatureAnnotation |
Add multiple tags to a feature |
SetTags |
SetTagsRequest |
FeatureAnnotation |
Replace all tags on a feature |
RemoveTags |
RemoveTagsRequest |
FeatureAnnotation |
Remove specific tags from a feature |
ListFeatureAnnotationsByPubmedId |
PubmedId |
FeatureAnnotationCollection |
List feature annotations by PubMed ID |
ListFeatureAnnotationsByDOI |
DOI |
FeatureAnnotationCollection |
List feature annotations by DOI |
Connect with Go
import (
"context"
"log"
"github.com/dictyBase/go-genproto/dictybaseapis/annotation"
"github.com/dictyBase/go-genproto/dictybaseapis/feature_annotation"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
// Tagged annotation client
annoConn, err := grpc.NewClient(
"localhost:9560",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatal(err)
}
defer annoConn.Close()
annoClient := annotation.NewTaggedAnnotationServiceClient(annoConn)
// Feature annotation client
featConn, err := grpc.NewClient(
"localhost:9570",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatal(err)
}
defer featConn.Close()
featClient := feature_annotation.NewFeatureAnnotationServiceClient(featConn)
ctx := context.Background()
_, _ = annoClient, featClient // use clients below
}
Create an Annotation
value, entry_id, created_by, tag, and ontology are required.
resp, err := annoClient.CreateAnnotation(ctx, &annotation.NewTaggedAnnotation{
Data: &annotation.NewTaggedAnnotation_Data{
Type: "annotation",
Attributes: &annotation.NewTaggedAnnotationAttributes{
Value: "localizes to nucleus",
EntryId: "DDB_G0285418",
CreatedBy: "user@dictybase.org",
Tag: "GO:0005634",
Ontology: "cellular_component",
Version: 1,
Rank: 0,
},
},
})
// resp.Data.Id — auto-generated annotation ID
Get an Annotation
resp, err := annoClient.GetAnnotation(ctx, &annotation.AnnotationId{Id: "12345"})
// Or by entry ID
resp, err := annoClient.GetEntryAnnotation(ctx, &annotation.EntryAnnotationRequest{
EntryId: "DDB_G0285418",
})
Update an Annotation
All attributes are optional; unset fields are left unchanged.
resp, err := annoClient.UpdateAnnotation(ctx, &annotation.TaggedAnnotationUpdate{
Data: &annotation.TaggedAnnotationUpdate_Data{
Type: "annotation",
Id: "12345",
Attributes: &annotation.TaggedAnnotationUpdateAttributes{
Value: "localizes to nucleolus",
EditableValue: "updated description",
IsObsolete: false,
},
},
})
Delete an Annotation
_, err := annoClient.DeleteAnnotation(ctx, &annotation.DeleteAnnotationRequest{
Id: "12345",
})
List Annotations with Pagination and Filters
ListAnnotations uses cursor-based pagination. The default page size is 10.
resp, err := annoClient.ListAnnotations(ctx, &annotation.ListParameters{
Limit: 10,
Cursor: 0,
Filter: "entry_id===DDB_G0285418",
})
// resp.Data — slice of annotations for this page
// resp.Meta.NextCursor — pass as Cursor to retrieve the next page (0 means last page)
// resp.Meta.Total — total number of records matching the filter
// resp.Meta.Limit — the effective page size
// Fetch the next page
if resp.Meta.NextCursor != 0 {
next, err := annoClient.ListAnnotations(ctx, &annotation.ListParameters{
Limit: 10,
Cursor: resp.Meta.NextCursor,
Filter: "entry_id===DDB_G0285418",
})
}
Filtering
ListAnnotations accepts a filter string in ListParameters. Filters follow the syntax field operator value and can be combined with boolean connectors.
Boolean Connectors
| Symbol | Meaning | Precedence |
|---|---|---|
; |
AND | Higher |
, |
OR | Lower |
AND takes precedence over OR.
String Operators
| Operator | Meaning |
|---|---|
=== |
Equals |
!== |
Not equals |
=~ |
Contains substring |
!~ |
Does not contain substring |
Numeric Operators
| Operator | Meaning |
|---|---|
== |
Equals |
!= |
Not equals |
> |
Greater than |
< |
Less than |
=< |
Less than or equal to |
>= |
Greater than or equal to |
Boolean Operators
| Operator | Meaning |
|---|---|
== |
Equals |
!= |
Not equals |
Filterable Fields
| Filter Key | Type | Operators | Description |
|---|---|---|---|
entry_id |
string | ===, !==, =~, !~ |
dictyBase gene/feature ID |
value |
string | ===, !==, =~, !~ |
Annotation text |
created_by |
string | ===, !==, =~, !~ |
Creator email |
tag |
string | ===, !==, =~, !~ |
Ontology term label |
ontology |
string | ===, !==, =~, !~ |
Ontology namespace |
version |
number | ==, !=, >, <, =<, >= |
Annotation version |
rank |
number | ==, !=, >, <, =<, >= |
Annotation rank |
is_obsolete |
boolean | ==, != |
Whether the annotation is obsolete |
Examples
// Annotations for a specific gene entry
resp, err := annoClient.ListAnnotations(ctx, &annotation.ListParameters{
Limit: 10,
Filter: "entry_id===DDB_G0285418",
})
// Annotations containing a keyword
resp, err := annoClient.ListAnnotations(ctx, &annotation.ListParameters{
Limit: 10,
Filter: "value=~actin",
})
// Annotations with a specific ontology tag
resp, err := annoClient.ListAnnotations(ctx, &annotation.ListParameters{
Limit: 10,
Filter: "tag===GO:0005634",
})
// Non-obsolete annotations of a given version or higher
resp, err := annoClient.ListAnnotations(ctx, &annotation.ListParameters{
Limit: 10,
Filter: "entry_id===DDB_G0285418;is_obsolete==false;version>=2",
})
// Annotations with either of two tags (OR)
resp, err := annoClient.ListAnnotations(ctx, &annotation.ListParameters{
Limit: 10,
Filter: "tag===GO:0005634,tag===GO:0005737",
})
Annotation Groups
Group multiple annotations together and manage them as a unit.
// Create a group
resp, err := annoClient.CreateAnnotationGroup(ctx, &annotation.AnnotationIdList{
Id: []string{"12345", "12346", "12347"},
})
// resp.Data.GroupId — the new group's ID
// Get a group
group, err := annoClient.GetAnnotationGroup(ctx, &annotation.GroupEntryId{
GroupId: resp.Data.GroupId,
})
// Add annotations to an existing group
updated, err := annoClient.AddToAnnotationGroup(ctx, &annotation.AnnotationGroupId{
GroupId: resp.Data.GroupId,
Id: []string{"12348"},
})
// List groups
groups, err := annoClient.ListAnnotationGroups(ctx, &annotation.ListGroupParameters{
Limit: 10,
Cursor: 0,
Filter: "entry_id===DDB_G0285418",
})
// Delete a group
_, err = annoClient.DeleteAnnotationGroup(ctx, &annotation.GroupEntryId{
GroupId: resp.Data.GroupId,
})
Group Filtering
ListAnnotationGroups uses the same filter syntax and fields as ListAnnotations. The filter is applied in two steps internally: first the matching annotation _key values are collected, then groups whose group array contains at least one of those keys are returned.
| Filter Key | Type | Description |
|---|---|---|
entry_id |
string | The entry being annotated |
created_by |
string | Email of the annotation creator |
tag |
string | Ontology term label |
ontology |
string | Ontology namespace |
rank |
number | Annotation ordering |
Group filtering supports the same operators and boolean connectors (; AND, , OR) as annotation filtering. See Filtering for the full operator reference.
// Groups containing annotations for a specific entry
groups, err := annoClient.ListAnnotationGroups(ctx, &annotation.ListGroupParameters{
Limit: 10,
Cursor: 0,
Filter: "entry_id===DDB_G0285418",
})
// Groups containing annotations with a specific tag
groups, err := annoClient.ListAnnotationGroups(ctx, &annotation.ListGroupParameters{
Limit: 10,
Filter: "tag===GO:0005634",
})
// Groups containing annotations matching either of two tags
groups, err := annoClient.ListAnnotationGroups(ctx, &annotation.ListGroupParameters{
Limit: 10,
Filter: "tag===GO:0005634,tag===GO:0005737",
})
// Groups containing non-obsolete annotations (is_obsolete is not a group filter
// field; filter by rank or tag instead for active annotations)
groups, err := annoClient.ListAnnotationGroups(ctx, &annotation.ListGroupParameters{
Limit: 10,
Filter: "entry_id===DDB_G0285418;rank>=0",
})
Create a Feature Annotation
id, created_by, updated_by, and name are required.
resp, err := featClient.CreateFeatureAnnotation(ctx, &feature_annotation.NewFeatureAnnotation{
Data: &feature_annotation.NewFeatureAnnotation_Data{
Type: "feature_annotation",
Id: "DDB_G0285418",
Attributes: &feature_annotation.NewFeatureAnnotationAttributes{
CreatedBy: "user@dictybase.org",
UpdatedBy: "user@dictybase.org",
Name: "act15",
Synonyms: []string{"actin15"},
Pubmed: []string{"12345678"},
Publications: []string{"10.1234/example"},
},
},
})
Get a Feature Annotation
resp, err := featClient.GetFeatureAnnotation(ctx, &feature_annotation.FeatureAnnotationId{
Id: "DDB_G0285418",
})
// Or by name
resp, err := featClient.GetFeatureAnnotationByName(ctx, &feature_annotation.FeatureName{
Name: "act15",
})
Update a Feature Annotation
All attributes are optional; unset fields are left unchanged.
resp, err := featClient.UpdateFeatureAnnotation(ctx, &feature_annotation.FeatureAnnotationUpdate{
Data: &feature_annotation.FeatureAnnotationUpdate_Data{
Type: "feature_annotation",
Id: "DDB_G0285418",
Attributes: &feature_annotation.FeatureAnnotationUpdateAttributes{
UpdatedBy: "user@dictybase.org",
Synonyms: []string{"actin15", "act15_v2"},
Pubmed: []string{"12345678", "87654321"},
},
},
})
Tag Operations on Feature Annotations
Feature annotations support custom tag properties (key–value pairs with optional ontology references).
// Add a single tag
resp, err := featClient.AddTag(ctx, &feature_annotation.AddTagRequest{
FeatureId: "DDB_G0285418",
Tag: &feature_annotation.TagProperty{
Tag: "expression",
Value: "high",
Ontology: "dicty_expression",
},
})
// Add multiple tags at once
resp, err := featClient.AddTags(ctx, &feature_annotation.AddTagsRequest{
FeatureId: "DDB_G0285418",
Tags: []*feature_annotation.TagProperty{
{Tag: "expression", Value: "high", Ontology: "dicty_expression"},
{Tag: "localization", Value: "nucleus", Ontology: "cellular_component"},
},
})
// Replace all tags (atomically sets the tag list)
resp, err := featClient.SetTags(ctx, &feature_annotation.SetTagsRequest{
FeatureId: "DDB_G0285418",
Tags: []*feature_annotation.TagProperty{
{Tag: "function", Value: "actin binding", Ontology: "molecular_function"},
},
})
// Remove specific tags
resp, err := featClient.RemoveTags(ctx, &feature_annotation.RemoveTagsRequest{
FeatureId: "DDB_G0285418",
Tags: []*feature_annotation.TagProperty{
{Tag: "expression"},
},
})
List Feature Annotations by Publication
// By PubMed ID
resp, err := featClient.ListFeatureAnnotationsByPubmedId(ctx, &feature_annotation.PubmedId{
Id: "12345678",
})
// By DOI
resp, err := featClient.ListFeatureAnnotationsByDOI(ctx, &feature_annotation.DOI{
Id: "10.1234/example",
})
ArangoDB Schema
Tagged Annotations
erDiagram
annotation ||--o{ annotation_cvterm : "annoterm edge"
annotation ||--o| annotation : "annover edge (previous version)"
annotation ||--o{ annotation_group : "group membership"
cv ||--o{ cvterm : "contains"
annotation {
string _key PK
string entry_id
string value
string editable_value
string created_by
string ontology
string tag
string cvtid
int version
int rank
bool is_obsolete
timestamp created_at
}
annotation_group {
string _key PK
string group_id
list group "array of annotation _key values"
timestamp created_at
timestamp updated_at
}
cvterm {
string _key PK
string label
string name
}
cv {
string _key PK
string namespace
}
The annotation collection stores individual annotated statements linked to a dictyBase gene or feature (entry_id). Each annotation references an ontology term through the cvtid field, with the term's label and namespace denormalized into tag and ontology for efficient filtering. Version history is tracked via annotation_version edges connecting old rows to their replacements.
Edge collections and their graphs:
| Edge Collection | Graph | From → To | Purpose |
|---|---|---|---|
annotation_cvterm |
annotation_tag |
annotation → cvterm |
Links an annotation to its ontology term |
annotation_version |
annotation_history |
annotation → annotation (old → new) |
Tracks annotation revision history |
Autocomplete Search Assets
The AutocompleteAnnotation method uses an ArangoSearch view and three analyzers. NewTaggedAnnotationRepo creates the view and the analyzers at server start. The start is idempotent, so a restart does not change the assets.
| Asset | Type | Purpose |
|---|---|---|
annotation_autocomplete_norm |
norm analyzer |
Converts a value to lower case and removes the accents. |
annotation_autocomplete_delim |
pipeline analyzer |
Splits a value at the commas and the spaces. Then it applies the norm step. |
annotation_autocomplete_ngram |
pipeline analyzer |
Applies the norm step. Then it indexes the n-grams of 2 and 3 characters. |
annotation_autocomplete |
arangosearch view |
Indexes the annotation collection. |
The view indexes these attributes of the annotation collection:
| Attribute | Analyzers | Purpose |
|---|---|---|
value |
annotation_autocomplete_norm, annotation_autocomplete_delim, annotation_autocomplete_ngram |
Whole-value prefix, allele prefix, and fuzzy matches |
entry_id |
annotation_autocomplete_norm |
The strain filter inside the search |
is_obsolete |
identity |
The active-annotation filter inside the search |
At each start the service compares the stored links of the view with the links that the search needs. The service updates the view in place with SetProperties when the links differ. The service does not delete the view, so the view identifier does not change.
The search resolves the _id of the synonym and genotype terms before it runs the statement. The statement then filters the annotations on the tag edges. Therefore the statement does not scan the cvterm collection.
Full Search Assets
The SearchAnnotation method uses an ArangoSearch view, three custom analyzers, and two built-in analyzers. NewTaggedAnnotationRepo creates the view and the custom analyzers at server start. The start is idempotent, so a restart does not change the assets.
| Asset | Type | Purpose |
|---|---|---|
annotation_search_norm |
norm analyzer |
Converts a value to lower case and removes accents. |
annotation_search_delim |
pipeline analyzer |
Splits a value at commas and spaces. Then it applies the norm step. |
annotation_search_ngram |
pipeline analyzer |
Applies the norm step. Then it indexes n-grams of 2 and 3 characters. |
text_en |
Built-in text analyzer |
English stemming and tokenization for token and phrase branches. |
identity |
Built-in identity analyzer |
Exact match for boolean and filter attributes. |
annotation_full_search |
arangosearch view |
Indexes the annotation and cvterm collections. |
The view links two collections:
annotation collection link:
| Attribute | Analyzers | Purpose |
|---|---|---|
value |
annotation_search_norm, annotation_search_delim, annotation_search_ngram, text_en |
Prefix, allele prefix, fuzzy, token, and phrase matches on annotation values |
entry_id |
annotation_search_norm |
In-index strain prefix filter STARTS_WITH(d.entry_id, "dbs") |
is_obsolete |
identity |
In-index active-annotation filter d.is_obsolete == false |
cvterm collection link:
| Attribute | Analyzers | Purpose |
|---|---|---|
label |
annotation_search_norm, annotation_search_ngram, text_en |
Prefix, fuzzy, token, and phrase matches on ontology term labels |
graph_id |
identity |
In-index ontology filter d.graph_id == @<onto>_graph_id |
deprecated |
identity |
In-index active-term filter d.deprecated == false |
At each start the service compares the stored links of the view with the required links. The service updates the view in place with SetProperties when the links differ. The service does not delete the view, so the view identifier does not change.
Before the search statement runs, a single target-resolution query resolves the six tag term identifiers and the four ontology graph identifiers. If an ontology is not loaded, its identifier defaults to an empty string. The search statement executes with 28 view branches, 0 collection scans, and 28 graph traversals.
Feature Annotations
erDiagram
feature ||--o{ feature_publication : "edges"
publication ||--o{ feature_publication : "edges"
feature {
string _key PK
string feature_id UK
string feature_type
string name
string created_by
string updated_by
list synonyms
list publications "DOI references"
list pubmed "PubMed IDs"
json dblinks "external database cross-references"
json properties "custom tag key-value pairs"
bool is_obsolete
timestamp created_at
timestamp updated_at
}
publication {
string _key PK
string id UK
}
Feature annotations model biological features (genes, proteins, etc.) with rich metadata including synonyms, publications, database cross-references, and custom tag properties. Publications are stored in a separate publication collection and linked via the feature_publication edge collection, allowing many-to-many relationships.
Edge collections and their graphs:
| Edge Collection | Graph | From → To | Purpose |
|---|---|---|---|
feature_publication |
feature_pub_graph |
feature → publication |
Links a feature annotation to its associated publications |
Deploy path: buildless deploy with named just arguments (deploy refactor P0.2/P0.3).
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
modware-annotation
command
Package main is the entry point for the modware-annotation microservice CLI.
|
Package main is the entry point for the modware-annotation microservice CLI. |
|
internal
|
|
|
app/server
Package server provides gRPC server startup functions for the annotation service.
|
Package server provides gRPC server startup functions for the annotation service. |
|
app/service
Package service implements the gRPC service handlers for the annotation service.
|
Package service implements the gRPC service handlers for the annotation service. |
|
app/validate
Package validate provides argument validation helpers for CLI commands.
|
Package validate provides argument validation helpers for CLI commands. |
|
collection
Package collection provides generic collection utility functions.
|
Package collection provides generic collection utility functions. |
|
iterutil
Package iterutil provides generic iterator utility functions for Go's iter.Seq type.
|
Package iterutil provides generic iterator utility functions for Go's iter.Seq type. |
|
message
Package message defines publisher interfaces for annotation messaging.
|
Package message defines publisher interfaces for annotation messaging. |
|
message/nats
Package nats provides NATS messaging publisher implementations.
|
Package nats provides NATS messaging publisher implementations. |
|
model
Package model defines data structures for annotations.
|
Package model defines data structures for annotations. |
|
pipe
Package pipe provides functional pipeline utilities for composing functions in sequence.
|
Package pipe provides functional pipeline utilities for composing functions in sequence. |
|
repository
Package repository defines interfaces and error types for annotation data storage.
|
Package repository defines interfaces and error types for annotation data storage. |
|
repository/arangodb
Package arangodb implements the annotation repository using ArangoDB.
|
Package arangodb implements the annotation repository using ArangoDB. |