Искать строки согласования участника через Search с откатом на REST
Один запрос Search на сайт находит кандидатов по согласующему и статусам, дальше читаются только списки, где они нашлись. При ошибке Search — прежний обход партиций по REST. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,15 @@ public interface ISharePointClient
|
||||
Task<IReadOnlyList<JsonElement>> GetItemsByListTitleAsync(
|
||||
string siteUrl, string listTitle, string odataQuery, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Запрос в SharePoint Search (postquery) от имени приложения — видит ВСЕ
|
||||
/// элементы, а не только доступные пользователю. Возвращает строки результата
|
||||
/// как «имя managed property → значение» (RowLimit — не больше 500).
|
||||
/// Индекс отстаёт от списков, поэтому найденное нужно перепроверять по REST.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<IReadOnlyDictionary<string, string?>>> SearchAsync(
|
||||
string siteUrl, string queryText, IReadOnlyList<string> selectProperties, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Обновить поля элемента списка, найденного по заголовку. fields — анонимный
|
||||
/// объект «внутреннее имя колонки → значение»; перечисленные колонки
|
||||
@@ -220,6 +229,57 @@ public sealed class SharePointClient(
|
||||
$"{siteUrl.TrimEnd('/')}/_api/web/lists/getByTitle('{Escape(listTitle)}')/items?{odataQuery}",
|
||||
cancellationToken);
|
||||
|
||||
public async Task<IReadOnlyList<IReadOnlyDictionary<string, string?>>> SearchAsync(
|
||||
string siteUrl, string queryText, IReadOnlyList<string> selectProperties, CancellationToken cancellationToken)
|
||||
{
|
||||
var body = JsonSerializer.Serialize(new
|
||||
{
|
||||
request = new
|
||||
{
|
||||
Querytext = queryText,
|
||||
RowLimit = 500,
|
||||
SelectProperties = selectProperties,
|
||||
TrimDuplicates = false
|
||||
}
|
||||
});
|
||||
|
||||
var json = await SendAsync(
|
||||
HttpMethod.Post, siteUrl, $"{siteUrl.TrimEnd('/')}/_api/search/postquery", body,
|
||||
allowNotFound: false, cancellationToken);
|
||||
|
||||
var result = new List<IReadOnlyDictionary<string, string?>>();
|
||||
if (json is null
|
||||
|| !json.Value.TryGetProperty("PrimaryQueryResult", out var primary)
|
||||
|| !primary.TryGetProperty("RelevantResults", out var relevant)
|
||||
|| !relevant.TryGetProperty("Table", out var table)
|
||||
|| !table.TryGetProperty("Rows", out var rows))
|
||||
return result;
|
||||
|
||||
// nometadata отдаёт Rows массивом, verbose — обёрнутым в results; терпим оба.
|
||||
if (rows.ValueKind == JsonValueKind.Object && rows.TryGetProperty("results", out var wrapped))
|
||||
rows = wrapped;
|
||||
if (rows.ValueKind != JsonValueKind.Array) return result;
|
||||
|
||||
foreach (var row in rows.EnumerateArray())
|
||||
{
|
||||
if (!row.TryGetProperty("Cells", out var cells)) continue;
|
||||
if (cells.ValueKind == JsonValueKind.Object && cells.TryGetProperty("results", out var wrappedCells))
|
||||
cells = wrappedCells;
|
||||
|
||||
var dict = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var cell in cells.EnumerateArray())
|
||||
{
|
||||
var key = cell.TryGetProperty("Key", out var k) ? k.GetString() : null;
|
||||
if (key is null) continue;
|
||||
dict[key] = cell.TryGetProperty("Value", out var v) && v.ValueKind == JsonValueKind.String ? v.GetString() : null;
|
||||
}
|
||||
|
||||
result.Add(dict);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task UpdateItemByListTitleAsync(
|
||||
string siteUrl, string listTitle, int itemId, object fields, CancellationToken cancellationToken) =>
|
||||
MergeAsync(
|
||||
|
||||
Reference in New Issue
Block a user