2005-12-28

En waneer je nu eens geen text retrieval wil doen ?

Ik heb vorige week de Gewasbeschermingskennisbank overgebracht naar Oracle. Het probleem dat daarbij boven water kwam, is dat we in oracle niet zo eenvoudig text indexen en volledig veld indexen door elkaar kunnen gaan gebruiken. Op dit moment ondersteunen we per tabel in wqoracle 4 indexen. Op recordnummer, op datum van invoer, op datum van wijziging en de context-index voor text retrieval op de inhoud van de XML elementen.
Bij de gewasbeschermingskennisbank moet echter het onderscheid kunnen worden gemaakt tussen middelen die zijn toegestaan in "siergewassen onder glas" en middelen toegestaan in "plantgoed voor siergewassen onder glas". Deze laatsten worden ook gevonden als je zoekt naar "siergewassen onder glas".
De wat onbeholpen work-around die ik er nu voor gebruik is om naast het normale xml element een tweede xml element te definiëren met dezelfde inhoud maar ontdaan van woordscheidingstekens, zoals spaties en die te gebruiken voor de zoekactie.
Het werkt, maar ik denk dat we de mogelijkheid moeten inbouwen om 'normale indexen' te kunnen gebruiken (normaal in de database wereld, anders dan die wij in onze bibliotheekomgeving doorgaans gebruiken) en ergens in een zoekcommando moeten kunnen geven dat we voor een bepaald veld de andere dan de contextindex moeten gebruiken

2005-12-15

WebQuery 5.34

Inderdaad, alweer een nieuwe versie van WebQuery.

Deze versie is vandaag in gebruik genomen vanwege de correctie van een fout in de afhandeling van de multipart-variant van de formulierafhandeling. Deze fout trad alleen op in formulieren waarmee een bestand werd overgebracht naar de server. Onder bepaalde omstandigheden werd dan een deel van het formulier na de browse-knop niet of niet goed verwerkt. Dat probleem is in deze versie opgelost.

Er is één andere wijziging: wq_sfx wordt nu standaard meegegeven als xslt-parameter ($wq_sfx). Doel hiervan is om uniformering van de "next"
link en evt. andere links te vereenvoudigen. De xslt "ora-default.xslt", bedoeld als vervanging en uitbreiding van ora_errors.xslt, maakt hier al gebruik van (let op het streepje: dat is een minteken, geen underscore).
De nieuwe uitleen gebruikt deze xslt in de testomgeving.

2005-12-09

WebQuery 5.33 en wqoracle 1.3 actief

Zoals je aan het onderwerp al zag, er zijn weer nieuwe versies van WebQuery en wqoracle in gebruik genomen, en daarbij hoort een lijstje met de wijzigingen ten opzichte van de vorige versie:

WebQuery 5.33 (in combinatie met wqoracle; ik test nieuwe features als deze niet meer met wqstub/mindbsrv, dus gebruik het daarmee niet): wq_max, waarmee in een zoekvraag het te tonen aantal records per resultaatpagina kan worden opgegeven, kent en nieuw gebruik. Als aan wq_max de waarde 0 wordt meegegeven, wordt alleen het aantal hits getoond en geen resultaat set. In de oude versie had wq_max=0 het zelfde effect als het weglaten van wq_max (dan wordt de default-waarde van hit-limit uit de cgiparm parameter file gebruikt). Hiermee vervalt tevens het keyword "hits" (achter de servicenaam in de url), dat vrijwel nooit gebruikt is en in combinatie met wqoracle niet werkte.
Verder worden nu om alle zoektermen (behalve in wq_qry) haakjes gezet, om te voorkomen dat onbedoeld op alle velden gezocht wordt.

Ik wil er nogmaals op wijzen dat oracle context indexen (en dus ook het zoekmechanisme in wqoracle) een groot aantal niet-alfanumerieke tekens als operators interpreteert. Een paar voorbeelden (de aanhalingstekens zijn een markering van de zoekterm, tik die dus niet in als je het effect wil zien): zoek je op "plant-hg", dan vind je alle records, waarin het betreffende veld de kreet "plant" staat, NIET gevolgd door "hg". Zet accolades om de zoekterm, of een backslash voor het minteken, als je dat wilt voorkomen. "{plant-hg}" en "plant\-hg" geven het gewenste resultaat. "A&F" vindt alle records waarin in het betreffende veld zowel een A als een F staan (vergelijkbaar met het zoeken op "A F" in minisis). Ook hier helpen accolades en backslash om de kreet "A&F" te vinden. "A F" heeft in dit geval het zelfde effect, omdat je dan zoekt naar een losse "A" gevolgd door een losse "F", en de index de "&" in het record afwijkend behandelt. Vermijd ook woorden als "AND" en "OR" in een zoekterm, tenzij je die bewust als operator gebruiken wilt. Gebruik haakjes en accolades altijd in paren, en realiseer je dat "plant soil" (39 hits in titel) niet meer het zelfde resultaat geeft als "soil plant" (124 hits) of "soil & plant" (1536 hits). Kortom, "elluk foordeel hep se nadeel".

wqoracle 1.3: wq_max=0 (zie hierboven). Daarnaast is een interne wijziging doorgevoerd op verzoek van oracle, als workaround voor een van de oracle-bugs die tot problemen bij het zoeken leidde. Deze wijziging komt er op neer dat de xml-velden nu in porties van 4000 bytes worden opgehaald uit de database, in plaats van zoveel mogelijk in één keer. Tenslotte is de maximale veldlengte van 132KB naar 1MB uitgebreid omdat isn 51066 in titelplus te groot was (ca 250KB).

Age Jan

2005-12-07

Uploaden van documenten in een database

Al enige tijd bestaat de mogelijkheid om bestanden toe te voegen aan records in een database. Op dit moment gebeurt dat bij Wageningen UR publicaties, maar we gaan dat nu bijvoorbeeld ook doen bij de documentatiedatabase van COGEM (COmmissie GEnetische Modificatie)en bij de leermiddelendatabase in het kader van LORENET. Deze laatste 2 databases zijn echter Oracle databases en dat heeft wat consequenties. Oorspronkelijk draaide de Minisis db server en WebQuery allebei op de HP3000. Het uploaden van een bestand was een gecoordineerde actie tussen die twee processen, op dezelfde computer op basis van afspraken over de plaats van die bestanden. Tegenwoordig draait WebQuery op de Web server en WQoracle op de database server. WebQuery slaat de bestanden niet op in de database, maar gewoon in directories op de web server. De bestandsnaam wordt vervolgens doorgegeven aan WQoracle waarna het in de database wordt opgeslagen. Bij invoer van een record is er een bestandsnaam gegenereerd die weliswaar de naam van de tabel bevat, maar verder betekenisloos is. Wordt het bestand toegevoegd aan een bestaand record, dan wordt het ISN meegenomen in de bestandsnaam. Zo is een bestand altijd terug te herleiden tot een uniek database record. Wanneer er nog geen record is, kan dat natuurlijk niet. Wanneer de interface het enigszins toestaat, probeer dan in het invoerscherm het uploaden niet aan te bieden, maar doe dat pas nadat het record is ingevoerd.
Het verwijderen van een file is een ander probleem. WebQuery en WQoracle zijn gescheiden processen, niet gekoppeld door een transactie logging mechanisme. Wanneer je een nieuwe file toevoegt in plaats van de vorige, of je wil er een verwijderen, dan kun je het record aanpassen en eventueel een nieuwe file uploaden, maar er is geen mechanisme om een oude te verwijderen. Deze blijven dan 'los' in de directory achter en dat geeft vervuiling. WQoracle zou WebQuery kunnen 'informeren' over een te verwijderen file, maar als het verwijderen mislukt dan hebben we geen mechanisme om de hele boel terug te draaien. Vervuiling blijft dan dus mogelijk.
We zouden er ook voor kunnen zorgen dat de filename nooit uit het XML kan worden verwijderd, maar dat alleen een delete attribuut kan worden aangezet, waar je dan in het stylesheet rekening moet houden (dit maakt ook een UNDELETE mogelijk) We zouden dan periodiek in batch kunnen opschonen. We denken erover na. Heb je een brilant idee, laat horen !!

