Skip to main content
Woohoo! Qlik Community has won “Best in Class Community” in the 2024 Khoros Kudos awards!
Announcements
Nov. 20th, Qlik Insider - Lakehouses: Driving the Future of Data & AI - PICK A SESSION
cancel
Showing results for 
Search instead for 
Did you mean: 
fluxfrog
Contributor III
Contributor III

Export data from bookmark using NodeJS script

Hi!

I would like to export data selected by a bookmark in a certain application to be exported using a NodeJS script running on some server. Is this possible? The end goal here is to (semi-)automate a user segmentation process.
I can connect using Node and Enigma.js and list various things, but I am not sure if I can get a selection from a bookmark using Enigma. Or can I talk to the Capability API directly somehow?

Another possibility would be to write some kind of component that runs as an extension within the application, that the QlikSense user can click to automatically export the data somehow, but I would prefer the NodeJS solution if possible, as it would integrate better with our other systems.

Labels (1)
  • API

1 Solution

Accepted Solutions
han
Employee
Employee

Hopefully, this could be a start

 

 

const enigma = require("enigma.js");
const schema = require("enigma.js/schemas/12.612.0.json");

const bookmarkListProps = {
  qInfo: {
    qId: "BookmarkList",
    qType: "BookmarkList"
  },
  qBookmarkListDef: {
    qType: "bookmark",
    qData: {
      // dynamic data stored by the Qlik Sense client
      title: "/qMetaDef/title",
      description: "/qMetaDef/description",
      sheetId: "/sheetId",
      selectionFields: "/selectionFields",
      creationDate: "/creationDate"
    }
  }
};

(async () => {
  const session = enigma.create(<ENIGMA CONFIG>);
  const global = await session.open();
  const app = await global.openDoc(<APP_ID>);

  // creates a session object
  const bookmarkListModel = await app.createSessionObject(bookmarkListProps);
  const bookmarkListLayout = await bookmarkListModel.getLayout();
  const bookmarks = bookmarkListLayout.qBookmarkList.qItems;

  var filteredBookmarks = bookmarks.filter(function (item) {
    return item.qMeta.published &&            // Check if Bookmark is published
           item.qMeta.title.includes("Book")  // And contains a name "pattern"
  });

  console.log(filteredBookmarks);
  // Iterate over the items: apply bookmark and fetch values from field
})()
  .catch((err) => {
    console.error("ERR:", err);
  })
  .finally(() => {
    //session.close();
  });

 

 

 

View solution in original post

8 Replies
han
Employee
Employee

What would you like the outcome to be?
Do you want the selected values that the bookmark will apply or do you want the "datamodel" that the bookmark will produce?

https://qlik.dev/apis/json-rpc/qix/doc#%23%2Fentries%2FDoc%2Fentries%2FApplyBookmark if you want to apply/"use" the bookmark

fluxfrog
Contributor III
Contributor III
Author

Hi!

I would like my Qlik Sense users to be able to create customer segments using bookmarks in a certain application (to identify some group of customers). Then, I would like my script to look at these bookmarks, and write a text file containing the customer numbers for all these customers every night, or something like that.

han
Employee
Employee

It should be possible to have them publish bookmarks with some name convention soo that your script could find the bookmark and apply it. And after that grab the selected values from some field.

 

But I guess it would be a nicer experience for the users if you just make an extension that will post the values from a field to an internal server.

Long time since I did an extension but it should be pretty easy to make

fluxfrog
Contributor III
Contributor III
Author

I see your point, but for various reasons, I think the naming convention would be the best way here. However, what I don't understand is how I can use Enigma.js to achieve this. I can't find any examples of anything like this.

I have found a bunch of examples of have to use the Capability API, but that is always in the context of some frontend code, and I don't know how to translate that to anything that can be run in a NodeJS script on my servers.

han
Employee
Employee

Hopefully, this could be a start

 

 

