Как обойти ограничение на 5000 строк в SharePoint Online с помощью Caml?

Используя Caml, я пытаюсь вернуть все элементы с сайта SharePoint Online, которые были изменены за последний год. Запрос приводит к ошибке, которая гласит: «Попытка выполнить операцию запрещена, поскольку она превышает пороговое значение представления списка, установленное администратором». Этот порог составляет 5000 элементов в SharePoint Online, и, насколько я могу судить после обсуждения с администратором SharePoint, его нельзя изменить.

Эта ошибка довольно хорошо документирована, но ни одно из рассмотренных мной решений не решило мой вопрос.

Я уже справлялся с этой проблемой, используя тег <RowLimit></RowLimit> в моем запросе Caml и установив переменную $position, которая отслеживает, где я нахожусь после каждого фрагмента из 5000 элементов. См. Ниже запрос, который работает для списков, содержащих более 5000 элементов:

$query = @"
<View Scope="RecursiveAll">
    <Query>
        <OrderBy><FieldRef Name="Created" Ascending="false"/></OrderBy>
    </Query>
    <RowLimit Paged="TRUE">5000</RowLimit>
</View>
"@

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

$oneYearAgo = (Get-Date).AddDays(-365)
$oneYearAgoString = $oneYearAgo.ToString("yyyy-MM-ddTHH:mm:ssZ")

$query = @"
<View Scope='RecursiveAll'>
    <Query>
        <OrderBy><FieldRef Name='Modified' Ascending='false'/></OrderBy>
        <Where>
            <Geq>
                <FieldRef Name="Modified"/>
                <Value Type="DateTime">
                    $oneYearAgoString
                </Value>
            </Geq>
        </Where>
    </Query>
    <RowLimit Paged="TRUE">5000</RowLimit>
</View>
"@

Этот возвращает ошибку «Предпринятая операция запрещена, потому что она превышает пороговое значение представления списка, установленное администратором», хотя логика за пределами двух запросов Caml почти идентична.

Что-то не так с моим запросом Caml?


person rtoken    schedule 22.07.2019    source источник
comment
Индексирован ли измененный столбец? Вы можете найти проиндексированные столбцы списка в нижней части раздела столбцов на странице настроек списка.   -  person Thriggle    schedule 23.07.2019
comment
Я встретился с одним из наших администраторов SharePoint, и этот столбец не был проиндексирован. Однако это происходит сейчас, и, к сожалению, мы все еще наблюдаем ту же ошибку.   -  person rtoken    schedule 26.07.2019


Ответы (2)


Вариант 1. Создайте отфильтрованное представление списка, чтобы получить количество элементов меньше 5000. Затем получите элементы в этом конкретном представлении.

Вариант 2. Получить все элементы коллекции элементов без запроса. Затем используйте Linq для запроса коллекции элементов. Вы можете обратиться сюда для использования Linq в PowerShell. Что-то вроде этого:

$FilteredItems = $ItemCollection.Where({($_.Modified-eq $oneYearAgoString)
person bahadrdsr    schedule 23.07.2019
comment
Спасибо за Ваш ответ! Я не думаю, что первый вариант будет для нас жизнеспособным, но я рассмотрю второй вариант немного подробнее. Раньше я не использовал Linq, поэтому ценю совет! - person rtoken; 26.07.2019

Я хотел бы предложить вам использовать поле запроса "ListItemCollectionPosition". Таким образом, вы можете создать рекурсивный метод и перебирать весь список (даже если он содержит более 5000 элементов).

public static List<SharePointNode> GetDocumentsByCaml(ClientContext localCTX, List list, SharePointNode spCurrentNode, ListItemCollectionPosition position = null)
    {
        List<SharePointNode> lstDocuments = new List<SharePointNode>();
        try
        {
            CamlQuery camlQuery = new CamlQuery();
            camlQuery.ViewXml = @"<View Scope='Recursive'><Query>
                                   <Eq><FieldRef Name='FSObjType' /><Value Type='Integer'>0</Value></Eq></Query><RowLimit>1000</RowLimit></View>";

            camlQuery.ListItemCollectionPosition = position;
            camlQuery.FolderServerRelativeUrl = spCurrentNode.URL;

            ListItemCollection listItems = list.GetItems(camlQuery);
            localCTX.Load(listItems);
            localCTX.Load(listItems, a => a.Include(item => item.File, 
                                                    item => item.File.CheckedOutByUser, 
                                                    item => item.File.CheckOutType));
            localCTX.ExecuteQuery();

            foreach (ListItem itemOfInterest in listItems)
            {
                SharePointNode spNodeDoc = new SharePointNode();
                // check here every listItem and compare the last modified date
                // if it was modified in the past year -> put it into your result list.

                lstDocuments.Add(spNodeDoc);
            }

            if (listItems.ListItemCollectionPosition != null)
            {
                lstDocuments.AddRange(GetDocumentsByCaml(localCTX, list, spCurrentNode, listItems.ListItemCollectionPosition));
            }
        }
        catch (Exception ex)
        {
            // log the message
        }

        return lstDocuments;
    }
person MaxEMunich    schedule 26.07.2019
comment
Дополнительная информация: SharePointNode - мой собственный класс - person MaxEMunich; 26.07.2019
comment
Спасибо за ответ, Макс. К сожалению, это то, что я уже делаю. Этот процесс хорошо работает для первого запроса Caml в моем вопросе выше, но не работает для моего второго запроса. - person rtoken; 26.07.2019