Поиск SharePoint 2013. Кастомная модель ранжирования

Небольшой пример использования кастомной модели ранжирования при создание решений, основанных на поиске SharePoint 2013.

В этом примере я покажу как создать поиск сотрудников, информация о которых хранится в списке. При этом будет создана простая кастомная модель ранжирования, обеспечивающая необходимое ранжирование.

Список

Список будет предельно простым, содержащим следующие поля:

  • FullName (Title) - полное имя сотрудника. Конкатенация значений полей Surname, Name;
  • Surname - фамилия сотрудника;
  • Name - имя сотрудника;
  • Position - должность;
  • Grade - уровень грейда (целое число от 0 до 5);

Тестовые данные:

Тестовые данные

Тестовые данные

Требования к ранжированию

Модель ранжирования должна следовать следующим приоритетам при расчете ранга:

  • выводить только сотрудников и никакой больше информации;
  • вхождения, найденные в поле Surname являются наиболее важными;
  • вхождения, найденные в поле Position являются вторыми по важности;
  • выводить выше остальных результатов, равных по количеству вхождений, сотрудников с наиболее высоким грейдом.

При поиске "bell" должны быть выведены сотрудники по фамилии Bell в слдующем порядке:

  • John Bell (grade=4)
  • Robert Bell (grade=4)
  • Jessica Bell (grade=1)
  • Larry Bell (grade=0)

Контрольная группа

Перед тем как приступить посмотрим, как работает стандартная модель ранжирования:

Результаты поиска при использовании стандартной модели ранжирования

Результаты поиска при использовании стандартной модели ранжирования

В результатах поиска кладовщик оказался выше своего руководства, что неприемлемо. Конечно со временем, когда пользователи будут переходить по ссылкам в результатах поиска (игнорируя кладовщика), модель ранжирования "обучится" и опустит вниз результаты поиска, переходы по которым не осуществляются. Но бизнес не ждет.

Модель ранжирования

Для реализации нам понадобится кастомная модель ранжирования. В нашем случае нет необходимости в нейронных сетях, будет достаточно простой линейной модели. Болванка для будущей модели:

<RankingModel2Stage name="Employee OneStage Linear Model" description="" id="{GUID}" xmlns="urn:Microsoft.Search.Ranking.Model.2NN">
<RankingModel2NN id="{GUID}" precalcEnabled="1">
  <HiddenNodes count="1">
    <Thresholds>
      <Threshold>0.000405176389917475</Threshold>
    </Thresholds>
    <Layer2Weights>
      <Weight>1</Weight>
    </Layer2Weights>
  </HiddenNodes>
  <RankingFeatures>
    <!-- Features -->
  </RankingFeatures>
</RankingModel2NN>  
</RankingModel2Stage>
XML

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

Стандартную фичу поиска вхождений в тексте (BM25Main) настраиваем на поиск только в полях Surname, Name, Position. Остальные поля нас не интересуют. Веса расставляем в соответствии с требованиями:

<RankingModel2Stage name="Employee Search Model" description="" id="{GUID}" xmlns="urn:Microsoft.Search.Ranking.Model.2NN">
<RankingModel2NN id="{GUID}" precalcEnabled="1">
  <HiddenNodes count="1">
    <Thresholds>
      <Threshold>0.000405176389917475</Threshold>
    </Thresholds>
    <Layer2Weights>
      <Weight>1</Weight>
    </Layer2Weights>
  </HiddenNodes>
  <RankingFeatures>
    <!-- Features -->
  </RankingFeatures>
</RankingModel2NN>  
</RankingModel2Stage>
XML

