feature-chaining.rst 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. .. _app-schema.feature-chaining:
  2. Feature Chaining
  3. ================
  4. Scope
  5. -----
  6. This page describes the use of "Feature Chaining" to compose complex features from simpler components, and in particular to address some requirements that have proven significant in practice.
  7. * Handling multiple cases of multi-valued properties within a single Feature Type
  8. * Handing nesting of multi-valued properties within other multi-valued properties
  9. * Linking related (through association) Feature Types, and in particular allowing re-use of the related features types (for example the O&M pattern has relatedObservation from a samplingFeature, but Observation may be useful in its own right)
  10. * Encoding the same referenced property object as links when it appears in multiple containing features
  11. * Eliminating the need for large denormalized data store views of top level features and their related features. Denormalized views would still be needed for special cases, such as many-to-many relationships, but won't be as large.
  12. For non-application schema configurations, please refer to :ref:`app-schema.data-access-integration`.
  13. Mapping steps
  14. -------------
  15. Create a mapping file for every complex type
  16. `````````````````````````````````````````````
  17. We need one mapping file per complex type that is going to be nested, including non features, e.g. gsml:CompositionPart.
  18. Non-feature types that cannot be individually accessed (eg. CompositionPart as a Data Type) can still be mapped separately for its reusability. For this case, the containing feature type has to include these types in its mapping file. The include tag should contain the nested mapping file path relative to the location of the containing type mapping file.
  19. In :download:`GeologicUnit_MappingFile.xml`::
  20. <includedTypes>
  21. <Include>CGITermValue_MappingFile.xml</Include>
  22. <Include>CompositionPart_MappingFile.xml</Include>
  23. </includedTypes>
  24. Feature types that can be individually accessed don't need to be explicitly included in the mapping file, as they would be configured for GeoServer to find.
  25. Such types would have their mapping file associated with a corresponding datastore.xml file, which means that it can be found from the data store registry.
  26. In other words, if the type is associated with a datastore.xml file, it doesn't need to be explicitly included if referred from another mapping file.
  27. **Example**:
  28. For this output: :download:`MappedFeature_Output.xml`, here are the mapping files:
  29. * :download:`MappedFeature_MappingFile.xml`
  30. * :download:`GeologicUnit_MappingFile.xml`
  31. * :download:`CompositionPart_MappingFile.xml`
  32. * :download:`GeologicEvent_MappingFile.xml`
  33. * :download:`CGITermValue_MappingFile.xml`
  34. *GeologicUnit type*
  35. You can see within GeologicUnit features, both gml:composition (CompositionPart type) and gsml:geologicHistory (GeologicEvent type) are multi-valued properties.
  36. It shows how multiple cases of multi-valued properties can be configured within a single Feature Type.
  37. This also proves that you can "chain" non-feature type, as CompositionPart is a Data Type.
  38. *GeologicEvent type*
  39. Both gsml:eventEnvironment (CGI_TermValue type) and gsml:eventProcess (also of CGI_TermValue type) are multi-valued properties.
  40. This also shows that "chaining" can be done on many levels, as GeologicEvent is nested inside GeologicUnit.
  41. Note that gsml:eventAge properties are configured as inline attributes, as there can only be one event age per geologic event, thus eliminating the need for feature chaining.
  42. Configure nesting on the nested feature type
  43. ````````````````````````````````````````````
  44. In the nested feature type, make sure we have a field that can be referenced by the parent feature.
  45. If there isn't any existing field that can be referred to, the system field *FEATURE_LINK* can be mapped to hold the foreign key value. This is a multi-valued field, so more than one instances can be mapped in the same feature type, for features that can be nested by different parent types. Since this field doesn't exist in the schema, it wouldn't appear in the output document.
  46. In the source expression tag:
  47. * OCQL: the value of this should correspond to the OCQL part of the parent feature
  48. **Example One**: Using *FEATURE_LINK* in CGI TermValue type, which is referred by GeologicEvent as gsml:eventProcess and gsml:eventEnvironment.
  49. In GeologicEvent (the container feature) mapping::
  50. <AttributeMapping>
  51. <targetAttribute>gsml:eventEnvironment</targetAttribute>
  52. <sourceExpression>
  53. <OCQL>id</OCQL>
  54. <linkElement>gsml:CGI_TermValue</linkElement>
  55. <linkField>FEATURE_LINK[1]</linkField>
  56. </sourceExpression>
  57. <isMultiple>true</isMultiple>
  58. </AttributeMapping>
  59. <AttributeMapping>
  60. <targetAttribute>gsml:eventProcess</targetAttribute>
  61. <sourceExpression>
  62. <OCQL>id</OCQL>
  63. <linkElement>gsml:CGI_TermValue</linkElement>
  64. <linkField>FEATURE_LINK[2]</linkField>
  65. </sourceExpression>
  66. <isMultiple>true</isMultiple>
  67. </AttributeMapping>
  68. In CGI_TermValue (the nested feature) mapping::
  69. <AttributeMapping>
  70. <!-- FEATURE_LINK[1] is referred by geologic event as environment -->
  71. <targetAttribute>FEATURE_LINK[1]</targetAttribute>
  72. <sourceExpression>
  73. <OCQL>ENVIRONMENT_OWNER</OCQL>
  74. </sourceExpression>
  75. </AttributeMapping>
  76. <AttributeMapping>
  77. <!-- FEATURE_LINK[2] is referred by geologic event as process -->
  78. <targetAttribute>FEATURE_LINK[2]</targetAttribute>
  79. <sourceExpression><
  80. <OCQL>PROCESS_OWNER</OCQL>
  81. </sourceExpression>
  82. </AttributeMapping>
  83. The ENVIRONMENT_OWNER column in CGI_TermValue view corresponds to the ID column in GeologicEvent view.
  84. **Geologic Event property file:**
  85. .. list-table::
  86. :widths: 15 15 15 15 50
  87. * - **id**
  88. - **GEOLOGIC_UNIT_ID:String**
  89. - **ghminage:String**
  90. - **ghmaxage:String**
  91. - **ghage_cdspace:String**
  92. * - ge.26931120
  93. - gu.25699
  94. - Oligocene
  95. - Paleocene
  96. - urn:cgi:classifierScheme:ICS:StratChart:2008
  97. * - ge.26930473
  98. - gu.25678
  99. - Holocene
  100. - Pleistocene
  101. - urn:cgi:classifierScheme:ICS:StratChart:2008
  102. * - ge.26930960
  103. - gu.25678
  104. - Pliocene
  105. - Miocene
  106. - urn:cgi:classifierScheme:ICS:StratChart:2008
  107. * - ge.26932959
  108. - gu.25678
  109. - LowerOrdovician
  110. - LowerOrdovician
  111. - urn:cgi:classifierScheme:ICS:StratChart:2008
  112. **CGI Term Value property file:**
  113. .. list-table::
  114. :widths: 10 30 30 30
  115. * - **id**
  116. - **VALUE:String**
  117. - **PROCESS_OWNER:String**
  118. - **ENVIRONMENT_OWNER:String**
  119. * - 3
  120. - fluvial
  121. - NULL
  122. - ge.26931120
  123. * - 4
  124. - swamp/marsh/bog
  125. - NULL
  126. - ge.26930473
  127. * - 5
  128. - marine
  129. - NULL
  130. - ge.26930960
  131. * - 6
  132. - submarine fan
  133. - NULL
  134. - ge.26932959
  135. * - 7
  136. - hemipelagic
  137. - NULL
  138. - ge.26932959
  139. * - 8
  140. - detrital deposition still water
  141. - ge.26930473
  142. - NULL
  143. * - 9
  144. - water [process]
  145. - ge.26932959
  146. - NULL
  147. * - 10
  148. - channelled stream flow
  149. - ge.26931120
  150. - NULL
  151. * - 11
  152. - turbidity current
  153. - ge.26932959
  154. - NULL
  155. The system field *FEATURE_LINK* doesn't get encoded in the output::
  156. <gsml:GeologicEvent>
  157. <gml:name codeSpace="urn:cgi:classifierScheme:GSV:GeologicalUnitId">gu.25699</gml:name>
  158. <gsml:eventAge>
  159. <gsml:CGI_TermRange>
  160. <gsml:lower>
  161. <gsml:CGI_TermValue>
  162. <gsml:value codeSpace="urn:cgi:classifierScheme:ICS:StratChart:2008">Oligocene</gsml:value>
  163. </gsml:CGI_TermValue>
  164. </gsml:lower>
  165. <gsml:upper>
  166. <gsml:CGI_TermValue>
  167. <gsml:value codeSpace="urn:cgi:classifierScheme:ICS:StratChart:2008">Paleocene</gsml:value>
  168. </gsml:CGI_TermValue>
  169. </gsml:upper>
  170. </gsml:CGI_TermRange>
  171. </gsml:eventAge>
  172. <gsml:eventEnvironment>
  173. <gsml:CGI_TermValue>
  174. <gsml:value>fluvial</gsml:value>
  175. </gsml:CGI_TermValue>
  176. </gsml:eventEnvironment>
  177. <gsml:eventProcess>
  178. <gsml:CGI_TermValue>
  179. <gsml:value>channelled stream flow</gsml:value>
  180. </gsml:CGI_TermValue>
  181. </gsml:eventProcess>
  182. **Example Two**:
  183. Using existing field (gml:name) to hold the foreign key, see :download:`MappedFeature_MappingFile.xml`:
  184. gsml:specification links to gml:name in GeologicUnit::
  185. <AttributeMapping>
  186. <targetAttribute>gsml:specification</targetAttribute>
  187. <sourceExpression>
  188. <OCQL>GEOLOGIC_UNIT_ID</OCQL>
  189. <linkElement>gsml:GeologicUnit</linkElement>
  190. <linkField>gml:name[3]</linkField>
  191. </sourceExpression>
  192. </AttributeMapping>
  193. In :download:`GeologicUnit_MappingFile.xml`:
  194. GeologicUnit has 3 gml:name properties in the mapping file, so each has a code space to clarify them::
  195. <AttributeMapping>
  196. <targetAttribute>gml:name[1]</targetAttribute>
  197. <sourceExpression>
  198. <OCQL>ABBREVIATION</OCQL>
  199. </sourceExpression>
  200. <ClientProperty>
  201. <name>codeSpace</name>
  202. <value>'urn:cgi:classifierScheme:GSV:GeologicalUnitCode'</value>
  203. </ClientProperty>
  204. </AttributeMapping>
  205. <AttributeMapping>
  206. <targetAttribute>gml:name[2]</targetAttribute>
  207. <sourceExpression>
  208. <OCQL>NAME</OCQL>
  209. </sourceExpression>
  210. <ClientProperty>
  211. <name>codeSpace</name>
  212. <value>'urn:cgi:classifierScheme:GSV:GeologicalUnitName'</value>
  213. </ClientProperty>
  214. </AttributeMapping>
  215. <AttributeMapping>
  216. <targetAttribute>gml:name[3]</targetAttribute>
  217. <sourceExpression>
  218. <OCQL>id</OCQL>
  219. </sourceExpression>
  220. <ClientProperty>
  221. <name>codeSpace</name>
  222. <value>'urn:cgi:classifierScheme:GSV:MappedFeatureReference'</value>
  223. </ClientProperty>
  224. </AttributeMapping>
  225. The output with multiple gml:name properties and their code spaces::
  226. <gsml:specification>
  227. <gsml:GeologicUnit gml:id="gu.25678">
  228. <gml:description>Olivine basalt, tuff, microgabbro, minor sedimentary rocks</gml:description>
  229. <gml:name codeSpace="urn:cgi:classifierScheme:GSV:GeologicalUnitCode">-Py</gml:name>
  230. <gml:name codeSpace="urn:cgi:classifierScheme:GSV:GeologicalUnitName">Yaugher Volcanic Group</gml:name>
  231. <gml:name codeSpace="urn:cgi:classifierScheme:GSV:MappedFeatureReference">gu.25678</gml:name>
  232. If this is the "one" side of a one-to-many or many-to-one database relationship, we can use the feature id as the source expression field, as you can see in above examples.
  233. See :download:`one_to_many_relationship.JPG` as an illustration.
  234. If we have a many-to-many relationship, we have to use one denormalized view for either side of the nesting. This means we can either use the feature id as the referenced field, or assign a column to serve this purpose. See :download:`many_to_many_relationship.JPG` as an illustration.
  235. .. note::
  236. * For many-to-many relationships, we can't use the same denormalized view for both sides of the nesting.
  237. Test this configuration by running a getFeature request for the nested feature type on its own.
  238. Configure nesting on the "containing" feature type
  239. ``````````````````````````````````````````````````
  240. When nesting another complex type, you need to specify in your source expression:
  241. * **OCQL**: OGC's Common Query Language expression of the data store column
  242. * **linkElement**:
  243. * the nested element name, which is normally the targetElement or mappingName of the corresponding type.
  244. * on some cases, it has to be an OCQL function (see :ref:`app-schema.polymorphism`)
  245. * **linkField**: the indexed XPath attribute on the nested element that OCQL corresponds to
  246. **Example:** Nesting composition part in geologic unit feature.
  247. In Geologic Unit mapping file::
  248. <AttributeMapping>
  249. <targetAttribute>gsml:composition</targetAttribute>
  250. <sourceExpression>
  251. <OCQL>id</OCQL>
  252. <linkElement>gsml:CompositionPart</linkElement>
  253. <linkField>FEATURE_LINK</linkField>
  254. </sourceExpression>
  255. <isMultiple>true</isMultiple>
  256. </AttributeMapping>
  257. * *OCQL*: id is the geologic unit id
  258. * *linkElement*: links to gsml:CompositionPart type
  259. * *linkField*: FEATURE_LINK, the linking field mapped in gsml:CompositionPart type that also stores the geologic unit id. If there are more than one of these attributes in the nested feature type, make sure the index is included, e.g. FEATURE_LINK[2].
  260. **Geologic Unit property file:**
  261. .. list-table::
  262. :widths: 15 5 20 60
  263. * - **id**
  264. - **ABBREVIATAION:String**
  265. - **NAME:String**
  266. - **TEXTDESCRIPTION:String**
  267. * - gu.25699
  268. - -Py
  269. - Yaugher Volcanic Group
  270. - Olivine basalt, tuff, microgabbro, minor sedimentary rocks
  271. * - gu.25678
  272. - -Py
  273. - Yaugher Volcanic Group
  274. - Olivine basalt, tuff, microgabbro, minor sedimentary rocks
  275. **Composition Part property file:**
  276. .. list-table::
  277. :widths: 40 40 20 20
  278. * - **id**
  279. - **COMPONENT_ROLE:String**
  280. - **PROPORTION:String**
  281. - **GEOLOGIC_UNIT_ID:String**
  282. * - cp.167775491936278812
  283. - interbedded component
  284. - significant
  285. - gu.25699
  286. * - cp.167775491936278856
  287. - interbedded component
  288. - minor
  289. - gu.25678
  290. * - cp.167775491936278844
  291. - sole component
  292. - major
  293. - gu.25678
  294. Run the getFeature request to test this configuration. Check that the nested features returned in Step 2 are appropriately lined inside the containing features.
  295. If they are not there, or exceptions are thrown, scroll down and read the "Trouble Shooting" section.
  296. Multiple mappings of the same type
  297. ----------------------------------
  298. At times, you may find the need to have different FeatureTypeMapping instances for the same type.
  299. You may have two different attributes of the same type that need to be nested.
  300. For example, in gsml:GeologicUnit, you have gsml:exposureColor and gsml:outcropCharacter that are both of gsml:CGI_TermValue type.
  301. This is when the optional mappingName tag mentioned in :ref:`app-schema.mapping-file` comes in.
  302. Instead of passing in the nested feature type's targetElement in the containing type's linkElement, specify the corresponding mappingName.
  303. .. note::
  304. * The mappingName is namespace aware and case sensitive.
  305. * When the referred mappingName contains special characters such as '-', it must be enclosed with single quotes in the linkElement. E.g. <linkElement>'observation-method'</linkElement>.
  306. * Each mappingName must be unique against other mappingName and targetElement tags across the application.
  307. * The mappingName is only to be used to identify the chained type from the nesting type. It is not a solution for multiple FeatureTypeMapping instances where > 1 of them can be queried as top level features.
  308. * When queried as a top level feature, the normal targetElement is to be used. Filters involving the nested type should still use the targetElement in the PropertyName part of the query.
  309. * You can't have more than 1 FeatureTypeMapping of the same type in the same mapping file if one of them is a top level feature. This is because featuretype.xml would look for the targetElement and wouldn't know which one to get.
  310. The solution for the last point above is to break them up into separate files and locations with only 1 featuretype.xml in the intended top level feature location.
  311. E.g.
  312. * You can have 2 FeatureTypeMapping instances in the same file for gsml:CGI_TermValue type since it's not a feature type.
  313. * You can have 2 FeatureTypeMapping instances for gsml:MappedFeature, but they have to be broken up into separate files. The one that can be queried as top level feature type would have featuretype.xml in its location.
  314. Nesting simple properties
  315. -------------------------
  316. You don't need to chain multi-valued simple properties and map them separately.
  317. The original configuration would still work.
  318. .. _app-schema.filtering-nested:
  319. Filtering nested attributes on chained features
  320. -----------------------------------------------
  321. Filters would work as usual. You can supply the full XPath of the attribute, and the code would handle this.
  322. E.g. You can run the following filter on gsml:MappedFeatureUseCase2A::
  323. <ogc:Filter>
  324. <ogc:PropertyIsEqualTo>
  325. <ogc:Function name="contains_text">
  326. <ogc:PropertyName>gsml:specification/gsml:GeologicUnit/gml:description</ogc:PropertyName>
  327. <ogc:Literal>Olivine basalt, tuff, microgabbro, minor sedimentary rocks</ogc:Literal>
  328. </ogc:Function>
  329. <ogc:Literal>1</ogc:Literal>
  330. </ogc:PropertyIsEqualTo>
  331. </ogc:Filter>
  332. .. _app-schema.feature-chaining-by-reference:
  333. Multi-valued properties by reference (*xlink:href*)
  334. ---------------------------------------------------
  335. You may want to use feature chaining to set multi-valued properties by reference.
  336. This is particularly handy to avoid endless loop in circular relationships.
  337. For example, you may have a circular relationship between gsml:MappedFeature and gsml:GeologicUnit.
  338. E.g.
  339. * gsml:MappedFeature has gsml:GeologicUnit as gsml:specification
  340. * gsml:GeologicUnit has gsml:MappedFeature as gsml:occurrence
  341. Obviously you can only encode one side of the relationship, or you'll end up with an endless loop.
  342. You would need to pick one side to "chain" and use xlink:href for the other side of the relationship.
  343. For this example, we are nesting gsml:GeologicUnit in gsml:MappedFeature as gsml:specification.
  344. * Set up nesting on the container feature type mapping as usual::
  345. <AttributeMapping>
  346. <targetAttribute>gsml:specification</targetAttribute>
  347. <sourceExpression>
  348. <OCQL>GEOLOGIC_UNIT_ID</OCQL>
  349. <linkElement>gsml:GeologicUnit</linkElement>
  350. <linkField>gml:name[2]</linkField>
  351. </sourceExpression>
  352. </AttributeMapping>
  353. * Set up xlink:href as client property on the other mapping file::
  354. <AttributeMapping>
  355. <targetAttribute>gsml:occurrence</targetAttribute>
  356. <sourceExpression>
  357. <OCQL>id</OCQL>
  358. <linkElement>gsml:MappedFeature</linkElement>
  359. <linkField>gsml:specification</linkField>
  360. </sourceExpression>
  361. <isMultiple>true</isMultiple>
  362. <ClientProperty>
  363. <name>xlink:href</name>
  364. <value>strConcat('urn:cgi:feature:MappedFeature:', ID)</value>
  365. </ClientProperty>
  366. </AttributeMapping>
  367. As we are getting the client property value from a nested feature, we have to set it as if we are chaining the feature; but we also add the client property containing *xlink:href* in the attribute mapping. The code will detect the *xlink:href* setting, and will not proceed to build the nested feature's attributes, and we will end up with empty attributes with *xlink:href* client properties.
  368. This would be the encoded result for gsml:GeologicUnit::
  369. <gsml:GeologicUnit gml:id="gu.25678">
  370. <gsml:occurrence xlink:href="urn:cgi:feature:MappedFeature:mf2"/>
  371. <gsml:occurrence xlink:href="urn:cgi:feature:MappedFeature:mf3"/>
  372. .. note::
  373. * Don't forget to add *XLink* in your mapping file namespaces section, or you could end up with a StackOverflowException as the *xlink:href* client property won't be recognized and the mappings would chain endlessly.
  374. * :ref:`app-schema.resolve` may be used to force app-schema to do full feature chaining up to a certain level, even if an xlink reference is specified.