Apache Cayenne Get Collection Expression - expression

Using Apache Cayenne I am trying to figure out how to avoid doing iterative calls to the DB when I have a Collection of attribute values.
Say we have a persistent object Person with an attribute name of type String. If I have a List containing names of the Person I would like to generate an expression that allows a single DB call rather than iterating over the list and getting each Person in turn.
This doesn't actually exist but I would like something like this:
List<String> names = ...;
ExpressionFactory.orLikeExp(Person.NAME_PROPERTY, names);

You can use ExpressionFactory.join(..):
List<Expression> pairs = new ArrayList<>(names.size());
for(String name : names) {
// use an expression appropriate for comparison...
// "like" in this example, but can be "equals", etc.
pairs.add(ExpressionFactory.likeExp(Person.NAME_PROPERTY, name));
}
Expression e = ExpressionFactory.joinExp(Expression.OR, pairs);

Related

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...

Optimize Sitecore Lucene / Solr query

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.

Attribute routing not working with dictionaries

Being new to attribute routing, I'd like to ask for help getting this to work.
This test is a simple dynamic DB table viewer: Given a table name (or stored query name or whatever) and optionally some WHERE parameters, return query results.
Table COMPANIES (one of any number of tables which has an associated SELECT query stored somewhere, keyed by table name):
ID NAME HQ INDUSTRY
1 Apple USA Consumer electronics
2 Bose USA Low-quality, expensive audio equipment
3 Nokia FIN Mobile Phones
Controller:
[Route("view/{table}/{parameters}")]
public object Get(string table, Dictionary<string, string> parameters) {
var sql = GetSql(table);
var dbArgs = new DynamicParameters(parameters);
return Database.Query(sql, dbArgs); // Return stuff/unrelated to problem
}
SQL stored in some resource or table. Obviously the parameters must match exactly:
SELECT * FROM companies
WHERE name = :name
-- OR hq = :hq
-- OR ...etc. Doesn't matter since it never gets this far.
Request (Should look clean, but the exact URL format isn't important):
www.website.com/view/companies?hq=fin --> 404: No matching controller
www.website.com/view/companies/hq=fin --> parameters is null
www.website.com/view/companies/hq=fin&name=nokia --> Exception: A potentially dangerous Request.Path value was detected from the client (&).
When I use: [Route("view/{table}{parameters}")] I get:
A path segment cannot contain two consecutive parameters. They must be separated by a '/' or by a literal string. Parameter name: routeTemplate. Makes sense.
My question is: How do I accept a table name and any number of unknown parameters in the usual key1=val1&key2=val2 form (not some awkward indexed format like the one mentioned here) which will be later bound to SQL parameters, preferably using a vanilla data structure rather than something like FormCollection.
I don't think that binding URL parameters to a Dictionary is built-in to the framework. I'm sure there's a way to extend it if you wanted to.
I think quickest (but still acceptable) option is to get the query string parameters using Request.GetQueryNameValuePairs() like this:
[Route("view/{table}")]
public object Get(string table) {
Dictionary<string, string> parameters = Request.GetQueryNameValuePairs()
.ToDictionary(x => x.Key, x => x.Value);
var sql = GetSql(table);
var dbArgs = new DynamicParameters(parameters);
return Database.Query(sql, dbArgs); // Return stuff/unrelated to problem
}

Select one unique instance from LINQ query

I'm using LINQ to SQL to obtain data from a set of database tables. The database design is such that given a unique ID from one table (Table A) one and only one instance should be returned from an associated table (Table B).
Is there a more concise way to compose this query and ensure that only one item was returned without using the .Count() extension method like below:
var set = from itemFromA in this.dataContext.TableA
where itemFromA.ID == inputID
select itemFromA.ItemFromB;
if (set.Count() != 1)
{
// Exception!
}
// Have to get individual instance using FirstOrDefault or Take(1)
FirstOrDefault helps somewhat but I want to ensure that the returned set contains only one instance and not more.
It sounds like you want Single:
var set = from itemFromA in this.dataContext.TableA
where itemFromA.ID == inputID
select itemFromA.ItemFromB;
var onlyValue = set.Single();
Documentation states:
Returns the only element of a sequence, and throws an exception if there is not exactly one element in the sequence.
Of course that means you don't get to customize the message of the exception... if you need to do that, I'd use something like:
// Make sure that even if something is hideously wrong, we only transfer data
// for two elements...
var list = set.Take(2).ToList();
if (list.Count != 1)
{
// Throw an exception
}
var item = list[0];
The benefit of this over your current code is that it will avoid evaluating the query more than once.

How to instantiate an object within a linq query

This is kinda theoretical question,
I was looking at someone else' code (below) and my simple solution was to instantiate the collection outside linq, but I can guess there will be cases where I'd want to instantiate the objects inside the query, and perhaps only on a selection of elements.
Here's a simplified example of how this was being done (badly).
var pods = (from n in ids
where new Node(Convert.ToInt32(n)).HasValue("propertyName")
select new
{
Id = Convert.ToInt32(n),
Url = new Node(Convert.ToInt32(n)).Url,
Name = new Node(Convert.ToInt32(n)).Title()
}).ToList();
Irrelevant Note: in this case the Node constructor is getting data from a memory cache.
How can I improve this example to only instantiate each object once using linq?
Cheers.
Murray.
Use a let clause like this:
var pods = (
from n in ids
let id = Convert.ToInt32(n)
let node = new Node(id)
where node.HasValue("propertyName")
select new
{
Id = id,
Url = node.Url,
Name = node.Title()
}
).ToList();
For more information please see let clause (C# Reference):
In a query expression, it is sometimes
useful to store the result of a
sub-expression in order to use it in
subsequent clauses. You can do this
with the let keyword, which creates a
new range variable and initializes it
with the result of the expression you
supply. Once initialized with a value,
the range variable cannot be used to
store another value. However, if the
range variable holds a queryable type,
it can be queried.

Resources