Язык шаблонов FreeMarker (FTL): разделить многозначные XML метаданные поля для группировки соответствующих значений - PullRequest
0 голосов
/ 10 июля 2020

У меня есть XML метаданные для каталога людей, где у человека может быть несколько названий, отделов и номеров телефонов, и т. Д. c. связаны с их именем.

Метаданные с несколькими значениями существуют в каждом поле (например, поля: заголовок, отдел, телефон).

Кажется, что 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 и т. д. c.) для каждого возможного множественного значения .

Сейчас в наборе всего 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] ----

Нужно ли мне указывать параметр paramValue! defaultValue? Или есть проблема с макросом?

Мы пытались использовать модель данных, в которой любое поле, которое может содержать несколько значений, имело отдельное поле, и мы могли затем сгруппировать записи с несколькими значениями в их соответствующие заказы.

Однако в нашем приложении есть функция сужения поиска, которую мы хотим использовать, называемые фасетами, которая, похоже, позволяет группировать / искать только в одном метаполе (для каждого фасета). В этом случае, поскольку мы изменили нашу модель, чтобы поместить несколько значений (например, title) в несколько полей (например, title1, title2, title3, и т. Д. c.), И поскольку кажется, что фасеты можно использовать только для группировки / search по одному метаполю, поэтому они не будут правильно соответствовать тем, у которых несколько заголовков; поэтому мы вернулись к перегрузке одного поля несколькими значениями.

Существует ли модель 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)

, но фасеты поиска не работают, поскольку они, похоже, выполняют поиск только по одному полю. Если бы мы хотели найти все с title1 = "Academi c Director", мы бы не увидели эти title2, title3 и т. Д. c. записи со значениями «Academi c Director». Возможно, стоит попросить поставщика улучшить фасеты, чтобы они могли выполнять поиск по нескольким полям.

Я думаю, вы могли бы предложить изменить функциональность того, как работает 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 или ограничения фасетов.

  • Пост-выборка данных: (hook_post_datafetch. groovy) Это выполняется сразу после заполнения объекта ответа на основе необработанного возврата XML, но до построения других элементов ответа. Это чаще всего используется для изменения базовых данных перед построением фасетной навигации.

    Пример использования: переименование или сортировка фасетных категорий навигации, изменение действующих URL-адресов

  • Постобработка : (hook_post_process. groovy) Используется для изменения окончательной модели данных перед отображением результатов поиска.

    Пример использования: чистые заголовки; загружать дополнительные пользовательские данные в модель данных для целей отображения.

Для работы с кэшированными документами доступен дополнительный скрипт ловушки.

  • pre-cache: ( hook_pre_cache. groovy) Используется для изменения кэшированного документа перед отображением.

Ответы [ 2 ]

2 голосов
/ 14 июля 2020

Предполагается, что tittle> 1 и title (n) = phone (n) = Department (n).

В модели данных все значения метаданных присваиваются одному полю, разделенному вертикальной чертой |. Вы можете разделить первые метаданные ie .: personTitle1, используя встроенный FTL ?split('|'), и перечислить все присвоенные заголовки как отдельные значения.

Теперь, когда у вас есть первое поле, разбитое на отдельные значения, вы можете разделить остальные поля используют тот же метод, и, кроме того, вы можете отобразить соответствующий индекс вызова значения нашего 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>

Надеюсь, это имеет смысл.

1 голос
/ 11 июля 2020

Намерение состояло в том, что шаблоны не будут выполнять такую ​​группировку, и это ответственность того, что создает модель данных. Так что, по крайней мере, с 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>
...