I have a list with a lot of documents (> 5000). I need to retrieve one particular document from it. I use this query:
#"<View>
<Query>
<Where>
<Contains>
<FieldRef Name=""FileLeafRef""/>
<Value Type=""Text"">MyDocumentName</Value>
</Contains>
</Where>
</Query>
<RowLimit>1</RowLimit>
</View>"
And I get a ServerException stating that this operation is forbidden because it goes over the list treshold.
But if I remove the <Query> tag:
#"<View>
<RowLimit>1</RowLimit>
</View>"
It works, and retrieve a single file, which sadly isn't the one I wanted, obviously.
So I'm puzzled here. Why is it behaving like that?
Try
Value Type=File
#"<View>
<Query>
<Where>
<Contains>
<FieldRef Name=""FileLeafRef""/>
<Value Type=""File"">MyDocumentName</Value>
</Contains>
</Where>
</Query>
<RowLimit>1</RowLimit>
</View>"
If you don't like building the query by hand then a couple of good tools are:
- U2U CAML Query Builder which gives you an UI for building the query.
- CAML.NET which is a library for safely building CAML queries
After eventually finding the right keywords to ask Google, I found this question.
The problem is that no field in my library is indexed, therefore when I try to add a query with conditions, all the library has to be read through in order to find the document(s) I need. So obviously, if there is too many documents, I get the exception.
<RowLimit> doesn't prevent this behavior, it only acts on the result of the query.
That being said, there is one field that is automatically indexed: ID. So we can change our query a bit to manually page the documents whose ID are between two values, and simply put the query in a while loop, incrementing the current ID paging each time, like so:
ListItemCollection items = null;
const int paging = 2000;
int currentPaging = paging;
while (items == null || items.Count == 0 || currentPaging < 100000)
{
var query = new CamlQuery
{
ViewXml = $"<View><Query><Where><And><Contains><FieldRef Name='FileLeafRef' /><Value Type='File'>MyDocumentName</Value></Contains><And><Leq><FieldRef Name='ID' /><Value Type='Counter'>{currentPaging}</Value></Leq><Gt><FieldRef Name='ID' /><Value Type='Counter'>{currentPaging - paging}</Value></Gt></And></And></Where></Query></View>"
};
items = list.GetItems(query);
ctx.Load(items);
ctx.ExecuteQuery();
currentPaging += paging;
}
Here's the formatted XML used in the query (query.ViewXml), for better readability:
<View>
<Query>
<Where>
<And>
<Contains>
<FieldRef Name='FileLeafRef' />
<Value Type='File'>MyDocumentName</Value>
</Contains>
<And>
<Leq>
<FieldRef Name='ID' />
<Value Type='Counter'>{currentPaging}</Value>
</Leq>
<Gt>
<FieldRef Name='ID' />
<Value Type='Counter'>{currentPaging - paging}</Value>
</Gt>
</And>
</And>
</Where>
</Query>
</View>
Related
I'm having trouble getting a user's info using SP Services from a sub-site. Using the code below I get the error There are multiple root elements.
var employeeName = $(this).attr('ows_Employee');
var employeeInfoViewFields = '<ViewFields>\
<FieldRef Name="Title" />\
<FieldRef Name="Department" />\
<FieldRef Name="JobTitle" />\
</ViewFields>\
<Where>\
<Eq>\
<FieldRef Name="Title" />\
<Value Type="Text">' + employeeName + '</Value>\
</Eq>\
</Where>';
$().SPServices({
operation: 'GetListItems',
async: false,
listName: 'UserInfo',
// listName: 'User Information List',
CAMLViewFields: employeeInfoViewFields,
completefunc: function(xData, Status) {
// Do stuff
}
});
I eventually replaced SPServices with a standard ajax call. This works perfectly.
$.ajax({
url: "/_api/lists/getbytitle('User Information List')/items?$filter=Title eq '" + employeeName + "'&$select=Department,JobTitle",
type: "GET",
async: false,
success: function (xml) {
department = $(xml).find('d\\:Department, Department').text();
jobTitle = $(xml).find('d\\:JobTitle, JobTitle').text();
},
error: function (a, b, c) {
alert(c);
}
});
In my first outing with csom and the document library, I encountered a difficult error when attempting to access a document library object property as file.File.Title using the following code.
The error is
Object reference not set to an instance of an object on server.
The code shown is what "works" for my exercise.
What step am I missing to be able to use file.File.Title instead of file.FieldValues["Title"].
The code snippet is a very primitive attempt to get a list of files in the default Document library folder. In a following iteration, I need to update the caml to retrieve a specific file.
var lib = ctx.Web.DefaultDocumentLibrary();
ctx.Load(lib);
ctx.ExecuteQuery();
var files = lib.GetItems(CreateAllFilesQuery());
ctx.Load(files);
ctx.Load(files, items => items.Include( item => item.File.Title ));
ctx.ExecuteQuery();
foreach(var file in files )
{
if(!(file.FieldValues["Title"] == null) )
{
string FileName = file.FieldValues["Title"].ToString();
if (FileName == DocumentName)
return true;
}
}
public static CamlQuery CreateAllFilesQuery()
{
var qry = new CamlQuery();
qry.ViewXml = #"<View Scope=\'FilesOnly\'>
<Query></Query>
<ViewFields>
<FieldRef Name='Title' />
<FieldRef Name='ContentType' />
<FieldRef Name='DocIcon' />
</ViewFields>
</View>";
return qry;
}
After further research, I discovered that my caml query was at fault. The syntax error was placing slashes around the Scope value. The corrected query allows me to reference file.File.Title as expected. The additional benefit is that I am only getting back files rather than files and folders.
qry.ViewXml = #"<View Scope='FilesOnly'>
<Query></Query>
<ViewFields>
<FieldRef Name='Title' />
<FieldRef Name='ContentType' />
<FieldRef Name='DocIcon' />
</ViewFields>
</View>";
I am new to caml query and have been struggling for this.
I need the last modified List Item. Only one Item.
That means it should be orderby 'modified' and rowlimit should be 1.
But only rowlimit part of my query is working. Not the orderby part.
This is my Query :
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><OrderBy><FieldRef Name=\'Modified\' Ascending="FALSE"/></OrderBy><RowLimit>1</RowLimit></Query></View>";')
I dont know where I am going wrong.
I even tried removing the Query tags in the above mentioned query.
The query is working, its getting only one record. orderby isnt working i believe.
This is in jQuery. I have written in a function and am calling that function in my Ready function.
Please help me.
Thanks.
In fact, in your example the query returns all results since query contains some errors.
The line:
camlQuery.set_viewXml('<View><Query><OrderBy><FieldRef Name=\'Modified\' Ascending="FALSE"/></OrderBy><RowLimit>1</RowLimit></Query></View>";')
should be replaced with:
camlQuery.set_viewXml('<View><Query><OrderBy><FieldRef Name="Modified" Ascending="FALSE"/></OrderBy></Query><RowLimit>1</RowLimit></View>');
Example: how to get the last item
function getLastItem(listTitle,Success,Error){
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getByTitle(listTitle);
var query = new SP.CamlQuery();
query.set_viewXml('<View><Query><OrderBy><FieldRef Name="Modified" Ascending="FALSE"/></OrderBy></Query><RowLimit>1</RowLimit></View>');
var items = list.getItems(query);
ctx.load(items);
ctx.executeQueryAsync(
function() {
if(items.get_count() > 0) {
var item = items.getItemAtIndex(0);
Success(item);
}
else
{
Success(null);
}
},
Error
);
}
getLastItem('Contacts',function(item){
console.log(item.get_item('Modified'));
},function(sender,args){
console.log(args.get_message());
});
Rowlimit must be put outside the query. Actually I'm using SharepointPlus (a JavaScript API to deal with Sharepoint) that creates automatically the query for me :-)
So the XML code sent to the server should look like this:
<GetListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/">
<listName>Your list</listName>
<viewName></viewName>
<query>
<Query>
<OrderBy>
<FieldRef Name="Status" Ascending="false"></FieldRef>
</OrderBy>
</Query>
</query>
<rowLimit>1</rowLimit>
<viewFields>
<ViewFields Properties="True">
<FieldRef Name="ID"></FieldRef>
</ViewFields>
</viewFields>
<queryOptions>
<QueryOptions>
<DateInUtc>False</DateInUtc>
<Paging ListItemCollectionPositionNext=""></Paging>
<IncludeAttachmentUrls>True</IncludeAttachmentUrls>
<IncludeMandatoryColumns>False</IncludeMandatoryColumns>
<ExpandUserField>False</ExpandUserField>
<ViewAttributes Scope="Recursive"></ViewAttributes>
</QueryOptions>
</queryOptions>
</GetListItems>
Here there is a full sample, Ascending='True' or Ascending='False' it's case sensitive
<View>
<Query>
<Where>
<Eq>
<FieldRef Name='OrderNum' />
<Value Type='Number'>90696</Value>
</Eq>
</Where>
<OrderBy>
<FieldRef Name='Modified' Ascending='False'/>
</OrderBy>
</Query>
<ViewFields>
<FieldRef Name='Title' />
<FieldRef Name='ID' />
</ViewFields>
<RowLimit>10</RowLimit>
Sharepoint tips
AymKdn is correct, the OrderBy clause should be outside of the Query clause in your Caml. Here's an example:
caml.set_viewXml('<View><OrderBy><FieldRef Name="Modified" Ascending="False"/></OrderBy><RowLimit>1</RowLimit></View>');
The Query clause is used for filtering (Where clause).
Is there any way to determine which files stored in my site collection that are the largest? I would like to identify them somehow and then delete them to free up some space.
Thanks for any ideas.
You could use a site data query to find all documents that are bigger than a certain size:
SPWeb web = SPContext.Current.Web;
SPSiteDataQuery query = new SPSiteDataQuery();
// restrict to document libraries
query.Lists = "<Lists ServerTemplate=\"101\" />";
query.ViewFields = "<FieldRef Name=\"File_x0020_Size\" />";
query.RowLimit = 20;
query.Query = "<Where><Geq><FieldRef Name=\"File_x0020_Size\" /><Value Type=\"Number\">{size in bytes}</Value></Geq></Where><OrderBy><FieldRef Name=\"File_x0020_Size\" Ascending=\"FALSE\" /></OrderBy>";
DataTable bigFiles = web.GetSiteData(query);
Query in readable:
<Where>
<Geq>
<FieldRef Name="File_x0020_Size" />
<Value Type="Number">{size in bytes}</Value>
</Geq>
</Where>
<OrderBy>
<FieldRef Name="File_x0020_Size" Ascending="FALSE" />
</OrderBy>
You have to use SharePoint API and iterate through all items in all lists in all sites in your site collection.
I am listing files from multiple folders using the below code:
SPSiteDataQuery q = new SPSiteDataQuery();
q.Lists = "<Lists BaseType='1'/>";
q.Query = "<Where> <Where>
<And><Neq><FieldRef Name='FSObjType'/><Value Type='Lookup'>1</Value></Neq>
<Contains><FieldRef Name='ProjectProperty.Title' /><Value Type='Text'>Site_Name_Value_From_TextBox</Value></Contains>
</And>
</Where></Where>";
q.Webs = "<Webs Scope='Recursive' />";
q.ViewFields = "<FieldRef Name='ID' /><FieldRef Name='LinkFilename' /><FieldRef Name='File_x0020_Type' /><FieldRef Name='Title' /><FieldRef Name='FileRef' /><ListProperty Name='Title' /><ProjectProperty Name='Title' />";
the code is working fine for listing items, but i need to filter items by Site name and Library Name, i used
<Contains><FieldRef Name='ProjectProperty.Title' /><Value Type='Text'>Site_NAme</Value></Contains>
but i get no results, when i use Title or FileRef or any other field filter is working fine, what should i use instead of "ProjectProperty" and "ListProperty "??
Try
<FieldRef Name="SiteName" /><Value Type='Text'>Site_Name</Value>
or
<FieldRef Name="ows_SiteName" /><Value Type='Text'>Site_Name</Value>