Optimize Sitecore Lucene / Solr query - linq

I am trying to optimize some Lucene/Solr queries that are done via the Sitecore ContentSearch API. Specifically, when it comes to searching a MultiListField.
Environment:
Sitecore 8.1u2, Solr
I have the following Method for querying the values of a multilistfield:
public static Expression<Func<SearchResultItem, bool>> MultiFieldContainsExpression(IEnumerable<string> fieldNames, IEnumerable<string> ids)
{
//fieldNames = ["field_A", "field_X"]
//ids = [GUID_A, GUID_X]
Expression<Func<SearchResultItem, bool>> expression = PredicateBuilder.True<SearchResultItem>();
foreach (string fieldname in fieldNames)
{
ids.ForEach(id =>
{
expression = expression.Or(i => i[fieldName].Contains(IdHealper.NormalizeGuid(id, true)));
});
}
return expression;
}
The resulting Lucene query looks like this:
((field_A:(*GUID_A*) OR field_A:(*GUID_X*) OR field:_X:(*GUID_A*) OR field_X:(*GUID_X*)))
I want the query to be more like this (or even better if possible):
((field_A:(*GUID_A* OR *GUID_X*) OR (field_X:(*GUID_A* OR *GUID_X*)))
Basically, to check if the array of values in the field contains any value from another array. Thank you very much in advance.

Sitecore by default indexs a multilist field as a space separated list of lowercase Guids (Guid.ToString("N")). It could be useful to denormalize the relationship and store the item name or content within the item document through the use of a Computed Field. With the Computed Field it can iterate over the referenced items and turn them into a single field containing their names. You’ll still want to keep the Guid field around for cases when you want to limit the results to just a particular referenced item for the case where you know the exact Guid.

Related

Elasticsearch query not returning expected results for multiple should filters

I am performing an Elasticsearch query using the high-level-rest-api for Java and expect to see records that are either active or do not have a reference id. I'm querying by name for the records and if I hit the index directly with /_search?q=, I see the results I want.
Is my logic correct (pseudo-code):
postFilters.MUST {
Should {
MustNotExist {referenceId}
Must {status = Active}
}
Should {
MustNotExist {referenceId}
Must {type = Person}
}
}
What I get are records that are active with a reference id. But, I want to include records that also do not have a referenceId, hence why I have MustNotExist {referenceId}.
For simplicity, the second Should clause can be dropped (for testing) as the first one is not working as expected by itself.
In my case, I had to use a match query instead of a term query because the value I was querying for was not a primitive or a String. For example, the part where Must, type = Person, Person was an enum, and so looking for "Person" was not quite right, whereas match allowed it to "match".

Realm Xamarin LINQ Select

Is there a way to restrict the "columns" returned from a Realm Xamarin LINQ query?
For example, if I have a Customer RealmObject and I want a list of all customer names, do I have to query All<Customer> and then enumerate the results to build the names list? That seems cumbersome and inefficient. I am not seeing anything in the docs. Am I missing something obvious here? Thanks!
You have to remember that Realm is an object based store. In a RDBMS like Sqlite, restricting the return results to a sub-set of "columns" of an "record" makes sense, but in an object store, you would be removing attributes from the original class and thus creating a new dynamic class to then instantiate these new classes as objects.
Thus is you want just a List of strings representing the customer names you can do this:
List<string> names = theRealm.All<Customer>().ToList().Select(customer => customer.Name).ToList();
Note: That you take the Realm.All<> results to a List first and then using a Linq Select "filter" just the property that you want. Using a .Select directly on a RealmResults is not currently supported (v0.80.0).
If you need to return a complex type that is a subset of attributes from the original RealObject, assuming you have a matching POCO, you can use:
var custNames = theRealm.All<Customer>().ToList().Select((Customer c) => new Name() { firstName = c.firstName, lastName = c.lastName } );
Remember, once you convert a RealmResult to a static list of POCOs you do lose the liveliness of using RealmObjects.
Personally I avoid doing this whenever possible as Realm is so fast that using a RealmResult and thus the RealObjects directly is more efficient on processing time and memory overhead then converting those to POCOs everytime you need to new list...

Finding items from a list in an array stored in a DB field

I have a legacy database that has data elements stored as a comma delimited list in a single database field. (I didn't design that, I'm just stuck with it.)
I have a list of strings that I would like to match to any of the individual values in the "array" in the DB field and am not sure how to do this in Linq.
My list:
List<string> items= new List<string>();
items.Add("Item1");
items.Add("Item2");
The DB field "Products" would contain data something like:
"Item1,Item3,Item4"
"Item3,Item5,Item6"
"Item2,Item7,Item6"
"Item1,Item2"
"Item1"
My first pass at the Linq query was:
var results = (from o in Order
.Where(p=> items.Contains(p.Products)
But I know that won't work. because it will only return the records that contain only "Item1" or "Item2". So with the example data above it would return 0 records. I need to have it return two records.
Any suggestions?
There is a simple clever trick for searching comma-separated lists. First, add an extra , to the beginning and end of the target value (the product list), and the search value. Then search for that exact string. So for example, you would search ,Item1,Item3,Item4, for ,Item1,. The purpose of this is to prevent false positives, i.e., Item12,Item3 finding a match for Item1, while allowing items at the beginning/end of the list to be properly found.
Then, you can use the LINQ .Any method to check that any item in your list is a match to the product list, like the following:
var results = (from o in Order
.Where(o => items.Any(i => (","+o.Products+",").Contains(","+i+",")))
One way would be to parse the list in the Products field:
var results = (from o in Order
.Where(o => items.Any(i => o.Products.Split(',').Contains(i))
But that would parse the string multiple times for each record. You could try pulling back ALL of the records, parsing each record once, then doing the comparison:
var results = from o in Order
let prods = o.Products.Split(',')
where items.Any(i => prods.Contains(i))
select o;

LINQ to SQL simple query to get single int value

IQueryable<double?> query = (from t in ctx.MyList orderby t.MyNums select t.MyNums).Take(1);
IQueryable<double> q2 = query.Cast<double>();
IEnumerator<double> enumerator = q2.GetEnumerator();
while (enumerator.MoveNext())
{
double d = enumerator.Current;
return System.Convert.ToInt32(d);
}
The context for the above code is that I'm attempting to get the greatest integer value from a SharePoint list column. SharePoint seems to treat all list item values as "Number" so that's the reason that I initially had "double?" and not "int?". How could I write that query better? Also, at the moment, it doesn't work at all.. it says "Can only specify query options (orderby, where, take, skip) after last navigation." What does that mean? Thanks..
Additional Information e.g. "Why Max() doesn't work in SharePoint Web Services"
Go to the following link: http://msdn.microsoft.com/en-us/library/dd673933.aspx. The note contained there says the following:
The set of queries expressible in the LINQ syntax is broader than those enabled in the representational state transfer (REST)-based URI syntax that is used by data services. A NotSupportedException is raised when the query cannot be mapped to a URI in the target data service.
Did you try using Max instead?
double max = ctx.MyList.Max(t => t.MyNums);
return (int) max;
Don't forget to check for a null value in case all items in the list are null
return (int)(ctx.MyList.Max(x => x.MyNums) ?? 0);

linq problem with distinct function

I am trying to bind distinct records to a dropdownlist. After I added distinct function of the linq query, it said "DataBinding: 'System.String' does not contain a property with the name 'Source'. " I can guarantee that that column name is 'Source'. Is that name lost when doing distinct search?
My backend code:
public IQueryable<string> GetAllSource()
{
PromotionDataContext dc = new PromotionDataContext(_connString);
var query = (from p in dc.Promotions
select p.Source).Distinct();
return query;
}
Frontend code:
PromotionDAL dal = new PromotionDAL();
ddl_Source.DataSource = dal.GetAllSource();
ddl_Source.DataTextField = "Source";
ddl_Source.DataValueField = "Source";
ddl_Source.DataBind();
Any one has a solution? Thank you in advance.
You're already selecting Source in the LINQ query, which is how the result is an IQueryable<string>. You're then also specifying Source as the property to find in each string in the databinding. Just take out the statements changing the DataTextField and DataValueField properties in databinding.
Alterantively you could remove the projection to p.Source from your query and return an IQueryable<Promotion> - but then you would get distinct promotions rather than distinct sources.
One other quick note - using query syntax isn't really helping you in your GetAllSources query. I'd just write this as:
public IQueryable<string> GetAllSource()
{
PromotionDataContext dc = new PromotionDataContext(_connString);
return dc.Promotions
.Select(p => p.Source)
.Distinct();
}
Query expressions are great for complicated queries, but when you've just got a single select or a where clause and a trivial projection, using the dot notation is simpler IMO.
You're trying to bind strings, not Promotion objects... and strings do not have Source property/field
Your method returns a set of strings, not a set of objects with properties.
If you really want to bind to a property name, you need a set of objects with properties (eg, by writing select new { Source = Source })

Resources