JDO querys with OR in GAE - google-app-engine

Got an entity A with like this
public class A
private String a;
private String b;
..
..
..
How do I write a JDO query that select all the A objects that matches either keyword==a OR keyword ==b
Full code looks something like this:
Query q = pm.newQuery(SELECT FROM A WHERE a == keyword || b == keyword ORDER BY date DESC");
List<A> results = (List<A>)q.execute(keyword);
This code does not give any error but does not give any results. Removing the || part gives results.
Thanks

You can't do an OR on two different fields. Here is a snippet from the docs:
In the JDOQL string syntax, you can
separate multiple filters with ||
(logical "or") and && (logical "and"),
although keep in mind that || can only
be employed when the filters it
separates all have the same field
name. In other words, || is only legal
in situations where the filters it
separates can be combined into a
single contains() filter:
// legal, all filters separated by || are on the same field
Query query = pm.newQuery(Employee.class,
"(lastName == 'Smith' || lastName == 'Jones')" +
" && firstName == 'Harold'");
// not legal, filters separated by || are on different fields
Query query = pm.newQuery(Employee.class,
"lastName == 'Smith' || firstName == 'Harold'");
One way around this might be to store your a and b in a list. If you have a list of items called foo, you could do a query where foo = keyword, and if any item in foo matches, you will get that object back in your results. You don't say much about what a and b are, so I don't know if this will work for you or not :)
Update:
public class Example {
String firstName;
String lastName;
List<String> allNames;
public Example(String first, String last){
firstName = first;
lastName = last;
allNames = new ArrayList<String>();
allNames.add(first);
allNames.add(last);
}
}
With something like that, you could then do a query where "allNames == 'Smith' || allNames=='Jones'".

Related

Dapper.net with unknown quantity of array elements

What I'm trying to do it have a filter object that is populated like so
var filter = new Filter
{
ThingID = 1,
Keywords = new[] { "op", "s" }
};
And then be able to build up the query like this:
var sb = new StringBuilder();
sb.AppendLine("select * from Stuff where 1=1");
if (filter.ThingID.HasValue)
{
sb.AppendLine(" and ThingID = #ThingID");
}
if (filter.Keywords != null)
{
for (int i = 0; i < filter.Keywords.Length; i++)
{
string keyword = filter.Keywords[i];
if (!string.IsNullOrWhiteSpace(keyword))
{
sb.AppendLine(" and ( Model like '%' || #Keywords" + i + " || '%' )");
}
}
}
var sql = sb.ToString();
var results = Query<Stuff>(sql, filter).ToList();
This works fine if just the ThingID prop is populated, but as far as I can tell Dapper is not feeding the Keywords in as a parameter in any way. Is this possible with Dapper, or does it only work in the context of " where Keywords in #Keywords"?
Parameters need to match by name; you are adding parameters like #Keywords17, but there is no Keywords17 property for it to add. It doesn't interpret that as Keywords[17], if that is what you mean. There is some automatic expansion of flat arrays, but that is intended for expanding in #Keywords (although the expansion itself is not specific to in). There is not currently something that would help you automatically there; I would suggest DynamicPaameters instead:
var args = new DynamicParameters();
args.Add("ThingID", 1);
...
if (!string.IsNullOrWhiteSpace(keyword))
{
sb.AppendLine(" and ( Model like '%' || #Keywords" + i + " || '%' )");
args.Add("Keywords" + i, keyword);
}
...
var cmd = new CommandDefinition(sql, args, flags: CommandFlags.NoCache);
var results = Query<Stuff>(cmd).AsList();
note the subtle two changes at the end here - CommandFlags.NoCache will help avoid building lots of lookup entries for similar but different SQL (although you might choose to pay this to reduce per-item cost, up to you). The AsList instead of ToList avoids an extra list allocation.

Using contains to match two fields

