How to query a single column using greenDAO 3? - greendao

I need to get data from a single column, but how can achieve this task using greenDAO?
The reason why I don't query all columns is that I think it will cut down efficiency, does it really matter?
Or greenDAO is too efficient so that it will not make any difference between query a single column or the whole entity?

If you don't want to do ItemDao.loadAll(), you can perform rawQuery(String query, String[] args) on the database to get a column
List<ColumnType> column_content = new ArrayList<>();
Cursor mCursor = daoSession.getDatabase()
.rawQuery("SELECT " +
ItemDao.Properties.COLUMN1.columnName + " FROM " + ItemDao.TABLENAME, new String[0]);
for(mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext()) {
// The Cursor is now set to the right position
column_content.add(mCursor.getColumnType(0));
}

Related

Rewrite raw PostgreSQL query to use SelectQueryBuilder from TypeORM

I've written this query that fetches user ids (that's for now, cos I actually need way more fields from the user table as well as from another table called image that is related the user table).
The problem with this query is that it returns a plain object and I need an entity object, I mean I know I could just deserialise it to whatever model I need, but the thing also is that I normally deserialise entity to a required response model. Also, I would like to avoid making
a couple of requests: one fetching user ids and the other fetching right entity objects by those ids using queryBuilder.
So, it seems that one possible solution would be to rewrite this query to make use of queryBuilder straight away.
const matchedUsers = await this.usersRepository.query(
`
SELECT id FROM users
WHERE id IN (
SELECT "usersId" FROM locations_available_fighters_users
WHERE "locationsId" IN (
SELECT "locationsId" FROM locations_available_fighters_users
WHERE "usersId" = ${ me.getId() }
)
) AND is_active IS TRUE
AND id != ${ me.getId() }
AND weight = '${ me.getWeight() }'
AND gender = '${ me.getGender() }'
AND role_name = '${ me.getRoleName() }';
`
);
If the problems you're trying to solve are:
Use a single query
Get the returned data as entity objects rather than raw results
Use QueryBuilder to avoid writing raw queries
I believe what you should be looking into is typeorm subqueries.
For the query you posted in the question, you can try something like below using QueryBuilder:
// Subquery for your inner most subquery,
const locationsQb = connection.getRepository(LocationsAvailableFightersUser)
.createQueryBuilder("lafu_1")
.select("lafu_1.locationsId")
.where("lafu_1.usersId = :usersID", { usersID: me.getId() });
// Subquery for your middle subquery,
const usersQb = connection.getRepository(LocationsAvailableFightersUser)
.createQueryBuilder("lafu_2")
.select("lafu_2.usersId")
.where("lafu_2.locationsId IN (" + locationsQb.getQuery() + ")");
// Query for retrieving `User` entities as you needed,
const matchedUsers = await connection.getRepository(User)
.createQueryBuilder("user")
.where("user.id IN (" + usersQb.getQuery() + ")")
.setParameters(locationsQb.getParameters())
.getMany();
Here I assumed that,
LocationsAvailableFightersUser is the entity for locations_available_fighters_users table
User is the entity for users table
Hope this helps you. Cheers 🍻 !!!

JPA Criteria API - possible to do a prefixed, tokenized search with wildcards?

