Developer

How to find elements in a workspace

Objective: find elements in the workspace that match a specific text string. This is done to get properties of that element, to edit it or to delete it. Also, you may want to find a specific canvas to retrieve its content or to add more elements to it. In this page we will search for a specific Canvas for its name and will display the content of that Canvas.

This page contains the implementation details for v3 REST APIs and for v3 GraphQL APIs.

Implementation using REST APIs

In v3 we have access to a search API. This API allows to run a search for a string and we will get all the objects matching that string in their text content.

Endpoint /v3/workspaces/<workspaceId>/search
Method POST
Comments Searches a workspace with a query string, can filter by element types.
IMPORTANT: currently this search API does not match or search the content of Comments in elements.
The output contains a "matchText" field that shows what is the text matching the search query.
Endpoint /v3/workspaces/<workspaceId>/elements?canvas=<CANVAS_ID>
Method GET
Comments Returns the content of the Canvas specified by <CANVAS_ID>

You can specify what type of elements are included in the search. If you want to search only on Image, Shape and Text elements, you can specify the "filterTypes" field with those values, as in this example of the request body:

{
  "filterTypes": [
    "Image", "Shape", "Text"
  ],
  "orderDirection": "desc",
  "query": "<STRING_TO_MATCH>"
} 

If you want to run a search for all the elements in the workspace, and not for a specific set, then simply remove the "filterTypes" key in the body request of the API call.

{
  "orderDirection": "desc",
  "query": "<STRING_TO_MATCH>" 
}

Below there is an example of the response body of this API. The search was for "January 2021", for Canvas elements. The output contains 2 results.

  • The "matchText" field shows the text that matched the search query.
  • The "element" key contains the properties of each matched element.
  • The values for the "id" fields are only examples, they will be different in your workspace.

This is an example of the results (response body) for a search for for "January 2021":

{
    "data": [
        {
            "matchText": "January 2021 reports: summary data",
            "score": 0.046,
            "element": {
                "id": "5ef27790ff5317444e00052a",
                "zIndex": 1330,
                "transform": {
                    "x": 115610.49999999997,
                    "y": -33618,
                    "scaleX": 1,
                    "scaleY": 1
                },
                "pinned": false,
                "comments": [],
                "type": "Canvas",
                "name": "January 2021 reports: summary data",
                "style": {
                    "width": 47914,
                    "height": 25208,
                    "borderColor": {
                        "r": 238, "g": 85, "b": 80, "a": 1
                    }
                }
            }
        },
        {
            "matchText": "January 2021: all projects",
            "score": 0.032,
            "element": {
                "id": "617c44b4b9575c0c10e783c9",
                "zIndex": 2963,
                "transform": {
                    "x": 60092,
                    "y": -34409,
                    "scaleX": 1,
                    "scaleY": 1
                },
                "pinned": false,
                "comments": [],
                "type": "Canvas",
                "name": "January 2021: all projects",
                "style": {
                    "width": 43243,
                    "height": 20170,
                    "borderColor": {
                        "r": 153, "g": 198, "b": 255, "a": 1
                    }
                }
            }
        }
    ]
}
Now you can process the results or run a new search with a search query with more specific terms, for example "January 2021 summary data".

Code sample

See below code samples for a search on a Canvas (by name), to list the content of that Canvas.
We will run a search for the name, restricted to Canvas type (see the "filterTypes" field in the request body). For these examples we are assuming that there will be a Canvas that has the name we are trying to match with the search query.

curl --location --request POST '/v3/workspaces/<workspaceId>/search' \
--header 'Authorization: Bearer <SET_TOKEN>' \
--data-raw '{
  "filterTypes": [
    "Canvas"
  ],
  "orderDirection": "desc",
  "query": "<STRING_TO_MATCH>"
}'
# if you want to get a shorter version of the results of the search, add the following line at to the script above, after "}'":
# | jq . | egrep "(matchText|\"id\"|type)" 

# Python Code (python 3.5+)
import requests
import datetime
import pprint

'''

Required modules:
    requests 2.22.0

'''

token = ''

