While I am searching the keyword in sitecore using sitecore.seach it returns the item name(where it is located) for the search keyword. Is there any option to get the field name along the item?
I use the following code:
using (IndexSearchContext context = searchIndex.CreateSearchContext())
{
SearchHits hits = context.Search(searchString, new SearchContext(SiteRoot));
var results = hits.FetchResults(0, 100);
foreach (SearchResult result in results)
{
try
{
Item item = result.GetObject<Item>();
if (item != null)
{
results.AddResultToCategory(result, categoryName);
}
}
...
}
}
If I understand your goal correctly, you want to find out the exact fields (field names) where the search string was found, right?
If that's the case, then you should get a collection of item fields (item.Fields), and iterate through it checking for the search string in the field value.
As far as I know, the Sitecore Search shell application works the same way when displaying results: the item is taken form the SearchResult, and the field collection is just iterated looking for the hit using simple text.IndexOf() comparison.
And I'm not aware about a different way to accomplish this.
Related
Using Sitecore 8.2 with MVC.
I'm trying to implement the search functionality in a MVC view. (with a textbox and submit button)
There is a folder in the content tree called Books which has a list of items. Each item will have these fields - Title, Author, Price
When user searches for a term, it will be checked for a match with any of the 3 fields of the item and return the results.
This method is not working as it returns null Item.
public PartialViewResult GetSearchBooks(string txtSearch)
{
string index = string.Format("sitecore_{0}_index", Sitecore.Context.Database.Name);
List<SearchResultItem> query;
List<Item> matches = new List<Item>();
using (var context = ContentSearchManager.GetIndex(index).CreateSearchContext())
{
query = context.GetQueryable<SearchResultItem>()
.Where(p => p.Path.StartsWith("/sitecore/content/Book")).ToList();
}
foreach(SearchResultItem sritem in query)
{
Item item = sritem.GetItem(); //item is null here
if(item.Fields["Title"].Value.Contains(txtSearch) ||
item.Fields["Title"].Value.Contains(txtSearch) ||
item.Fields["Title"].Value.Contains(txtSearch))
matches.Add(item);
}
return(matches);
}
Is it the right approach. If not please suggest one.
Paths
Avoid querying the path like this:
context.GetQueryable<SearchResultItem>()
.Where(p => p.Path.StartsWith("/sitecore/content/Book"));
Instead use
context.GetQueryable<SearchResultItem>()
.Where(p => p.Paths.Contains(idOfBookFolderItem));
For more info on why, see http://blog.paulgeorge.co.uk/2015/05/29/sitecore-contentsearch-api-filtering-search-on-folder-path/
Approach
You need to hand the entire query to the search api in one go.
List<SearchResultItem> matches;
using (var context = ContentSearchManager.GetIndex(indexName).CreateSearchContext())
{
var predicate = PredicateBuilder.True<SearchResultItem>();
// must have this (.and)
predicate = predicate.And(p => p.Paths.Contains(bookFolderItem.ID));
// must have this (.and)
predicate = predicate.And(p => p.Name == searchTerm);
matches = context.GetQueryable<SearchResultItem>().Where(predicate).ToList();
}
This returns SearchResultItems not Items. If you need the item, just call GetItem.
Matches[i].GetItem()
Null items
This may indicate that your index is out of sync with the database. Try re-indexing from control panel, or in the case of the web database, REpublish the expected content.
Searching template fields
This just searches against the item name. You're limited to being able to specify the generic fields in SearchResultItem class. If you want to search specific fields on items, you can inherit from SearchResultItem and add those fields.
public class BookSearchResultItem : SearchResultItem
{
[IndexField("Book Title")]
public string BookTitle { get; set; }
[IndexField("Book Author")]
public string BookAuthor { get; set; }
}
You can then pass this into the query and search on those fields
List<BookSearchResultItem> matches;
using (var context = ContentSearchManager.GetIndex(indexName).CreateSearchContext())
{
var predicate = PredicateBuilder.True<BookSearchResultItem>();
// must have this (.and)
predicate = predicate.And(p => p.Paths.Contains(bookFolderItem.ID));
// must have this (.and)
predicate = predicate.And(
PredicateBuilder.False<BookSearchResultItem>() // in any of these fields
.Or(p => p.BookTitle == searchTerm)
.Or(p => p.BookAuthor == searchTerm)
.Or(p => p.Name == searchTerm));
matches = context.GetQueryable<BookSearchResultItem>().Where(predicate).ToList();
}
Searching all 'content'
If you find that having to specify the explicit fields is an unwanted hassle or you are performing searches across different templates with different fields, you can instead use the special computed 'content' field which combines all the text data from an item into one indexed field. So instead of the original query which did this
predicate = predicate.And(p => p.Name == searchTerm);
You can instead do just use
predicate = predicate.And(p => p.Content == searchTerm);
Which will find results where the searchterm exists in any field on the item.
First, did you check "query" contains any result?
I would suggest performing the following search query:
query = context.GetQueryable<SearchResultItem>()
.Where(p => p.TemplateId == yourBookItemTemplateID &&
(p.Fields["Title"].Value.Contains(txtSearch) ||
p.Fields["Author"].Value.Contains(txtSearch) ||
p.Fields["Price"].Value.Contains(txtSearch));
return query.Select(x => x.GetItem());
i would not suggest this approach to use. Let me explain why, or what you can do better.
First create your own Sitecore index and do not simply use the default master or web index. If you do that, you can safe the following line of code .Where(p => p.Path.StartsWith("/sitecore/content/Book")).ToList();, cause in a custom index you can simply restrict, what exactly is crawled.
Second you should never access the Sitecore item out of the search results. Reasons for that is the performance. Item item = sritem.GetItem(); You use a search, because its a performant way to access a huge amount of data. When you now access for every result the Sitecore item from the database, you lose your benefit of using a search.
You should simply use the Result Type, in your case the basic SearchResultItem. At the End of your filtering you should call something like var results = query.GetResults(); instead of accessing the items directly.
Here I found a simple example of a sitecore search, with custom index and without accessing the items directly, maybe this helps you.
http://www.mattburkedev.com/sitecore-7-contentsearch-tips/
Now to your problem.
Did you debug the search and looked into the rest of the fields of sritem? Are they all filled? If i remember correctly there is a property which stores the itemId, to retrieve the item with GetItem(). Maybe you could give us the values of the property while trying to retrieve the item.
Sometimes when the index is out of date, the returned search items may no longer exist in your content tree, So rebuild the index and try the search again,
Couple of enhancements that you can apply to your search:
As mentioned in Christian answer you can create index for just your Books tree, which means to set the root of the index to the Books root item.Web index usually used for full site content search.
Instead of getting all books items then go through all items; you can use predicates instead; even after you create the new index use the predicates to get the desired items only.
Also if your site is multilingual add a predicate to filter the required language else you will get multiple versions of the same item.
I have a IQueryable<T> object as search results object.
I apply the filtering and sorting on this search object.
Before I call the GetResults(), I want to order the results based on one of the field's (Fieldname - Priority) value. So for all the items in the IQueryable<T> object, I want to order them desc by Priority field, so all the items which has a value for that field stay at the top and the rest are at the bottom.
I have the fieldmap entry for Priority field.
search.OrderByDescending(i => !string.IsNullOrEmpty(i.GetItem().GetFieldValue("Priority")))
The above command doesn't work. Apparently, I can't use Sitecore extension methods with IQueryable?
If I convert search.ToList(). Do the ordering and then convert it back to AsQueryable(), I get the following error:
There is no method 'GetResults' on type 'Sitecore.ContentSearch.Linq.QueryableExtensions'
that matches the specified arguments
Is there a neat and quick way to get around this?
Cheers
I think you just need to add your field to your SearchResultItem and mark it as an int. I am making the assumption that the field is an int. Make a custom class that inherits SearchResultItem.
public class CustomSearchResultItem : SearchResultItem
{
[IndexField("Priority")]
public int Priority { get; set; }
}
Then use it in your search. Finally order by it.
using (var context = ContentSearchManager.GetIndex("sitecore_master_index").CreateSearchContext())
{
var results = context.GetQueryable<CustomSearchResultItem>().Where(prod => prod.Content.Contains("search box text").OrderByDescending(t => t.Priority);
}
Some data found here.
http://www.sitecore.net/learn/blogs/technical-blogs/sitecore-7-development-team/posts/2013/10/sorting-and-ordering-results.aspx
You can order search results using multiple fields by using the OrderByDescending combined with ThenByDescending. So you would need to order by Priority and then by [Name|Date|Whatever].
I want to order them desc by Priority field, so all the items which has a value for that field stay at the top and the rest are at the bottom.
I sort them first on the criteria chosen by the user - like Name, Date created etc. Once I get the results back, I need to order them by priority field
You are conflicting yourself in the questions and comments. If you want the results with priority first and then by user selected results then the following will work:
query = dataQuery.OrderByDescending(i => i.Title).ThenByDescending(i => i["Priority"]);
var results = query.GetResults().Hits.Select(h => h.Document);
There was a bug in earlier version of Sitecore which meant that the ThenBy clause will be added before the OrderBy clause hence it is added in reverse above. You may want to check if this is fixed in the current version. If so simply change your query to:
query = dataQuery.OrderByDescending(i => i["Priority"]).ThenByDescending(i => i.Title);
You don't have to add the field to your SearchResultItem if you just want to order by it, only if you need the actual value of that field returned to as well.
If you need to order by a custom user supplied value then you can pass in i => i["whatever-field-the-user-has-selected"] instead of i.Title.
You can find more info in this blog post.
I need to present a content editor interface that allows editors to select specific pages in order to generate a link list for website visitors.
It seems treelist/treelistEX is providing the expected interface and I have combined that with a source path to lock the editors to a start destination rather than the entire sitecore tree. Opted for treelist EX as this appears to be the most efficient way as it doesnt render the tree in full each time unless its called upon.
In terms of the output however I'm getting a pipe separated list of GUIDs- is this something I need to iterate through manually using linkmanager or some such to obtain the items title and its sitecore link? Or is there an existing process that manages such a multi-list and breaks it up into its components.
If anyone can provide an example of that code and how to draw out the title and URL that would be great.
Thanks
There is no built-in solution for getting title and url from the items selected in Treelist.
You can treat your Treelist field as a Multilist field (they both store just list of pipe separated IDs in the background) and use GetItems() method:
Sitecore.Data.Fields.MultilistField treelistField = Sitecore.Context.Item.Fields["myTreelistFieldName"];
Item [] selectedItems = treelistField.GetItems();
foreach (Item item in selectedItems)
{
string itemName = item.Name;
string displayName = item.DisplayName; // fallback to Name if not set
string title = item["Title"]; // assuming there is a field called Title
string url = LinkManager.GetItemUrl(item);
}
Is there any way to sort the following items according to the days of week.
In C# I can do something like this:
string [] initialArray = {"Friday", "Monday", ... } ;
string [] sortedArray = initialArray.OrderBy(s => Enum.Parse(typeof(DayOfWeek), s)).ToArray() ;
But I don't know how can I achieve this kind of functionality with Sitecore.
If what you really care is to display the days sorted on the front-end regardless of how they are organised in the Content Editor then simply sort them in code before you display them, e.g.
using System.Linq;
var openingHours = Sitecore.Context.Item.Children
.OrderBy(s => Enum.Parse(typeof(DayOfWeek), s.DisplayName));
If you want to sort them in the Content Editor then you need to create a custom sorter. Sitecore Climber has provided links, but for this specific example you can use:
using Sitecore.Data.Comparers;
using Sitecore.Data.Items;
public class DayOfWeekComparer : Comparer
{
protected override int DoCompare(Item item1, Item item2)
{
var x = (int)Enum.Parse(typeof(DayOfWeek), item1.DisplayName);
var y = (int)Enum.Parse(typeof(DayOfWeek), item2.DisplayName);
return x.CompareTo(y);
}
}
Then in the core database create an item of item type /sitecore/templates/System/Child Sorting under /sitecore/system/Settings/Subitems Sorting and set the type to your class.
You should set the Subitem Sorting on the Standard values of a template. In this instance it looks like you have a simple Folder template, so you would need to create a more specific template for your Opening Hours folder. Even so, the user can still decide to re-order the items OR change the default sort for that folder. The only guaranteed way to force the output is by sorting before you render, i.e. the first bit of code.
please check next articles:
http://firebreaksice.com/how-to-sort-sitecore-items-in-the-content-editor/
http://sitecore.alexiasoft.nl/2009/08/04/sorting-sitecore-items/
http://sitecoreblog.blogspot.in/2010/11/change-default-subitems-sort-order.html
I would like to be able to store reusable html-formatted text in Sitecore and reference in codebehind for inclusion in a custom user control. What is the best practice for doing this? If a user selects option A, for example, I would reference standard text A in my control. Any examples for how to accomplish this are appreciated. Thanks.
You have a couple options:
Store the text in the Standard Values of the same template that defines your option list. That makes it available on the same item, but standard for all items. Use security to lock down the field if you are worried about it being edited. This could also be accomplished with the new "cloning" feature in 6.4, I believe.
Create a structure outside of your Home element for storing this data. Based on the option selected, find an item in your content tree which corresponds to the selected item, and read the text off of it. You would need to find this item either relative to /sitecore/Content, or relative to your website root if multi-site support is a requirement.
No.2 in pseudo-code:
//get the item where we have the text values
Item textBase = Sitecore.Context.Database.SelectSingleItem(textBasePath);
//find the child w/ the same name as the selected option
Item textItem = textBase.Axes.GetChild(selectedOptionValue);
string value = textItem["text"];
I think I would do something like techphoria414's 2. option:
ie you have your normal "page" templates as per usual, but then you have some fields (multilist, treelist fields), where you put the source pointing to your other items contain the different texts.
then you basically just have to get the items from the current item (with some very quick'n'dirty code/pseudocode):
var CurrentItem = Sitecore.Context.Item;
Sitecore.Data.Fields.MultilistField mlf1 = CurrentItem.Fields["myExternalTexts"];
if(mlf1 != null)
{
foreach (Item itm in mlf1.GetItems())
{
lit += Sitecore.Web.UI.WebControls.FieldRenderer.Render(itm, "richtext");
}
}
You ofc shouldn't just add them to a literal and you should you Sitecore built in Field renderes if using Sitecore 6 or above and it's a Rich text field.
I hope that helps.