We have a problem that at the moment we are not allowed to use ElasticSearch, so we need to implement a search function with MySQL. One desired feature is a prefixed, tokenized search, so a sentence like
"The quick brown fox jumped over the lazy dog"
could be findable when you search for "jump". I think I would need to define a rule like (pseudocode):
(*)(beginning OR whitespace)(prefix)(*)
I assume it is possible to do that with JPA (Criteria API)? But what if we have two terms? All of them have to be combined by AND, e.g. the above rule should result in TRUE for both terms in at least one column. That means "jump fox" would result in a hit, but "jump rabbit" would not. Is that also possible with Criteria API?
Or do you know a better solution than Criteria API? I heard Hibernate can do LIKE queries more elegantly (with less code) but unfortunately we use EclipseLink.
Based on the answer below here is my full solution. It's all in one method to keep it simple here ("simple JPA criteria API" is an oxymoron though). If anyone wants to use it, consider some refactoring
public List<Customer> findMatching(String searchPhrase) {
List<String> searchTokens = TextService.splitPhraseIntoNonEmptyTokens(searchPhrase);
if (searchTokens.size() < 1 || searchTokens.size() > 5) { // early out and denial of service attack prevention
return new ArrayList<>();
}
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Customer> criteriaQuery = criteriaBuilder.createQuery(Customer.class);
Root<Customer> rootEntity = criteriaQuery.from(Customer.class);
Predicate[] orClausesArr = new Predicate[searchTokens.size()];
for (int i = 0; i < searchTokens.size() ; i++) {
// same normalization methods are used to create the indexed searchable data
String assumingKeyword = TextService.normalizeKeyword(searchTokens.get(i));
String assumingText = TextService.normalizeText(searchTokens.get(i));
String assumingPhoneNumber = TextService.normalizePhoneNumber(searchTokens.get(i));
String assumingKeywordInFirstToken = assumingKeyword + '%';
String assumingTextInFirstToken = assumingText + '%';
String assumingPhoneInFirstToken = assumingPhoneNumber + '%';
String assumingTextInConsecutiveToken = "% " + assumingText + '%';
Predicate query = criteriaBuilder.or(
criteriaBuilder.like(rootEntity.get("normalizedCustomerNumber"), assumingKeywordInFirstToken),
criteriaBuilder.like(rootEntity.get("normalizedPhone"), assumingPhoneInFirstToken),
criteriaBuilder.like(rootEntity.get("normalizedFullName"), assumingTextInFirstToken),
// looking for a prefix after a whitespace:
criteriaBuilder.like(rootEntity.get("normalizedFullName"), assumingTextInConsecutiveToken)
);
orClausesArr[i] = query;
}
criteriaQuery = criteriaQuery
.select(rootEntity) // you can also select only the display columns and ignore the normalized/search columns
.where(criteriaBuilder.and(orClausesArr))
.orderBy(
criteriaBuilder.desc(rootEntity.get("customerUpdated")),
criteriaBuilder.desc(rootEntity.get("customerCreated"))
);
try {
return entityManager
.createQuery(criteriaQuery)
.setMaxResults(50)
.getResultList();
} catch (NoResultException nre) {
return new ArrayList<>();
}
}
The Criteria API is certainly not intended for this but it can be used to create LIKE predicates.
So for each search term and each column you want to search you would create something like the following:
column like :term + '%'
or column like ' ' + :term + '%'
or column like ',' + :term + '%'
// repeat for all other punctuation marks and forms of whitespace you want to support.
This will create horribly inefficient queries!
I see the following alternatives:
Use database specific features. Some databases have some text search capabilities.
If you can limit your application to one or few databases that might work.
Create your own index: Use a proper tokenizer to analyze the columns you want to search and put the resulting tokens in a separate table with backreferences to the original table.
Now search that for the terms you are looking for.
As long as you do only prefixed searches database indexes should be able to keep this reasonable efficient and it is easier to maintain and more flexible than what you can obtain by using the Criteria API on its own.

Doing subqueries in Mybatis, or query recursively the selected values

