Is It Possible To Cast A Range - grails

I'd like to do something like this:
def results = Item.findAll("from Item c, Tag b, ItemTag a where c = a.item and b = a.tag and (b.tag like :q or c.uri like :q) " + ob,[q:q])
def items = (Item) results[0..1][0]
but I get
Cannot cast object '[Ljava.lang.Object;#1e224a5' with class '[Ljava.lang.Object;' to class 'org.maflt.ibidem.Item'
I can get what I need with this, but it doesn't seem like it's the best solution:
def items = [] as Set
def cnt = results.size()
for (i=0;i<cnt-1;i++) { items << results[i][0] }
items = items as List
UPDATE
The solution suggested to use
def results = Item.findAll("from Item c, Tag b, ItemTag a where c = a.item and b = a.tag and (b.tag like :q or c.uri like :q) " + ob,[q:q])
def items = results[0] as List
doesn't work. The query actually produces the correct SQL:
select
item0_.id as id3_0_,
tag1_.id as id16_1_,
itemtag2_.id as id6_2_,
item0_.version as version3_0_,
item0_.last_updated as last3_3_0_,
item0_.docsize as docsize3_0_,
item0_.customer_id as customer5_3_0_,
item0_.uri as uri3_0_,
item0_.created_by_person_id as created7_3_0_,
item0_.updated_by_person_id as updated8_3_0_,
item0_.md5 as md9_3_0_,
item0_.original_file_name as original10_3_0_,
item0_.date_created as date11_3_0_,
item0_.doctype as doctype3_0_,
item0_.identifier as identifier3_0_,
tag1_.version as version16_1_,
tag1_.tagtype_id as tagtype3_16_1_,
tag1_.tag as tag16_1_,
tag1_.last_updated as last5_16_1_,
tag1_.customer_id as customer6_16_1_,
tag1_.created_by_person_id as created7_16_1_,
tag1_.updated_by_person_id as updated8_16_1_,
tag1_.date_created as date9_16_1_,
itemtag2_.version as version6_2_,
itemtag2_.updated_by_person_id as updated3_6_2_,
itemtag2_.weight as weight6_2_,
itemtag2_.tag_id as tag5_6_2_,
itemtag2_.item_id as item6_6_2_,
itemtag2_.last_updated as last7_6_2_,
itemtag2_.date_created as date8_6_2_,
itemtag2_.source_id as source9_6_2_,
itemtag2_.created_by_person_id as created10_6_2_
from
item item0_,
tag tag1_,
item_tag itemtag2_
where
item0_.id=itemtag2_.item_id
and tag1_.id=itemtag2_.tag_id
and (
lower(tag1_.tag) like '%english%'
or lower(item0_.original_file_name) like '%english%'
)
order by
item0_.id
But unfortunately the items = results[0] as List doesn't work. It only returns 3 rows and only items[0] is an Item.
items.each{println it.class}
gives:
class org.maflt.ibidem.Item
class org.maflt.ibidem.Tag
class org.maflt.ibidem.ItemTag

As your HQL selects several entities, you should be more specific. The following should return only items:
def items = Item.executeQuery("select c from Item as c, Tag as b, ItemTag as a where c = a.item and b = a.tag and (b.tag like :q or c.uri like :q) " + ob,[q:q])
Cheers!

Related

Chaining multiple ActiveRecord `or` queries

I've got an array of columns that I want to loop through and optionally chain an or query onto an ActiveRecord query chain. I can get it to work, but the resulting query appends the or onto the query chain, therefore making the columns in my inital query optional. Here's my class:
class Claim
class MatchingAttributeFinder
ATTRIBUTE_GROUPS_TO_MATCH = [
["teacher_reference_number"],
["email_address"],
["national_insurance_number"],
["bank_account_number", "bank_sort_code", "building_society_roll_number"],
].freeze
def initialize(source_claim, claims_to_compare = Claim.submitted)
#source_claim = source_claim
#claims_to_compare = claims_to_compare
end
def matching_claims
claims = #claims_to_compare.where.not(id: #source_claim.id)
ATTRIBUTE_GROUPS_TO_MATCH.each do |attributes|
vals = values_for_attributes(attributes)
next if vals.blank?
concatenated_columns = "CONCAT(#{attributes.join(",")})"
claims = claims.or(
Claim.where("LOWER(#{concatenated_columns}) = LOWER(?)", vals.join)
)
end
claims
end
private
def values_for_attributes(attributes)
attributes.map { |attribute|
#source_claim.read_attribute(attribute)
}.reject(&:blank?)
end
end
end
The generated SQL looks like this:
SELECT "claims".* FROM "claims" WHERE (((("claims"."submitted_at" IS NOT NULL AND "claims"."id" != 'a7b25b99-4477-42b1-96ab-8262582c5541' OR (LOWER(CONCAT(teacher_reference_number)) = LOWER('0902344'))) OR (LOWER(CONCAT(email_address)) = LOWER('genghis.khan#mongol-empire.com'))) OR (LOWER(CONCAT(national_insurance_number)) = LOWER('QQ891011C'))) OR (LOWER(CONCAT(bank_account_number,bank_sort_code,building_society_roll_number)) = LOWER('34682151972654123456789/ABCD')))
But what I actually want is more like this:
SELECT "claims".* FROM "claims" WHERE "claims"."submitted_at" IS NOT NULL AND "claims"."id" != 'd6a53b4d-c569-49e6-a2ea-ac44b69b0451' AND (LOWER(concat(teacher_reference_number)) = LOWER('0902344') OR LOWER(concat(email_address)) = LOWER('genghis.khan#mongol-empire.com') OR LOWER(concat(national_insurance_number)) = LOWER('QQ891011C') OR LOWER(concat(bank_account_number,bank_sort_code,building_society_roll_number)) = LOWER('34682151972654123456789/ABCD'))
Is there any way to set up something like an empty scope that I can chain my OR queries to?
Try chaning all the "or" together first and then chain the original query
def matching_claims
claims = #claims_to_compare.where.not(id: #source_claim.id)
ors = nil
ATTRIBUTE_GROUPS_TO_MATCH.each do |attributes|
vals = values_for_attributes(attributes)
next if vals.blank?
concatenated_columns = "CONCAT(#{attributes.join(",")})"
aux = Claim.where("LOWER(#{concatenated_columns}) = LOWER(?)", vals.join)
if ors.nil?
ors = aux
else
ors = ors.or(aux)
end
end
claims.merge(ors)
end

