TripPin-osa 7 – Kehittynyt rakenne M-tyypeillä
Muistiinpano
Tämä sisältö viittaa tällä hetkellä sisällön vanhaan toteutukseen yksikkötestauksessa Visual Studiossa. Sisältö päivitetään lähitulevaisuudessa kattamaan uudet Power Query SDK -testit.
Tässä moniosaisessa opetusohjelmassa käsitellään uuden tietolähdelaajennuksen luomista Power Querylle. Opetusohjelma on tarkoitus tehdä järjestyksessä – jokainen oppitunti perustuu aiemmilla oppitunneilla luotuun liittimeen ja lisää uusia ominaisuuksia liittimeen.
Tällä oppitunnilla:
Edellisessä osiossa määritit taulukkorakenteen käyttämällä yksinkertaista rakennetaulukkojärjestelmää. Tämä rakennetaulukkomenetelmä toimii monissa REST-ohjelmointirajapitujissa/tieto-Näyttöyhteys, mutta palvelut, jotka palauttavat valmiita tai syvälle sisäkkäisiä tietojoukkoja, saattavat hyötyä tässä opetusohjelmassa lähestymistavasta, jossa hyödynnetään M-tyyppijärjestelmää.
Tämä oppitunti opastaa sinua seuraavien vaiheiden läpi:
- Yksikkötestien lisääminen.
- Mukautettujen M-tyyppien määrittäminen.
- Rakenteen pakottaminen tyyppien avulla.
- Yleisen koodin muodostaminen erillisiksi tiedostoiksi.
Yksikkötestien lisääminen
Ennen kuin aloitat kehittyneen rakenteen logiikan hyödyntämisen, lisäät liittimeen joukon yksikkötestejä, mikä vähentää tahattoman jonkin rikkomisen todennäköisyyttä. Yksikkötestaus toimii seuraavasti:
- Kopioi yleinen koodi UnitTest-mallista tiedostoon
TripPin.query.pq
. - Lisää osailmoitus tiedostosi
TripPin.query.pq
yläosaan. - Luo jaettu tietue (kutsutaan ).
TripPin.UnitTest
- Määritä kullekin testille -
Fact
arvo. - Kutsu
Facts.Summarize()
, jotta kaikki testit suoritetaan. - Viittaa edelliseen kutsuun jaettuna arvona sen varmistamiseksi, että se arvioidaan, kun projekti suoritetaan Visual Studiossa.
section TripPinUnitTests;
shared TripPin.UnitTest =
[
// Put any common variables here if you only want them to be evaluated once
RootTable = TripPin.Contents(),
Airlines = RootTable{[Name="Airlines"]}[Data],
Airports = RootTable{[Name="Airports"]}[Data],
People = RootTable{[Name="People"]}[Data],
// Fact(<Name of the Test>, <Expected Value>, <Actual Value>)
// <Expected Value> and <Actual Value> can be a literal or let statement
facts =
{
Fact("Check that we have three entries in our nav table", 3, Table.RowCount(RootTable)),
Fact("We have Airline data?", true, not Table.IsEmpty(Airlines)),
Fact("We have People data?", true, not Table.IsEmpty(People)),
Fact("We have Airport data?", true, not Table.IsEmpty(Airports)),
Fact("Airlines only has 2 columns", 2, List.Count(Table.ColumnNames(Airlines))),
Fact("Airline table has the right fields",
{"AirlineCode","Name"},
Record.FieldNames(Type.RecordFields(Type.TableRow(Value.Type(Airlines))))
)
},
report = Facts.Summarize(facts)
][report];
Kun valitset Suorita projektissa, kaikki faktat arvioidaan ja saat raportin tulosteen, joka näyttää tältä:
Käyttämällä testipohjaisen kehityksen periaatteita lisäät nyt testin, joka tällä hetkellä epäonnistuu, mutta joka lisätään pian uudelleen ja korjataan (tämän opetusohjelman loppuun mennessä). Tarkemmin sanottuna lisäät testin, joka tarkistaa yhden Ihmiset entiteettiin takaisin saamistasi sisäkkäsistä tietueista (Sähköpostit).
Fact("Emails is properly typed", type text, Type.ListItem(Value.Type(People{0}[Emails])))
Jos suoritat koodin uudelleen, sinun pitäisi nähdä, että sinulla on epäonnistunut testi.
Nyt sinun tarvitsee vain ottaa toiminto käyttöön, jotta tämä toimii.
Mukautettujen M-tyyppien määrittäminen
Edellisen oppitunnin skeeman pakottamismenetelmässä käytettiin rakennetaulukoita, jotka on määritetty nimi-/tyyppipareina. Se toimii hyvin käsiteltäessä litistettyjä/relaatiotietoja, mutta ei tue sisäkkäisten tietueiden, taulukoiden/luetteloiden asetustyyppejä eikä mahdollista käyttää tyyppimääritelmiä uudelleen kaikissa taulukoissa tai entiteeteissä.
TripPin-tapauksessa Ihmiset ja lentokenttien entiteettien tiedot sisältävät jäsennettyjä sarakkeita, ja niissä on myös tyyppi (Location
) osoitetietojen esittämiseksi. Sen sijaan, että määrittäisit rakennetaulukossa Nimi/Tyyppi-parit, määrität kaikki nämä entiteetit käyttämällä mukautettuja M-tyypin määrittelyjä.
Seuraavassa on lyhyt päivitys M-kielen tyypeistä kielimäärityksestä:
Tyypin arvo on arvo, joka luokittelee muut arvot. Arvo, joka luokitellaan tyypin mukaan, noudattaa tätä tyyppiä. M-tyyppijärjestelmä koostuu seuraavanlaisista tyypeistä:
- Primitiivityypit, jotka luokittelevat primitiiviset arvot (
binary
,date
,datetime
,datetimezone
,duration
, ,list
,logical
, ,null
,number
record
,text
,time
type
) ja sisältävät myös joukon abstrakteja tyyppejä (function
, ,table
any
ja )none
- Tietuetyypit, jotka luokittelevat tietuearvot kenttien nimien ja arvotyyppien perusteella
- Luettelotyypit, jotka luokittelevat luetteloita käyttäen yksittäistä kohteen perustyyppiä
- Funktiotyypit, jotka luokittelevat funktioiden arvot parametrien tyyppien ja palautusarvojen perusteella
- Taulukkotyypit, jotka luokittelevat taulukon arvot sarakkeiden nimien, saraketyyppien ja avainten perusteella
- Tyhjäarvoiset tyypit, jotka luokittelevat arvon tyhjäarvoksi kaikkien perustyypin luokittelemien arvojen lisäksi
- Tyyppityypit, jotka luokittelevat arvot, jotka ovat tyyppejä
Käyttämällä saat raakaa JSON-tulosta (ja/tai etsimällä määritelmät palvelun $metadata), voit määrittää seuraavat tietuetyypit edustamaan monimutkaisia OData-tyyppejä:
LocationType = type [
Address = text,
City = CityType,
Loc = LocType
];
CityType = type [
CountryRegion = text,
Name = text,
Region = text
];
LocType = type [
#"type" = text,
coordinates = {number},
crs = CrsType
];
CrsType = type [
#"type" = text,
properties = record
];
Huomaa, miten viittaukset LocationType
ja LocType
edustavat sen jäsennettyjä CityType
sarakkeita.
Ylimmän tason entiteeteille (jotka haluat esittää taulukoina) voit määrittää taulukkotyyppejä:
AirlinesType = type table [
AirlineCode = text,
Name = text
];
AirportsType = type table [
Name = text,
IataCode = text,
Location = LocationType
];
PeopleType = type table [
UserName = text,
FirstName = text,
LastName = text,
Emails = {text},
AddressInfo = {nullable LocationType},
Gender = nullable text,
Concurrency = Int64.Type
];
Sen jälkeen päivität muuttujan SchemaTable
(jota käytetään entiteetin hakutaulukkona yhdistämismääritysten kirjoittamiseen) ja käytät seuraavia uusia tyyppimääritelmiä:
SchemaTable = #table({"Entity", "Type"}, {
{"Airlines", AirlinesType },
{"Airports", AirportsType },
{"People", PeopleType}
});
Rakenteen pakottaminen tyyppien avulla
Käytät yleistä funktiota (Table.ChangeType
) rakenteen toteuttamiseksi tiedoissasi samalla tavalla kuin SchemaTransformTable
edellisessä osiossa.
Toisin kuin SchemaTransformTable
, Table.ChangeType
arvona on todellinen M-taulukkotyyppi argumenttina, ja se käyttää rakennetta rekursiivisesti kaikissa sisäkkäsissä tyypeissä. Sen allekirjoitus näyttää tältä:
Table.ChangeType = (table, tableType as type) as nullable table => ...
Funktion Table.ChangeType
koko koodiluettelo löytyy table.ChangeType.pqm-tiedostosta.
Muistiinpano
Joustavuuden vuoksi funktiota voidaan käyttää taulukoissa sekä tietueluetteloissa (näin taulukot esitetään JSON-asiakirjassa).
Sen jälkeen sinun täytyy päivittää liittimen koodi, jos haluat muuttaa parametrin schema
table
type
kohteesta kohteeseen , ja lisätä kutsun Table.ChangeType
kohteeseen .GetEntity
GetEntity = (url as text, entity as text) as table =>
let
fullUrl = Uri.Combine(url, entity),
schema = GetSchemaForEntity(entity),
result = TripPin.Feed(fullUrl, schema),
appliedSchema = Table.ChangeType(result, schema)
in
appliedSchema;
GetPage
päivitetään käyttämään rakenteen kenttien luetteloa (lisätietoja laajennettavan kohteen nimistä, kun saat tulokset), mutta jättää todellisen rakenteen pakottamisen arvoon GetEntity
.
GetPage = (url as text, optional schema as type) as table =>
let
response = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
body = Json.Document(response),
nextLink = GetNextLink(body),
// If we have no schema, use Table.FromRecords() instead
// (and hope that our results all have the same fields).
// If we have a schema, expand the record using its field names
data =
if (schema <> null) then
Table.FromRecords(body[value])
else
let
// convert the list of records into a table (single column of records)
asTable = Table.FromList(body[value], Splitter.SplitByNothing(), {"Column1"}),
fields = Record.FieldNames(Type.RecordFields(Type.TableRow(schema))),
expanded = Table.ExpandRecordColumn(asTable, fields)
in
expanded
in
data meta [NextLink = nextLink];
Sisäkkäisiä tyyppejä on määritetty
Nykyisen kohteen PeopleType
määritys määrittää Emails
kentän tekstiluetteloksi ({text}
).
Jos otat tyypit käyttöön oikein, yksikkötestin type.ListItem-kutsun pitäisi nyt palauttaa type text
kohteen sijaantype any
.
Yksikkötestien suorittaminen näyttää taas, että ne ovat nyt ohi.
Yleisen koodin muodostaminen erillisiksi tiedostoiksi
Muistiinpano
M-moduulilla on jatkossa parannettu tuki ulkoisiin moduuleihin tai yleiseen koodiin viittaamiseen, mutta tämän lähestymistavan pitäisi kestää siihen asti.
Tässä vaiheessa laajennuksessasi on miltei yhtä paljon "yleistä" koodia kuin TripPin-liitinkoodissa. Tulevaisuudessa nämä yleiset funktiot ovat joko osa sisäistä vakiofunktiokirjastoa tai voit viitata niihin toisesta laajennuksesta. Nyt voit muodostaa koodin uudelleen seuraavalla tavalla:
- Siirrä uudelleenkäytettävät funktiot erillisiin tiedostoihin (.pqm).
- Aseta tiedoston Muodosta toiminto -ominaisuudeksi Kääntä. Näin varmistat, että se sisällytetään tiedostoon muodostamisvaiheessa.
- Määritä funktio, jolla koodi ladataan käyttämällä Expression.Evaluate-funktiota.
- Lataa kaikki yleisimmät funktiot, joita haluat käyttää.
Tätä koskeva koodi sisältyy alla olevaan katkelmaan:
Extension.LoadFunction = (fileName as text) =>
let
binary = Extension.Contents(fileName),
asText = Text.FromBinary(binary)
in
try
Expression.Evaluate(asText, #shared)
catch (e) =>
error [
Reason = "Extension.LoadFunction Failure",
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
Message.Parameters = {fileName, e[Reason], e[Message]},
Detail = [File = fileName, Error = e]
];
Table.ChangeType = Extension.LoadFunction("Table.ChangeType.pqm");
Table.GenerateByPage = Extension.LoadFunction("Table.GenerateByPage.pqm");
Table.ToNavigationTable = Extension.LoadFunction("Table.ToNavigationTable.pqm");
Yhteenveto
Tässä opetusohjelmassa on tehty useita parannuksia tapaan, jolla voit pakottaa rakenteen REST-ohjelmointirajapinnasta saamiisi tietoihin. Liitin koodaa tällä hetkellä tarkasti rakennetietojaan, joilla on suorituskykyetu suorituksen aikana, mutta jotka eivät pysty sopeutumaan palvelun metatietojen ylitöiden muutoksiin. Tulevat opetusohjelmat siirtyvät puhtaasti dynaamiseen lähestymistapaan, joka johtaa rakenteen palvelun $metadata asiakirjasta.
Rakennemuutosten lisäksi tässä opetusohjelmassa lisättiin koodisi yksikkötestit ja muotoiltiin yleiset apufunktiot erillisiksi tiedostoiksi yleisen luettavuuden parantamiseksi.