const enigma = require("enigma.js");
const schema = require("enigma.js/schemas/12.612.0.json");

const bookmarkListProps = {
  qInfo: {
    qId: "BookmarkList",
    qType: "BookmarkList"
  },
  qBookmarkListDef: {
    qType: "bookmark",
    qData: {
      // dynamic data stored by the Qlik Sense client
      title: "/qMetaDef/title",
      description: "/qMetaDef/description",
      sheetId: "/sheetId",
      selectionFields: "/selectionFields",
      creationDate: "/creationDate"
    }
  }
};

(async () => {
  const session = enigma.create(<ENIGMA CONFIG>);
  const global = await session.open();
  const app = await global.openDoc(<APP_ID>);

  // creates a session object
  const bookmarkListModel = await app.createSessionObject(bookmarkListProps);
  const bookmarkListLayout = await bookmarkListModel.getLayout();
  const bookmarks = bookmarkListLayout.qBookmarkList.qItems;

  var filteredBookmarks = bookmarks.filter(function (item) {
    return item.qMeta.published &&            // Check if Bookmark is published
           item.qMeta.title.includes("Book")  // And contains a name "pattern"
  });

  console.log(filteredBookmarks);
  // Iterate over the items: apply bookmark and fetch values from field
})()
  .catch((err) => {
    console.error("ERR:", err);
  })
  .finally(() => {
    //session.close();
  });

 

 

 

fluxfrog
Contributor III
Contributor III
Author

Thanks a lot!

That will work great for my purpose, but I still can't find a lot of information about how to actually retrieve the selected data in the script. The examples seems to always be about doing stuff in the browser, like selecting fields and applying bookmarks, so I'm still unsure about where to find more about this part:

// Iterate over the items: apply bookmark and fetch values from field

fluxfrog
Contributor III
Contributor III
Author

Actually, I now managed to do this:

  app.applyBookmark('394db859-58d1-4fbf-89e2-321d219f480e');
  let qObj = await app.getObject("mwEZnM");
  const dataExport = await qObj.exportData("OOXML");
  let downloadLink = `https://my.server.url${dataExport.qUrl}`;

That produces an url that I can use to get the data directly from a list in the application with the bookmark applied. Great success! Thanks again. Is this the way to do it? Our would there be a better solution?

han
Employee
Employee

It works as you do it but you will get one exported file per bookmark. Not sure that's what you want or you want to combine them all?

You can create a Listbox with the field that you are interested in and iterate over the bookmarks and retrieving the values (e.g selected and possible) from the Listbox

 

const listObjectProps = {
  qInfo: {
    qType: "my-list-object"
  },
  qListObjectDef: {
    qDef: {
      qFieldDefs: ["<FIELD_NAME>"]
    },
    qInitialDataFetch: [
      {
        qTop: 0,
        qHeight: 100,
        qLeft: 0,
        qWidth: 1
      }
    ]
  }
};

const getValuesFromBookmark = async (filteredBookmarks, app) => {
  const listbox = await app.createSessionObject(listObjectProps);
  var allSelectedValues = [];

  for (const bookmark of filteredBookmarks) {
    await app.applyBookmark(bookmark.qInfo.qId);
    const listBoxLayout = await listbox.getLayout();
    const values = listBoxLayout.qListObject.qDataPages[0].qMatrix;

    for (const value of values) {
      if (value[0].qState === "S" || value[0].qState === "O") {
        const elemNum = value[0].qElemNumber;
        const allready = allSelectedValues.some((v) => v["qElemNumber"] === elemNum);)
          if (!allready) {
            allSelectedValues.push(value)
          }
      }
    }
  }

  return allSelectedValues.flat();
};

// ==== And call it
   const v = await getValuesFromBookmark(filteredBookmarks, app)
   console.log(v)

 

The maximum number of rows possible to fetch at once is 10000 soo if your field contains more values you have to implement pagination. Maybe it's possible to only get the selectedvalues in the response (have checked the props of the listbox)