I have a database table which has a bunch of fields including one called Type and another called Code. I also have a list of Type and Code pairs that are encapsulated in a class. Something like this:
public class TypeAndCode
{
public byte Type { get; set; }
public int Code { get; set; }
// overrides for Equals and GetHashCode to compare if Type and Code are equal
}
Now what I need to do is select from the table only those entries who type AND code match an entry in my collection. So, for example, I tried something like this:
var query = myTable.Where(a => myTCList.Contains(new TypeAndCode() { Type = a.Type, Code = a.Code }).ToList();
But it'll give me a NotSupportedException:
Unable to create a constant value of type 'TypeAndCode'. Only primitive types
or enumeration types are supported in this context.
Is there a way to make this work so that I can retrieve from the database only those entries that have a Code and Type that match my list of Code and Type? I'm trying to avoid having to retrieve all the entries (it's a big table) and match them in memory.
I'm aware that I could try something like
var query = myTable.Where(a => listOfTypes.Contains(a.Type) && listOfCodes.Contains(a.Codes))
But that will make some spurious matches where the type and code are from different pairs in my original list.
You can use Any instead:
var query = myTable
.Where(a => myTCList.Any(t => t.Type == a.Type && t.Code == a.Code ))
.ToList();
You should be able to just do this manually without the overloaded methods from your class:
myTCList.Any(x => x.Type == a.Type && x.Code == a.Code)
My ulitmate solution here, in case anybody else encounters a similar problem, was to set up a temporary table that I could write the pairs I wanted to match to which I could them join with the database table. After doing the join and materializing the results, you can delete the temporary table.
Something like:
ctx.myTempTable = (from pair in mypairs
select new myTempTable() { Type = pair.Type, Code = pair.Code }).ToList();
ctx.SaveChanges();
var query = from q in myTable
join t in ctx.myTempTable
on new { q.Type, q.Code } equals new { t.Code, t.Type }
select q;
The whole thing is in a try/catch/finally block with the finally block used to clear up the temporary table(s)

Using LINQ to find Excel columns that don't exist in array?

I have a solution that works for what I want, but I'm hoping to get some slick LINQ types to help me improve what I have, and learn something new in the process.
The code below is used verify that certain column names exist on a spreadsheet. I was torn between using column index values or column names to find them. They both have good and bad points, but decided to go with column names. They'll always exist, and sometimes in different order, though I'm working on this.
Details:
GetData() method returns a DataTable from the Excel spreadsheet. I cycle through all the required field names from my array, looking to see if it matches with something in the column collection on the spreadsheet. If not, then I append the missing column name to an output parameter from the method. I need both the boolean value and the missing fields variable, and I wasn't sure of a better way than using the output parameter. I then remove the last comma from the appended string for the display on the UI. If the StringBuilder object isn't null (I could have used the missingFieldCounter too) then I know there's at least one missing field, bool will be false. Otherwise, I just return output param as empty, and method as true.
So, Is there a more slick, all-in-one way to check if fields are missing, and somehow report on them?
private bool ValidateFile(out string errorFields)
{
data = GetData();
List<string> requiredNames = new [] { "Site AB#", "Site#", "Site Name", "Address", "City", "St", "Zip" }.ToList();
StringBuilder missingFields = null;
var missingFieldCounter = 0;
foreach (var name in requiredNames)
{
var foundColumn = from DataColumn c in data.Columns
where c.ColumnName == name
select c;
if (!foundColumn.Any())
{
if (missingFields == null)
missingFields = new StringBuilder();
missingFieldCounter++;
missingFields.Append(name + ",");
}
}
if (missingFields != null)
{
errorFields = missingFields.ToString().Substring(0, (missingFields.ToString().Length - 1));
return false;
}
errorFields = string.Empty;
return true;
}
Here is the linq solution that makes the same.
I call the ToArray() function to activate the linq statement
(from col in requiredNames.Except(
from dataCol in data
select dataCol.ColumnName
)
select missingFields.Append(col + ", ")
).ToArray();
errorFields = missingFields.ToString();
Console.WriteLine(errorFields);

How to filter on the value of a specific element in a list?

