Κοινή χρήση μέσω


TripPin μέρος 10 - Βασική αναδίπλωση ερωτήματος

Σημείωμα

Αυτό το περιεχόμενο αναφέρεται επί του παρόντος σε περιεχόμενο από μια υλοποίηση παλαιού τύπου για αρχεία καταγραφής στο Visual Studio. Το περιεχόμενο θα ενημερωθεί στο εγγύς μέλλον για να καλύψει το νέο SDK Power Query στο Visual Studio Code.

Αυτή η εκμάθηση πολλαπλών τμημάτων καλύπτει τη δημιουργία μιας νέας επέκτασης προέλευσης δεδομένων για το Power Query. Το εκπαιδευτικό βοήθημα προορίζεται να γίνει διαδοχικά. Κάθε μάθημα βασίζεται στη σύνδεση που δημιουργήθηκε σε προηγούμενα μαθήματα, προσθέτοντας σταδιακά νέες δυνατότητες στη σύνδεσή σας.

Σε αυτό το μάθημα, θα κάνετε τα εξής:

  • Μάθετε τα βασικά στοιχεία της αναδίπλωσης ερωτήματος
  • Μάθετε σχετικά με τη συνάρτηση Table.View
  • Αναπαραγωγή δεικτών χειρισμού αναδίπλωσης ερωτήματος OData για:
  • $top
  • $skip
  • $count
  • $select
  • $orderby

Μία από τις ισχυρές δυνατότητες της γλώσσας M είναι η ικανότητά της να προωθεί τις εργασίες μετασχηματισμού σε μία ή περισσότερες υποκείμενες προελεύσεις δεδομένων. Αυτή η δυνατότητα αναφέρεται ως Αναδίπλωση ερωτήματος (άλλα εργαλεία/τεχνολογίες αναφέρονται επίσης σε παρόμοια συνάρτηση με την Προώθηση κατηγορημάτων ή την Ανάθεση ερωτημάτων).

Όταν δημιουργείτε μια προσαρμοσμένη σύνδεση που χρησιμοποιεί μια συνάρτηση M με ενσωματωμένες δυνατότητες αναδίπλωσης ερωτημάτων, όπως OData.Feed ή Odbc.DataSource, η σύνδεση λαμβάνει αυτόματα αυτήν τη δυνατότητα δωρεάν.

Αυτό το εκπαιδευτικό βοήθημα αναπαράγει την ενσωματωμένη συμπεριφορά αναδίπλωσης ερωτήματος για το OData εφαρμόζοντας χειρισμούς συναρτήσεων για τη συνάρτηση Table.View . Αυτό το τμήμα της εκμάθησης υλοποιεί ορισμένους από τους πιο εύκολους χειρισμούς στην υλοποίηση (δηλαδή, εκείνους που δεν απαιτούν ανάλυση παράστασης και παρακολούθηση κατάστασης).

Για να κατανοήσετε περισσότερα σχετικά με τις δυνατότητες ερωτημάτων που μπορεί να προσφέρει μια υπηρεσία OData, μεταβείτε στις Συμβάσεις URL OData v4.

Σημείωμα

Όπως αναφέρθηκε προηγουμένως, η συνάρτηση OData.Feed παρέχει αυτόματα δυνατότητες αναδίπλωσης ερωτημάτων. Δεδομένου ότι η σειρά TripPin αντιμετωπίζει την υπηρεσία OData ως ένα κανονικό REST API, χρησιμοποιώντας Web.Contents αντί για OData.Feed, θα πρέπει να υλοποιήσετε οι ίδιοι τους χειρισμούς αναδίπλωσης ερωτημάτων. Για χρήση από τον πραγματικό κόσμο, συνιστούμε να χρησιμοποιείτε το OData.Feed όποτε αυτό είναι εφικτό.

Μεταβείτε στην Επισκόπηση αξιολόγησης ερωτημάτων και αναδίπλωσης ερωτημάτων στο Power Query για περισσότερες πληροφορίες σχετικά με την αναδίπλωση ερωτήματος.

Χρήση του Table.View

Η συνάρτηση Table.View επιτρέπει σε μια προσαρμοσμένη σύνδεση να παρακάμψει τους προεπιλεγμένους χειρισμούς μετασχηματισμού για την προέλευση δεδομένων σας. Μια υλοποίηση του Table.View θα παρέχει μια συνάρτηση για έναν ή περισσότερους από τους υποστηριζόμενους χειρισμούς. Εάν ένας χειρισμός δεν έχει υλοποιηθεί ή επιστρέψει μια error τιμή κατά την αξιολόγηση, ο μηχανισμός M επιστρέφει στον προεπιλεγμένο χειρισμό του.

