如何在java/groovy中减少自定义API端点的输出

huangapple go评论89阅读模式
英文:

How to reduce output for a custom API Endpoint in java/groovy

问题

以下是翻译好的内容:

我正在努力改进我们的Confluence服务器平台的自定义搜索功能。
我们有一个名为Scriptrunner的插件,允许我们在代码中使用Groovy而不是Java。

我正在处理的代码是一个搜索API端点,目前它工作得很好,但是返回了许多不必要的信息,甚至有重复的,所以我希望以最高效的方式缩小搜索输出。

平台上有一个我正试图用于实现的javadoc,链接:https://docs.atlassian.com/ConfluenceServer/javadoc/7.8.1/com/atlassian/confluence/search/v2/SearchManager.html

我想实现以下部分:

search(ISearch search, Set<String> requestedFields)
使用给定的条件进行搜索返回的searchResults中只填充了projection中请求的字段其他字段在searchResult中无效

但是我不知道如何正确生成Set<String> requestedFields

以下是我的尝试:

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()

如果我想使用另一种方法:

search(ISearch search)
使用给定的条件进行搜索

这段脚本工作得很好,但返回了很多我想要削减的信息。

除了我想要实现的方法之外,我也愿意接受任何其他类型的建议,以便我只能指定我想要输出的信息,从而节省输出大小和处理能力。

附注:
我已经尝试在他们的社区页面上非常详细地提出了相同的问题,但我想我可能需要一些开发人员的帮助,因为我刚刚从处理这个问题中学到了Java/Groovy的知识。

详细信息在这里:https://community.atlassian.com/t5/Confluence-questions/Need-to-optimize-the-output-of-a-custom-Search-API-Endpoint-in/qaq-p/1515177

更新:

以下是实现为API端点的工作代码:

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)

    // 查询可以是此处指定的任何类型:
    //https://developer.atlassian.com/server/confluence/searching-using-the-v2-search-api/
    def paramQueryString = (queryParams.get(new String("q")))[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()
}

当我调用API:

http://10.10.10.11:8080/rest/scriptrunner/latest/custom/testSearch?q=ArticleThatWillBeDeleted

我得到以下JSON响应:

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


<details>
<summary>英文:</summary>

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&lt;String&gt; 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(&quot;q&quot;))).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 = [ &#39;content&#39;, &#39;displaytitle&#39; ] 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.
</details>
# 答案1
**得分**: 1
```plaintext
`JsonBuilder`会渲染所有对象属性,而不仅限于你从服务器请求的字段。
我认为最简单的渲染请求字段的方法如下:
```groovy
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()

<details>
<summary>英文:</summary>
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()


</details>

huangapple
  • 本文由 发表于 2020年10月27日 21:34:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/64555639.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定