Usage of Criteria Builder with not in on joined field - spring-boot

I have a Hotel entity & HotelReservation entity.
I try to fetch available Hotels to make a reservation.
Idea:
Fetch All Hotels and remove which are not available. (Hotel is not available if it's reserved in a certain time - in function, there are parameters from -> to). I followed the examples on stackoverflow.com and produce this code:
fun availableHotelsBetweenDates(from: ZonedDateTime, to: ZonedDateTime): Specification<Hotel> = Specification<Hotel> { root, query, cb ->
val reservationsBetweenDates: Subquery<HotelReservation> = query.subquery(HotelReservation::class.java)
val subqueryRoot = reservationsBetweenDates.from(HotelReservation::class.java)
val join: Join<Hotel, HotelReservation> = root.join("reservations", JoinType.LEFT)
reservationsBetweenDates.select(subqueryRoot.get("id"))
reservationsBetweenDates.where(activeReservationsBetweenDates(from, to).toPredicate(subqueryRoot, query, cb))
reservationsBetweenDates.distinct(true)
cb.not(join.get<Long>("id").`in`(reservationsBetweenDates))
}
This function returns 0 entities (should return 2 - because I have 3 hotels and only one reservation for one hotel).
Function activeReservationsBetweenDates(from, to) return a Specification<HotelReservation> of all HotelReservation between from, to.
How to properly use not in, with subquery on nested field?

Related

LINQ Query Syntax: Group By and OrderBy and take one row

Following query will group the records in the Assignment table, added within the given day, by the person assisted and the completed time.
(from l in Assignments where ((DateTime)l.AddedLocalTime).Date == TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, TimeZoneInfo.Local).Date
group l by new {l.FkAssistedBy, l.CompletedTime} into groups
orderby groups.Key.CompletedTime descending
select new { user = groups.Key.FkAssistedBy, groups.Key.CompletedTime })
Here, if a particular user has completed multiple assignments within the day, there will be multiple records per the user. But what I want is to group by the user and get ONLY the last completed record per user. How do I use the Take(1) or First() on the above query to get the desired result? I need to output the assistedBy and completedTime fields.
This query should work with LINQ to Objects:
(
from l in Assignments
where ((DateTime)l.AddedLocalTime).Date == TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, TimeZoneInfo.Local).Date
group l by new { l.FkAssistedBy } into g
select new
{
user = g.Key.FkAssistedBy,
CompletedRecord = g.OrderbyDescending(x = c.CompletedTime).FirstOrDefault()
}
)

Eloquent : Creating additional row with alias to the result set