WebQuery op het web

Bij wat zoekacties naar deze blog op Google kwam ik twee leuke dingen tegen. Allereerst een WebQuery handleiding van mijn hand in het Spaans. Nou ja, wel een oude handleiding en een soort van samenvatting. Grappig dat iemand dat heeft gedaan, Met name omdat we alleen een Nederlandse, Engelse en Franse versie hebben gemaakt. Er is nooit Spaanse interesse voor WebQuery geweest. Er is overigens ook een geregistreerd product met dezelfde naam. Iets om op te letten.
Een tweede ding is een alternatieve manier om vanuit huis 'off campus access' tot de desktop library te krijgen. Zoek in Google op 'pauzeren' en klik op het derde resultaat. Klik op de login knop en vervolg ...... niet iets om in de helpteksten op te nemen :)

2005-11-28

characters sets en foutafhandeling

wellicht ten overvloede nog even een paar opmerkingen over xslt's in samenwerking met WebQuery:

a) als er gebruik gemaakt wordt van xslt's (dus inmiddels bijna altijd) bepaalt WebQuery aan de hand van de output opties van de xslt wat de eigenschappen van de output zijn. Neem dus in IEDERE xslt een output regel op, en geef daarin een method en een encoding aan. Vergeet je die, dan zijn de defaults xml en utf-8, dus waarschijnlijk niet wat je wilt.

b) als er een fout optreedt buiten de xslt, wordt ALTIJD de xslt aangeroepen. Bij een aantal algemene fouten kan dat een andere dan de "gewenste" xslt zijn, omdat wq_sfx dan nog niet is geinterpreteerd. Zorg er dus voor dat IEDERE xslt gebruik maakt van (voorlopig nog) ora-errors.xslt voor de foutafhandeling.

2005-11-23

Klaar voor OAI interface !

In minisis konden we zoeken op een range van waarden, bijvoorbeeld jaren van uitgave, door de twee waarden gescheiden door een dubbele slash als zoek criterium mee te geven. (bv 1950//1960 of sneeuw//sneeuwschuiver). Dit is niet erg eenvoudig te realiseren in een text georiënteerde zoekindex. In Minisis is het pas in een van de laatste versies op veler verzoek geïmplementeerd. In Oracle text retrieval bestaat deze mogelijkheid dus niet.
Dit is een probleem voor onze OAI (Open Archive Inititative) interface op de Wageningen UR publicaties.
Daarvoor moeten we namelijk eenvoudig kunnen zoeken naar gewijzigde dan wel ingevoerde records tussen twee datums. Om dit mogelijk te maken moeten we een extra index definiëren. We hadden tot nu toe voor iedere Oracle tabel 2 indexen. De text index op het gehele xml document en een aparte index voor het ISN. Er komen nu standaard 2 indexen bij voor de invoer- en wijzigingsdatum van een record.

In WebQuery 5.32 en wqoracle 1.2, die sinds gisteren de productieversies zijn, is het gebruik van deze nieuwe indexen mogelijk gemaakt. Je kunt nu in de "creation date" en de "modification date" zoeken op een range . Daarvoor zijn twee nieuwe pseudo-velden gedefinieerd, wq_cdt en wq_mdt. De zoekstring voor deze velden MOET een (één) slash bevatten als delimiter tussen begin- en eind-moment. Beide momenten mogen afgekapt worden waar je wilt. Een paar voorbeelden: wq_mdt=2005-10/2005-11 zoekt alle in oktober gewijzigde records in dit geval in de NIEUWS database.
De zoekvraag wq_cdt=2005-10-19T11:30/2005-10-20 betekent: zoek alle op 19 oktober na 11:30 uur ingevoerde records.

Zo kan ook de OIA interface op Wageningen UR publicaties gebruik gaan maken van de snellere Oracle database.

2005-11-18

De nabijheidsoperator

In een eerdere post vertelde ik al dat in het nieuwe Bibliotheek Content Management System we te maken hebben met standaard 'phrase searching' en dat word searching expliciet moet worden gedaan door het gebruik van de 'AND' operator. We hebben nu echter ook de "near()" operator om te zoeken naar woorden binnen een record. Zo kan bijvoorbeeld met http://library.wur.nl/WebQuery/wurpubs?title=near((water,management),2) gezocht worden naar alle records in WaY waarbij in de titel de woorden water en management voorkomen, mits tussen deze woorden maximaal 2 andere woorden zitten.
In dit geval vinden we dus titels waarin "water management" staat, maar ook "water quality management" en "management of irrigation water" wordt gevonden.

2005-11-16

Waarom zie je nieuwe records soms niet op het web ?

Wanneer je, op dit moment, een nieuw record of nieuw exemlaar in de catalogus invoert, dan valt op dat je het record of exemplaar nog niet in de catalogus op het web kunt vinden. Dit verschijnsel is een gevolg van de wijze waarop Minisis het laatst ingevoerde record bijhoudt. Om deze 'pointer' bij te werken moet Minisis namelijk de database heropenen. Op het web wordt de database in principe alleen 's-avonds gesloten en heropent en soms overdag als een zoekvraag een time out veroorzaakt. Vandaar dat je de nieuwe dingen doorgaans pas de volgende dag ziet verschijnen in de webcatalogus. In de Oracle situatie is dat straks afgelopen en zal je de nieuwe records wel direct te zien krijgen.

2005-11-14

SEP overzichten snel te genereren in WebQuery Oracle

SEP (Standard Evaluation Protocol) overzichten zijn verplicht materiaal voor de wetenschappelijke verslaglegging. Die zijn nu eenvoudig te genereren uit WaY met één stylesheet. Het duurt weliswaar een seconde of 4 á 5 maar dan staat ie er. Daarvoor zijn in die tijd 72 (!!) zoekvragen aan WebQuery gesteld. Dan is die response tijd wel acceptabel dacht ik.

2005-11-10

zoeken op PLANT-BZ geeft als resultaat PLANT-HG

Da's raar. Zoeken op PLANT-BZ geeft PLANT-HG :) Dat is nou het gevolg van het transparant doorgeven van zoekvragen aan Oracle text Retrieval. Wanneer in de text een Oracle operator staat wordt dat doorgegeven. Dat kunnen we voorkomen, maar dan kunnen subtiele dingen als het afwijken van standaard 'phrase searching' (zie mijn vorige post) niet meer. Dus het is uitkijken met die operatoren. De '-' is bijvoorbeeld zo'n operator. Zoeken op het acroniem PLANT-BZ is dus zoeken op records waar in het acroniem het woord PLANT staat, mits er niet het woord BZ in voorkomt !!! Wil je voorkomen dat operatoren niet als zodanig worden herkend dan moet je om het geheel accolades zetten. Wil je dat sommige operatoren wel ( in dit geval 'or' om zowel PLANT-BZ als PLANT-HG te vinden) worden herkend en anderen niet, escape dan die niet gewenste operatoren met een '/'.

Word searching en phrase searching

Ofschoon we met WebQuery eigenlijk verschoond willen blijven van database afhankelijke dingen, blijkt het in de praktijk toch te verleidelijk om niet gebruik te maken van directe kracht van onderliggende database. We gaan er maar van uit dat we niet te vaak van database zullen wisselen. Daarom toch maar wat oplossingen gebaseerd op Oracle text retrieval.
Onder Minisis was zoeken in een veld standaard 'word searching'. Onder Oracle is het standaard 'phrase searching'. Om toch word searching te doen binnen een veld moet je expliciet een operator tussen woorden gebruiken. In Oracle text retrieval is dat bijvoorbeeld 'and' en | of 'or'. In plaats van een 'or' of | mag je ook een comma gebruiken. Het heeft een iets andere betekenis, maar in de praktijk geeft het hetzelfde resutaat als een or. Vergeet niet haakjes om de totale vraag te zetten. We gaan dat overigens WebQuery automatisch laten doen.

Waarom niet in het Nederlands

Ik ben deze blog begonnen in het Engels. Ik denk echter dat het interessanter is om hem voornamelijk intern te gebruiken binnen Bibliotheek Wageningen UR. Misschien zelfs voornamelijk binnen AOB. Vandaar dat ik maar over ga in het Nederlands. Op dit moment ben ik zelf bezig met Cardex en Way.

2005-10-20

wijziging en invoerdatum en wijziger / invoerdersnaam in records

Ik heb gisteravond een nieuwe versie van item, lener en rubriekgedownload. Daarin zitten nu de invoerdatum, invoerder, wijziginsdatumen wijziger in de record attributen cd, cu, md resp mu. Die worden doorwqoracle automatisch bijgewerkt. Daarnaast is in item de veldnaambibliotheek-laatste-status aangepast (er stond laaste ipv laatste).
Ik probeer vandaag de attributen van titelbeschrijving op de (hopelijk)juiste plek te zetten; als dat lukt staat die morgen vernieuwd op deserver. Voor alle vier geldt dat de gegevens dan up-to-date zijn toteergisteren (door een diskruimteprobleem op de hp3000 zijn de bestandenhelaas niet 100% synchroon, er zijn ook wat gegevens van gisterengedeeltelijk verwerkt. Voor het testen zal dat waarschijnlijk geenprobleem zijn).
Nog even een opmerking over invoerdatum en invoerder. Het is debedoeling dat die attributen er op recordniveau ALTIJD zijn, en wqoraclemaakt ze ook altijd aan bij nieuwe records. Bij conversies m.b.v hetconversiescript worden ze daarom vanaf gisteren ook altijd aangemaakt.De algoritme daarvan zit als volgt in elkaar:
- Als er een veld is dat naar éém van die attributen moet wordengeconverteerd, is het noodzakelijk om een conversie-xslt analoog aan devolgende te maken (dit is de huidige versie lener-conversie-utf8.xslt,waarin alle velden nog met hun minisis-tag worden opgenomen):
<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl"version="1.0"> <!-- AJK 2005-10-19 --> <xsl:import href="default-conversie-utf8.xslt"/> <!-- --> <xsl:output method="xml" version="1.0" encoding="utf-8"indent="yes"/> <!-- LENER --> <xsl:template match="lenerLENER" mode="conversie"> <xsl:element name="lener"> <xsl:apply-templates select="@*" mode="copy"/> <xsl:apply-templates select="N100/N101"mode="cu-attribute"/> <xsl:apply-templates select="N100/N102"mode="cd-attribute"/> <xsl:apply-templates select="N100/N103"mode="mu-attribute"/> <xsl:apply-templates select="N100/N104"mode="md-attribute"/> <xsl:apply-templates select="*" mode="copy"/> </xsl:element> </xsl:template> <!-- do NOT copy the following elements --> <xsl:template match="N101N102N103N104" mode="copy"/> <!-- --></xsl:stylesheet>
de modes "cu-attribute", "cd-attribute", "md-attribute" en"mu-attribute" regelen dat de inhoud van de genoemde velden wordenomgezet in de aangegeven attributes. Voor datumvelden gaan de templateser van uit dat de datum er als "jjjj-mm-dd" in staat. Daar wordt dan"T00:00:00Z" aan toegevoegd om er een geldige timestamp van te maken.
Als er aan het record-element NIET op bovenstaande wijze een cd- en eencu- attrubute zijn toegevoegd, wordt in het cu-attribute "conversie"gezet. Voor het cd-attribute is het iets ingewikkelder. Als er wel eenmd-attribute is gemaakt, wordt dat in het cd-attribute gekopieerd. Alser ook geen md-attribute is, wordt er het tijdstip in gezet waarop dexml-conversie gestart is.
Tenslotte, nu ik het toch over de attributen heb, nog even een uitlegvan de manier waarop wqoracle die dingen vult.Als een record wordt ingevoerd, worden cd en cu op recordniveau gevuldmet de het huidige tijdstip en de gebruikersnaam van degene die inWebQuery is ingelogd. Als niet is ingelogd wordt het ip adres alsgebruikersnaam gebruikt, maar in principe is inloggen verplicht voorvrijwel elke update-functionaliteit.Als een record wordt gewijzigd worden md en mu op analoge wijze gevuldresp aangepast. Bovendien wordt rv met één verhoogd.
Iets soortgelijks gebeurt met de eventueel op andere niveaus van de xsdgedefinieerde attributen. Daarbij gelden echter twee beperkingen omervoor te zorgen dat alleen zinvolle gegevens worden vastgelegd:
a) invoerattributen (cd en cu) worden gevuld op het hoogst mogelijkeniveau. Bij een nieuw record dus allleen op recordniveau.
b) wijzigingsattributen (md, mu en rv) worden gevuld op recordniveau énop het laagst mogelijk niveau.
c) als op een bepaald niveau de attributen niet gedefinieerd zijn komenze terecht op het eerstvolgende hogere niveau waar ze wel gedefinieerd zijn, Age Jan Kuperus

