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.
{ "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.