if __name__ == "__main__":
    portal = ''
    workspaceId = '<SET_WORKSPACE_ID>'  # REMEMBER TO ADD THE WORKSPACE ID

    canvasNameToMatch = '<STRING_TO_MATCH>'

    # Run search
    # Path: /v3/workspaces//search

    API_endpoint = '/v3/workspaces/' + workspaceId + '/search'

    data_load = {
        "filterTypes": [
            "Canvas"
        ],
        "orderDirection": "desc",
        "query": canvasNameToMatch
    }

    the_request = requests.post(
        portal + API_endpoint,
        headers={"Authorization": "Bearer " + token,
                 "Content-Type": "application/json"
                 },
        json=data_load
    )

    canvasSearchResponse = the_request.json()
    # pprint.pprint(canvasSearchResponse) # uncomment to see the list

    canvasList = canvasSearchResponse['data']
    print("Search results:")
    pprint.pprint(canvasList)

    canvasID = ''

    for theElement in canvasList:
        print(theElement)
        theCanvasName = theElement['element']['name']
        if theCanvasName == canvasNameToMatch:
            canvasID = theElement['element']['id']

    # Let's get the content from the canvas
    API_endpoint = '/v3/workspaces/' + workspaceId + '/elements?canvas=' + canvasID

    the_request = requests.get(
        portal + API_endpoint,
        headers={"Authorization": "Bearer " + token,
                 "Content-Type": "application/json"
                 }
    )

    listOfCanvasContent = the_request.json()
    # You can process this list of arrays of elements data
    canvasContent = listOfCanvasContent['data']
    print("Canvas content:")
    pprint.pprint(listOfCanvasContent)  

// Node.js Javascript REST API example for search and list content of Canvas
/*

How to run:
node this_script_name.js

Requires "axios" module (0.19.0), run:
npm install axios

website: https://github.com/axios/axios

*/

var axios = require('axios');

const token = '<SET_TOKEN>';
const portal = '';
const workspaceId = '<SET_WORKSPACE_ID>';
const api_version = 'v3';

function runRequest(portal, api_version, api_endpoint, the_method, data_load) {
    var request_values = {
        url: `${portal}/${api_version}${api_endpoint}`,
        method: the_method,
        headers: {
            'Content-Type': 'application/json',
            'Authorization': "Bearer " + token
        },
        data: data_load,
    };

    let req = axios(request_values)
        .catch(error => {
            console.error(`ERROR processing ${portal}/${api_version}${api_endpoint}`);
            console.error(error.message);
            console.error(error.response.data);
            return Promise.reject(error);
        });;

    return req;
}

async function runSearchAndGetCanvasContent() {
    try {

        // Run search
        // Path: /v3/workspaces//search
        
        const canvasNameToMatch = '<STRING_TO_MATCH>';

        var api_endpoint = `/workspaces/${workspaceId}/search`;
        var the_method = 'POST';

        var data_load = {
            "filterTypes": [
                "Canvas"
            ],
            "orderDirection": "desc",
            "query": canvasNameToMatch
        }

        var canvasSearchResponse = await runRequest(portal, api_version, api_endpoint, the_method, data_load);
        const canvasList = canvasSearchResponse.data.data;
        console.log("Search results:");
        console.log(canvasList);

        var canvasID = '';

        for (var i in canvasList) {
            theCanvasName = canvasList[i].element.name;
            console.log("theCanvasName:", theCanvasName)
            if (theCanvasName == canvasNameToMatch) {
                canvasID = canvasList[i].element.id
                break
            }
        }

        // Let's get the content of the Canvas
        api_endpoint = `/workspaces/${workspaceId}/elements?canvas=${canvasID}`;
        the_method = 'GET';
        data_load = '';

        var listOfCanvasContent = await runRequest(portal, api_version, api_endpoint, the_method, data_load);
        const canvasContent = listOfCanvasContent.data.data; // It is an array of elements data
        console.log("Canvas Content:", canvasContent); 

    } catch (error) {
        console.error('ERROR:');
        console.error(error.message);
    }
}

// Run the requests
runSearchAndGetCanvasContent();           

OUTPUT

The scripts above will show 2 sets of outputs:

  • The results of the search
  • The content of the Canvas that matches the search query

Search Results output (values for "id" are samples only, they will be different for your workspace):

{
  "data": [
    {
      "matchText": "January 2021 meeting",
      "score": 0.04,
      "element": {
        "comments": [],
        "id": "618186ede12c4366912c6d18",
        "name": "January 2021 meeting",
        "pinned": false,
        "style": {
          "borderColor": {
            "a": 1, "b": 255, "g": 198, "r": 153
          },
          "height": 2419, "width": 2159
        },
        "transform": {
          "scaleX": 1, "scaleY": 1, "x": -1976, "y": 2972
        },
        "type": "Canvas",
        "zIndex": 3751
      }
    }
  ]
}

Canvas Content output (values for "id" and "url" are samples only, they will be different for your workspace):

{
  "data": [
    {
      "asset": {
        "documentFormat": "pdf",
        "url": "https://s3.us-east-1.amazonaws.com/acceptance-new.public-assets.bluescape.com/sessions/objects/E-IYMCBISe7QIPlxanns/61819fbdaf0a7a58abdf4ae9.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIVWWSYKG7DKFMQJA%2F20211103%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20211103T232428Z&X-Amz-Expires=300&X-Amz-Signature=bb7f45deefb4cea22c1f09d8f8d18d671febc123ce1acf74dda8e882abfd170c&X-Amz-SignedHeaders=host"
      },
      "attachments": [],
      "comments": [],
      "filename": "New_CICD_Process_slim-21-pages.pdf",
      "height": 720,
      "id": "61819fbdaf0a7a58abdf4ae9",
      "ingestionState": "complete_success",
      "pinned": false,
      "preview": {
        "imageFormat": "jpeg",
        "url": "https://s3.us-east-1.amazonaws.com/acceptance-new.public-assets.bluescape.com/sessions/objects/E-IYMCBISe7QIPlxanns/61819fbdaf0a7a58abdf4ae9.pdf_thumb.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIVWWSYKG7DKFMQJA%2F20211103%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20211103T232428Z&X-Amz-Expires=300&X-Amz-Signature=d423bfdc3443d1a16fee63209de4564c55c767ab1dea51656228be8dd9d6b712&X-Amz-SignedHeaders=host"
      },
      "title": "New_CICD_Process_slim-21-pages.pdf",
      "transform": {
        "scaleX": 0.78125, "scaleY": 0.7819444444444444, "x": -1784, "y": 3451
      },
      "type": "Document",
      "width": 1280,
      "zIndex": 3758
    },
    {
      "blocks": [
        {
          "align": "left",
          "content": [
            {
              "text": "Description:  documents for the January 2021 meeting"
            }
          ],
          "type": "TextBlock"
        }
      ],
      "comments": [],
      "id": "61819feaaf0a7a58abdf4aeb",
      "pinned": false,
      "style": {
        ...
        ...
      },
      "text": "Description:  documents for the January 2021 meeting",
      "transform": {
        "scaleX": 1, "scaleY": 1, "x": -1784, "y": 3200
      },
      "type": "Text",
      "zIndex": 3759
    },
    ...
    ...
    {
      "asset": {
        "imageFormat": "jpeg",
        "url": "https://s3.us-east-1.amazonaws.com/acceptance-new.public-assets.bluescape.com/sessions/objects/E-IYMCBISe7QIPlxanns/6181a214af0a7a58abdf4af9.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIVWWSYKG7DKFMQJA%2F20211103%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20211103T232428Z&X-Amz-Expires=300&X-Amz-Signature=ea7f72b657acb4ac40efe61209d93e75479370766e8261b60a2d23cf1742f7bc&X-Amz-SignedHeaders=host"
      },
      "attachments": [],
      "comments": [],
      "filename": "Compliance reviewer.jpg",
      "height": 1920,
      "id": "6181a214af0a7a58abdf4af9",
      "ingestionState": "complete_success",
      "pinned": false,
      "title": "Compliance reviewer.jpg",
      "transform": {
        "scaleX": 0.5208333333333334, "scaleY": 0.5208333333333334, "x": -1784, "y": 4144
      },
      "type": "Image",
      "width": 1920,
      "zIndex": 3763
    },
    {
      "blocks": [
        {
          "align": "left",
          "content": [
            {
              "text": "Compliance Department person"
            }
          ],
          "type": "TextBlock"
        }
      ],
      "comments": [],
      "id": "6181a50caf0a7a58abdf4afd",
      "pinned": false,
      "style": {
        ...
        ...
      },
      "text": "Compliance Department person",
      "transform": {
        "scaleX": 1, "scaleY": 1, "x": -1153, "y": 5132
      },
      "type": "Text",
      "zIndex": 3764
    }
  ]
}