Όταν μια προσαρμοσμένη σύνδεση χρησιμοποιεί μια συνάρτηση που δεν υποστηρίζει έμμεση αναδίπλωση ερωτήματος, όπως Web.Contents, οι προεπιλεγμένοι χειρισμοί μετασχηματισμού εκτελούνται πάντα τοπικά. Εάν το REST API στο οποίο συνδέεστε υποστηρίζει παραμέτρους ερωτήματος ως μέρος του ερωτήματος, το Table.View σάς επιτρέπει να προσθέσετε βελτιστοποιήσεις που επιτρέπουν την προώθηση της εργασίας μετασχηματισμού στην υπηρεσία.

Η συνάρτηση Table.View έχει την ακόλουθη υπογραφή:

Table.View(table as nullable table, handlers as record) as table

Η υλοποίηση σας ενσωματώνει την κύρια συνάρτηση προέλευσης δεδομένων σας. Υπάρχουν δύο απαιτούμενοι χειρισμόι για το Table.View:

  • GetType— επιστρέφει την αναμενόμενη τιμή table type του αποτελέσματος του ερωτήματος
  • GetRows— επιστρέφει το πραγματικό table αποτέλεσμα της συνάρτησης προέλευσης δεδομένων

Η απλούστερη υλοποίηση θα είναι παρόμοια με το ακόλουθο παράδειγμα:

TripPin.SuperSimpleView = (url as text, entity as text) as table =>
    Table.View(null, [
        GetType = () => Value.Type(GetRows()),
        GetRows = () => GetEntity(url, entity)
    ]);

Ενημερώστε τη TripPinNavTable συνάρτηση ώστε να καλεί TripPin.SuperSimpleView αντί GetEntityγια :

withData = Table.AddColumn(rename, "Data", each TripPin.SuperSimpleView(url, [Name]), type table),

Εάν εκτελέσετε εκ νέου τις δοκιμές μονάδας, θα δείτε ότι η συμπεριφορά της συνάφειάς σας δεν έχει αλλάξει. Στην περίπτωση αυτή, η υλοποίηση Table.View απλώς μεταβιβάζει την κλήση στο GetEntity. Δεδομένου ότι δεν έχετε υλοποιήσει χειρισμούς μετασχηματισμού (ακόμα), η αρχική url παράμετρος παραμένει ανέγγιχτη.

Αρχική υλοποίηση του Table.View

Η παραπάνω υλοποίηση του Table.View είναι απλή, αλλά όχι πολύ χρήσιμη. Η ακόλουθη υλοποίηση χρησιμοποιείται ως γραμμή βάσης, δεν υλοποιεί καμία λειτουργία αναδίπλωσης, αλλά διαθέτει τη δημιουργία σκελετού που χρειάζεστε για να το κάνετε.

TripPin.View = (baseUrl as text, entity as text) as table =>
    let
        // Implementation of Table.View handlers.
        //
        // We wrap the record with Diagnostics.WrapHandlers() to get some automatic
        // tracing if a handler returns an error.
        //
        View = (state as record) => Table.View(null, Diagnostics.WrapHandlers([
            // Returns the table type returned by GetRows()
            GetType = () => CalculateSchema(state),

            // Called last - retrieves the data from the calculated URL
            GetRows = () => 
                let
                    finalSchema = CalculateSchema(state),
                    finalUrl = CalculateUrl(state),

                    result = TripPin.Feed(finalUrl, finalSchema),
                    appliedType = Table.ChangeType(result, finalSchema)
                in
                    appliedType,

            //
            // Helper functions
            //
            // Retrieves the cached schema. If this is the first call
            // to CalculateSchema, the table type is calculated based on
            // the entity name that was passed into the function.
            CalculateSchema = (state) as type =>
                if (state[Schema]? = null) then
                    GetSchemaForEntity(entity)
                else
                    state[Schema],

            // Calculates the final URL based on the current state.
            CalculateUrl = (state) as text => 
                let
                    urlWithEntity = Uri.Combine(state[Url], state[Entity])
                in
                    urlWithEntity
        ]))
    in
        View([Url = baseUrl, Entity = entity]);

