- Contrary to other data interaction mechanisms like ODBC, BDC, etc., web services are self-describing. All you need to know about methods, inputs and outputs is in the WSDL file (check http://Sp_Server/_vti_bin/search.asmx?wsdl). No need to contact the developer or database administrator for a password or parameters type.
- Armed with Ajax (remember, Ajax is just JavaScript, nothing to brag about or to fear), and, say, a Content Editor Web Part (or just a basic HTML page), you don’t even need to have access rights to any server to get this functionality on your site. Everything is done on the client side.
Generate the query
In theory, you’d need to study a little bit how to build a QueryPacket, or learn about the SQL Search language. In reality, you don’t have to study anything at all, thanks to query generators like SharePoint Search Service Tool or Search Coder. I often use both, since they have complementary functionalities. So it shouldn’t take you too long to come up with the following query. Notice that “AND (CONTAINS (ContentType,'"post"'))” is the trick to filter by content type.
<QueryPacket xmlns="urn:Microsoft.Search.Query" Revision="1000"> <Query domain="QDomain"> <SupportedFormats> <Format>urn:Microsoft.Search.Response.Document.Document</Format> </SupportedFormats> <Context> <QueryText language="en-US" type="MSSQLFT"> <![CDATA[ SELECT Title, Rank, Size, Description, Write, Path, PersonalSpace, Author, Title, Path, Created, CreatedBy, PictureURL, Account, EmployeeID FROM portal..scope() WHERE FREETEXT(DefaultProperties, 'My search terms') AND ( ("SCOPE" = 'All Sites') ) AND (CONTAINS (ContentType,'"post"')) ORDER BY "Rank" Desc, "Created" Desc" ]]> </QueryText> </Context> <Range><StartAt>1</StartAt><Count>10</Count></Range> <EnableStemming>true</EnableStemming> <TrimDuplicates>true</TrimDuplicates> <IgnoreAllNoiseQuery>true</IgnoreAllNoiseQuery> <ImplicitAndBehavior>true</ImplicitAndBehavior> <IncludeRelevanceResults>true</IncludeRelevanceResults> <IncludeSpecialTermResults>true</IncludeSpecialTermResults> <IncludeHighConfidenceResults>true</IncludeHighConfidenceResults> </Query> </QueryPacket>
Call the Web Service
In a previous blog, I’ve showed how to use Darren’s JavaScript library to interact with SharePoint web services. I could have done the same here, especially since he developed a library specifically for search, but I chose to use JQuery instead. Why? No reason, I just like to explore different technologies ;)
Final Result
So now all you have to do is copy the code below, change the server name and paste the code into a Content Editor Web Part (or any other HTML page). Notice that while this example is about filtering by content type, the same technique can be used to filter by scope and any metadata you wish. In fact, you’ll quickly realize that this search web service is way more powerful than SharePoint Content Query Web Part, as search doesn’t care about site collection boundaries and has access to a wide variety of filters.
<script language="javascript"> // _spBodyOnLoadFunctionNames.push is a SharePoint OOTB function // that ensures the function is called only after the DOM has been loaded _spBodyOnLoadFunctionNames.push("DisplayBlogSearchResults"); // Change these parameters as needed var maxResultsToDisplayBlogs = 5; var webSite = “http://url.of.site/”; function DisplayBlogSearchResults() { // the search terms is passed in the query string (e.g., blogsearch?k=tax+reform) var query = unescape(querySt("k")); var queryXML = "<QueryPacket xmlns=\"urn:Microsoft.Search.Query\" Revision=\"1000\">"+ " <Query domain=\"QDomain\">"+ " <SupportedFormats><Format>urn:Microsoft.Search.Response.Document.Document"+ " </Format></SupportedFormats>"+ " <Context>"+ " <QueryText language=\"en-US\" type=\"MSSQLFT\"><![CDATA[ "+ "SELECT Title, Rank, Size, Description, Write, Path, PersonalSpace, "+ "Author, Title, Path, Created, CreatedBy, "+ " PictureURL, Account, EmployeeID FROM "+ " portal..scope() " + //" WHERE CONTAINS ('\"" + query + "\"') " + " WHERE FREETEXT(DefaultProperties, '" + query + "') " + " AND ( (\"SCOPE\" = 'All Sites') ) AND (CONTAINS (ContentType,'\"post\"'))" + " ORDER BY \"Rank\" Desc, \"Created\" Desc" + " ]]>" + " </QueryText>" + " </Context>"+ " <Range><StartAt>1</StartAt><Count>" + maxResultsToDisplayBlogs + "</Count></Range>"+ " <EnableStemming>true</EnableStemming>"+ "<TrimDuplicates>true</TrimDuplicates>"+ "<IgnoreAllNoiseQuery>true</IgnoreAllNoiseQuery>"+ "<ImplicitAndBehavior>true</ImplicitAndBehavior>"+ "<IncludeRelevanceResults>true</IncludeRelevanceResults>"+ "<IncludeSpecialTermResults>true</IncludeSpecialTermResults>"+ "<IncludeHighConfidenceResults>true</IncludeHighConfidenceResults>"+ "</Query></QueryPacket>"; var soapEnv = "<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " + "xmlns:xsd='http://www.w3.org/2001/XMLSchema' " + "xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"+ " <soap:Body>"+ "<QueryEx xmlns='http://microsoft.com/webservices/OfficeServer/QueryService'>"+ " <queryXml>" + escapeHTML(queryXML) + "</queryXml>"+ " </QueryEx>"+ " </soap:Body>"+ "</soap:Envelope>"; $.ajax({ url: webSite +"/_vti_bin/search.asmx", type: "POST", dataType: "xml", data: soapEnv, complete: processResult, contentType: "text/xml; charset=\"utf-8\"" }); } // processResult is called async. when the web service returns something function processResult(xData, status) { var TotalResults = 0; var xmlDoc = xData.responseXML; var docs = xmlDoc.selectNodes("//RelevantResults"); // Total Results returned // (should be equal or less than <Count> parameter of search query) TotalResults = docs.length; // Total available results (while the query only returns // the first <Count> results, there might be more available) var TotalAvailable = 0; var docTotalAvailable = xmlDoc.selectNodes("//xs:element[@name='RelevantResults']"); if (docTotalAvailable.length != 0) { TotalAvailable = docTotalAvailable[0].getAttribute("msprop:TotalRows"); } var strDisplay=""; if (TotalResults >0) { strDisplay = "<table width='100%' style='BORDER: #8ebbf5 1px solid'><tr>"; strDisplay += "<td><img src='http://atgdev-intranet.imf.org/_layouts/images/buddychat.jpg' "; strDisplay += "style='float:left;vertical-align:middle'/>"; strDisplay += "<span style='font-size:12px'>"; strDisplay += "<strong>You may also be interested by these " + TotalResults strDisplay += " blogs:</strong></span></td>"; strDisplay += "</tr>"; } for(var i = 0; i < TotalResults ; i++){ var title = docs[i].selectSingleNode("TITLE") != null ? docs[i].selectSingleNode("TITLE").text : "TITLE not found"; var path = docs[i].selectSingleNode("PATH") != null ? docs[i].selectSingleNode("PATH").text : "PATH not found"; var creationDate = docs[i].selectSingleNode("CREATED") != null ? docs[i].selectSingleNode("CREATED").text : "CREATED not found"; var author = docs[i].selectSingleNode("AUTHOR") != null ? docs[i].selectSingleNode("AUTHOR").text : "PATH not found"; strDisplay += "<tr><td>"; strDisplay += "<img src='/_layouts/images/bullet.gif' style='vertical-align:middle' /> "; strDisplay += "<a href='" + path + "'>" + title + "</a>"; strDisplay += "<div style='color:#dbdbdb;text-align:right'>written on " + formatDateString(creationDate) strDisplay += " by <span style='color:#545454'>" + author + "</span></div>"; strDisplay += "</td></tr>"; } // Verify if we have displayed the total available or not if (TotalAvailable > TotalResults) { strDisplay += "<tr style='text-align:right'><td><a href=''><br />See all " + TotalAvailable + " results...</a></td></tr>" } if (TotalResults >0) { strDisplay += "</table>"; } // Display result is specific DIV (id=idBlogSearchResults). Could be located anywhere in your page. $("#idBlogSearchResults").html(strDisplay); } function querySt(ji) { hu = window.location.search.substring(1); gy = hu.split("&"); for (i=0;i<gy.length;i++) { ft = gy[i].split("="); if (ft[0] == ji) return ft[1]; } } function escapeHTML (str) { return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); } function unescapeHTML (str) { return str.replace(/</g,'<').replace(/>/g,'>'); } function formatDateString(strDate) { var yearStr = strDate.substr(0, 4); var monthStr = strDate.substr(5, 2); var dayStr = strDate.substr(8, 2); return monthStr + "/" + dayStr + "/" + yearStr; } </script>
Have fun!
You have to express more your opinion to attract more readers, because just a video or plain text without any personal approach is not that valuable. But it is just form my point of view
ReplyDeleteNice fill someone in on and this mail helped me alot in my college assignement. Say thank you you seeking your information.
ReplyDeleteGood fill someone in on and this post helped me alot in my college assignement. Thank you on your information.
ReplyDeleteGood article made my day. Thanks
ReplyDelete