Query returns collection columns in random order

javascript maps make my head spin. :wink:

Here is the solution at which I arrived:

  1. Create a meta-data Collection which lists each table/collection that should be user-downloadable along with the associated WIX codename associated to each column/field in those collections and the order in which you want those columns/fields to appear in a download.

  2. Use the following javascript and HTML code to query and order your data in the proper columnar order. (I left in all the console.log messages I used to develop this but they are all commented out. You can restore them as necessary to understand what is happening after creating your meta-data table and changing the collection names to reflect your collections).

Your meta-data collection table should be structured something like this:

The most import elements of that meta-data table are the CollectionCode and the FieldCode --which are how WIX Javascript identifies those elements programmatically.

here are the control elements on the website page which access the code:

They look like this in preview mode: (I set the HTML element to “hidden”)

//
here is the javascript code from the web page:
/
/

import wixData from ‘wix-data’ ;

$w . onReady ( function () {
$w ( “#html2” ). onMessage (( event ) => {
console . log ( “event data” , event . data )
$w ( “#text1” ). text = event . data ; // Write messages from html component to text1.
});

});

function sendMessageToComponent ( message ) {
// send message to the HTML element
console . log ( message )
$w ( “#html2” ). postMessage ( message );
}

export async function csvDownload () {
let collectionName = $w ( “#dropdown1” ). value //this dropdown list is data-connected to a meta-data collection which lists all the “end-user download-available” collections on the site
//console.log(collectionName)
let headerOrder = //initialize an array to hold the specific collection column order desired
await wixData . query ( “CollectionsMetaData” ) // this collection contains the collection and field names–and the field names are manually pre-assigned a numberic position value within the collection
. limit ( 1000 ) //I have 7 user-survey data input collections with over cumulative 400 fields so far
. eq ( ‘collectionCode’ , collectionName ) //look for the portion of meta data that applies to the selected collection from the dropdown list
. eq ( ‘includeInReport’ , “Yes” ) //get only the fields marked for inclusion in a download report
. ascending ( ‘position’ ) //MOST IMPORT–order the columnar items as indicated by the meta data position assignment!
. find ()
. then (( headers ) => {
for ( let x = 0 ; x < headers . length ; x ++) { //create an ordered array of “item” identifiers
//console.log(x,headers.items.fieldCode,headers.items.position)
headerOrder . push ([ headers . items [ x ]. fieldCode , headers . items [ x ]. position ]) //we add the fiedl WIX codenames to the array in the order we want them returned in the CSV file
}
//console.log(headerOrder)
})
. then ( async ( nextUp ) => { //now we will query the collection selected in the dorp-down list
await wixData . query ( collectionName )
. limit ( 1000 )
. find ()
. then ( async ( results ) => {
let jsonArr = “” //initialize a json array to be passed to the HTML element embedded in the page which contains the download code
if ( results . items . length > 0 ) { //make sure we have something to process
let tempArr = “” //initialize a temporary array to holed the queried values for one row at a time as they come in
for ( let r = 0 ; r < results . items . length ; r ++) { //determine how many records were detected in the collection
for ( let h = 0 ; h < headerOrder . length ; h ++) { //now determine which field we want to pass to JSON nesxt based on the preveiously queried meta data
let fieldcode = ( headerOrder [ h ][ 0 ]) //this is the WiX collection “codeNaeme” for the desired field
let qCode = “results.items[r].” + fieldcode ; //create a dynamic variable that refences the desired collection column for the results returned from the query collection
//console.log (qCode)
let fieldVal = eval( qCode ) //THIS IS THE SHIZNIT: we dynamically evaluate the value of the field/column we need IN THE PROPER (assigned) COLUMN ORDER
//tempArr.push([ fieldcode, fieldVal ])
let comma = “” ; //now we’re going to emulate what the JSON stringify command does to create a string we can pass to the HTML download code (courtesy of Steve Cropper)
if ( h < headerOrder . length - 1 ) { comma = ‘,’ } else { comma = ‘’ } //if this is our very last column, we don’t need a comma between elements, otherwise we do
tempArr += ‘"’ + fieldcode + ‘": "’ + fieldVal + ‘"’ + comma //format our data lin with the field title, the value, and (if necessaryy) a comma
}
let comma = “” ; //some final JSON stringify formatting (next 3 lines)
if ( r < results . length - 1 ) { comma = ‘,’ } else { comma = ‘’ }
tempArr = “{” + tempArr + “}” + comma //this separates each record detected by the primarily-targeted table collection query
//console.log(tempArr)
jsonArr += tempArr //add this row’s data line to our final jason array we will pass to the HTML code
tempArr = “” //reset the temp array to receive the next row’s data
}
} else { console . log ( “no results found” ) }
//console.log(jsonArr)
//let jsonPayload = JSON.stringify(jsonArr); //we don’t need this anymore–we’ve already created a “stringified” json array
let jsonPayload = “[” + jsonArr . replaceAll ( ‘\n\n’ , ‘\n\n’ ) + “]” //in my case, I had to do some clean-up to eliminate “escaped” new-linecharacters contained in collection text fields
//console.log("headers ",jsonPayload);
sendMessageToComponent ({ name : collectionName + “.csv” , action : “data” , payload : jsonPayload }); //finally, we send the results to Steve Cropper’s HTML code with one additionals value, i.e. “naem: collectionName”) which adds that name to the CSV download
});

    }); 

}

export function button1_click ( event ) {
csvDownload ()
}

//
NOTES REGARDING THE HTML MESSAGE SECTION:
/
/
In order to pass a specific download filename:

this modification to the page javascript: " sendMessageToComponent ({ name : collectionName
requires a modification to Steve Cropper’s HTML Element code as follows:

function messageReceived(message) { 
    if (message && message.action === 'data' && message.payload) { 
        // Convert JSON to csv 
       download_file( **message.name** ,  csvFromJson(message.payload)); 

//
Here is the full HTML code pasted into the HTML page element:
/
/

Document Loading...

document.onreadystatechange = function () { 
    if (document.readyState === "complete") { 
        console.log("Document Loaded!"); 
        updateNoticeBoard("Document Loaded!"); 
    } 
} 

function updateNoticeBoard(message) { 
    let messageArea = document.getElementById('noticeBoard'); 
    messageArea.innerHTML = message; 
} 

window.onmessage = (event) => { 
    console.log("Message Received: "+(event.data.action ? event.data.action : "Bad Message")); 
    if (event.data) { 
        messageReceived(event.data); 
    } 
}; 

function sendMessageToPage(message) { 
    window.parent.postMessage(message, "*"); 
} 

function messageReceived(message) { 
    if (message && message.action === 'data' && message.payload) { 
        // Convert JSON to csv 
        download_file(message.name, csvFromJson(message.payload)); 
    } else if (message && message.payload) { 
        updateNoticeBoard(message.payload); 
    } 
} 

function dynamic_text() { 
    return "create your dynamic text here"; 
} 

// Courtesy https://stackoverflow.com/questions/18249290/generate-csv-for-excel-via-javascript-with-unicode-characters 
function download_file(name, contents, mime_type) { 
    mime_type = mime_type || "text/csv;charset=utf-8"; 

    var blob = new Blob(['\uFEFF', contents], {type: mime_type}); 

    var dlink = document.createElement('a'); 
    dlink.download = name; 
    dlink.href = window.URL.createObjectURL(blob); 
    dlink.onclick = function(e) { 
        // revokeObjectURL needs a delay to work properly 
        var that = this; 
        setTimeout(function() { 
            window.URL.revokeObjectURL(that.href); 
        }, 1500); 
    }; 

    dlink.click(); 
    dlink.remove(); 
} 

function csvFromJson(jsonData) { 
    const items = JSON.parse(jsonData); 
    const replacer = (key, value) => value === null ? '' : value // specify how you want to handle null values here 
    const header = Object.keys(items[0]) 
    console.log(header) 
    let csv = items.map(row => header.map(fieldName => JSON.stringify(row[fieldName], replacer)).join(',')) 
    csv.unshift(header.join(',')) 
    csv = csv.join('\r\n') 

    console.log(csv) 
    return csv 
} 
</script> 

Hope this may be of some use to anybody else facing the same issues.
As “Gramps” says: No cats were harmed in the development of this code"
In fact, they typically walk all over my keyboard while I’m trying to concentrate, therefore, I refuse to be held responsible for any grammatic, programmatic or logical errors.

:wink: