Jaa


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:

  • Taulukon rakenteen pakottaminen M-tyyppien avulla
  • Sisäkkäisten tietueiden ja luetteloiden tyyppien määrittäminen
  • Muodosta koodi uudelleenkäyttöä ja yksikkötestausta varten

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:

  1. Yksikkötestien lisääminen.
  2. Mukautettujen M-tyyppien määrittäminen.
  3. Rakenteen pakottaminen tyyppien avulla.
  4. 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:

  1. Kopioi yleinen koodi UnitTest-mallista tiedostoonTripPin.query.pq.
  2. Lisää osailmoitus tiedostosi TripPin.query.pq yläosaan.
  3. Luo jaettu tietue (kutsutaan ).TripPin.UnitTest
  4. Määritä kullekin testille - Fact arvo.
  5. Kutsu Facts.Summarize() , jotta kaikki testit suoritetaan.
  6. 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ä:

Ensimmäinen yksikkötesti.

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.

Yksikkötesti, jossa on virhe.

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, numberrecord, text, timetype) ja sisältävät myös joukon abstrakteja tyyppejä (function, , tableanyja )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 typekohteesta 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.

Yksikkötesti onnistui.

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:

  1. Siirrä uudelleenkäytettävät funktiot erillisiin tiedostoihin (.pqm).
  2. Aseta tiedoston Muodosta toiminto -ominaisuudeksi Kääntä. Näin varmistat, että se sisällytetään tiedostoon muodostamisvaiheessa.
  3. Määritä funktio, jolla koodi ladataan käyttämällä Expression.Evaluate-funktiota.
  4. 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.

Seuraavat vaiheet

TripPin, osa 8 – Diagnostiikan lisääminen