Fragment Selector Providers
Data.Path identifies a file. Data.Selector can identify a fragment inside that file. Data.SelectorFormat tells ProcessCore which selector language to use when comparing two fragments.
CSV Fragment Selector
The built-in CSV provider understands RFC 7111 row, column, and cell selectors.
With the provider, we can read and write textual selectors into their typed representation.
let provider = CsvFragmentSelectorProvider()
let columnSelector = provider.TryParse "col=1-3"
|
let cellSelector = provider.TryParse "cell=2,2"
|
The provider can also relate two selectors. The relation can be either Exact, Contains, or Disjunct.
In this case, col=1-3 contains cell=2,2.
// same selector is exact match
provider.Relate (columnSelector.Value) (columnSelector.Value)
|
// column contains cell
provider.Relate (columnSelector.Value) (cellSelector.Value)
|
// disjoint columns
provider.Relate (columnSelector.Value) ((provider.TryParse "col=4-6").Value)
|
Providers matter when both data nodes have selectors. Without a registered provider, ProcessCore can see that the paths match but cannot know whether col=1-3 contains cell=2,2.
let dataset = Dataset("fragment-demo")
let exportedColumns = Data(path = "measurements.csv", selector = "col=1-3", selectorFormat = CsvFragmentSelectorProvider.SelectorFormatUri, encodingFormat = "text/csv")
let measuredCell = Data(path = "measurements.csv", selector = "cell=2,2", selectorFormat = CsvFragmentSelectorProvider.SelectorFormatUri, encodingFormat = "text/csv")
let interpretedSample = Sample("Interpreted sample", additionalType = "Sample")
let export = Process("Export CSV")
export.SetOutputData(exportedColumns)
let interpret = Process("Interpret selected cell")
interpret.SetInputData(measuredCell)
interpret.SetOutputSample(interpretedSample)
dataset.AddProcess(export)
dataset.AddProcess(interpret)
let beforeRegistration =
exportedColumns.DownstreamSamples(scope = dataset.AllProcesses())
|> Seq.map (fun m -> m.Name)
beforeRegistration
|
Register the provider on the dataset. Registration is stored on the root dataset, so child datasets share the same selector-provider lookup.
dataset.RegisterFragmentSelectorProvider(provider)
// dataset.RegisterFragmentSelectorProvider(provider :> IFragmentSelectorProvider) // also works when upcast to interface
let registeredProvider =
dataset.TryGetFragmentSelectorProvider(CsvFragmentSelectorProvider.SelectorFormatUri)
|> Option.map (fun p -> p.SelectorFormat)
registeredProvider
|
let afterRegistration =
exportedColumns.DownstreamSamples(scope = dataset.AllProcesses())
|> Seq.map (fun m -> m.Name)
afterRegistration
|
Custom Fragment Selector Providers
The idea behind the inclusion of generic fragment selectors syntax into the ProcessCore is so that any kind of fragment can be defined given a proper fragment selector specification.
In the datamodel, this corresponds to an implementation of the IFragmentSelectorProvider interface, which can be registered on a dataset and will be used to relate any two selectors with the same SelectorFormat.
Usually, you should inherit from FragmentSelectorProviderBase<'Selector>, which implements IFragmentSelectorProvider and requires parsers and typed comparers.
The provider parses strings into a typed selector and returns a semantic relation.
type PrefixSelectorProvider() =
inherit FragmentSelectorProviderBase<string>()
override _.SelectorFormat = "urn:example:prefix-selector"
override _.TryParse(text: string) =
if System.String.IsNullOrWhiteSpace text then None
else Some (text.Trim('/'))
override _.ToSelectorString(selector: string) =
selector
override _.Relate(container: string) (candidate: string) =
if container = candidate then Exact
elif candidate.StartsWith(container + "/") then Contains
else Unknown
let customProviderResult =
let p = PrefixSelectorProvider()
(p :> IFragmentSelectorProvider).TryRelate "assay/table" "assay/table/row/1"
customProviderResult
|
What To Use When
Task |
API |
|---|---|
Mark a file fragment |
|
Use CSV row/column/cell fragments |
|
Enable selector-aware traversal |
|
Inspect registered providers |
|
Add a selector language |
|
type CsvFragmentSelectorProvider = inherit FragmentSelectorProviderBase<CsvFragmentSelector> new: unit -> CsvFragmentSelectorProvider override Relate: container: CsvFragmentSelector -> candidate: CsvFragmentSelector -> FragmentRelation override ToSelectorString: selector: CsvFragmentSelector -> string override TryParse: text: string -> CsvFragmentSelector option static member TryGetZeroBasedColumnIndex: selector: string -> int option override SelectorFormat: string static member SelectorFormatUri: string
<summary> RFC 7111 fragment selector provider for text/csv row, column, and cell fragments. </summary>
--------------------
new: unit -> CsvFragmentSelectorProvider
type Dataset = inherit DynamicObj new: identifier: string * ?title: string * ?description: string * ?additionalType: string * ?license: string * ?datePublished: string * ?dateCreated: string * ?dateModified: string * ?processes: Process seq * ?hasPart: Dataset seq * ?dataFiles: Data seq * ?agents: Agent seq * ?citations: ScholarlyArticle seq * ?dataContexts: DataContext seq * ?additionalProperty: Annotation seq -> Dataset member AddAdditionalProperty: pv: Annotation -> unit member AddAgent: agent: Agent -> unit member AddCitation: article: ScholarlyArticle -> unit member AddDataContext: dataContext: DataContext -> unit member AddDataFile: data: Data -> unit member AddPart: child: Dataset -> unit member AddProcess: proc: Process -> unit member AllAgents: unit -> ResizeArray<Agent> ...
<summary> Container and context for data, processes, administrative metadata, and datamap entries. schema.org/Dataset </summary>
--------------------
new: identifier: string * ?title: string * ?description: string * ?additionalType: string * ?license: string * ?datePublished: string * ?dateCreated: string * ?dateModified: string * ?processes: Process seq * ?hasPart: Dataset seq * ?dataFiles: Data seq * ?agents: Agent seq * ?citations: ScholarlyArticle seq * ?dataContexts: DataContext seq * ?additionalProperty: Annotation seq -> Dataset
namespace Microsoft.FSharp.Data
--------------------
type Data = inherit DynamicObj new: path: string * ?selector: string * ?selectorFormat: string * ?encodingFormat: string * ?additionalType: string * ?hasPart: Data seq * ?additionalProperty: Annotation seq -> Data member AddAdditionalProperty: pv: Annotation -> unit member AddPart: data: Data -> unit member AllAnnotations: ?scope: ResizeArray<Process> -> ResizeArray<Annotation> member AllConnectedNodes: ?scope: ResizeArray<Process> -> ResizeArray<IONode> member AllConnectedProcesses: ?scope: ResizeArray<Process> -> ResizeArray<Process> member ConnectedData: ?scope: ResizeArray<Process> -> ResizeArray<Data> member ConnectedSamples: ?scope: ResizeArray<Process> -> ResizeArray<Sample> member DownstreamAnnotations: ?recipeName: string * ?scope: ResizeArray<Process> -> ResizeArray<Annotation> ...
<summary> Data file or selected fragment produced or consumed by processes. schema.org/MediaObject or File </summary>
--------------------
new: path: string * ?selector: string * ?selectorFormat: string * ?encodingFormat: string * ?additionalType: string * ?hasPart: Data seq * ?additionalProperty: Annotation seq -> Data
type Sample = inherit DynamicObj new: name: string * ?additionalType: string * ?additionalProperty: Annotation seq -> Sample member AddAdditionalProperty: pv: Annotation -> unit member AllAnnotations: ?scope: ResizeArray<Process> -> ResizeArray<Annotation> member AllConnectedNodes: ?scope: ResizeArray<Process> -> ResizeArray<IONode> member AllConnectedProcesses: ?scope: ResizeArray<Process> -> ResizeArray<Process> member ConnectedData: ?scope: ResizeArray<Process> -> ResizeArray<Data> member ConnectedSamples: ?scope: ResizeArray<Process> -> ResizeArray<Sample> member DownstreamAnnotations: ?recipeName: string * ?scope: ResizeArray<Process> -> ResizeArray<Annotation> member DownstreamData: ?scope: ResizeArray<Process> -> ResizeArray<Data> ...
<summary> Input or output biological, chemical, or digital sample in the process graph. bioschemas.org/Sample </summary>
--------------------
new: name: string * ?additionalType: string * ?additionalProperty: Annotation seq -> Sample
type Process = inherit DynamicObj new: name: string * ?executesRecipe: Recipe * ?additionalType: string * ?input: IONode * ?output: IONode * ?parameterValue: Annotation seq -> Process member AddParameterValue: pv: Annotation -> unit member AnnotationsByName: name: string -> ResizeArray<Annotation> member CanonicalizeAllNodes: ds: Dataset -> unit member ClearInput: unit -> unit member ClearOutput: unit -> unit override Equals: obj: obj -> bool override GetHashCode: unit -> int member GetParameterValue: name: string -> Annotation ...
<summary> Core transformation node. Connects inputs to outputs by executing a recipe. bioschemas.org/LabProcess </summary>
--------------------
new: name: string * ?executesRecipe: Recipe * ?additionalType: string * ?input: IONode * ?output: IONode * ?parameterValue: Annotation seq -> Process
type PrefixSelectorProvider = inherit FragmentSelectorProviderBase<string> new: unit -> PrefixSelectorProvider override Relate: container: string -> candidate: string -> FragmentRelation override ToSelectorString: selector: string -> string override TryParse: text: string -> string option override SelectorFormat: string
--------------------
new: unit -> PrefixSelectorProvider
type FragmentSelectorProviderBase<'Selector> = interface IFragmentSelectorProvider new: unit -> FragmentSelectorProviderBase<'Selector> abstract Relate: container: 'Selector -> candidate: 'Selector -> FragmentRelation abstract ToSelectorString: 'Selector -> string abstract TryParse: string -> 'Selector option abstract SelectorFormat: string
<summary> Typed selector-provider contract for implementations of a selector language. </summary>
--------------------
new: unit -> FragmentSelectorProviderBase<'Selector>
val string: value: 'T -> string
--------------------
type string = System.String
type String = interface seq<char> interface IEnumerable interface ICloneable interface IComparable interface IComparable<string> interface IConvertible interface IEquatable<string> interface IParsable<string> interface ISpanParsable<string> new: value: nativeptr<char> -> unit + 8 overloads ...
<summary>Represents text as a sequence of UTF-16 code units.</summary>
--------------------
System.String(value: nativeptr<char>) : System.String
System.String(value: char array) : System.String
System.String(value: System.ReadOnlySpan<char>) : System.String
System.String(value: nativeptr<sbyte>) : System.String
System.String(c: char, count: int) : System.String
System.String(value: nativeptr<char>, startIndex: int, length: int) : System.String
System.String(value: char array, startIndex: int, length: int) : System.String
System.String(value: nativeptr<sbyte>, startIndex: int, length: int) : System.String
System.String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: System.Text.Encoding) : System.String
System.String.Trim( trimChars: char array) : string
System.String.Trim(trimChar: char) : string
System.String.StartsWith(value: char) : bool
System.String.StartsWith(value: string, comparisonType: System.StringComparison) : bool
System.String.StartsWith(value: string, ignoreCase: bool, culture: System.Globalization.CultureInfo) : bool
<summary> Selector-provider contract used by the core traversal layer. </summary>
ProcessCore