Αν κοιτάξετε την κλήση στο Table.View, θα δείτε μια επιπλέον συνάρτηση περιτύλιξης γύρω από την handlers εγγραφή.Diagnostics.WrapHandlers Αυτή η βοηθητική συνάρτηση βρίσκεται στη λειτουργική μονάδα Διαγνωστικά (που παρουσιάστηκε στο μάθημα προσθήκης διαγνωστικού ελέγχου) και σας παρέχει έναν χρήσιμο τρόπο για την αυτόματη ανίχνευση τυχόν σφαλμάτων που προκύπτουν από μεμονωμένους χειρισμούς.

Οι GetType συναρτήσεις και GetRows ενημερώνονται για να χρησιμοποιήσουν δύο νέες βοηθητικές συναρτήσεις—CalculateSchema και CalculateUrl. Αυτή τη στιγμή, οι υλοποιήσεις αυτών των συναρτήσεων είναι αρκετά απλές, παρατηρήστε ότι περιέχουν τμήματα από αυτά που είχαν γίνει προηγουμένως από τη GetEntity συνάρτηση.

Τέλος, παρατηρήστε ότι ορίζετε μια εσωτερική συνάρτηση (View) που αποδέχεται μια state παράμετρο. Καθώς υλοποιείτε περισσότερους χειρισμούς, θα καλούν αναδρομικά την εσωτερική View συνάρτηση, ενημερώνοντας και μεταβιβάζοντας state την υπηρεσία στην πορεία.

Ενημερώστε τη TripPinNavTable συνάρτηση για άλλη μια φορά, αντικαθιστώντας την κλήση στο TripPin.SuperSimpleView με μια κλήση στη νέα TripPin.View συνάρτηση και επαναλάβετε τις δοκιμές μονάδας. Δεν θα δείτε ακόμα νέες λειτουργίες, αλλά τώρα έχετε μια σταθερή γραμμή βάσης για δοκιμές.

Υλοποίηση αναδίπλωσης ερωτήματος

Δεδομένου ότι ο μηχανισμός M επιστρέφει αυτόματα στην τοπική επεξεργασία όταν δεν είναι δυνατή η αναδίπλωση ενός ερωτήματος, πρέπει να κάνετε ορισμένα επιπλέον βήματα για να επαληθεύσετε ότι οι χειρητές Table.View λειτουργούν σωστά.

Ο μη αυτόματος τρόπος επικύρωσης της συμπεριφοράς αναδίπλωσης είναι να παρακολουθήσετε τις αιτήσεις διεύθυνσης URL που πραγματοποιούν οι δοκιμές μονάδας χρησιμοποιώντας ένα εργαλείο όπως το Fiddler. Εναλλακτικά, η καταγραφή διαγνωστικού ελέγχου που προσθέσατε για να TripPin.Feed εκπέμβει την πλήρη διεύθυνση URL που εκτελείται, η οποία θα πρέπει να περιλαμβάνει τις παραμέτρους συμβολοσειράς ερωτήματος OData που προσθέτουν οι χειριστές σας.

Ένας αυτοματοποιημένος τρόπος για να επικυρώσετε την αναδίπλωση ερωτήματος είναι να επιχειρήσετε να αποτύχει η εκτέλεση της δοκιμής μονάδας εάν ένα ερώτημα δεν αναδιπλωθεί πλήρως. Αυτό μπορείτε να το κάνετε ανοίγοντας τις ιδιότητες έργου και ορίζοντας το στοιχείο Σφάλμα στην "Αποτυχία αναδίπλωσης σε true". Με ενεργοποιημένη αυτήν τη ρύθμιση, κάθε ερώτημα που απαιτεί τοπική επεξεργασία οδηγεί στο ακόλουθο σφάλμα:

Δεν ήταν δυνατή η αναδίπλωση της παράστασης στην προέλευση. Δοκιμάστε μια απλούστερη παράσταση.

Μπορείτε να το δοκιμάσετε προσθέτοντας ένα νέο Fact αρχείο δοκιμής μονάδας που περιέχει έναν ή περισσότερους μετασχηματισμούς πίνακα.

// Query folding tests
Fact("Fold $top 1 on Airlines", 
    #table( type table [AirlineCode = text, Name = text] , {{"AA", "American Airlines"}} ), 
    Table.FirstN(Airlines, 1)
)

Σημείωμα

Η ρύθμιση Σφάλμα στην αποτυχία αναδίπλωσης είναι μια προσέγγιση "όλα ή τίποτα". Εάν θέλετε να δοκιμάσετε ερωτήματα που δεν έχουν σχεδιαστεί για αναδίπλωση ως μέρος των δοκιμών μονάδας, θα χρειαστεί να προσθέσετε κάποια λογική υπό όρους για να ενεργοποιήσετε/απενεργοποιήσετε αντίστοιχα τις δοκιμές.