Implementation using GraphQL APIs

In GraphQl we currently do not have a search API. The approach here is to run a query for the type of element you want, and then loop through the list of results looking for the specific element matching the string you are after.

# Uses 'jq' to filter the results, to filter the string to match: | jq '.data.elements[] | select(.name|test("<STRING_TO_MATCH>"))'
curl --location --request POST '/v3/graphql' \
--header 'Authorization: Bearer <SET_TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query getCanvasesfromWorkspace($workspaceId: String!) {\n  elements(workspaceId: $workspaceId, type: Canvas) {\n    ... on Canvas {\n      type: __typename\n      id\n      name\n      transform {\n        x\n        y\n      }      \n    }\n  }    \n}","variables":{"workspaceId":"<workspaceId>"}}' | jq '.data.elements[] | select(.name|test("<STRING_TO_MATCH>"))'

import requests
import pprint

# List the content of a canvas searching for a match for canvas "name" in list of canvases from the workspace

'''
Required modules:
   requests 2.22.0
'''

url = "/v3/graphql"
token = "<SET_TOKEN>"
workspaceId = "<workspaceId>"

# String to match on the Canvas "name" field
canvasNameToMatch = '<STRING_TO_MATCH>'

getCanvasesQuery = """
    query getCanvasesfromWorkspace($workspaceId: String!) {
        elements(workspaceId: $workspaceId, type: Canvas) {
            ... on Canvas {
            type: __typename
            id
            name
            transform {
                x
                y
            }      
            }
        }    
    }
    """

getCanvasContentQuery = """
    query getCanvasContent($workspaceId: String!, $canvasId: String!) {
        elements(workspaceId: $workspaceId, canvasId: $canvasId ) {            
            type: __typename
            id            
            transform {
                x
                y
            }     
            ...on Image {
                title
                filename
                asset { imageFormat }
            } 
            ... on Text {
                text
            }
            ... on Document {
                title
                filename
                asset { documentFormat }
            }
            
        }    
    }
    """

variables = {
    "workspaceId": workspaceId
}

def makePostQuery(query, variables):
    response = requests.post(url,
                             headers={"Authorization": "Bearer " + token
                                      },
                             json={
                                 'query': query,
                                 'variables': variables
                             })
    return response.json()

if __name__ == "__main__":
    response = makePostQuery(getCanvasesQuery, variables)
    listOfCanvases = response['data']['elements']

    print("List of Canvases:")
    pprint.pprint(listOfCanvases)

    for canvasItem in listOfCanvases:
        if canvasItem['name'] == canvasNameToMatch:
            # Get the content of the matching Canvas
            variablesLocal = {
                "workspaceId": workspaceId,
                "canvasId": canvasItem['id']
            }
            CanvasContent = makePostQuery(
                getCanvasContentQuery, variablesLocal)                
            listOfCanvasContent = CanvasContent['data']['elements'] # Process this list as needed            
            print("Canvas content:")
            pprint.pprint(ListCanvasContent) # uncomment to see the list 
            break  # leave the loop

// Node.js Javascript
// List the content of a canvas searching for a match for canvas "name" in list of canvases from the workspace

/*

How to run:
node this_script_name.js

Requires "axios" module (0.19.0), run:
npm install axios

website: https://github.com/axios/axios

*/

const axios = require('axios');

const url = "/v3/graphql"
const token = "<SET_TOKEN>"
const workspaceId = "<workspaceId>"
// String to match on the Canvas "name" field
const canvasNameToMatch = 'January 2021 meeting'

const makeQuery = async (query, params) => {
  try {
    const response = await axios.post(url,
      {
        query: query,
        variables: params
      },
      {
        headers: { "Authorization": "Bearer " + token }
      })
    return response.data
  }
  catch (error) {
    console.error(error)
  }
}