Suppose I have a user table with columns id, name, age
With a normal get query User::get(), I get all results with those column's value.
But instead of just getting the id, name, age columns, is it possible if I add another column, perhaps a column with an alias of future_age with a value of age + 1.
The above result could be achieved in SQL like so:
SELECT res.*, (SELECT sum(res.age+1) from users where id = res.id) as future_age from users as res
I've thought about looping through the result and creating new key. but I think this would make the query execution time slow when the data is lengthy.
Is it possible to create the future_age key/column (I don't know the term hehe) directly on the query?
Currently, this is my query:
$user = User::get();
$new_res = []
if($user->count() > 0) {
foreach ($user as $u) {
$u['future_age'] = $u->age + 1
$new_res[] = $u;
}
}
The query works tho, but I don't think this is good if I have a large set of data.

Dynamic Linq on DataTable error: no Field or Property in DataRow, c#

I have some errors using Linq on DataTable and I couldn't figure it out how to solve it. I have to admit that i am pretty new to Linq and I searched the forum and Internet and couldn't figure it out. hope you can help.
I have a DataTable called campaign with three columns: ID (int), Product (string), Channel (string). The DataTable is already filled with data. I am trying to select a subset of the campaign records which satisfied the conditions selected by the end user. For example, the user want to list only if the Product is either 'EWH' or 'HEC'. The selection criteria is dynaically determined by the end user.
I have the following C# code:
private void btnClick()
{
IEnumerable<DataRow> query =
from zz in campaign.AsEnumerable()
orderby zz.Field<string>("ID")
select zz;
string whereClause = "zz.Field<string>(\"Product\") in ('EWH','HEC')";
query = query.Where(whereClause);
DataTable sublist = query.CopyToDataTable<DataRow>();
}
But it gives me an error on line: query = query.Where(whereClause), saying
No property or field 'zz' exists in type 'DataRow'".
If I changed to:
string whereClause = "Product in ('EWH','HEC')"; it will say:
No property or field 'Product' exists in type 'DataRow'
Can anyone help me on how to solve this problem? I feel it could be a pretty simple syntax change, but I just don't know at this time.
First, this line has an error
orderby zz.Field<string>("ID")
because as you said, your ID column is of type int.
Second, you need to learn LINQ query syntax. Forget about strings, the same way you used from, orderby, select in the query, you can also use where and many other operators. Also you'll need to learn the equivalent LINQ constructs for SQL-ish things, like for instance IN (...) is mapped to Enumerable.Contains etc.
With all that being said, here is your query
var productFilter = new[] { "EWH", "HEC" };
var query =
from zz in campaign.AsEnumerable()
where productFilter.Contains(zz.Field<string>("Product"))
orderby zz.Field<int>("ID")
select zz;
Update As per your comment, if you want to make this dynamic, then you need to switch to lambda syntax. Multiple and criteria can be composed by chaining multiple Where clauses like this
List<string> productFilter = ...; // coming from outside
List<string> channelFilter = ...; // coming from outside
var query = campaign.AsEnumerable();
// Apply filters if needed
if (productFilter != null && productFilter.Count > 0)
query = query.Where(zz => productFilter.Contains(zz.Field<string>("Product")));
if (channelFilter != null && channelFilter.Count > 0)
query = query.Where(zz => channelFilter.Contains(zz.Field<string>("Channel")));
// Once finished with filtering, do the ordering
query = query.OrderBy(zz => zz.Field<int>("ID"));

LINQ Lamba Select all from table where field contains all elements in list

I need a Linq statement that will select all from a table where a field contains all elements in a list<String> while searching other fields for the entire string regardless of words.
It's basically just a inclusive word search on a field where all words need to be in the record and string search on other fields.
Ie I have a lookup screen that allows the user to search AccountID or Detail for the entire search string, or search clientID for words inclusive words, I'll expand this to the detail field if I can figure out the ClientId component.
The complexity is that the AccountId and Detail are being searched as well which Basically stops me from doing the foreach in the second example due to the "ors".
Example 1, this gives me an the following error when I do query.Count() afterwards:
query.Count(); 'query.Count()' threw an exception of type 'System.NotSupportedException' int {System.NotSupportedException}
+base {"Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator."} System.SystemException {System.NotSupportedException}
var StrList = searchStr.Split(' ').ToList();
query = query.Where(p => p.AccountID.Contains(searchStr) || StrList.All(x => p.clientID.Contains(x)) || p.Detail.Contains(searchStr));
Example 2, this gives me an any search word type result:
var StrList = searchStr.Split(' ').ToList();
foreach (var item in StrList)
query = query.Where(p => p.AccountID.Contains(searchStr) || p.clientID.Contains(item) || p.Detail.Contains(searchStr));
Update
I have a table with 3 fields, AccountID, ClientId, Details
Records
Id, AccountID, CLientId, Details
1, "123223", "bobo and co", "this client suxs"
2, "654355", "Jesses hair", "we like this client and stuff"
3, "456455", "Microsoft", "We love Mircosoft"
Search examples
searchStr = "232"
Returns Record 1;
searchStr = "bobo hair"
Returns no records
searchStr = "bobo and"
Returns Record 1
searchStr = "123 bobo and"
Returns returns nothing
The idea here is:
if the client enters a partial AccountId it returns stuff,
if the client wants to search for a ClientId they can type and cancel down clients by search terms, ie word list. due to the large number of clients the ClientID Will need to contain all words (in any order)
I know this seems strange but it's just a simple interface to find accounts in a powerful way.
I think there are 2 solutions to your problem.
One is to count the results in memory like this:
int count = query.ToList().Count();
The other one is to not use All in your query:
var query2 = query;
foreach (var item in StrList)
{
query2 = query2.Where(p => p.clientID.Contains(item));
}
var result = query2.Union(query.Where(p => p.AccountID.Contains(searchStr) || p.Detail.Contains(searchStr)));
The Union at the end acts like an OR between the 2 queries.

Getting Last rows from the result of Linq to Sql statement

I couldn't get last articles of every writers in this statement.
List<Editor> lstEditors = dataContext.GetTable<Editor>().Where(t => t.M_Active).Select(t => t).ToList();
var lstArticles = from article in DAO.context.GetTable<Article>().ToList()
join editor in lstEditors on article.RefEditorId equals editor.EditorId
select
new
{
article.M_ArticleId,
article.M_Subject,
article.M_Text,
editor.M_EditorId,
editor.M_Member.M_EditorPicture,
M_NameSurname = editor.M_Member.M_Fname + " " + editor.M_Member.M_Lname
};
Be careful, your query is fetching all the contents of both the Editor and the Yazi tables and then performs Linq-to-Objects on it.
I'm not sure what you ask exactly either, do you want to obtain the list of all writers (editors) along with the last article of each one of these writers?
Do you want to get the writers that did not write any articles yet also?
Edit:
explanation of methods causing an immediate query
Any time you call one of the methods listed below on an IQueryable object (tables or other queries), it performs the actual query to SQL server:
ToList(), ToArray(), ToLookup(), ToDictionay()
Count(), Sum(), Avg(), Aggregate(), Min(), Max()
First(), FirstOrDefault(), Last(), LastOrDefault()
getting last article written by each writer
//create a subquery that returns an editor and its last article date
var editorLastArticleDates =
from article in DAO.context.GetTable<Article>()
group article by article.RefEditor into g
let lastArticleDate= g.Max(x => x.Date)
select new
{
Editor = g.Key,
LastArticleDate = lastArticleDate,
};
//Note: We did not do a ToList() here so the query is not executed
// The editorLastArticleDates object is a IQueryable<>
var query =
from article in DAO.context.GetTable<Article>()
join editorLastArticleDate in editorLastArticleDates
on new { article.Editor, article.Date } // 1
equals new { editorLastArticleDate.Editor, // 2
Date = editorLastArticleDate.LastArticleDate } // 3
select new
{
article.M_ArticleId,
article.M_Subject,
article.M_Text,
article.RefEditor.M_EditorId,
article.RefEditor.M_Member.M_EditorPicture,
M_NameSurname = article.RefEditor.M_Member.M_Fname + " "
+ article.RefEditor.M_Member.M_Lname,
};
//Note: We did not do a ToList() yet so the query is not executed
// The query object is a IQueryable<>
Console.WriteLine(query.ToString()); //Displays SQL query on the console
var results = query.ToList(); // SQL query is executed on this line.
In the code above, I left some remarks on things I had problems with:
When using join, the section between new and equals access only variables declared before the join keyword while the section after the equals keyword has access to the variable defined between join and in.
When writing your join condition, make sure you use equals and not ==.
When using new { XXX, YYY } syntax in your join condition, you declare anonymous types. If the property names are not identical on both sides, it will not compile. In order to have identical property names in this sample, I added the Date = before my value.
By the way, you should use LinqPad to test your queries, it is really a nice tool.

Resources