Οι υπόλοιπες ενότητες αυτού του προγράμματος εκμάθησης προσθέτουν το καθένα ένα νέο πρόγραμμα χειρισμού Table.View . Ακολουθείτε μια προσέγγιση Ανάπτυξης βάσει δοκιμών (TDD ), όπου προσθέτετε πρώτα τις αποτυχημένες δοκιμές μονάδων και, στη συνέχεια, εφαρμόζετε τον κώδικα M για να τις επιλύσετε.

Οι παρακάτω ενότητες χειρισμού περιγράφουν τη λειτουργικότητα που παρέχεται από τον χειρισμό, την ισοδύναμη σύνταξη ερωτήματος OData, τις δοκιμές μονάδων και την υλοποίηση. Χρησιμοποιώντας τον κώδικα δημιουργίας σκελετού που περιγράφηκε προηγουμένως, κάθε υλοποίηση προγράμματος χειρισμού απαιτεί δύο αλλαγές:

  • Προσθήκη του χειρισμού στο Table.View που ενημερώνει την state εγγραφή.
  • CalculateUrl Τροποποίηση για ανάκτηση των τιμών από το state και προσθήκη στις παραμέτρους συμβολοσειράς διεύθυνσης URL ή/και ερωτήματος.

Χειρισμός του Table.FirstN με OnTake

Ο OnTake χειρισμός λαμβάνει μια count παράμετρο, η οποία είναι ο μέγιστος αριθμός γραμμών που θα πραγματοποιηθούν από GetRowsτο . Στους όρους OData, μπορείτε να το μεταφράσετε στην παράμετρο $top ερωτήματος.

Χρησιμοποιείτε τις ακόλουθες δοκιμές μονάδας:

// Query folding tests
Fact("Fold $top 1 on Airlines", 
    #table( type table [AirlineCode = text, Name = text] , {{"AA", "American Airlines"}} ), 
    Table.FirstN(Airlines, 1)
),
Fact("Fold $top 0 on Airports", 
    #table( type table [Name = text, IataCode = text, Location = record] , {} ), 
    Table.FirstN(Airports, 0)
),

Αυτές οι δοκιμές χρησιμοποιούν το Table.FirstN για να φιλτράρουν με βάση το αποτέλεσμα που έχει οριστεί στον πρώτο αριθμό X γραμμών. Εάν εκτελέσετε αυτές τις δοκιμές με σφάλμα στην αποτυχία αναδίπλωσης που έχει οριστεί σε (προεπιλογή), οι δοκιμές θα πρέπει να False είναι επιτυχείς, αλλά εάν εκτελέσετε το Fiddler (ή ελέγξετε τα αρχεία καταγραφής ανίχνευσης), παρατηρήστε ότι η αίτηση που στέλνετε δεν περιέχει παραμέτρους ερωτήματος OData.

Ανίχνευση διαγνωστικού ελέγχου.

Εάν ορίσετε τη ρύθμιση Σφάλμα στην Αναδίπλωση αποτυχίας σε True, οι δοκιμές αποτυγχάνουν με το Please try a simpler expression. σφάλμα. Για να διορθώσετε αυτό το σφάλμα, πρέπει να ορίσετε τον πρώτο σας χειρισμό Table.View για OnTake.

Ο OnTake χειρισμός μοιάζει με τον ακόλουθο κώδικα:

OnTake = (count as number) =>
    let
        // Add a record with Top defined to our state
        newState = state & [ Top = count ]
    in
        @View(newState),

Η CalculateUrl συνάρτηση ενημερώνεται για να εξαγάγει την Top τιμή από την state εγγραφή και να ορίσει τη σωστή παράμετρο στη συμβολοσειρά ερωτήματος.