Using GAE-Java-JDO, is it possible to filter on the value of a specific element in a list?
WHAT WORKS
Normally, I would have the following:
#PersistenceCapable
class A {
String field1;
String field2;
// id, getters and setters
}
Then I would build a simple query:
Query q = pm.newQuery(A.class, "field1 == val");
q.declareParameters("String val");
List<A> list = new ArrayList<A>((List<A>) q.execute("foo"));
WHAT I WOULD LIKE
The above works fine. But what I would like to have is all of the fields stored in a list:
#PersistenceCapable
class AA {
ArrayList<String> fields;
// id, getters and setters
}
and then be able to query on a specific field in the list:
int index = 0;
Query q = pm.newQuery(A.class, "fields.get(index) == val");
q.declareParameters("int index, String val");
List<A> list = new ArrayList<A>((List<A>) q.execute(index, "foo"));
But this throws an exception:
org.datanucleus.store.appengine.query.DatastoreQuery$UnsupportedDatastoreFeatureException:
Problem with query
<SELECT FROM xxx.AA WHERE fields.get(index) == val PARAMETERS int index, String val,>:
Unsupported method <get> while parsing expression:
InvokeExpression{[PrimaryExpression{strings}].get(ParameterExpression{ui})}
My impression from reading the GAE-JDO doc is that this is not possible:
"The property value must be supplied by the application; it cannot refer to or be calculated in terms of other properties"
So... any ideas?
Thanks in advance!
If you only need to filter by index+value, then I think prefixing the actual list-values with their index should work. (If you need to also filter by actual values, then you'll need to store both lists.)
i.e. instead of the equivalent of
fields= ['foo', 'bar', 'baz] with query-filter fields[1] == 'bar'
you'd have
fields= ['0-foo', '1-bar', '2-baz'] with query-filter fields == '1-bar'
(but in java)

Build dynamic Expression for search

I'm in trouble, can't figure out seems a very simple thing that in plain SQL can be done within 1 minute, it's been several hours so far. Here is the situation:
I have single field where user may enter as many words as he/she likes
I need to build Expression to find match
Let's say there are 3 fields in database: firstname, middlename, lastname
I need to split the search entry and compare against those 3 fields
I'm dealing with Silverlight RIA, EF
Once again search entry contains UNKNOWN number of words
Here under what I'm trying to accomplish, return type is mandatory:
public Expression<Func<MyEntity, bool>> GetSearchExpression(string text)
{
Expression<Func<MyEntity, bool>> result;
var keywords = text.Trim().Split(" ");
foreach(var keyword in keywords)
{
// TODO:
// check whether 'OR' is required (i.e. after second loop)
// (firstname = 'keyword'
// AND
// middlename = 'keyword'
// AND
// lastname = 'keyword')
// OR
// (firstname like '%keyword%'
// AND
// middlename like '%keyword%'
// AND
// lastname like '%keyword%')
}
return result;
}
Thanks in advance!
The simplest thing would be to use Joe Albahari's PredicateBuilder to do something like this:
var predicate = PredicateBuilder.False<MyEntity>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (
p => p.FirstName.Contains (temp) &&
p.LastName.Contains (temp) &&
p.MiddleName.Contains (temp));
}
return predicate;
I left out the equality-checks because "Contains" (i.e. like '%...%') will cover that possibility anyway.
I do have to point out, though, that your conditions don't make any sense, from a business logic standpoint. Under what circumstances do you want to find someone whose first, last, and middle name all contain "John"? I suspect what you really want is something more like this:
var predicate = PredicateBuilder.True<MyEntity>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.And (
p => p.FirstName.Contains (temp) ||
p.LastName.Contains (temp) ||
p.MiddleName.Contains (temp));
}
return predicate;
One final note: Because PredicateBuilder requires you to call .AsExpandable() when you are creating your query, I don't know whether this will work for you. You might have to resort to building your own expressions, which can be somewhat tedious. This can get you started, though:
var pParam = Expression.Parameter(typeof(MyEntity), "p");
var predicate = Expression.Constant(true);
foreach (string keyword in keywords)
{
var keywordExpr = Expression.Constant(keyword);
// TODO: create an expression to invoke .FirstName getter
// TODO: create an expression to invoke string.Contains() method
//TODO: do the same for lastname and middlename
predicate = Expression.And(predicate,
Expression.Or(
Expression.Or(firstNameContainsKeyword,
middleNameContainsKeyword),
lastNameContainsKeyword));
}
return Expression.Lambda<Func<MyEntity, bool>>(predicate, pParam);

Resources