Требования выводить сначала сотрудников с более высоким грейдом нам поможет решить фича BucketedStatic. С помощью этой фичи можно изменять значения ранга в зависимости от значения свойства (что-то вроде оператора switch в C#):

<BucketedStatic name="EmployeeGrade" default="0" propertyName="EmployeeGrade">
  <Bucket name="0" value="0"><HiddenNodesAdds><Add>0</Add></HiddenNodesAdds></Bucket>
  <Bucket name="1" value="1"><HiddenNodesAdds><Add>1</Add></HiddenNodesAdds></Bucket>
  <Bucket name="2" value="2"><HiddenNodesAdds><Add>2</Add></HiddenNodesAdds></Bucket>
  <Bucket name="3" value="3"><HiddenNodesAdds><Add>3</Add></HiddenNodesAdds></Bucket>
  <Bucket name="4" value="4"><HiddenNodesAdds><Add>4</Add></HiddenNodesAdds></Bucket>
  <Bucket name="5" value="5"><HiddenNodesAdds><Add>5</Add></HiddenNodesAdds></Bucket>
</BucketedStatic>
XML

Новая модель готова. Для начала регистрируем её с помощью PowerShell скрипта из MSDN:

$myRankingModel = Get-Content .\myrm.xml 
$myRankingModel = [String]$myRankingModel
$ssa = Get-SPEnterpriseSearchServiceApplication
$owner = Get-SPenterpriseSearchOwner -Level ssa
$newrm = New-SPEnterpriseSearchRankingModel -SearchApplication $ssa -Owner $owner -RankingModelXML $myRankingModel</pre>

Модель в SharePoint зарегистрирована, можно приступать к настройке интерфейса.

Интерфейс пользователя

Поиск сотрудников будет реализован на отдельной странице. Добавляем на неё веб-части Search Box и Search Results. В свойствах веб-части Search Results открываем диалог для настройки запроса:

Для выбора модели ранжирования переходим на вкладку Sorting. Указываем поле сортировки (Rank) и модель ранжирования (Employee OneStage Linear Model):

Чтобы в результатах были только сотрудники можно добавить фильтр к запросу по типу содержимого. Для этого на вкладке Basics к тексту запроса (Query text) дописываем соответствующий фильтр:

ContentType=CustomEmployee

Все готово. Сохраняем изменения и проверяем результат.

Результат

При запросе "bell":

При запросе "john*":

Все работает отлично. Можно изменить веса полей Surname и Name, чтобы совпадение найденное в фамилии было более важным, чем совпадение в имени. В этом случае Ивановы будут выше Иванов в результатах поиска.

Простое решение, основанное на поиске SharePoint 2013, реализация которого укладывается в 2 часа. И никакого кодинга.

Напоследок модель ранжирования целиком:

<RankingModel2Stage name="Employee OneStage Linear Model"
        description=""
        id="825f23c2-f06f-49a0-8b9f-2d2d754bf459"
        xmlns="urn:Microsoft.Search.Ranking.Model.2NN">
  <RankingModel2NN id="f7c4bce6-7f55-4b79-97e8-cf79a795a12b"
        precalcEnabled="0">
    <HiddenNodes count="1">
      <Thresholds>
        <Threshold>0.000405176389917475</Threshold>
      </Thresholds>
      <Layer2Weights>
        <Weight>1</Weight>
      </Layer2Weights>
    </HiddenNodes>
    <RankingFeatures>
      <!-- Поиск в содержимом -->
      <BM25Main name="ContentRank" k1="1">
        <Layer1Weights>
          <Weight>0.267001870579081</Weight>
        </Layer1Weights>
        <!-- Свойства и их веса -->
        <Properties>
          <Property name="EmployeeSurname" w="10" b="0.5" propertyName="EmployeeSurname" />
          <Property name="EmployeeName" w="10" b="0.5" propertyName="EmployeeName" />
          <Property name="EmployeePosition" w="5" b="0.5" propertyName="EmployeePosition" />
        </Properties>
      </BM25Main>
      <!-- Ранг на основе значения поля -->
      <BucketedStatic name="EmployeeGrade" default="0" propertyName="EmployeeGrade">
        <Bucket name="0" value="0"><HiddenNodesAdds><Add>0</Add></HiddenNodesAdds></Bucket>
        <Bucket name="1" value="1"><HiddenNodesAdds><Add>1</Add></HiddenNodesAdds></Bucket>
        <Bucket name="2" value="2"><HiddenNodesAdds><Add>2</Add></HiddenNodesAdds></Bucket>
        <Bucket name="3" value="3"><HiddenNodesAdds><Add>3</Add></HiddenNodesAdds></Bucket>
        <Bucket name="4" value="4"><HiddenNodesAdds><Add>4</Add></HiddenNodesAdds></Bucket>
        <Bucket name="5" value="5"><HiddenNodesAdds><Add>5</Add></HiddenNodesAdds></Bucket>
      </BucketedStatic>
    </RankingFeatures>
  </RankingModel2NN>
</RankingModel2Stage>
XML

P.S.

Кому интересен поиск в SharePoint 2013 и его новые возможности - регистрируйтесь на трансляцию октябрьской встречи RuSUG 17 октября. Будет интересно.

Виталий Жуков

Виталий Жуков

Техлид, Архитектор, Разработчик, Microsoft MVP. Более 20 лет опыта в области системной интеграции и разработки программного обеспечения. Специализируюсь на проектировании и внедрении масштабируемых высокопроизводительных программных решений в различных отраслях.

Смотрите также