How does sorting work with limit in kotlin exposed model? - kotlin

I have following snippet of code:
UserDataModel
.find {
UserDataTable.type eq type and (
UserDataTable.userId eq userId
)
}
.limit(count)
.sortedByDescending { it.timestamp }
sortedByDescending is a part of kotlin collections API. The my main concern is: how does exposed lib return top (according to timestamp) count rows from table if select query looks like this and does not contain ORDER BY clause?
SELECT USERDATA.ID, USERDATA.USER_ID, USERDATA.TYPE,
USERDATA.PAYLOAD, USERDATA."TIMESTAMP"
FROM USERDATA
WHERE USERDATA.TYPE = 'testType'
and USERDATA.USER_ID = 'mockUser'
LIMIT 4
And is it possible that sometimes or somehow returned result would be different for the same data?
Really struggled here. Thank you in advance.

You're sorting the results after query has been executed.
You need to use orderBy method as described in docs
UserDataModel
.find {
UserDataTable.type eq type and (UserDataTable.userId eq userId)
}
.limit(count)
.orderBy(UserDataTable.timestamp to SortOrder.DESC)

Related

Add array of other records from the same table to each record

My project is a Latin language learning app. My DB has all the words I'm teaching, in the table 'words'. It has the lemma (the main form of the word), along with the definition and other information the user needs to learn.
I show one word at a time for them to guess/remember what it means. The correct word is shown along with some wrong words, like:
What does Romanus mean? Greek - /Roman/ - Phoenician - barbarian
What does domus mean? /house/ - horse - wall - senator
The wrong options are randomly drawn from the same table, and must be from the same part of speech (adjective, noun...) as the correct word; but I am only interested in their lemma. My return value looks like this (some properties omitted):
[
{ lemma: 'Romanus', definition: 'Roman', options: ['Greek', 'Phoenician', 'barbarian'] },
{ lemma: 'domus', definition: 'house', options: ['horse', 'wall', 'senator'] }
]
What I am looking for is a more efficient way of doing it than my current approach, which runs a new query for each word:
// All the necessary requires are here
class Word extends Model {
static async fetch() {
const words = await this.findAll({
limit: 10,
order: [Sequelize.literal('RANDOM()')],
attributes: ['lemma', 'definition'], // also a few other columns I need
});
const wordsWithOptions = await Promise.all(words.map(this.addOptions.bind(this)));
return wordsWithOptions;
}
static async addOptions(word) {
const options = await this.findAll({
order: [Sequelize.literal('RANDOM()')],
limit: 3,
attributes: ['lemma'],
where: {
partOfSpeech: word.dataValues.partOfSpeech,
lemma: { [Op.not]: word.dataValues.lemma },
},
});
return { ...word.dataValues, options: options.map((row) => row.dataValues.lemma) };
}
}
So, is there a way I can do this with raw SQL? How about Sequelize? One thing that still helps me is to give a name to what I'm trying to do, so that I can Google it.
EDIT: I have tried the following and at least got somewhere:
const words = await this.findAll({
limit: 10,
order: [Sequelize.literal('RANDOM()')],
attributes: {
include: [[sequelize.literal(`(
SELECT lemma FROM words AS options
WHERE "partOfSpeech" = "options"."partOfSpeech"
ORDER BY RANDOM() LIMIT 1
)`), 'options']],
},
});
Now, there are two problems with this. First, I only get one option, when I need three; but if the query has LIMIT 3, I get: SequelizeDatabaseError: more than one row returned by a subquery used as an expression.
The second error is that while the code above does return something, it always gives the same word as an option! I thought to remedy that with WHERE "partOfSpeech" = "options"."partOfSpeech", but then I get SequelizeDatabaseError: invalid reference to FROM-clause entry for table "words".
So, how do I tell PostgreSQL "for each row in the result, add a column with an array of three lemmas, WHERE existingRow.partOfSpeech = wordToGoInTheArray.partOfSpeech?"
Revised
Well that seems like a different question and perhaps should be posted that way, but...
The main technique remains the same. JOIN instead of sub-select. The difference being generating the list of lemmas for then piping then into the initial query. In a single this can get nasty.
As single statement (actually this turned out not to be too bad):
select w.lemma, w.defination, string_to_array(string_agg(o.defination,','), ',') as options
from words w
join lateral
(select defination
from words o
where o.part_of_speech = w.part_of_speech
and o.lemma != w.lemma
order by random()
limit 3
) o on 1=1
where w.lemma in( select lemma
from words
order by random()
limit 4 --<<< replace with parameter
)
group by w.lemma, w.defination;
The other approach build a small SQL function to randomly select a specified number of lemmas. This selection is the piped into the (renamed) function previous fiddle.
create or replace
function exam_lemma_definition_options(lemma_array_in text[])
returns table (lemma text
,definition text
,option text[]
)
language sql strict
as $$
select w.lemma, w.definition, string_to_array(string_agg(o.definition,','), ',') as options
from words w
join lateral
(select definition
from words o
where o.part_of_speech = w.part_of_speech
and o.lemma != w.lemma
order by random()
limit 3
) o on 1=1
where w.lemma = any(lemma_array_in)
group by w.lemma, w.definition;
$$;
create or replace
function exam_lemmas(num_of_lemmas integer)
returns text[]
language sql
strict
as $$
select string_to_array(string_agg(lemma,','),',')
from (select lemma
from words
order by random()
limit num_of_lemmas
) ll
$$;
Using this approach your calling code reduces to a needs a single SQL statement:
select *
from exam_lemma_definition_options(exam_lemmas(4))
order by lemma;
This permits you to specify the numbers of lemmas to select (in this case 4) limited only by the number of rows in Words table. See revised fiddle.
Original
Instead of using a sub-select to get the option words just JOIN.
select w.lemma, w.definition, string_to_array(string_agg(o.definition,','), ',') as options
from words w
join lateral
(select definition
from words o
where o.part_of_speech = w.part_of_speech
and o.lemma != w.lemma
order by random()
limit 3
) o on 1=1
where w.lemma = any(array['Romanus', 'domus'])
group by w.lemma, w.definition;
See fiddle. Obviously this will not necessary produce the same options as your questions provides due to random() selection. But it will get matching parts of speech. I will leave translation to your source language to you; or you can use the function option and reduce your SQL to a simple "select *".

