I am working on improving a custom search for our Confluence-Server platform. We have a plugin called Scriptrunner that allow us to use Groovy instead of Java for the code.
The code I am working on is a Search API endpoint, and it currently works fine but returns a lot of unnecessary information and even duplicate, so I want to narrow down the search output in the most efficient way.
The platform have a javadoc that I am trying to use for the implementation, link : https://docs.atlassian.com/ConfluenceServer/javadoc/7.8.1/com/atlassian/confluence/search/v2/SearchManager.html
I want to implement the following part
search(ISearch search, Set<String> requestedFields) Perform a search with a given criteria, the returns searchResults only have the fields requested in the projection filled out, no other fields are valid in the searchResult.
But I cannot understand how to properly generate the Set<String> requestedFields
.
Here is my attempt to do so :
import... def searchManager = ComponentLocator.getComponent(SearchManager) def paramQueryString = "ArticleThatWillBeDeleted" def query = BooleanQuery.andQuery(new TextQuery(paramQueryString)); def sort = new RelevanceSort(); def searchFilter = SiteSearchPermissionsSearchFilter.getInstance(); def searchContent = new ContentSearch(query, sort, searchFilter, 0, 100); Set<String> requestedFields = new HashSet<String>(); requestedFields.add("displayTitle"); def searchresult = searchManager.search(searchContent,requestedFields) return searchresult.getAll()
If I want to use the other method
search(ISearch search) Perform a search with a given criteria.
The script works perfectly fine, but returns a lot of information that I want to cut down.
Beside the method I want to implement, I am also open for any other type of suggestion where I can specify only the information I want to be outputted so I can safe output size and processing power.
P.S. I have already try to ask the same question in great detail on their community page, but figured I could use some developer help with that as I just learned about Java/Groovy from working on that.
In great detail : https://community.atlassian.com/t5/Confluence-questions/Need-to-optimize-the-output-of-a-custom-Search-API-Endpoint-in/qaq-p/1515177
UPDATE:
Here is the working code implemented as an API endpoint:
import com.atlassian.seraph.auth.DefaultAuthenticator import com.atlassian.confluence.user.UserAccessor import com.atlassian.confluence.user.AuthenticatedUserThreadLocal import com.atlassian.confluence.spaces.SpaceManager import com.atlassian.sal.api.component.ComponentLocator import com.atlassian.confluence.search.service.ContentTypeEnum import com.atlassian.confluence.search.v2.SearchManager import com.atlassian.confluence.search.v2.searchfilter.SiteSearchPermissionsSearchFilter import com.atlassian.confluence.search.v2.ContentSearch import com.atlassian.confluence.search.v2.query.* import com.atlassian.confluence.search.v2.sort.RelevanceSort import com.atlassian.confluence.search.v2.SearchSort import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate import groovy.json.JsonBuilder import groovy.transform.BaseScript import javax.ws.rs.core.MultivaluedMap import javax.ws.rs.core.Response import org.codehaus.jackson.map.ObjectMapper import java.util.HashSet @BaseScript CustomEndpointDelegate delegate testSearch( httpMethod: "GET", groups: ["access_group_1","access_group_2"] ) { MultivaluedMap queryParams, String body -> def searchManager = ComponentLocator.getComponent(SearchManager) // Query can be of any type noted here: //https://developer.atlassian.com/server/confluence/searching-using-the-v2-search-api/ def paramQueryString = (queryParams.get(new String("q"))).get(0) def query = BooleanQuery.andQuery(new TextQuery(paramQueryString)); def sort = new RelevanceSort(); def searchFilter = SiteSearchPermissionsSearchFilter.getInstance(); def searchContent = new ContentSearch(query, sort, searchFilter, 0, 100); def searchresult = searchManager.search(searchContent) return Response.ok(new JsonBuilder([results: searchresult.getAll()]).toString()).build() }
When I call the API
http://10.10.10.11:8080/rest/scriptrunner/latest/custom/testSearch?q=ArticleThatWillBeDeleted
I am getting the following JSON response
{ "results": [ { "displayTitle": "ArticleThatWillBeDeleted", "handle": { "className": "com.atlassian.confluence.pages.Page", "id": 359071873 }, "lastUpdateDescription": "", "ownerTitle": null, "spaceName": "Employee Team Space", "creatorUser": { "backingUser": { "active": true, "lowerName": "vnikolov", "directoryId": 142049281, "fullName": "Vasil Nikolov", "emailAddress": "vnikolov@domain.com", "email": "vnikolov@domain.com", "name": "vnikolov", "displayName": "Vasil Nikolov" }, "lowerName": "vnikolov", "key": { "stringValue": "8a606c8c56a371040156a37301341285" }, "fullName": "Vasil Nikolov", "email": "vnikolov@domain.com", "name": "vnikolov" }, "resultExcerpt": "this is the body of the article that will be delted.", "ownerType": null, "lastModifier": "vnikolov", "urlPath": "/display/WIT/ArticleThatWillBeDeleted", "resultExcerptWithHighlights": "this is the body of the article that will be delted.", "explain": { "present": false, "empty": true }, "lastModifierUser": { "backingUser": { "active": true, "lowerName": "vnikolov", "directoryId": 142049281, "fullName": "Vasil Nikolov", "emailAddress": "vnikolov@domain.com", "email": "vnikolov@domain.com", "name": "vnikolov", "displayName": "Vasil Nikolov" }, "lowerName": "vnikolov", "key": { "stringValue": "8a606c8c56a371040156a37301341285" }, "fullName": "Vasil Nikolov", "email": "vnikolov@domain.com", "name": "vnikolov" }, "extraFields": { "content-version": "1" }, "lastModificationDate": "2020-10-20T20:42:27+0000", "type": "page", "content": " nthis is the body of the article that will be delted.n ", "creationDate": "2020-10-20T20:41:46+0000", "personalLabels": [], "status": "current", "spaceKey": "WIT", "contentVersion": 1, "creator": "vnikolov", "displayTitleWithHighlights": "ArticleThatWillBeDeleted", "homePage": false, "sanitisedContent": "this is the body of the article that will be delted." } ] }
I am getting 4 times the body (resultExcerpt , resultExcerptWithHighlights , content , sanitisedContent)
All I need from that is just the content
and if possible to chop it down a limited size or character length.
When I try to implement the requestedFields
by adding the following line and modify the searchresult
def requestedFields = [ 'content', 'displaytitle' ] as Set def searchresult = searchManager.search(searchContent,requestedFields)
The JSON response I am getting is that :
{ "results": [ { "resultExcerpt": "", "explain": { "present": false, "empty": true }, "resultExcerptWithHighlights": "", "extraFields": {}, "displayTitleWithHighlights": "" } ] }
The other thing I noticed is that in the working example the returned class is :
com.atlassian.confluence.search.v2.lucene.LuceneSearchResult@1233a8a4
and in the requestedFields
attempt the result class is :
com.atlassian.confluence.search.v2.ProjectedSearchResult@6c688cdd
I want to find a way to control the output of the API, it does not necessary need to be the requestedFields
method I am trying to implement.
Advertisement
Answer
the JsonBuilder
renders all object properties and not only the fields you requested from server.
the simplest way i see to render requested fields:
def requestedFields = [ 'content', 'displaytitle' ] as Set def searchresult = searchManager.search(searchContent,requestedFields) def table = searchresult.getAll().collect{row-> requestedFields.collectEntries{fldName-> [ fldName , row.getField(fldName) ] } } def json = new groovy.json.JsonBuilder(table).toPrettyString() Response.ok(json).build()