UPDATE:
I understood that the solution to my problem is doing subqueries, which apply a different filter each time, and they have a reduced result set. But I can't find a way to do that in MyBatis logic. Here is my query code
List<IstanzaMetadato> res = null;
SqlSession sqlSession = ConnectionFactory.getSqlSessionFactory().openSession(true);
try {
IstanzaMetadatoMapper mapper = sqlSession.getMapper(IstanzaMetadatoMapper.class);
IstanzaMetadatoExample example = new IstanzaMetadatoExample();
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, String> entry = it.next();
example.createCriteria().andIdMetadatoEqualTo(entry.getKey()).andValoreEqualTo(entry.getValue());
}
example.setDistinct(true);
res = mapper.selectByExample(example);
I need to execute a new selectByExample but inside the while cycle, and it has to query the previus "SELECTED" results....
Is there a Solution ?
ORIGINAL QUESTION:
I have this table structure
I have to select rows from the table with different filters, specified by the final user.
Those filters are specified by a couple (id_metadato, valore), in example you can have id_metadato = 3 and valore = "pippo";
the user can specify 0-n filters from the web page typing 0-n values inside the search boxes which are based on id_metadato
Obviusly, the more filters the users specifies, the more restriction would have the final query.
In example if the user fills only the first search box, the query will have only a filter and would provide all the rows that will have the couple (id_metadato, valore) specified by the user.
If he uses two search boxes, than the query will have 2 filters, and it will provide all the rows that verify the first condition AND the second one, after the "first subquery" is done.
I need to do this dinamically, and in the best efficient way. I can't simply add AND clause to my query, they have to filter and reduce the result set every time.
I can't do 0-n subqueries (Select * from ... IN (select * from ....) ) efficiently.
Is there a more elegant way to do that ? I'm reading dynamic SQL queries tutorials with MyBatis, but I'm not sure that is the correct way. I'm still trying to figure out the logic of the resosultio, then I will try to implement with MyBatis.
Thanks for the answers
MyBatis simplified a lot this process of nesting subqueries, it was sufficient to concatenate the filter criterias and to add
the excerpt of the code is the following
try {
IstanzaMetadatoMapper mapper = sqlSession.getMapper(IstanzaMetadatoMapper.class);
IstanzaMetadatoExample example = new IstanzaMetadatoExample();
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, String> entry = it.next();
if (listaIdUd.isEmpty()) {
example.createCriteria().andIdMetadatoEqualTo(entry.getKey()).andValoreEqualTo(entry.getValue());
example.setDistinct(true);
listaIdUd = mapper.selectDynamicNested(example);
continue;
}
example.clear();
example.createCriteria().andIdMetadatoEqualTo(entry.getKey()).andValoreEqualTo(entry.getValue()).andIdUdIn(listaIdUd);
example.setDistinct(true);
listaIdUd = mapper.selectDynamicNested(example);
}

linq nested query conditional order by

I have a parent query with sub query return child ,
what i need to sort child query according to one property in parent.
here is sudo code:
from menu in db.Menus
orderby menu.Order
select new
{
Title= menu.Title,
OrderNumber = menu.Order,
data = (from menuItem in menu.Items
let g = Guid.NewGuid()
orderby g
select new
{
id = worker.ID,
Title = worker.JobTitle
})
.Take(4)
};
that works ok,But what i need to sort some menu in random(NewGuid) and sort others with their item priority some thing like it:
let g = Guid.NewGuid()
orderby menu.ISRandom ? g: menuItem.Order
But it give error about mismatch guid and int.What's the soloution?
second:How can i replace take(4) with take(menu.size)?
thank's
You can solve the ordering problem by adding ToString():
let g = Guid.NewGuid().ToString()
orderbymenu.ISRandom ? g : menuItem.Order.ToString()
Using Take(someProperty) isn't allowed in an EF LINQ query. This is because the take is translated into a TOP(x) clause, which can't possibly refer to a column in the SQL result. You can only do this afterwards, after pulling the results into memory without Take (or taking some reasonable fixed maximum).
It seems you want to read four records from menu.Items at random.
I think you'd be way better off just reading all the items, and then doing the random selection later in code in memory.

How to write JPA Query to the equivalent mysql query

I want to write equivalent JPA query to the following mysql query
select active_package,sum(duration),sum(charge),count(*)
from User
where call_type="MO"
and start_date between '2012-02-01' and '2012-02-09'
group by active_package;
For JPA Query the corresponding Attributes are below.
activePackage,callType,duration,charge,startDate
Entity class is User.
I want to use the createQuery() of JPA.
Can any one tell me or give me the link where can i find the solution for this.
Try this one, it should work, if not, please comment, we will get it work :).
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createQuery(Tuple.class);
Root<User> entity = cq.from(User.class);
cq.multiselect(entity.get("activePackage"),
cb.sum(entity.get("duration").as(Long.class)),
cb.sum(entity.get("charge").as(Double.class),
cb.count(entity).as(Long.class)));
cq.where(cb.equal(entity.get("callType"), "MO"),
cb.between(entity.get("startDate").as(Date.class),
new Date(), new Date()));
cq.groupBy(entity.get("activePackage"));
List<Tuple> resultList = entityManager.createQuery(cq).getResultList();
for (Tuple result : resultList) {
System.out.println(result.get(0) + " " + result.get(1)
+ " " + result.get(2) + " " + result.get(3));
}
Also if you want to filter only by date, but have timestamp in your model, you can check this Compare date's date part only with Timestamp in Hibernate answer.
Also JPA provides constructing result classes as return values, so you can group your columns. Read more.