3

I have been making a CAML query search webpart, which already provides the cascaded search on the documents through out the directory using CAML on Columns as metadata to search.

scree for cascading search

For now I have the dropdown to make cascading search work. But now I want it to behave like Google Search no criteria defined just a text box to search:

Type anything relevant and the CAML should be so that it searches all the columns against the documents in the library and return those which matches anywhere. Somewhere we call it "full text search", mind it I am not desiring Content Search for now and not using SharePoint Search Services.

Solution Approach 1: Just a thought I concatenate all the metadata values against each document and apply pattern matching logic (somewhat like KMP algorithm) on this concatenated set with the search statement entered in the search text box. I dont know should I go for it or not and how will I achieve it because it might need permutation and combination of the concatenated metadata values?

Any solutions?

Current code for cascading:

private string GetCamlQuery()
    {
        string queryString = string.Empty;
        string searchQueryString = string.Empty;
        string sortQueryString = string.Empty;
        StringBuilder querySB = new StringBuilder();
        List<SearchCriteria> searchCriteriaList = null;

        if (ViewState["SearchCriteriaList"] != null)
        {
            searchCriteriaList = ViewState["SearchCriteriaList"] as List<SearchCriteria>;
        }
        else
        {
            searchCriteriaList = new List<SearchCriteria>();
            searchCriteriaList.Add(new SearchCriteria("ContentType", "Document"));
        }
        if (ddlDocument.SelectedIndex > 0 && txtSearchTag.Text.Trim() != string.Empty)
        {
            if (ddlDocument.SelectedItem.Text == "Area" || ddlDocument.SelectedItem.Text == "Document Type" || ddlDocument.SelectedItem.Text == "Tags" || ddlDocument.SelectedItem.Text == "Document Category" || ddlDocument.SelectedItem.Text == "Customer")
                searchCriteriaList.Add(new SearchCriteria(ddlDocument.SelectedItem.Text, txtSearchTag.Text.Trim()));
        }

        List<string> conditions = new List<string>();
        foreach (SearchCriteria item in searchCriteriaList)
        {
            if (item.Key == "ContentType")
                conditions.Add("<Eq><FieldRef Name='ContentType'/><Value Type='Text'>" + item.Values + "</Value></Eq>");
            else if (item.Key == "Area")
                conditions.Add("<Contains><FieldRef Name='Area'/><Value Type='Text'>" + item.Values + "</Value></Contains>");
            else if (item.Key == "Document Type")
                conditions.Add("<Contains><FieldRef Name='TypeOfDoc' /><Value Type='Text'>" + item.Values + "</Value></Contains>");
            else if (item.Key == "Tags")
            {
                conditions.Add("<Contains><FieldRef Name='Tags' /><Value Type='Text'>" + item.Values + "</Value></Contains>");
            }
            else if (item.Key == "Document Category")
            {
                conditions.Add("<Contains><FieldRef Name='Category' /><Value Type='Text'>" + item.Values + "</Value></Contains>");
            }
            else if (item.Key == "Customer")
            {
                conditions.Add("<Contains><FieldRef Name='CustomerName' /><Value Type='Text'>" + item.Values + "</Value></Contains>");
            }

        }


        querySB.Append("<Where>");
        string merged = MergeCAMLConditions(conditions, MergeType.And);
        querySB.Append(merged);
        querySB.Append("</Where>");
        searchQueryString = querySB.ToString();
        ViewState["SearchCriteriaList"] = searchCriteriaList;
        ViewState["SearchQueryString"] = searchQueryString;
        queryString = searchQueryString;
        ViewState["queryString"] = queryString;
        return queryString;

    }
  • Why you don't want to use search? – dstarkowski Sep 2 '15 at 17:25
  • @dstarkowski , My SharePoint Server is not capable enough, Cant budget more infra – ankit Sep 3 '15 at 6:11
2

I used Camlex function "WhereAny"

public string exp(string t)
    {
        var expressions = new List<Expression<Func<SPListItem, bool>>>();
        string token = t;
        //expressions.Add(x => ((string)x["ContentType"]).Contains("Document"));
        expressions.Add(x => ((string)x["Area"]).Contains(token));
        expressions.Add(x => ((string)x["TypeOfDoc"]).Contains(token));
        expressions.Add(x => ((string)x["Category"]).Contains(token));
        expressions.Add(x => ((string)x["Tags"]).Contains(token));
        expressions.Add(x => ((string)x["CustomerName"]).Contains(token));
        expressions.Add(x => ((string)x["Comments"]).Contains(token));
        expressions.Add(x => ((string)x["SubTypeOfDoc"]).Contains(token));
        expressions.Add(x => ((string)x["runTimeFolder"]).Contains(token));
        expressions.Add(x => ((string)x["Country"]).Contains(token));

        // prepare query
        string caml = Camlex.Query().WhereAny(expressions).ToString();
        return caml;
    }

The above function returned me a CAML query. And that query is fired over the Document Library, result is stored in DataTable and displayed.

1

It is possible and there is a simpler approach. I have implemented a similar Search web part where I am building CAML queries dynamically. I used the Camlex.Net library for doing this. You can get that library in codeplex here or through NuGet gallery.

The advantages of this library is that it is LINQ based, you can choose to get back SPQuery object or the CAML string AND there is this option of WhereAll() /WhereAny() methods which gives greater flexibility. In criteria search you would use WhereAll() and in free text search you would use WhereAny().

Refer to Section "V. Dynamic filtering conditions" in documentation tab of the codeplex site. Also, you can construct your query around so that the 'In' CAML query is generated as it is cleaner than 'Or' which is what you would use for free text search. Refer to this for example of how to construct 'In' queries using Camlex.Net.

After using this library, search(CAML based) works great and my coding/development time was reduced a lot and this code is easier to maintain/make changes in future.

  • great solution @Suhas can you tell me what this function will return public static class ExpressionsHelper { public static Expression<Func<SPListItem, bool>> CombineAnd( IEnumerable<Expression<Func<SPListItem, bool>>> expressions) { ... } public static Expression<Func<SPListItem, bool>> CombineOr( IEnumerable<Expression<Func<SPListItem, bool>>> expressions) { ... } } what will the dots be replaced with ? – ankit Sep 8 '15 at 5:20
  • @ankit The dots represent the internal implementation, you do not have to implement this class. You can directly call these methods to combine expressions to get a single expression. For Example: var languageConditions = new List<Expression<Func<SPListItem, bool>>>(); languageConditions.Add(x => (string)x["Language"] == "Russian"); languageConditions.Add(x => (string)x["Language"] == "English"); var langExpr = ExpressionsHelper.CombineOr(languageConditions); In the above code 2 expressions are combined in OR form. You use these to form complex combinations of AND, OR , etc. – suhas Sep 8 '15 at 6:02
  • These functions CombineOR and CombineAND should return something as per their declarations. So it doesnot allow to bulid. I wanted to know the return statement. – ankit Sep 8 '15 at 6:45
  • These functions return type is "Expression<Func<SPListItem, bool>>" which is LINQ expression. You can just create a List variable of type List<Expression<Func<SPListItem, bool>>>, add your individual expressions to this list and pass it to CombineAnd/CombineOr to get back the combined LINQ expression i.e. Expression<Func<SPListItem, bool>> – suhas Sep 8 '15 at 7:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.