2005-10-13

N op M joins in WQoracle nog niet mogelijk

De engelse en nederlandse rubrieksnamen komen niet goed mee in titelplus. Vergelijk
http://library.wur.nl/WebQuery/titelplus?wq_isn=1671326&wq_sfx=XML (waaruit blijkt dat er iets fout gaat), http://library.wur.nl/WebQuery/clcwwwf/1671326?wq_sfx=XML, (waaruit blijkt dat 't soms wel goed lijkt te gaan, maar alleen bij het eerste onderwerp)

Dit komt omdat oracle geen mogelijkheid heeft om een n op m relatie tussen
twee xml velden goed in éém sql statement te regelen (of omdat we die
mogelijkheid nog niet gevonden hebben)
Op dit moment is het alternatief om de joins bij herhaling 1 en verder met een document lookup te doen n de presentatie xslt. (Zie bijvoorbeeld het template voor A150 en A140 in wurpubs_list.xslt)

2005-09-14

Verschillen tussen Minisis en Oracle in WebQuery

Hierbij een korte uitleg van de belangrijkste verschillen tussen de minisis- en oracle syntax van WebQuery. Het is de bedoeling dat die verschillen verdwijnen, maar zolang dat niet zo is...

algemeen

het aantal hits wordt bij oracle niet exact aangegeven (ook bij minisis klopt het wel eens niet, maar dan is er iets mis met de index). Bij gebruik van haspath (zie verderop) is het helemaal niet betrouwbaar, behalve op de laatste pagina van een set (wqoracle zorgt er voor dat het op die pagina altijd klopt).

Daarmee hangt samen dat er in de huidige versie, als er alleen maar haspath calls in de query zitten, geen next element gegenereerd wordt, ook niet als er wel meer records zijn. Daar zoek ik nog een oplossing voor.