Is there any way to set multiple LIKE options inside where clause in cakephp 3

I want to retrieve all rows matched on multiple partial prase against with a column. My situation can be explained as following raw sql:
SELECT *
FROM TABLENAME
WHERE COLUMN1 LIKE %abc%
OR COLUMN1 LIKE %bcd%
OR COLUMN1 LIKE %def%;
Here, abc, bcd, def are array elements. i.e: array('abc','bcd','def'). Is there any way to write code passing this array to form the above raw sql using cakephp 3?
N.B: I am using mysql as DBMS.
probably you can use Collection to create a proper array, but I think a foreach loop will do the job in the same amount of code. So here is my solution supposing $query stores your Query object
$a = ['abc','bcd','def'];
foreach($a as $value)
{
$or[] = function ($exp, $q) use ($value) {
return $exp->like('column1', '%'.$value.'%');
};
}
$query->where(['or' => $or]);
you could also use orWhere() but I see it will be deprecated in 3.6

JpaRepository: Spring Sort for runtime query variabels

There is my plain sql query:
SELECT id, title,
IF(o.fixed_on_top = 1 AND o.fixing_on_top_day_counter > 5, 1 ,0) actual_fixed_on_top
FROM orders o
ORDER BY actual_fixed_on_top DESC, publication_date DESC;
How can I perform sorting like this on JpaRepository?
Maybe using Criteria API?
But I dint find any examples..
Thanks!
EDIT:
ok, studied different ways, and convinced that it cant be implemented through Criteria API (but it is a pity)
One working variant: Native SQL Query, i am sure.
You can do it by using the QueryDslPredicateExecutor which provides the following two method:
Iterable<T> findAll(Predicate predicate, OrderSpecifier<?> ... orderSpecifiers);
Page<T> findAll(Predicate predicate, Pageable pageable);
The PageRequest class implements Pageable and allows you to specified the sorting you want.
You could also add a Pageable parameter in a #Query annotated method on a regular JpaRepository and Spring Data Jpa will do the rest, for example:
#Query("select e from SomeEntity e where e.param1 = :param1")
public Page<SomeEntity> findSome(#Param("param1") String param1, Pageable pageable);
(sorry cannot comment yet)
I recently had the same issue.
The only solution worked for me, is to specify the order on the relationship.
I don't see a relationship in your query
example:
#OneToMany
#OrderBy("date")
...
Spring data jpa does understand order by see doc
findByAgeOrderByLastnameDesc() will be parsed into where x.age = ?1 order by x.lastname desc clause.
I see your case a bit complicated, but you can do JQPL with #Query annotation, Sample:
#Query("SELECT o FROM Order o WHERE write your clause ORDER BY o.something desc")
public Order findByCustomPK(#Param("paramIfNeeded"));
I found the solution:
public class MyEntitySpecifications {
public static Specification<MyEntity> GetByPageSpecification() {
return new Specification<MyEntity>() {
#Override
public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
Expression fixingExpr = cb.greaterThan(root.get(MyEntity_.fixingDueDate), new DateTime(DateTimeZone.UTC));
cq.orderBy(new OrderImpl(cb.selectCase().when(fixingExpr, 1).otherwise(0), false));
return cb...;
}
};
}
}
The main idea is using case expression instead of simple logical.
plain sql equuivalent is:
select ...
from my_entity e
where ...
order by case when e.fixing_due_date > now() then 1 else 0 end desc
So we can build dynamic queries with criteria api specifications as well.
No direct access to EntityManager, no plain sql.

SELECT MAX query returns only 1 variable + codeigniter

I use codeigniter and have an issue about SELECT MAX ... I couldnot find any solution at google search...
it looks like it returns only id :/ it's giving error for other columns of table :/
Appreciate helps, thanks!
Model:
function get_default()
{
$this->db->select_max('id');
$query = $this->db->getwhere('gallery', array('cat' => "1"));
if($query->num_rows() > 0) {
return $query->row_array(); //return the row as an associative array
}
}
Controller:
$default_img = $this->blabla_model->get_default();
$data['default_id'] = $default_img['id']; // it returns this
$data['default_name'] = $default_img['gname']; // it gives error for gname although it is at table
To achieve your goal, your desire SQL can look something like:
SELECT *
FROM gallery
WHERE cat = '1'
ORDER BY id
LIMIT 1
And to utilise CodeIgniter database class:
$this->db->select('*');
$this->db->where('cat', '1');
$this->db->order_by('id', 'DESC');
$this->db->limit(1);
$query = $this->db->get('gallery');
That is correct: select_max returns only the value, and no other column. From the specs:
$this->db->select_max('age');
$query = $this->db->get('members');
// Produces: SELECT MAX(age) as age FROM members
You may want to read the value first, and run another query.
For an id, you can also use $id = $this->db->insert_id();
See also: http://www.hostfree.com/user_guide/database/active_record.html#select
CodeIgniter will select * if nothing else is selected. By setting select_max() you are populating the select property and therefore saying you ONLY want that value.
To solve this, just combine select_max() and select():
$this->db->select('somefield, another_field');
$this->db->select_max('age');
or even:
$this->db->select('sometable.*', FALSE);
$this->db->select_max('age');
Should do the trick.
It should be noted that you may of course also utilize your own "custom" sql statements in CodeIgniter, you're not limited to the active record sql functions you've outlined thus far. Another active record function that CodeIgniter provides is $this->db->query(); Which allows you to submit your own SQL queries (including variables) like so:
function foo_bar()
{
$cat = 1;
$limit = 1;
$sql = "
SELECT *
FROM gallery
WHERE cat = $cat
ORDER BY id
LIMIT $limit
";
$data['query'] = $this->db->query($sql);
return $data['query'];
}
Recently I have been utilizing this quite a bit as I've been doing some queries that are difficult (if not annoying or impossible) to pull off with CI's explicit active record functions.
I realize you may know this already, just thought it would help to include for posterity.
2 helpful links are:
http://codeigniter.com/user_guide/database/results.html
http://codeigniter.com/user_guide/database/examples.html

Fetch Single Item Using DataContext

I'm doing the following:
public MyItem FetchSingleItem(int id)
{
string query = "SELECT Something FROM Somewhere WHERE MyField = {0}";
IEnumerable<MyItem> collection = this.ExecuteQuery<MyItem>(query, id);
List<MyItem> list = collection.ToList<MyItem>();
return list.Last<MyItem>();
}
It's not very elegant really and I was hoping there's something a little better to get a single item out using DataContext. I'm extending from DataContext in my repository. There's a valid reason why before you ask, but that's not the point in this question ;)
So, any better ways of doing this?
Cheers
If it is SQL Server, change your SQL to:
SELECT TOP 1 Something FROM Somewhere ...
Or alternatavely, change these lines
List<MyItem> list = collection.ToList<MyItem>();
return list.Last<MyItem>();
into this one:
return collection.First();
myDataContext.MyItem.Where(item => item.MyField == id)
.Select(item => item.Something)
.FirstOrDefault();
The record returned is undefined, since you have no ORDER BY. So it's hard to do an exact translation. In general, though, reverse the order and take the First():
var q = from s in this.Somewhere
where s.MyField == id
orderby s.Something desc
select s.Something;
return q.First();
Relational tables are unordered. So if you don't specify the record you want precisely, you must consider the returned record as randomly selected.