// Calculates the final URL based on the current state.
CalculateUrl = (state) as text => 
    let
        urlWithEntity = Uri.Combine(state[Url], state[Entity]),

        // Uri.BuildQueryString requires that all field values
        // are text literals.
        defaultQueryString = [],

        // Check for Top defined in our state
        qsWithTop =
            if (state[Top]? <> null) then
                // add a $top field to the query string record
                defaultQueryString & [ #"$top" = Number.ToText(state[Top]) ]
            else
                defaultQueryString,

        encodedQueryString = Uri.BuildQueryString(qsWithTop),
        finalUrl = urlWithEntity & "?" & encodedQueryString
    in
        finalUrl

Κατά την επανάληψη των δοκιμών μονάδας, παρατηρήστε ότι η διεύθυνση URL στην οποία αποκτάτε πρόσβαση περιέχει την $top παράμετρο. Λόγω κωδικοποίησης διεύθυνσης URL, $top εμφανίζεται ως %24top, αλλά η υπηρεσία OData είναι αρκετά έξυπνη για να μετατραπεί αυτόματα.

Ανίχνευση διαγνωστικού ελέγχου με το επάνω μέρος.

Χειρισμός Table.Skip με OnSkip

Ο OnSkip χειρισμός μοιάζει πολύ με OnTakeτο . Λαμβάνει μια count παράμετρο, η οποία είναι ο αριθμός των γραμμών που θα παραλειφούν από το σύνολο αποτελεσμάτων. Αυτός ο χειρισμός μεταφράζεται όμορφα στην παράμετρο ερωτήματος OData $skip .

Δοκιμές μονάδας:

// OnSkip
Fact("Fold $skip 14 on Airlines",
    #table( type table [AirlineCode = text, Name = text] , {{"EK", "Emirates"}} ), 
    Table.Skip(Airlines, 14)
),
Fact("Fold $skip 0 and $top 1",
    #table( type table [AirlineCode = text, Name = text] , {{"AA", "American Airlines"}} ),
    Table.FirstN(Table.Skip(Airlines, 0), 1)
),

Εφαρμογή:

// OnSkip - handles the Table.Skip transform.
// The count value should be >= 0.
OnSkip = (count as number) =>
    let
        newState = state & [ Skip = count ]
    in
        @View(newState),

Αντιστοίχιση ενημερώσεων με το CalculateUrl:

qsWithSkip = 
    if (state[Skip]? <> null) then
        qsWithTop & [ #"$skip" = Number.ToText(state[Skip]) ]
    else
        qsWithTop,

Περισσότερες πληροφορίες: Table.Skip

Χειρισμός Table.SelectColumns με OnSelectColumns

Ο OnSelectColumns χειρισμός καλείται όταν ο χρήστης επιλέγει ή καταργεί στήλες από το σύνολο αποτελεσμάτων. Ο χειρισμός λαμβάνει μια list τιμή text , η οποία αντιπροσωπεύει μία ή περισσότερες στήλες που θα επιλεγούν.

Σε όρους OData, αυτή η λειτουργία αντιστοιχεί στην επιλογή ερωτήματος $select .

Το πλεονέκτημα της αναδίπλωσης επιλογής στήλης γίνεται εμφανές όταν ασχολείστε με πίνακες με πολλές στήλες. Ο $select τελεστής καταργεί μη επιλεγμένες στήλες από το σύνολο αποτελεσμάτων, με αποτέλεσμα πιο αποτελεσματικά ερωτήματα.

Δοκιμές μονάδας:

// OnSelectColumns
Fact("Fold $select single column", 
    #table( type table [AirlineCode = text] , {{"AA"}} ),
    Table.FirstN(Table.SelectColumns(Airlines, {"AirlineCode"}), 1)
),
Fact("Fold $select multiple column", 
    #table( type table [UserName = text, FirstName = text, LastName = text],{{"russellwhyte", "Russell", "Whyte"}}), 
    Table.FirstN(Table.SelectColumns(People, {"UserName", "FirstName", "LastName"}), 1)
),
Fact("Fold $select with ignore column", 
    #table( type table [AirlineCode = text] , {{"AA"}} ),
    Table.FirstN(Table.SelectColumns(Airlines, {"AirlineCode", "DoesNotExist"}, MissingField.Ignore), 1)
),

Οι δύο πρώτες δοκιμές επιλέγουν διαφορετικούς αριθμούς στηλών με Table.SelectColumns και περιλαμβάνουν μια κλήση Table.FirstN για την απλοποίηση της υπόθεσης δοκιμής.

Σημείωμα

Εάν η δοκιμή επρόκειτο απλώς να επιστρέψει τα ονόματα των στηλών (χρησιμοποιώντας το Table.ColumnNames και όχι δεδομένα, η αίτηση στην υπηρεσία OData δεν θα αποσταλεί ποτέ στην πραγματικότητα. Αυτό συμβαίνει επειδή η κλήση στο GetType θα επιστρέψει το σχήμα, το οποίο περιέχει όλες τις πληροφορίες που χρειάζεται ο μηχανισμός M για να υπολογίσει το αποτέλεσμα.

Η τρίτη δοκιμή χρησιμοποιεί την επιλογή MissingField.Ignore , η οποία υποδεικνύει στον μηχανισμό M να παραβλέψει τυχόν επιλεγμένες στήλες που δεν υπάρχουν στο σύνολο αποτελεσμάτων. Ο χειρισμός δεν χρειάζεται να ανησυχεί για αυτή την επιλογή. Ο OnSelectColumns μηχανισμός M τον χειρίζεται αυτόματα (δηλαδή, οι στήλες που λείπουν δεν περιλαμβάνονται στη columns λίστα).

Σημείωμα

Η άλλη επιλογή για table.SelectColumns, MissingField.UseNull, απαιτεί μια σύνδεση για την υλοποίηση του OnAddColumn προγράμματος χειρισμού. Αυτό θα γίνει σε επόμενο μάθημα.

Η υλοποίηση για OnSelectColumns το κάνει δύο πράγματα:

  • Προσθέτει τη λίστα των επιλεγμένων στηλών στο state.
  • Υπολογίζει ξανά την Schema τιμή, ώστε να μπορείτε να ορίσετε τον σωστό τύπο πίνακα.
OnSelectColumns = (columns as list) =>
    let
        // get the current schema
        currentSchema = CalculateSchema(state),
        // get the columns from the current schema (which is an M Type value)
        rowRecordType = Type.RecordFields(Type.TableRow(currentSchema)),
        existingColumns = Record.FieldNames(rowRecordType),
        // calculate the new schema
        columnsToRemove = List.Difference(existingColumns, columns),
        updatedColumns = Record.RemoveFields(rowRecordType, columnsToRemove),
        newSchema = type table (Type.ForRecord(updatedColumns, false))
    in
        @View(state & 
            [ 
                SelectColumns = columns,
                Schema = newSchema
            ]
        ),

CalculateUrl Το ενημερώνεται για να ανακτήσει τη λίστα των στηλών από την κατάσταση και να τις συνδυάσει (με διαχωριστικό) για την $select παράμετρο.

// Check for explicitly selected columns
qsWithSelect =
    if (state[SelectColumns]? <> null) then
        qsWithSkip & [ #"$select" = Text.Combine(state[SelectColumns], ",") ]
    else
        qsWithSkip,

Χειρισμός Table.Sort με OnSort

Ο OnSort χειρισμός λαμβάνει μια λίστα εγγραφών τύπου:

type [ Name = text, Order = Int16.Type ]

Κάθε εγγραφή περιέχει ένα Name πεδίο, που υποδεικνύει το όνομα της στήλης και ένα Order πεδίο που ισούται με Order.Ascending ή Order.Descending.

Σε όρους OData, αυτή η λειτουργία αντιστοιχεί στην επιλογή ερωτήματος $orderby . Η $orderby σύνταξη έχει το όνομα στήλης ακολουθούμενο από asc ή desc για να υποδείξει αύξουσα ή φθίνουσα σειρά. Όταν ταξινομείτε σε πολλές στήλες, οι τιμές διαχωρίζονται με κόμμα. Εάν η παράμετρος columns περιέχει περισσότερα από ένα στοιχεία, είναι σημαντικό να διατηρηθεί η σειρά με την οποία εμφανίζονται.

Δοκιμές μονάδας:

// OnSort
Fact("Fold $orderby single column",
    #table( type table [AirlineCode = text, Name = text], {{"TK", "Turkish Airlines"}}),
    Table.FirstN(Table.Sort(Airlines, {{"AirlineCode", Order.Descending}}), 1)
),
Fact("Fold $orderby multiple column",
    #table( type table [UserName = text], {{"javieralfred"}}),
    Table.SelectColumns(Table.FirstN(Table.Sort(People, {{"LastName", Order.Ascending}, {"UserName", Order.Descending}}), 1), {"UserName"})
)

Εφαρμογή:

// OnSort - receives a list of records containing two fields: 
//    [Name]  - the name of the column to sort on
//    [Order] - equal to Order.Ascending or Order.Descending
// If there are multiple records, the sort order must be maintained.
//
// OData allows you to sort on columns that do not appear in the result
// set, so we do not have to validate that the sorted columns are in our 
// existing schema.
OnSort = (order as list) =>
    let
        // This will convert the list of records to a list of text,
        // where each entry is "<columnName> <asc|desc>"
        sorting = List.Transform(order, (o) => 
            let
                column = o[Name],
                order = o[Order],
                orderText = if (order = Order.Ascending) then "asc" else "desc"
            in
                column & " " & orderText
        ),
        orderBy = Text.Combine(sorting, ", ")
    in
        @View(state & [ OrderBy = orderBy ]),

Ενημερώσεις σε CalculateUrl:

qsWithOrderBy = 
    if (state[OrderBy]? <> null) then
        qsWithSelect & [ #"$orderby" = state[OrderBy] ]
    else
        qsWithSelect,

Χειρισμός Table.RowCount με GetRowCount

Σε αντίθεση με τους άλλους χειρισμούς ερωτημάτων που υλοποιείτε, ο GetRowCount χειρισμός επιστρέφει μια μοναδική τιμή, τον αριθμό των γραμμών που αναμένονται στο σύνολο αποτελεσμάτων. Σε ένα ερώτημα M, αυτή η τιμή θα ήταν συνήθως το αποτέλεσμα του μετασχηματισμού Table.RowCount .

Έχετε μερικές διαφορετικές επιλογές σχετικά με τον τρόπο χειρισμού αυτής της τιμής ως μέρος ενός ερωτήματος OData:

  • Η $count παραμέτρου ερωτήματος, η οποία επιστρέφει το πλήθος ως ξεχωριστό πεδίο στο σύνολο αποτελεσμάτων.
  • Το τμήμα /$count διαδρομής, το οποίο επιστρέφει μόνο το συνολικό πλήθος, ως ανυσματική τιμή.

Το μειονέκτημα της προσέγγισης παραμέτρου ερωτήματος είναι ότι εξακολουθεί να χρειάζεται να στείλετε ολόκληρο το ερώτημα στην υπηρεσία OData. Δεδομένου ότι το πλήθος επιστρέφει ενσωματωμένο ως μέρος του συνόλου αποτελεσμάτων, πρέπει να επεξεργαστείτε την πρώτη σελίδα δεδομένων από το σύνολο αποτελεσμάτων. Παρόλο που αυτή η διαδικασία εξακολουθεί να είναι πιο αποτελεσματική από την ανάγνωση ολόκληρου του συνόλου αποτελεσμάτων και την καταμέτρηση των γραμμών, πιθανώς εξακολουθεί να είναι περισσότερη δουλειά από ό, τι θέλετε να κάνετε.

Το πλεονέκτημα της προσέγγισης τμήματος διαδρομής είναι ότι λαμβάνετε μόνο μία ανυσματική τιμή στο αποτέλεσμα. Αυτή η προσέγγιση καθιστά ολόκληρη την επιχείρηση πολύ πιο αποτελεσματική. Ωστόσο, όπως περιγράφεται στην προδιαγραφή OData, το τμήμα διαδρομής /$count επιστρέφει ένα σφάλμα εάν συμπεριλάβετε άλλες παραμέτρους ερωτήματος, όπως $top ή $skip, το οποίο περιορίζει τη χρησιμότητά του.

Σε αυτό το εκπαιδευτικό βοήθημα, υλοποιείτε τον GetRowCount χειρισμό χρησιμοποιώντας την προσέγγιση τμήματος διαδρομής. Για να αποφύγετε τα σφάλματα που θα λάβετε εάν συμπεριληφθούν άλλες παράμετροι ερωτήματος, επιλέξατε για άλλες τιμές κατάστασης και επιστρέψατε ένα "σφάλμα χωρίς εφαρμογή" (...) εάν εντοπίσατε κάποια. Η επιστροφή οποιουδήποτε σφάλματος από ένα πρόγραμμα χειρισμού Table.View υποδεικνύει στον μηχανισμό M ότι η λειτουργία δεν μπορεί να αναδιπλωθεί και θα πρέπει να επιστρέψει στον προεπιλεγμένο χειρισμό (σε αυτή την περίπτωση θα μετρούσε τον συνολικό αριθμό γραμμών).

Πρώτα, προσθέστε μια δοκιμή μονάδας:

// GetRowCount
Fact("Fold $count", 15, Table.RowCount(Airlines)),

Δεδομένου ότι το /$count τμήμα διαδρομής επιστρέφει μια μοναδική τιμή (σε μορφή απλού κειμένου) αντί για ένα σύνολο αποτελεσμάτων JSON, πρέπει επίσης να προσθέσετε μια νέα εσωτερική συνάρτηση (TripPin.Scalar) για την πραγματοποίηση της αίτησης και τον χειρισμό του αποτελέσματος.

// Similar to TripPin.Feed, but is expecting back a scalar value.
// This function returns the value from the service as plain text.
TripPin.Scalar = (url as text) as text =>
    let
        _url = Diagnostics.LogValue("TripPin.Scalar url", url),

        headers = DefaultRequestHeaders & [
            #"Accept" = "text/plain"
        ],

        response = Web.Contents(_url, [ Headers = headers ]),
        toText = Text.FromBinary(response)
    in
        toText;

Η υλοποίηση, στη συνέχεια, χρησιμοποιεί αυτή τη συνάρτηση (εάν δεν βρεθούν άλλες παράμετροι ερωτήματος στο state):

GetRowCount = () as number =>
    if (Record.FieldCount(Record.RemoveFields(state, {"Url", "Entity", "Schema"}, MissingField.Ignore)) > 0) then
        ...
    else
        let
            newState = state & [ RowCountOnly = true ],
            finalUrl = CalculateUrl(newState),
            value = TripPin.Scalar(finalUrl),
            converted = Number.FromText(value)
        in
            converted,

Η CalculateUrl συνάρτηση ενημερώνεται για προσάρτηση /$count στη διεύθυνση URL εάν το RowCountOnly πεδίο έχει οριστεί στον .state

// Check for $count. If all we want is a row count,
// then we add /$count to the path value (following the entity name).
urlWithRowCount =
    if (state[RowCountOnly]? = true) then
        urlWithEntity & "/$count"
    else
        urlWithEntity,

Η νέα Table.RowCount δοκιμή μονάδας θα πρέπει τώρα να περάσει.

Για να ελέγξετε την περίπτωση επιστροφής, προσθέτετε μια άλλη δοκιμή που επιβάλλει το σφάλμα.

Πρώτα, προσθέστε μια μέθοδο βοηθητικής εφαρμογής που ελέγχει το αποτέλεσμα μιας try λειτουργίας για ένα σφάλμα αναδίπλωσης.

// Returns true if there is a folding error, or the original record (for logging purposes) if not.
Test.IsFoldingError = (tryResult as record) =>
    if ( tryResult[HasError]? = true and tryResult[Error][Message] = "We couldn't fold the expression to the data source. Please try a simpler expression.") then
        true
    else
        tryResult;

Στη συνέχεια, προσθέστε μια δοκιμή που χρησιμοποιεί αμφότερα τα Table.RowCount και Table.FirstN για να επιβάλει το σφάλμα.

// test will fail if "Fail on Folding Error" is set to false
Fact("Fold $count + $top *error*", true, Test.IsFoldingError(try Table.RowCount(Table.FirstN(Airlines, 3)))),

Μια σημαντική σημείωση εδώ είναι ότι αυτή η δοκιμή επιστρέφει τώρα ένα σφάλμα εάν το σφάλμα στο σφάλμα αναδίπλωσης έχει οριστεί σε false, επειδή η Table.RowCount λειτουργία επιστρέφει στον τοπικό (προεπιλεγμένο) χειρισμό. Η εκτέλεση των δοκιμών με σφάλμα στο σύνολο σφαλμάτων αναδίπλωσης Table.RowCount έχει οριστεί ώστε να true προκαλεί αποτυχία και επιτρέπει την επιτυχή εκτέλεση της δοκιμής.

Συμπέρασμα

Η υλοποίηση του Table.View για τη σύνδεσή σας προσθέτει σημαντική πολυπλοκότητα στον κείμενό σας. Δεδομένου ότι ο μηχανισμός M μπορεί να επεξεργαστεί όλους τους μετασχηματισμούς τοπικά, η προσθήκη δεικτών χειρισμού Table.View δεν ενεργοποιεί νέα σενάρια για τους χρήστες σας, αλλά έχει ως αποτέλεσμα πιο αποτελεσματική επεξεργασία (και πιθανώς, πιο ευτυχισμένους χρήστες). Ένα από τα κύρια πλεονεκτήματα της επιλογής του Table.View είναι ότι σας επιτρέπει να προσθέσετε επαυξητικά νέες λειτουργίες χωρίς να επηρεάσετε τη συμβατότητα με προηγούμενες εκδόσεις για τη σύνδεσή σας.

Για τις περισσότερες συνδέσεις, ένας σημαντικός (και βασικός) χειρισμός προς υλοποίηση είναι OnTake (το οποίο μεταφράζεται $top σε στο OData), καθώς περιορίζει τον αριθμό των γραμμών που επιστρέφονται. Η εμπειρία Power Query εκτελεί πάντα μια OnTake σειρά από 1000 γραμμές κατά την εμφάνιση προεπισκοπήσεων στο πρόγραμμα περιήγησης και στο πρόγραμμα επεξεργασίας ερωτημάτων, ώστε οι χρήστες σας να μπορούν να βλέπουν σημαντικές βελτιώσεις επιδόσεων όταν εργάζονται με μεγαλύτερα σύνολα δεδομένων.