How to efficiently retrieve groups of objects from activerecord with a single SQL query?

I have a table products which has a product_type_code column on it. What I'd like to do is retrieve different numbers of objects based on this column (eg.: 3 products with product_type_code = 'fridge', 6 products with product_type_code = 'car', 9 products with product_type_code = 'house', etc.).
I know I can do like this:
fridges = Product.where(product_type_code: 'fridge').limit(3)
houses = Product.where(product_type_code: 'house').limit(9)
[...]
And even create a scope like this:
# app/models/product.rb
scope :by_product_type_code, -> (material) { where(product_type_code: product_type_code) }
However, this is not efficient since I go to the database 3 times, if I'm not wrong. What I'd like to do is something like:
scope :by_product_type_code, -> (hash) { some_method(hash) }
where hash is: { fridge: 3, car: 6, house: 9 }
and get an ActiveRecord_Relation containing 3 fridges, 6 cars and 9 houses.
How can I do that efficiently?
You can create a query using UNION ALL, which selects records having a specifc product_type_code and limit to use it with find_by_sql:
{ fridge: 3, car: 6, house: 9 }.map do |product_type_code, limit|
"(SELECT *
FROM products
WHERE product_type_code = '#{product_type_code}'
LIMIT #{limit})"
end.join(' UNION ALL ')
And you're gonna have a query like:
(SELECT * FROM products WHERE product_type_code = 'fridge'LIMIT 3)
UNION ALL
(SELECT * FROM products WHERE product_type_code = 'car'LIMIT 6)
UNION ALL
(SELECT * FROM products WHERE product_type_code = 'house'LIMIT 9)
#SebastianPalma's answer is the best solution; however if you were looking for a more "railsy" fashion of generating this query you can use arel as follows:
scope :by_product_type_code, ->(h) {
products_table = self.arel_table
query = h.map do |product_type,limit|
products_table.project(:id)
.where(products_table[:product_type_code].eq(product_type))
.take(limit)
end.reduce do |scope1, scope2|
Arel::Nodes::UnionAll.new(scope1,scope2)
end
self.where(id: query)
end
This will result in the sub query being part of the where clause.
Or
scope :by_product_type_code, ->(h) {
products_table = self.arel_table
query = h.map do |product_type,limit|
products_table.project(Arel.star)
.where(products_table[:product_type_code].eq(product_type))
.take(limit)
end.reduce do |scope1, scope2|
Arel::Nodes::UnionAll.new(scope1,scope2)
end
sub_query = Arel::Nodes::As.new(query,products_table)
self.from(sub_query)
end
This will result in the subquery being the source of the data.

Rails combine 4 arrays and sort the resulting array by "created_at"

So I am trying to combine 4 arrays of different items and then sort those arrays by the column "created_at" as in newest first. So the resulting array should be an array of all those items sorted so that the newest item is the first no matter what type they are. I can sort it so that the newest item but it sorts through each type of item and then start sorting the other type.
Example: the types are shirts and pants. I upload a shirt at 10:00pm and then a pant at 10:15pm and then again a shirt at 10:30pm. The array should be like (shirt(10:30), pant(10:15), shirt(10:00)) but instead I get a (shirt(10:00), shirt(10:30), pant(10:15))
Here is my code:
#tops = Top.first(15)
#bottoms = Bottom.first(15)
#footwears = Footwear.first(15)
#accs = Accessory.first(15)
#tempItems = []
#temp = []
#temp = #tempItems + #tops
#temp = #temp + #bottoms
#temp = #temp + #footwears
#temp = #temp + #accs
#temp.sort_by{ |temp| - temp.created_at.to_i}
#itemss = #temp.first(15)
You need to assign the sorted array back to #temp
...
#temp = #temp.sort_by{ |temp| - temp.created_at.to_i}
You can reduce the confusion by getting rid of unnecessary assignment code:
fifteen= [Top, Bottom, FootWear, Accessory].
flat_map{ |c| c.first(15) }.
sort_by{ |e| -e.created_at.to_i }.
first(15)

how to convert sql query in ruby and rails?

SELECT A.FirstName, A.LastName, B.PatientId, B.RoomNumber, B.AdmissionDate, B.DischargeDate, B.MeasureCategory
FROM DimPatient A, DimPatientStay B
WHERE A.Id = B.PatientId AND A.FirstName = 'Anuj' AND B.MeasureCategory = 'ED'
hi some updation for this
i solved this prob by
MODELNAME.find_by_sql("your sql query")
You can try this to find the result from sql query in Rails
query_params = Hash.new
sql_query = "SELECT A.FirstName, A.LastName, B.PatientId, B.RoomNumber, B.AdmissionDate, B.DischargeDate, B.MeasureCategory
FROM DimPatient A, DimPatientStay B
WHERE A.Id = B.PatientId AND A.FirstName = :first_name AND B.MeasureCategory = :measure_category"
query_params[:first_name] = first_name
query_params[:measure_category] = measure_category
#query_results = ActiveRecord::Base.connection.select_all(
ActiveRecord::Base.send("sanitize_sql_array",[sql_query, query_params] )
)
I guess you could try:
ActiveRecord::Base.connection().execute(#your_sql_here)
Suppose A is one class and B is another, you should use includes as following:
A.includes(:b).where(...) # add you condition in where
I suggest to check good video tutorials of ActiveRecord here

Need help in transforming this SQL statement to GORM

select
b.security_type,
b.symbol,
b.security_description,
b.trade_date_qty as 'axys_qty',
c.trade_date_qty as 'fidelity_qty',
c.trade_date_qty - b.trade_date_qty as 'qty_diff',
b.cost_basis as 'axys_cost',
c.cost_basis as 'fidelity_cost',
c.cost_basis - b.cost_basis as 'cost_diff'
from
account a
inner join advent_position b on a.fixed_account_number = b.account_number
inner join fidelity_position c on a.fixed_account_number = c.account_number and b.symbol = c.symbol
where
b.account_number = '636296651'
Basically, I have the ff. domains: Account, AdventPosition, FidelityPosition. I haven't set the relationship yet. I'm just wondering if there's a way to replicate the logic above using Criteria or HQL. Forgive me, I'm still new to Grails.
Thank you for any leads on this.
It'd be something close to this:
String hql = '''
select
b.securityType,
b.symbol,
b.securityDescription,
b.tradeDateQty,
c.tradeDateQty,
c.tradeDateQty - b.tradeDateQty,
b.costBasis,
c.costBasis,
c.costBasis - b.costBasis
from
Account a, AdventPosition b, FidelityPosition c
where
a.fixedAccountNumber = b.accountNumber
and a.fixedAccountNumber = c.accountNumber
and b.symbol = c.symbol
and b.accountNumber = :accountNumber
'''
def accountNumber = '636296651'
def results = Account.executeQuery(hql, [accountNumber: accountNumber])
The results will be an ArrayList of Object[], so you can iterate it with something like
for (row in results) {
def securityType = row[0]
def symbol = row[1]
def securityDescription = row[2]
def axys_qty = row[3]
def fidelity_qty = row[4]
def qty_diff = row[5]
def axys_cost = row[6]
def fidelity_cost = row[7]
def cost_diff = row[8]
}
I replaced the hard-coded account number with a named parameter; you can use regular ? like in SQL if you prefer and run 'def results = Account.executeQuery(hql, [accountNumber])', and of course if you intented it to be hard-coded then restore that and don't pass in the 2nd parameter, just run 'def results = Account.executeQuery(hql)'
just sharing the solution that I came up (while waiting for an answer :P) but note that the previous answer is way much better and faster:
def acc = Account.findByFixedAccountNumber('636296651')
List advPos = AdventPosition.findAllByAccountNumber('636296651')
List fidPos = advPos.collect {
FidelityPosition.findAllByAccountNumberAndSymbol('636296651', it.symbol)
}
def item = [:]
def res = []
def limit = advPos.size() - 1
for(i in 0..limit){
item.security_type = advPos[i].securityType
item.symbol = advPos[i].symbol
item.security_description = advPos[i].securityDescription
item.axys_qty = advPos[i].tradeDateQty
item.fidelity_qty = fidPos[i].tradeDateQty
item.qty_diff = item.fidelity_qty - item.axys_qty
item.axys_cost = advPos[i].costBasis
item.fidelity_cost = fidPos[i].costBasis
item.cost_diff = item.fidelity_cost - item.axys_cost
res.add(item)
}

Resources