async function getCanvasContentFoundByName() {

  const canvasListQuery =
    `query getCanvasesfromWorkspace($workspaceId: String!) {
      elements(workspaceId: $workspaceId, type: Canvas) {
          ... on Canvas {
          type: __typename
          id
          name
          transform {
              x
              y
          }      
          }
      }    
  }`

  const canvasListParams = {
    "workspaceId": workspaceId
  }
  const canvasResponse = await makeQuery(canvasListQuery, canvasListParams);
  const canvasList = canvasResponse.data.elements;
  console.log("Canvases List:");
  console.log(canvasList);

  var canvasID = ''

  // Search for the match in the "name" field
  for (var i in canvasList) {
    theCanvasName = canvasList[i].name;
    if (theCanvasName == canvasNameToMatch) {
      canvasID = canvasList[i].id
      break
    }
  }

  const getCanvasContentQuery =
    `query getCanvasContent($workspaceId: String!, $canvasId: String!) {
      elements(workspaceId: $workspaceId, canvasId: $canvasId ) {            
          type: __typename
          id            
          transform {
              x
              y
          }     
          ...on Image {
              title
              filename
              asset { imageFormat }
          } 
          ... on Text {
              text
          }
          ... on Document {
              title
              filename
              asset { documentFormat }
          }
          
      }    
    }`

  const textParamsGetCanvasContent = {
    "workspaceId": workspaceId,
    "canvasId": canvasID
  }

  const textResponse = await makeQuery(getCanvasContentQuery, textParamsGetCanvasContent);
  const listOfCanvasContent = textResponse.data.elements; // Process this list as needed 
  console.log("Canvas Content:");
  console.log(listOfCanvasContent);
}

// Run the requests
getCanvasContentFoundByName();

OUTPUT

The scripts above (that use GraphQL) will show 2 sets of outputs:

  • The results of the search
  • The content of the Canvas that matches the search query

Search Results output (values for "id" are samples only, they will be different for your workspace):

[
  {
    "type": "Canvas",
    "id": "60c40b72736f01e805f71b54",
    "name": "FEBRUARY 2021",
    "transform": {
      "x": -11461,
      "y": 11650
    }
  },
  {
    "type": "Canvas",
    "id": "618186ede12c4366912c6d18",
    "name": "January 2021 meeting",
    "transform": {
      "x": -1976,
      "y": 2972
    }
  },
  {
    "type": "Canvas",
    "id": "6182f80c0dab0b47b057decf",
    "name": "COMPLIANCE MATRIX",
    "transform": {
      "x": 0,
      "y": 0
    }
  },
  ...
  ...
  {
    "type": "Canvas",
    "id": "6182f84c0dff0b47b057dea6",
    "name": "Notes for next meeting",
    "transform": {
      "x": 0,
      "y": 0
    }
  }
]

Canvas Content output (values for "id" are samples only, they will be different for your workspace):

[
  {
    "type": "Document",
    "id": "61819fbdaf0a7a58abdf4ae9",
    "transform": {
      "x": -1784,
      "y": 3451
    },
    "title": "New_CICD_Process_slim-21-pages.pdf",
    "filename": "New_CICD_Process_slim-21-pages.pdf",
    "asset": {
      "documentFormat": "pdf"
    }
  },
  {
    "type": "Text",
    "id": "61819feaaf0a7a58abdf4aeb",
    "transform": {
      "x": -1784,
      "y": 3200
    },
    "text": "Description:  documents for the January 2021 meeting"
  },
  {
    "type": "Image",
    "id": "6181a214af0a7a58abdf4af9",
    "transform": {
      "x": -1784,
      "y": 4144
    },
    "title": "Compliance reviewer.jpg",
    "filename": "Compliance reviewer.jpg",
    "asset": {
      "imageFormat": "jpeg"
    }
  },
  {
    "type": "Text",
    "id": "6181a50caf0a7a58abdf4afd",
    "transform": {
      "x": -1153,
      "y": 5132
    },
    "text": "Compliance Department person"
  }
]

If you have any questions or comments, please contact us in the Bluescape Community site.