FreeMarker 模板语言 (FTL):拆分多值 XML 字段元数据以对各自的值进行分组



>我有一个人员目录的XML元数据,一个人可以在其中有多个头衔,部门和电话号码等与其姓名相关联。

具有多个值的元数据分别存在于一个字段中(例如字段:标题、部门、电话)。

FTL 似乎以正确的顺序返回记录的数据,即

name (returns: name)   
title (returns: title0, title1, title2)
department (returns: department0, department1, department2) 
and phone (returns: phone0, phone1, phone2)

我想像这样打印数据:

name
title0
department0
phone0
title1
department1
phone1
title2
department2
phone2

是否有一些 FTL 工具可以将具有多个值的字段放在数组或其他东西中,从而可以控制在何处打印(和分组)它们各自的值(如上所述)?

具有多个值(即职务、部门、电话)的字段将具有正常值,例如;phone0、phone1 和 phone2 -- 每个都是不同的电话号码。

我发现这个:

https://freemarker.apache.org/docs/ref_builtins.html

内置的序列似乎有些相关,

但我不确定它如何或是否会解决问题?

<小时 />

[更新1]

我尝试了@ddekany的宏和代码,只更改了

entry.<xxx> 

与我们的数据匹配的字段,在本例中,

<@listGroups peopleDirectory "name"; personName, personEntries>
Name: ${personName}
<#list personEntries as entry>
- Title: ${entry.personTitle1}
- Department: ${entry.personDepartment1}
- Phone: ${entry.personPhone1}
</#list>
</@listGroups>

其中每个

entry.<xxx> 

字段可能包含0-x值(竖线或逗号分隔),但在本例中,每个字段包含 4 个值

${entry.personTitle1}, ${entry.personDepartment1}, and ${entry.personPhone1}

1 后缀是我们的数据模型为每个可能的多值具有单独字段(即 personTitle1、personTitle2、personTitle3 等)时的残余。

目前该集合中只有 2 条记录。

但我收到以下错误:

When calling macro "listGroups", required parameter "items" (parameter #1) was specified, but had null/missing value. ---- Tip: If the parameter value expression on the caller side is known to be legally null/missing, you may want to specify a default value for it with the "!" operator, like paramValue!defaultValue. ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #macro listGroups items groupByName [in template "conf/people/default/simple.ftl" in macro "listGroups" at line 533, column 1] - Reached through: @listGroups peopleDirectory, "name"; ... [in template "conf/people/default/simple.ftl" at line 552, column 1] ~ Reached through: #nested [in template "web/templates/modernui/search_classic.ftl" in macro "Results" at line 244, column 13] ~ Reached through: @s.Results [in template "conf/people/default/simple.ftl" at line 523, column 9] ~ Reached through: #nested [in template "web/templates/modernui/search_classic.ftl" in macro "AfterSearchOnly" at line 94, column 9] ~ Reached through: @s.AfterSearchOnly [in template "conf/people/default/simple.ftl" at line 521, column 1] ~ Reached through: #nested [in template "web/templates/modernui/search_classic.ftl" in macro "AfterSearchOnly" at line 94, column 9] ~ Reached through: @s.AfterSearchOnly [in template "conf/people/default/simple.ftl" at line 90, column 3] ----

我是否需要指定一个参数值!默认值?还是宏有问题?

我们尝试使用数据模型,其中任何可能包含多个值的字段都有一个单独的字段,然后我们可以按各自的顺序对多值条目进行分组。

但是,我们的应用程序具有我们想要使用的搜索缩小功能,称为分面,它似乎只允许对单个元字段进行分组/搜索(针对每个分面)。在这种情况下,由于我们更改了模型以将多个值(例如标题)放入多个字段(即标题 1、标题 2、标题 3 等),并且由于似乎分面只能用于对一个元字段进行分组/搜索,因此它们无法正确匹配具有多个标题的值;这就是为什么我们回到用多个值重载单个字段的原因。

是否有一个 XML 模型可以允许我们应用的方面,但不需要不太理想的解决方案?

<小时 />

[更新2]

我尝试将 peopleDirectory 变量替换为:

<@listGroups s.result.metaData "name"; personName, personEntries>
Name: ${personName}
<#list personEntries as entry>
- Title: ${entry.personTitle1}
- Department: ${entry.personDepartment1}
- Phone: ${entry.personPhone1}
</#list>
</@listGroups>

但收到以下错误:

For "?sort_by" left-hand operand: Expected a sequence, but this has evaluated to an extended_hash (wrapper: f.t.SimpleHash): ==> items [in template "conf/people/default/simple.ftl" at line 539, column 10] ---- FTL stack trace ("~" means nesting-related): - Failed at: #list items?sort_by(groupByName) as item [in template "conf/people/default/simple.ftl" in macro "listGroups" at line 539, column 3] - Reached through: @listGroups s.result.metaData, "name"... [in template "conf/people/default/simple.ftl" at line 555, column 2] ~ Reached through: #nested [in template "web/templates/modernui/search_classic.ftl" in macro "Results" at line 244, column 13] ~ Reached through: @s.Results [in template "conf/people/default/simple.ftl" at line 523, column 9] ~ Reached through: #nested [in template "web/templates/modernui/search_classic.ftl" in macro "AfterSearchOnly" at line 94, column 9] ~ Reached through: @s.AfterSearchOnly [in template "conf/people/default/simple.ftl" at line 521, column 1] ~ Reached through: #nested [in template "web/templates/modernui/search_classic.ftl" in macro "AfterSearchOnly" at line 94, column 9] ~ Reached through: @s.AfterSearchOnly [in template "conf/people/default/simple.ftl" at line 90, column 3] ----

我也试过:

<@listGroups s.result.listMetadata "name"; personName, personEntries>
Name: ${personName}
<#list personEntries as entry>
- Title: ${entry.personTitle1}
- Department: ${entry.personDepartment1}
- Phone: ${entry.personPhone1}
</#list>
</@listGroups>

但收到:

For "?sort_by" left-hand operand: Expected a sequence, but this has evaluated to an extended_hash (com.google.common.collect.Multimaps$CustomListMultimap wrapped into com.appsearch.publicui.search.web.views.freemarker.AppsearchObjectWrapper$ListMultimapAdapter): ==> items [in template "conf/people/default/simple.ftl" at line 537, column 10] ---- FTL stack trace ("~" means nesting-related): - Failed at: #list items?sort_by(groupByName) as item [in template "conf/people/default/simple.ftl" in macro "listGroups" at line 537, column 3] - Reached through: @listGroups s.result.listMetadata, "n... [in template "conf/people/default/simple.ftl" at line 553, column 1] ~ Reached through: #nested [in template "web/templates/modernui/search_classic.ftl" in macro "Results" at line 244, column 13] ~ Reached through: @s.Results [in template "conf/people/default/simple.ftl" at line 529, column 9] ~ Reached through: #nested [in template "web/templates/modernui/search_classic.ftl" in macro "AfterSearchOnly" at line 94, column 9] ~ Reached through: @s.AfterSearchOnly [in template "conf/people/default/simple.ftl" at line 527, column 1] ~ Reached through: #nested [in template "web/templates/modernui/search_classic.ftl" in macro "AfterSearchOnly" at line 94, column 9] ~ Reached through: @s.AfterSearchOnly [in template "conf/people/default/simple.ftl" at line 90, column 3] ----

我们当前打印结果的方式如下所示:

<@s.AfterSearchOnly>
<#if response.resultPacket.resultsSummary.totalMatching != 0>
<@s.Results>
<#if s.result.metaData["personName"]??><strong>
${s.result.metaData["personName"]!} </strong> <br/>
<#else>
</#if>  
<#if s.result.metaData["personTitle1"]??>
${s.result.metaData["personTitle1"]!} <br/>
<#else>
</#if>   
<#if s.result.metaData["personDepartment1"]??>
${s.result.metaData["personDepartment1"]!} <br/>
<#else>
</#if>
<#if s.result.metaData["personPhone1"]??>
Tel: ${s.result.metaData["personPhone1"]!} <br/>
<#else>
</#if> 
</@s.Results>
</#if>   
</@s.AfterSearchOnly>

打印的结果如下:

Jane Doe
Chair|Professor|Co-Site Director, World Universtiy Florence|Academic Director
History Department|World Universtiy - Florence|Global Programs|Academic Directors
Tel: +1 000 111 2222|+1 333 444 5555|+1 666 777 8888|+1 999 101 1111
John Doe
Professor with Chair|Professor with Chair|Co-Site Director, World Universtiy|Academic Director
History Department|World Universtiy - Florence|Global Programs
Tel: +1 121 131 1414|+1 151 161 1717|+1 181 191 2020|+1 212 222 2323

<小时 />

[更新3]

我们控制数据模型、数据和数据的输入,并在某一时刻将数据结构化为每个值都有一个字段,即 title1、title2、title3,在这种情况下,可以根据需要打印结果,如下所示:

Jane Doe (personName)
Chair (personTitle1)
History Department (personDepartment1)
Tel: +1 000 111 2222 (personPhone1)
Professor (personTitle2)
World Universtiy - Florence (personDepartment2)
Tel: +1 333 444 5555 (personPhone2)
Co-Site Director, World Universtiy Florence (personTitle3)
Global Programs (personDepartment3)
Tel: +1 666 777 8888 (personPhone3)
Academic Director (personTitle4)
Academic Directors (personDepartment4)
Tel: +1 999 101 1111 (personPhone4)

John Doe (personName)
Professor with Chair (personTitle1)
History Department (personDepartment1)
Tel: +1 121 131 1414 (personPhone1)
Professor with Chair (personTitle2)
World Universtiy - Florence (personDepartment2)
+1 151 161 1717 (personPhone2)
Co-Site Director, World Universtiy (personTitle3)
Global Programs (personDepartment3)
+1 181 191 2020 (personPhone3)
Academic Director (personTitle4)
<!-- data missing -->  (personDepartment4) - don't know if this is a problem (could it jumble the results)
+1 212 222 2323 (personPhone4)

但是搜索方面不起作用,因为它们似乎只在单个字段上进行搜索。如果我们想找到所有标题 1 = "学术总监"的记录,我们不会看到那些带有"学术总监"值的 title2、title3 等记录。要求供应商增强方面,以便他们能够在多个字段上进行搜索可能是值得的。

我想您可能会建议更改 s.Results 工作方式的功能?我相信这是由应用程序提供的。我们需要向供应商请求增强功能。或者,您是否建议对数据的结构做一些不同的事情?如果是这样,与为每个值设置一个字段(如上所述)有什么不同吗?

另一种可能性...供应商提供钩子脚本:

https://docs.funnelback.com/develop/programming-options/hook-scripts.html

最常用的钩子

脚本是预处理钩子和后处理钩子。

  • 预处理:(hook_pre_process.groovy)这将在初始问题对象填充之后运行,但在任何输入处理发生之前运行。此时可以对查询进行操作以及添加或修改大多数问题属性。

    示例用法:修改用户的查询词;将邮政编码转换为地理坐标并添加地理空间约束

  • 额外搜索:(hook_extra_searches.groovy)这将在填充额外搜索问题之后但在允许修改额外搜索问题的任何额外搜索运行之前运行。

    示例用法:向额外搜索添加其他约束(例如范围)。

  • 预数据获取:(hook_pre_datafetch.groovy)这将在所有输入处理完成后运行,但在提交查询之前运行。此挂钩可用于操作由输入处理填充的任何其他数据模型元素。这最常用于修改分面导航。

    示例用途:更新元数据、gscope 或分面约束。

  • post-datafetch: (hook_post_datafetch.groovy) 这将在基于原始 XML 返回填充响应对象之后立即运行,但在构建其他响应元素之前运行。这最常用于在构建分面导航之前修改基础数据。

    示例用法:重命名或排序分面导航类别,修改实时网址

  • 后处理:(hook_post_process.groovy)这用于在呈现搜索结果之前修改最终数据模型。

    示例用法:干净的标题;将其他自定义数据加载到数据模型中以进行显示。

附加挂钩脚本可用于处理缓存的文档。

  • 预缓存:(hook_pre_cache.groovy)用于在显示之前修改缓存的文档。

假设标题>1和title(n)=phone(n)=Department(n)。

在数据模型中,所有元数据值都分配给一个用管道|分隔的文件。您可以使用 FTL 内置?split('|')拆分第一个元数据,即:personTitle1,并将所有分配的标题列为单个值。

现在您已将第一个字段分解为单独的值,您可以使用相同的方法划分其余字段,此外,您可以显示我们的 personTitle1 的相应值调用索引,以便字段匹配。看看下面的代码片段,以更好地理解我的意思:

<#list s.result.metaData["personTitle"]?split("|") as person>
${s.result.metaData["personTitle"]!?split('|')[person_index]!}
${s.result.metaData["personDepartment"]!?split('|')[person_index]!}
Tel: ${s.result.metaData["personPhone"]!?split('|')[person_index]!}
</#list>

希望这是有道理的。

目的是模板不会进行这样的分组,这是创建数据模型的任何内容的责任。因此,至少从 2.3.30 开始,没有内置功能来执行此操作(但我认为必须添加它,因为这会不断出现)。

现在,如果我们必须纯粹在模板中解决这个问题,你可以这样做(尽管在模板中解决这样的事情是一个相当反常的想法):

<#--
Splits a list to groups, and calls the nested content for each group.
Do NOT use this if the size of a group is above a few dozens, as it will become slow.
-->
<#macro listGroups items groupByName>
<#local group = []>
<#list items?sort_by(groupByName) as item>
<#local groupByValue = item[groupByName]!>
<#if item?is_first || groupByValue != lastGroupByValue>
<#if group?size != 0>
<#nested lastGroupByValue group>
</#if>
<#local group = []>
<#local lastGroupByValue = groupByValue>
</#if>
<#local group += [item]>
<#if item?is_last>
<#nested groupByValue group>
</#if>
</#list>
</#macro>

所以这只是一个宏,要实际列出分组的东西,请执行以下操作:

<@listGroups peopleDirectory "name"; personName, personEntries>
Name: ${personName}
<#list personEntries as entry>
- Title: ${entry.title}
- Department: ${entry.department}
- Phone: ${entry.phone}
</#list>
</@listGroups>

最新更新