Wildcards (wie heeft er een goede nederlandse naam voor?) kunnen ook problemen geven. Er zij er twee, % als equivalent van de minisis @, en _ voor het zoeken naar een willekeurig teken op de aangegeven positie. Combinaties (bv. %a_b_c%d_e% zijn toegestaan, maar kunnen uiteraard performanceproblemen geven.
WebQuery vertaalt (behalve in wq_qry) * naar % en ? naar _, dus je kunt in de meeste gevallen gewoon de * blijven gebruiken, alleen hoeft hij niet meer met javascript vervangen te worden en voor klanten blijft de * dus de standaard. Bij het intikken van % in een url moet je het schrijven als %25.

Anders dan bij minisis zitten alle gegevens in één index. Ook als een bepaald veld maar drie verschillende waarden kan hebben zal het zoeken naar de waarde a% alle termen die met een a beginnen verzamelen, dan met de veldnaam combineren en de meeste weer af laten vallen. Dat kan al snel tot interne oracle limieten overschrijden en dus tot foutmeldingen leiden.

Om dat leed enigszins te verzachten vertaalt WebQuery de vraag a=* naar een andere constructie (haspath, zie verderop), zodat deze veelmisbruikte constructie nog steeds hanteerbaar is. Er zit wel een nadeel aan: het gemelde aantal hits klopt niet meer en het next element verschijnt niet als de vraag geen inpath calls (zie verderop) bevat.

WebQuery/oracle werkt met xml en de oracle text extensies. Elk record is daarbij één oracle veld in een gelijknamige tabel, waarin alle xml elementen geindexeerd worden. Je kunt dus altijd op alle velden zoeken. WebQuery vertaalt de zoekvraag naar een sql statement waarin oracle text functies worden gebruikt. Wie gebruik maakt van wq_qry zal dat voorlopig zelf moeten doen. Het gaat in hoofdzaak om twee functies: inpath() en haspath(). Inpath zoekt naar de aangegeven waarde in het aangegeven pad, haspath bekijkt of het pad in het record voorkomt en is dus functioneel equivalent aan de minisis "present" search, alleen sneller omdat er met een index gewerkt wordt.

De "normale" WebQuery syntax is ongewijzigd, afgezien van de automatische vertaling van de wildcards * en ? naar de oracle-varianten % en _.

De wq_qry syntax is wel anders, omdat daarin de genoemde functies moeten worden gebruikt. Het is de bedoeling dat dat op een rustiger moment weer wordt teruggedraaid, zodat dan ook in wq_qry de bekende formuleringen gebruikt kunnen worden. De belangrijkste verschillen zijn:

"waarde" wordt: "waarde" inpath(//)

in principe kan je in plaats van // ook het hele xpath gebruiken, maar ivm. compatibiliteit met eventuele schemawijzigingen raad ik dat af als het niet om andere redenen noodzakelijk is.

"@" en present worden: haspath(//)

de present search is in minisis extreem traag, in oracle sneller en zekerder dan de overeenkomstige wildcard search.

de AND NOT relatie tussen twee termen wordt in wq_qry geschreven als NOT. Het blijft een relatie tussen twee termen, dus alleen "NOT jan inpath(//piet)" mag niet. Let op: in wq_rel blijft het "AND NOT".

Age Jan

2005-08-31

Gebruik van XML, XSLT en XHTML binnen Bibliotheek Wageningen UR

een voorstel voor verdergaande standaardisatie en uniformiteit.


Langzamerhand is binnen de afdeling een redelijke hoeveelheid ervaring opgedaan met het gebruik van XML en daarvan afgeleide technologie. Steeds weer blijkt echter dat hergebruik niet of alleen met veel extra inspanning mogelijk is. De voorstellen in dit document zijn gericht op betere herbruikbaarheid en structurering van de verschillende documenten.

XML


De huidige en toekomstige versies van WebQuery genereren in vergelijking tot de oude beter gestandaardiseerde XML. Bij de conversie naar XML en ander onderhoud waarbij de database nog in MINISIS blijft, is het handig daar vast rekening mee te houden door via de cgiparm file de nieuwe situatie zoveel mogelijk te benaderen. Dat houdt onder meer in:

  • gebruik voor het recordsetelement altijd de naam xxxset,
    en voor de recordelementen de naam xxx (dat laatste gebeurt automatisch bij wq_sfx=XML), waarin xxx staat voor de naam van de minisis database.

  • gebruik op het recordniveau uitsluitend de elementen die in de nieuwe situatie ook gegenereerd zullen worden, en geef ze inhoud conform de nieuwe situatie. Het gaat
    om de volgende elementen:
    • error
    • query
    • queryinfo (nog niet gebruiken, wordt in de nieuwe situatie altijd gegenereerd en bevat de query in gestructureerde vorm)
    • hits
    • xxx (de echte records, zie boven)
    • next
    • debug (niet gebruiken, wordt in de nieuwe situatie gegenereerd als om debugginginformatie gevraagd wordt)
  • gebruik zo min mogelijk verschillende (database)namen voor de zelfde gegevens. In de nieuwe situatie wordt altijd de naam van de tabel zelf gebruikt, ook bij joins.

XSLT


  • Genereer zo mogelijk XML of XHTML.
  • Valideer de gegenereerde uitvoer (als dat kan).
  • Structureer de XSLT's zoveel mogelijk. Dat betekent onder meer:

    • maak voor elke waarde van wq_sfx een aparte xslt. Weersta de verleiding om
      xslt's te combineren omdat er zoveel op de zelfde manier moet. Maak in dat geval
      eventueel een aparte xslt voor de gemeenschappellijke delen. Vaak is dat niet
      nodig; zet dan de gemeenschapplijke templates in de xslt met het kleinste aantal
      “eigen” templates of in de meestgebruikte, en importeer die in de andere xslt's.

    • maak templates zo klein mogelijk, zodat ze één element of attribuut behandelen.
      Gebruik voor de onderliggende elementen/attributen waar mogelijk “applytemplates”.
      Door die in de ogen van sommigen “extreme” uitsplitsing maak je niet
      alleen hergebruik eenvoudiger, maar je templates worden simpeler, omdat je veel
      minder conditionele statements nodig hebt (realiseer je dat een template alleen
      wordt gebruikt als het betreffende element voorkomt. De constructie waarbij een
      template wordt aangeroepen na een test op aanwezigheid van het betreffende
      element is principieel fout. Testen op afwezigheid (en dan iets met een ander
      element doen) kan wel zinvol zijn, maar is vaak niet nodig (bv. als altijd exact één
      van de twee aanwezig moet zijn).

    • kleine templates zijn beter leesbaar voor anderen dan grote. Fouten zijn daardoor
      eenvoudige op te sporen.

    • als je in een template de functionaliteit nodig hebt van een reeds bestaand
      template, maak daar dan zo mogelijk geen kopie van, maar gebruik het origineel
      als subtemplate:

      1. roep het aan via applyimports select=”.”

      2. geef het nieuwe template een andere mode mee, en roep het oude aan via
        applytemplates select=”.” (zonodig inclusief mode)

      3. doe het NIET met calltemplate als het ook met applytemplates kan.

    • maak gebruik van de importhiërarchie. Geïmporteerde templates, maar ook
      geïmporteerde globale parameters en variabelen hebben per definitie een lagere
      prioriteit dan die in de xslt waaarin ze geïmporteerd worden. Je kunt dus
      bijvoorbeeld in een algemeen bruikbare xslt (zie default.xslt) standaardwaarden
      zetten en die in een andere xslt overschrijven waar dat nodig is (zie bv.
      wwwtest.xslt en wwwtestupdate.xslt, die heel simpel zijn omdat ze voor vrijwel
      alles de functionaliteit van default.xslt gebruiken)

    • importeren van xslt's is meestal een betere manier van hergebruik dan kopieren
      en aanpassen.

    • maak voor de volgende pagina en andere links naar de zelfde tabel gebruik van
      de standaardparameter
      “service”, die vanaf WebQuery 5.28 altijd automatisch
      wordt meegegeven aan de xslt.

    • probeer zoveel mogelijk te generaliseren en via de cgiparm file te parametriseren.

    • gebruik zo min mogelijk parameters. Dat klinkt tegenstrijdig met het vorige punt,
      maar is het niet. Waar het om gaat is dat je nagaat of het nodig of zinvol kan zijn
      om iets variabel te maken, en hoe dat dan zou moeten. Soms is het beter een
      template voor een andere situatie te herschrijven in een andere xslt. Door goed
      structureren (imports) kan je ervoor zorgen dat de onderliggende templates weer
      wel uit de originele xslt gebruikt worden.

  • documenteer de templates door er boven te zetten waar ze voor bedoeld zijn. Niet
    door te zeggen wat ze doen (dat vertelt de code beter), maar door te zeggen wat
    daarvoor de reden is. Geef ook aan waar eventuele parameters voor gebruikt worden. Beperk de documentatie binnen de template tot het absolute minimum; het maakt het template per definitie minder leesbaar. Als het zou leiden tot betere leesbaarheid is het template te complex.

  • Denk na over de naamgeving, zowel van de templates als van de xsltfiles.
    Begin de naam van een xsltfile met de naam van de service waarvoor hij bedoeld is. Dat is in de nieuwe situatie altijd de enkelvoudvorm van een nederlandstalig zelfstandig
    naamwoord. Zet daarachter de elementengroep of de WebQuery suffix waarvoor het
    ding bedoeld (of beide: eerst de groep en dan de suffix, bv. titelbeschrijvingauteurkort.
    xslt). Combineer algemeen bruikbare templates (en algemeen bruikbaar
    betekent onder meer: onafhankelijk van de gebruikte service) tot zinvolle groepen en
    geef die een zinvolle naam, die in één taal aangeeft waarvoor ze bedoeld zijn. Dus
    niet “libraryauteur.xslt” maar “auteurkort.xslt” of “auteur.xslt”. Zo'n algemene naam
    kan NIET gebruikt worden voor templates die alleen voor auteurs uit de titelbeschrijvingservices gebruikt kunnen worden. In dat geval is “titelbeschrijvingauteur.
    xslt” een betere keuze. Voor de meeste templateverzamelingen zal iets dergelijks gelden, echt universeel bruikbare templates zijn helaas zeldzaam.

XHTML


XHTML wijkt op een aantal punten af van zijn voorganger, HTML 4.0. Omdat het xml is, wordt een aantal dingen strenger gecontroleerd. Daarom, en ter bevordering van de modulariteit, de volgende eisen en wensen:
  • geef in de XSLT aan dat de output XML is en geen HTML. De versie daarvan is dan ook 1.0 en niet 4.0.

  • schrijf elementen attribuutnamen altijd in kleine letters (voor xhtml is dat verplicht).

  • regel alle style attributen via een extern css style sheet waarnaar je in de html
    header verwijst. Gebruik ook voor de stylenamen uitsluitend kleine letters (sommige
    browsers maken wel, andere geen onderscheid tussen kleine letters en hoffdletters).

  • maak voor scripting gebruik van een externe scriptfile waarnaar je in de html header verwijst. Zet in event handlers (onclick etc.) geen complete scripts, maar alleen een aanroep van een functie.

    Een voorbeeld


    Voorbeelden hebben het nadeel dat ze vaak zonder nadenken gekopieerd en vervolgens aangepast worden totdat het resultaat werkt. Dat is hier uitdrukkelijk niet de bedoeling. Het onderstaande voorbeeld is op dit moment in gebruik voor de services
    wwwtest en titel, en daarmee voor allerlei verschillende testsituaties met verschillende
    databasetabellen. Het is voor een nieuwe service eenvoudig te gebruiken als
    generieke invoeren controleapplicatie, en als basis voor andere applicaties.
    Ongetwijfeld valt er nog het een en ander aan te verbeteren; het gaat echter om de
    algemene bruikbaarheid en de modulaire uitwerking van de functionaliteit.
    Het bestaat uit de volgende bestanden (zie bijlagen)

    • cgiparm files: wwwtest.WebQuery, titel.WebQuery

    • hulpbestand: recorddefinitions.
      xml

    • xslt files: default.xslt, defaultupdate.
      xslt, wwwtest.xslt


    Bijlage 1: wwwtest.WebQuery



    security on
    service wwwtest
    server db1.library.wur.nl
    xslt xslt/wwwtest.xslt
    xslt_html -
    #
    xslt_debug xslt/debug.xslt
    xslt_result xslt/wwwtest-result.xslt
    xslt_update xslt/wwwtest-update.xslt
    #
    xslt-parameter css='/css/WebQuery.css'
    xslt-parameter title='&&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;extra;'
    xslt-parameter script='no-script-just-testing'
    xslt-parameter browser='&&HTTP_USER_AGENT;'
    extra xslt: &&xslt;
    extra_test andere tekst bij suffix test

    Opmerkingen bij de bovenstaande regels:

    • WebQuery genereert zelf de xsltparameters “service” en “user”. Daarom mogen die
      niet in de cgiparmfile staan.

    • De “extra” regels worden in het voorbeeld niet volledig gebruikt; ze zijn een voorbeeld van een al langer bestaande maar relatief onbekende methode om de parameters slechts éénmaal te hoeven definiëren en toch bij de suffix “test” een
      andere titel te krijgen. In dat geval wordt bij het uitwerken van de xsltparameter
      “title” de regel “extra_test” gebruikt in plaats van de regel “extra”.


    Bijlage 2: titel.WebQuery



    server db1.library.wur.nl
    service titel
    security update
    xslt_pauline xslt/titelplus.xslt
    xslt xslt/debug.xslt
    hit-limit 500,10
    xslt_short xslt/cms_demo_short.xslt
    xslt_full xslt/cms_demo_full.xslt
    xslt_boolean xslt/boolean.xslt
    field ti=titel
    field au=auteur
    field dt=documenttype
    xslt-parameter css='/css/WebQuery.css'
    xslt-parameter title='&&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;extra;'
    xslt_wwwtest xslt/wwwtest.xslt
    xslt_update xslt/default-update.xslt
    extra titelbeschrijving
    extra_update titelbeschrijving update

Opmerkingen bij de inhoud van dit bestand (zie ook bijlage 1):


  • Gebruik de suffix “wwwtest” om te testen met de “default” xslt's.

  • De “extra” regels worden hier gebruikt om de titel bij update aan te passen.


Bijlage 3: recorddefinitions.xml


Dit bestand bevat de voor het update template benodigde informatie uit de xsd's,
aangevuld met opmaakgegevens. Per tabel is er een recorddefinition element.
Hieronder volgt een deel van het bestand (“...” staat voor een aantal weggelaten
regels):


<?xml version="1.0" encoding="ISO-8859-1"?>

<recorddefinition-set>
...
<recorddefinition name="titelbeschrijving">
<field name="documenttype" type="text" size="10"/>
<group name="artikel">
<group name="titel">
<group name="origineel" repeat="yes">
<field name="begintekst" type="text" size="50"/>
<field name="tekst" type="text" size="3000"/>
<field name="auteursvermelding" type="text" size="200"/>
</group>
<field name="engels" type="text"/>
</group>
<group name="auteur">
<group name="persoon" repeat="yes">
<field name="achternaam" type="text" size="50"/>
<field name="voorletters" type="text" size="30"/>
<field name="voorvoegsels" type="text" size="30"/>
<field name="functie" type="text" size="10"/>
<field name="titulatuur" type="text" size="20"/>
</group>
<group name="corporatie" repeat="yes">
<field name="naam" type="text" size="150"/>
<field name="plaats" type="text" size="36"/>
<field name="land" type="text" size="2"/>
<field name="code" type="text" size="6"/>
<field name="afdeling" type="text" size="100"/>
</group>
<group name="affiliatie">
<field name="naam" type="text" size="150"/>
<field name="plaats" type="text" size="36"/>
<field name="land" type="text" size="2"/>
<field name="code" type="text" size="6"/>
<field name="afdeling" type="text" size="100"/>
</group>
</group>
<group name="collatie">
<field name="paginering" type="text" size="40"/>
<field name="illustraties" type="text" size="60"/>
<field name="formaat" type="text" size="20"/>
<field name="begeleidend" type="text" size="40"/>
</group>
<field name="jaar" type="text" size="40"/>
<field name="deel" type="text" size="40"/>
<field name="nummer" type="text" size="40"/>
</group>
<group name="monografie">
...
</group>
...
</recorddefinition>
<recorddefinition name="wwwtest">
<field name="elem" type="textarea" rows="3" cols="100"/>
<field name="repeat" type="textarea" rows="5" cols="100" repeat="yes"/>
<group name="veld3">
<field name="sub1" type="text" size="50"/>
<field name="sub2" type="text" size="50"/>
<field name="sub3" type="text" size="50"/>
</group>
<group name="repsub" repeat="yes">
<field name="rsub1" type="text" size="50"/>
<field name="file01" type="file" size="50" boxtext="(check to remove this
occurrence)"/>
<field name="rsub3" type="text" size="50"/>
</group>
</recorddefinition>
</recorddefinition-set>



Bijlage 4: default.xslt



<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xml:space="default">

<!--default xslt voor gebruik met WebQuery-->
<xsl:output method="xml" version="1.0" encoding="ISO-8859-1" indent="yes" doctype-public="-//W3C//DTD XHTML 1.0
Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" omit-xml-declaration="no"/>
<!--Dit stylesheet is bedoeld als basis voor alle andere stylesheets. Het bevat defaults voor alle door WebQuery en wqoracle gegenereerde elementen, en voor de "standaard" door wqstub/mindbsrv gegenereerde elementen. Om de match-regels overzichtelijk te houden en overrides in andere templates te vereenvoudigen zijn verschillende modes gebruikt voor recordset, record en velden:
recordset: mode="recordset"
record: mode="record"
velden: default mode
De vormgeving van de html-elementen is geregeld via class attributen, zodat ze via een css kunnen worden aangepast.
Ten behoeve van het testen hebben alle templates een naam, die in de output als xml-commentaar wordt weergegeven. De templates worden nooit via deze naam (met call-template) aangeroepen, maar altijd via de match parameter (met apply-templates of apply-imports). De enige uitzondering op deze regel is het script-ref template, omdat daarin geen xml element maar een variabele verwerkt wordt.
Gebruikte xslt-parameters:
service = de gebruikte WebQuery service. Wordt standaard door WebQuery (5.28+) meegegeven
title = de te gebruiken titel. Kan in de cgiparm file worden meegegeven. Indien niet
meegegeven, wordt 'WebQuery' als titel weergegeven.
css = de te gebruiken cascasding style sheet. Kan in de cgiparm file worden meegegeven.
Indien niet meegegeven, wordt '/css/WebQuery.css' gebruikt.
script = de te gebruiken (java)script-file. Kan in de cgiparm file worden meegegeven.
Indien niet meegegeven, wordt geen scriptfile gebruikt.
-->

<xsl:param name="service" select="'/WebQuery'"/>
<xsl:param name="title" select="'WebQuery'"/>
<xsl:param name="css" select="'/css/WebQuery.css'"/>
<xsl:param name="script"/>
<!--
template voor het root element
Dit template bevat de basis html (header en body tags). De header kan via xslt-parameters in de
cgiparm file worden aangepast. Voor niet meegegeven parameters worden de volgende defaults gebruikt:
xslt-parameter title='WebQuery'
xslt-parameter css='/css/WebQuery.css'
Voor script is de default dat er geen script wordt gebruikt.
-->
<xsl:template match="/" name="default-root">
<xsl:comment>default-root</xsl:comment>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<link rel="stylesheet" type="text/css" href="{$css}"/>
<title>
<xsl:value-of select="$title"/>
</title>
<xsl:call-template name="script-ref">
<xsl:with-param name="script" select="$script"/>
</xsl:call-template>
</head>
<body class="page">
<xsl:apply-templates mode="recordset"/>
</body>
</html>
</xsl:template>
<!--
template voor de recordset
Dit template maakt een tabel waarin de records worden getoond, en de eerste regel van die tabel,
met de naam van de recordset.
-->
<xsl:template match="*" mode="recordset" name="default-recordset">
<xsl:comment>default-recordset; node='<xsl:value-of select="name()"/>'</xsl:comment>
<table class="status">
<tr class="page">
<td class="label">
<xsl:value-of select="name()"/>
</td>
</tr>
<xsl:apply-templates mode="record"/>
</table>
</xsl:template>
<!--
record level templates
Dit zijn templates voor alle elementen op het record niveau: error, hits, next, debug, query,
queryinfo en (als default) het record zelf.
error toont de onderliggende structuur (attributen en elementen) in tabelvorm.
hits toont een tabelregel met het aantal gevonden records. Omdat dit nooit nul kan zijn
(in dat geval wordt een error element gegenereerd) wordt alleen gecontroleerd of er
exact 1 record is, en dan de meervouds-s weggelaten.
next onderscheidt de twee mogelijke vormen: de oude (minisis) vorm iwordt herkend aan het
bestaan van het isn attribuut.
debug wordt genegeeerd, evenals
query en
queryinfo
* (de echte records) worden weergegeven als nieuwe tabel in een regel van de bovenliggende.
-->
<xsl:template match="error" mode="record" name="default-error">
<xsl:comment>default-error></xsl:comment>
<tr class="error">
<td>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</td>
</tr>
</xsl:template>
<!--
-->
<xsl:template match="hits" mode="record" name="default-hits">
<xsl:comment>default-hits</xsl:comment>
<tr class="hits">
<td class="label">
</td>
<td>
<xsl:value-of select="."/>
<xsl:text> record</xsl:text>
<xsl:if test=". != '1'">
<xsl:text>s</xsl:text>
</xsl:if>
<xsl:text> found</xsl:text>
</td>
</tr>
</xsl:template>
<!--
-->
<xsl:template match="next" mode="record" name="default-next">
<xsl:comment>default-next</xsl:comment>
<tr class="next">
<td class="label">
</td>
<td>
<xsl:choose>
<xsl:when test="@isn">
<a href="{@service}/{@isn}?{.}">more...</a>
</xsl:when>
<xsl:otherwise>
<a href="{$service}?wq_qry={../query}&wq_ofs={./@wq_ofs}&wq_max={./@wq_max}">more...</a>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
<!--
-->
<xsl:template match="debugqueryqueryinfo" mode="record" name="default-special">
<xsl:comment>default-special></xsl:comment>
<!-- just ignore these elements -->
</xsl:template>
<!--
-->
<xsl:template match="*" mode="record" name="default-record">
<xsl:comment>default-record; node='<xsl:value-of select="name()"/>'</xsl:comment>
<tr>
<xsl:attribute name="class"><xsl:choose><xsl:when test="position() mod 2 =
1">rowodd</xsl:when><xsl:otherwise>roweven</xsl:otherwise></xsl:choose></xsl:attribute>
<td class="label">
<xsl:value-of select="name(.)"/>
</td>
<td>
<table class="status">
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</table>
</td>
</tr>
</xsl:template>
<!--
-->
<!--
field level templates
* is identiek aan het template voor een record, afgezien van de mode.
@* toont het attribuut in een tabelregel
-->
<xsl:template match="*" name="default-all-elements">
<xsl:comment>default-all-elements; node='<xsl:value-of select="name()"/>'</xsl:comment>
<tr>
<xsl:attribute name="class"><xsl:choose><xsl:when test="position() mod 2 =
1">rowodd</xsl:when><xsl:otherwise>roweven</xsl:otherwise></xsl:choose></xsl:attribute>
<td class="label">
<xsl:value-of select="name(.)"/>
</td>
<td>
<table class="status">
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</table>
</td>
</tr>
</xsl:template>
<!--
-->
<xsl:template match="@*" name="default-all-attributes">
<xsl:comment>default-all-attributes; node='<xsl:value-of select="name()"/>'</xsl:comment>
<tr class="attribute">
<td class="label">
<xsl:value-of select="name(.)"/>
</td>
<td class="data">
<!-- xsl:apply-templates/ here generates a loop in libxslt; WHY ?? -->
<xsl:value-of select="."/>
</td>
</tr>
</xsl:template>
<!--
template voor het refereren naar een script
Deze code had ook rechtstreeks in het root template kunnen staan. Er is hier toch gekozen voor
een eigen template om in een aanroepend template eenvoudig iets anders te kunnen doen, bijvoorbeeld
als er van een andere scripttaal gebruik gemaakt wordt.
-->
<xsl:template name="script-ref">
<xsl:param name="script"/>
<xsl:comment>default-script-ref; script='<xsl:value-of select="$script"/>'</xsl:comment>
<!--
-->
<xsl:if test="$script">
<!--
volgens de html4.0 standaard moet de browser een eventuele inhoud van de script tag negeren
als het src attribute aanwezig is. Het is dus zinloos om daar iets in te zetten (bv.
"script niet gevonden"), omdat de browser dat NOOIT zal mogen laten zien.
-->
<script type="text/javascript" src="{$script}"/>
</xsl:if>
</xsl:template>
<!--
-->
</xsl:stylesheet>
Bijlage 5: defaultupdate.
xslt
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xml:space="default">
<!--
default xslt voor updates met WebQuery
-->
<xsl:import href="default.xslt"/>
<!--
-->
<xsl:output method="html" version="4.0" encoding="ISO-8859-1" indent="yes" doctype-public="-//W3C//DTD XHTML 1.0
Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" omit-xml-declaration="no"/>
<!--
Gebruikte parameters:
service = de gebruikte WebQuery service. Wordt standaard door WebQuery (5.28+) meegegeven
title = de te gebruiken titel. Kan in de cgiparm file worden meegegeven. Indien niet
meegegeven, wordt 'WebQuery' als titel weergegeven.
css = de te gebruiken cascasding style sheet. Kan in de cgiparm file worden meegegeven.
Indien niet meegegeven, wordt '/css/WebQuery.css' gebruikt.
script = de te gebruiken (java)script-file. Kan in de cgiparm file worden meegegeven.
Indien niet meegegeven, wordt geen scriptfile gebruikt.
Dit stylesheet verwacht een record definitie in het document 'record-definitions.xml' in deze directory
-->
<xsl:variable name="record-definition" select="document('record-definitions.xml')/recorddefinition-set"/>
<!--
record level templates
De op recordniveau mogelijke standaardelementen worden verwerkt door de default templates.
Omdat er in deze file ook een * template bestaat moet hier expliciet een apply-imports worden
uitgevoerd (of geprutst worden met prioriteiten, maar dat vond ik minder duidelijk).
-->
<xsl:template match="debugerrorhitsnextqueryqueryinfo" mode="record" name="special-record-update">
<xsl:apply-imports select="." mode="record"/>
<!-- use defaults -->
</xsl:template>
<!--
Het basisidee achter de overige templates in dit stylesheet is dat afwisselend de record-definitie
en het werkelijke record bekeken worden. Daartoe wordt het huidige record in een variabele gezet,
die als parameter aan het processing template wordt meeegegeven. Om alles goed te kunnen onderscheiden
worden de modes update en update-def gebruikt.
Het record-template geeft de besturing door aan het processing template. Dat bekijkt voor elk
gedefinieerd veld of het voorkomt. Zo ja, dan krijgt dat de besturing, en anders wordt een leeg
formulierveld gemaakt. Voor herhaalbare processing template altijd een leeg formulierveld aan.
Op groepsniveau werkt het om een soortgelijke manier.
-->
<xsl:template match="*" mode="record" name="default-record-update">
<xsl:comment>default-record-update; node='<xsl:value-of select="name()"/>'</xsl:comment>
<xsl:variable name="database-record" select="current()"/>
<tr>
<xsl:attribute name="class"><xsl:choose><xsl:when test="position() mod 2 =
1">rowodd</xsl:when><xsl:otherwise>roweven</xsl:otherwise></xsl:choose></xsl:attribute>
<td>
<form action="{$service}/update/{@isn}" method="post" accept-charset="UTF-8" enctype="multipart/form-data">
<table class="status">
<tr class="attribute">
<td class="label">
<xsl:value-of select="name($database-record)"/>
</td>
<td class="label">
<xsl:apply-templates select="@*" mode="update"/>
</td>
</tr>
<xsl:for-each select="$record-definition/recorddefinition[@name=name($database-record)]">
<xsl:apply-templates mode="update-def">
<xsl:with-param name="database-record" select="$database-record"/>
</xsl:apply-templates>
</xsl:for-each>
<tr>
</tr>
<tr>
<td class="label">
</td>
<td>
<input name="wq_crc" type="hidden" value="{@crc}"/>
<input name="wq_sfx" type="hidden" value="XML"/>
<input name="wq_dbg" type="hidden" value=":all"/>
<input type="submit" value="wijzig record"/>
<input type="reset" value="origineel scherm"/>
</td>
</tr>
</table>
</form>
</td>
</tr>
</xsl:template>
<!--
attribute templates
-->
<xsl:template match="@*" mode="update" name="update-all-attributes">
<xsl:comment>update-all-attributes; node='<xsl:value-of select="name()"/>'</xsl:comment>
<xsl:value-of select="name()"/>
<xsl:text>=</xsl:text>
<xsl:apply-templates/>
<xsl:text> </xsl:text>
</xsl:template>
<!--
field level template
-->
<xsl:template match="*" mode="update" name="default-field-update">
<xsl:comment>default-field-update; node='<xsl:value-of select="name()"/>'</xsl:comment>
<xsl:param name="definition" select="/.."/>
<xsl:variable name="database-record" select="current()"/>
<xsl:variable name="name">
<xsl:value-of select="name()"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="position()"/>
</xsl:variable>
<tr valign="top">
<xsl:attribute name="class"><xsl:choose><xsl:when test="position() mod 2 =
1">rowodd</xsl:when><xsl:otherwise>roweven</xsl:otherwise></xsl:choose></xsl:attribute>
<td class="label">
<xsl:value-of select="$name"/>
<xsl:apply-templates select="@occ" mode="update"/>
</td>
<td>
<xsl:choose>
<xsl:when test="name($definition) = 'group'">
<input type="hidden" name="{$name}"/>
<table class="status" width="100%">
<xsl:apply-templates select="$definition/*" mode="update-def">
<xsl:with-param name="database-record" select="$database-record"/>
</xsl:apply-templates>
</table>
</xsl:when>
<xsl:when test="$definition/@readonly = 'yes'">
<xsl:apply-templates mode="update"/>
</xsl:when>
<xsl:when test="$definition/@type = 'file'">
<xsl:apply-templates mode="update"/>
<input name="{$name}" type="checkbox" value="&&delete;"/>
<xsl:value-of select="$definition/@boxtext"/>
</xsl:when>
<xsl:when test="$definition/@type = 'textarea'">
<textarea name="{$name}" rows="{$definition/@rows}" cols="{$definition/@cols}" wrap="virtual">
<xsl:apply-templates mode="update"/>
</textarea>
</xsl:when>
<xsl:otherwise>
<input name="{$name}" type="{$definition/@type}">
<xsl:attribute name="size">
<xsl:choose>
<xsl:when test="$definition/@size > 60">
<xsl:text>55</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$definition/@size"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="value"><xsl:apply-templates mode="update"/></xsl:attribute>
</input>
<xsl:if test="$definition/@size > 60">
<xsl:text> ... [</xsl:text>
<xsl:value-of select="$definition/@size"/>
<xsl:text>]</xsl:text>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
<!--
processing template
-->
<xsl:template match="fieldgroup" mode="update-def" name="update-field-or-group">
<xsl:param name="database-record" select="/.."/>
<xsl:variable name="definition" select="current()"/>
<xsl:variable name="occurrences" select="count($database-record/*[name()=$definition/@name])"/>
<xsl:comment>update-field-or-group; <xsl:value-of select="name()"/>='<xsl:value-of select="@name"/>', database node='<xsl:valueof
select="name($database-record)"/>'</xsl:comment>
<tr>
<td>
<xsl:apply-templates select="$database-record/*[name()=$definition/@name]" mode="update">
<xsl:with-param name="definition" select="$definition"/>
</xsl:apply-templates>
</td>
</tr>
<xsl:if test="@repeat='yes' or $occurrences=0">
<xsl:variable name="name">
<xsl:value-of select="@name"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="$occurrences + 1"/>
</xsl:variable>
<tr valign="top">
<td class="label">
<xsl:value-of select="$name"/>
<xsl:text> [nieuw]</xsl:text>
</td>
<td>
<xsl:choose>
<xsl:when test="name() = 'group'">
<input type="hidden" name="{$name}"/>
<table class="status" width="100%">
<xsl:apply-templates select="$definition/*" mode="update-def">
<xsl:with-param name="database-record" select="/.."/>
</xsl:apply-templates>
</table>
</xsl:when>
<xsl:when test="@readonly = 'yes'">
<xsl:apply-templates mode="update-def"/>
</xsl:when>
<xsl:when test="@type = 'textarea'">
<textarea name="{$name}" rows="{@rows}" cols="{@cols}" wrap="virtual">
<xsl:apply-templates mode="update-def"/>
</textarea>
</xsl:when>
<xsl:otherwise>
<input name="{$name}" type="{@type}">
<xsl:attribute name="size">
<xsl:choose>
<xsl:when test="@size > 60">
<xsl:text>55</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@size"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="value"><xsl:apply-templates mode="update"/></xsl:attribute>
</input>
<xsl:if test="@size > 60">
<xsl:text> ... [</xsl:text>
<xsl:value-of select="@size"/>
<xsl:text>]</xsl:text>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:if>
</xsl:template>
<!--
-->
</xsl:stylesheet>
<\pre>

2005-06-20

Een bibliotheek van xslt templates

Ik stel voor dat we, voordat iedereen allerlei template libraries gaat zitten maken, eerst wat algemene afspraken over dit soort dingen maken. Vorige week heb ik voor "het" korte format iets dergelijks gedaan tbv. de uitleen.

M.i. belangrijke aspecten hiervan (ivm. herbruikbaarheid) zijn:

- inhoud: geen enkele html-tag, wel tekst-indeling volgens standaards (ISBD), behalve evt. op het niveau van de titelbeschrijving zelf. In geen geval style elementen of van de omgeving (bv database of toepassing) afhankelijke zaken. Op titelbeschrijving-niveau kan evt. een class attribute zinvol zijn.

- naamgeving (mijn voorkeur: duidelijke namen voor de xslt's, waarbij niet de applicatie maar de inhoud duidelijk wordt. (bv. titelbeschrijving-kort voor de enige echte korte titelbeschrijving (en over de inhoud daarvan zal de bibliotheek - dus niet alleen onze afdeling - het eens moeten zijn, dus eerst bespreken in projectgroepvergadering van 1501), geen underscores maar koppeltekens in de naam)

- template-gebruik: nooit call-template gebruiken maar altijd apply-templates,
en altijd consequent gebruiken daarvan, dus een template voor elk element op elk niveau, inclusief dezelfde mode als de caller. Conditionele vaste teksten op het laagst mogelijke niveau; pre-literals
vóór en post-literals ná de inhoud, ook voor herhalingen (dus bv. komma-spatie vóór de volgende occurrencee en niet ná de vorige, en een punt ná de laatste). Dit klinkt misschien overdreven, maar het is essentieel voor doelmatig hergebruik. In een toepassing kunnnen dan waar nodig templates gemaakt worden die details anders doen. In de huidige situatie (zoals in de productie-omgeving gegroeid) kan dat niet, omdat geen rekening gehouden is met hergebruik van delen.

- literals: altijd als text element opnemen, niet "los" neerzetten. Tabs en returns (alleen als ze echt in elke denkbare situatie noodzakelijk zijn) opnemen als numerieke enttites ( resp
), niet met de tab- resp. enter-toets.

- alleen templates in zo'n bibliotheek opnemen die universeel bruikbaar zijn, en dus vooraf nadenken over mogelijke herbruikbaarheid (ook in toepassingen die nog niet bedacht zijn ;-).

- goed documenteren, bij voorkeur boven elke template een korte uitleg in een javadoc-compatibel commentaar.

zie titelbeschrijving-short.xslt in de productie-omgeving voor een (nog niet compleet) voorbeeld. Dit is een afgeleide van het belangrijkste deel van het beruchte "restregel" template (dat ooit bedoeld was als "de" korte titelbeschrijving), en bevat nog een aantal daarvan afkomstige fouten. En, inderdaad, de naam ervan moet eigenlijk titelbeschrijving-kort worden en de documentatie moet er nog bij.

Age Jan

2005-03-17

The first application using Oracle as underlying dbms

We are about ot publish our first bibliographic database on the web using Oracle. At this moment the ISRIC world soil information database, is still relying on the underlying Minisis database. The database used to be prepared offline and published using WebQuery. ISRIC wants to add and modify records via the web interface. Since this is a relatively simple application we decided it would be our first Oracle based application. Modifying records works fine. Now we are implementing xsd validation and ran into problems using the libxml libraries. We needed to implement the newest version and these are stricter. So we ran into problems with silly xslt mistakes which did not create any problems before. We have been debugging and everything is working again.

2005-03-16

What is WebQuery ?

WebQuery is the name of an interface that easily enables searching a XML database via a SRU (Search and retrieve by URL) like interface. It also enables you to update the records in the database via a form. It is the basis of our new Library Content Management System and it is developed by the application development and management department of Wageningen University and Research Library.

We started with WebQuery in 1994. It's first version was ready in february 1995. It started off as an interface to search Minisis databases, a relational database management system for documentary databases, running on HP3000's. It still works with this DBMS and also with a newer version 9 of Minisis running on windows servers. At the moment we are developing a version that will also work with Oracle databases. We do not use a lot of Oracle specific tools. We want it to work with any database that can handle XML well. We are now developing for a Linux server environment.

2005-03-03

WebQuery 5.24

Ik heb zojuist WebQuery 5.24 geactiveerd. Hierin zijn de volgende problemen opgelost. Er zijn geen nieuwe features toegevoegd.

de buitenste haakjes verdwijnen

wq_rel is nu onafhankelijk van de onderliggende database (altijd AND, OR of AND NOT, in hoofdletters). Hoe het in terechtkomt is nog afhankelijk van de database (oracle: NOT; minisis: AND NOT)

WebQuery aborteert niet meer ("premature end of script headers" melding) bij &ER; en andere macro's die met een E beginnen en niet tot een tag leiden

wq_fmt binnen &KY: wordt nu voorafgegaan door een ampersand in plaats van een vraagteken.

wq_mod is nu echt optioneel; in plaats daarvan mag ook het juiste keyword (new of update) voor het isn gezet worden (dus bv. <form action="/WebQuery/wwwtest/update/7" method="post">). In principe was dat al zo, het werkte alleen niet goed (er werd altijd gecontroleerd of wq_mod er was) en is daarom nog nooit gebruikt.

In de command-line versie (WebQueryGet resp WebQueryPost) is het probleem opgelost waardoor WebQuery aborteerde bij conversie-cgiparm files (met *hp3000 in de server-regel)

Age Jan