Let's have a table elements with many string columns (c1, c2, c3)
Is there a rails way to find_by_all(my_string) where at least one of columns is set to "my_string" without listing all of them instead of querying like this :
Element.where(c1: "my_string").or.where(c2: "my_string").or.where(c3: "my_string")
or
Element.find_by_c1("my_string") || Element.find_by_c2("my_string") || Element.find_by_c3("my_string")
(I know i can use sql trick like: Element.where(CONCAT_WS('|', c1, c2, c3) LIKE '%my_string%'), but it's not really railzy)
You have to list all of columns you want to query.
columns = %w(c1 c2 c3)
Or use column_names method
columns = Element.column_names
then use rails_or and do it like
str = 'my_string'
columns.inject(Element.where(0)){|model, col| model.or(col => str) }
Note that you may want to exclude none-string columns like id, created_at, updated_at:
columns -= %w(id created_at updated_at)
You can do something like following (Of course if string is same)
Element.where('c1= :var OR c2= :var OR c3= :var', { var: 'my_string' })
For all the columns
Element.where(%w(c1 c2 c3).map{|col| "#{col} = :var"}.join(' OR '), { var: 'my_string' })
Related
I have the following Google Seet Table
Old New New2
W01
W02 W04
W03 W05 W06
I want to create a formular that transforms the table to this one
Old New
W02 W04
W03 W05
W05 W06
So any time a switch from Old to New or New to New2 happens should be displayed.
I wrote the following formular but i always get an error:
= IFS(B1 = "";""; AND(NOT(B1 = ""); NICHT(C1 = ""));FILTER({A1\ B1}; NICHT(A1=""));NICHT(B1 = "");FILTER({B1\ C1}; NICHT(B1="")))
Has anybody an idea?
Concatenate the results of two Query calls:
={
QUERY(A1:B4,
"select A,B where B<>''");
QUERY(B1:C4,
"select B,C where C<>'' label B '', C ''", 1)
}
or in German locale:
={
QUERY(A1:B4;
"select A,B where B<>''");
QUERY(B1:C4;
"select B,C where C<>'' label B '', C ''"; 1)
}
The label statements in the second query are necessary to suppress the column labels since you want to treat certain columns in New1 as Old.
I need to build a table based on the following data:
Ref
Product
R1
ProdA
R2
ProdC
R1
ProdB
R3
ProdA
R4
ProdC
And here the result I need:
My Product
All Ref
ProdA
R1#R3
ProdC
R2#R4
The particularity is that the 'My Product' column is computed elsewhere. So I need an arrayformula based on 'My Product' column to look in the first table to build the 'All Ref' column. You follow me?
I know that Arrayformula is not compatible with filter and join ... I expect a solution like this one Google sheet array formula + Join + Filter but not sure to understand all steps and if really adapted to my case study.
Hope you can help.
You could try something like this:
CREDIT: player0 for the method shared to similar questions
=ARRAYFORMULA(substitute(REGEXREPLACE(TRIM(SPLIT(TRANSPOSE(
QUERY(QUERY({B2:B&"😊", A2:A&"#"},
"select max(Col2)
where Col1 !=''
group by Col2
pivot Col1"),,999^99)), "😊")), "#$", )," ",""))
Step by step:
Instead of the workaround hacks I implemented a simple joinMatching(matches, values, texts, [sep]) function in Google Apps Script.
In your case it would be just =joinMatching(MyProductColumn, ProductColumn, RefColumn, "#").
Source:
// Google Apps Script to join texts in a range where values in second range equal to the provided match value
// Solves the need for `arrayformula(join(',', filter()))`, which does not work in Google Sheets
// Instead you can pass a range of match values and get a range of joined texts back
const identity = data => data
const onRange = (data, fn, args, combine = identity) =>
Array.isArray(data)
? combine(data.map(value => onRange(value, fn, args)))
: fn(data, ...(args || []))
const _joinMatching = (match, values, texts, sep = '\n') => {
const columns = texts[0]?.length
if (!columns) return ''
const row = i => Math.floor(i / columns)
const col = i => i % columns
const value = i => values[row(i)][col(i)]
return (
// JSON.stringify(match) +
texts
.flat()
// .map((t, i) => `[${row(i)}:${col(i)}] ${t} (${JSON.stringify(value(i))})`)
.filter((_, i) => value(i) === match)
.join(sep)
)
}
const joinMatching = (matches, values, texts, sep) =>
onRange(matches, _joinMatching, [values, texts, sep])```
Setup: Rails + Postgres.
I have table A with columns
id: int, name: string, address: string, s_array: varchar[], i_array: int[], created_at: datetime)
For a row, i need to find all the rows which have similar values.
In rails, then query would look like
row = A.find(1) # any random row
ignore_columns = %w[id created_at]
A.where(row.attributes.except(*ignore_columns))
This works if we don't have column with array type.
How to find all records where a value = [given array]?
Edit:
To be clear, I want to pass multiple columns in where clause where some columns are of type array. For values in where clause, I am passing hash (row.attributes.except(*ignore_columns) is a hash)
Edit 2: Example:
Lets say I have a Query table
Query(id: Int, name: String, terms: varchar[], filters: int[], city: string, created_at: datetime)
id = primary key/integer
terms = array of string
filters = array of integer (it is an enum and we can select multiple which is saved as array)
other fields = self explanatory
Suppose I have following rows
(1, "query1", ["john"], [0], "wall", <some_date>)
(1, "query2", ["eddard", "arya"], [0, 1], "Winterfell", <some_date>)
(1, "query3", ["sansa", "arya"], [1, 2], "Winterfell", <some_date>)
Now when I add new row
row = ActiveRecord of (1, "query4", ["eddard", "arya"], [0, 1], "Winterfell", <some_date>)
What I want is to search already existing records like this
ignore_attributes = %w[id name created_at]
Query.where(row.attributes.except(*ignore_attributes))
This query should return already existing query3 so that I won't need to add new row with name query4.
The problem is that because some column types are of array type, then passing them as hash/conditions in where clause is not working.
use find_by to find_all_by and it will return all matching results.
Try this example:-
##black list of attributes
ignore_attributes = %w[id name created_at]
MyModel.where(active: true).select(MyModel.attribute_names - ignore_attributes)
=====The above query can also be chained as:- =====
##you have a city column too
MyModel.where(active: true).select(MyModel.attribute_names - ignore_attributes).where.not(:city=>["Sydney","London"])
If you need this a permanent fix,you can add this line in your model.rb file.
but its dangerous.
self.ignored_columns = %w(id name created_at)
Hope it helps :)
Your query in rails look like below
row = A.find(1)
where_clauses = row.attributes.reject{|k,v| %[id, created_at].include?(k)}
A.where(where_clauses)
Let's say you have a simple query in Rails which goes like this
a = 42
Klass.where("`column_1` = ? OR `column_2` = ? OR `column_3` = ?", a, a, a)
Can this be done more elegantly so that you don't need to type a, a, a three times? It works fine but it looks horrible.
Can try something like this
Klass.where(["`column` = :a OR `column` = :a OR `column` = :a", { a: user_name }])
No you cannot do this more elegantly.
I'm guessing your columns should all be different columns, right? Please be more specific in your question.
You can only simplify the statement if the columns are the same and the values are different.
abc = [42,21,84]
Klass.where("column IN (?)", abc)
Personally I will use a find by with a in condition such as:
Klass.find_all_by_column([1, 2, 3])
Replace 'column' with column name you wish to search in.
Replace 1,2,3 by your value, it's an array you can add or remove values from it.
My example will generate a SQL query such as:
SELECT * FROM Klass WHERE Klass.column IN (1,2,3)
More on this on Active record documentation.
Maybe your line
Klass.where("`column` = ? OR `column` = ? OR `column` = ?", a, a, a)
can be changed into
Klass.where(["`column` = ? OR `column` = ? OR `column` = ?"] + a*3)
having the domain classes:
class A {
Date dateCreated
static hasMany = [b:B]
...
}
class B {
String name
String value
...
}
What createCriteria or HQL query can I use to return a list with:
A's creation dateB's value for A with the name entry set to 'X'
Note: Although there's no explicit constraint, there's only one "value" for each 'X' and a combination.
Thanks
The HQL would be
def results = A.executeQuery(
'select a.id, a.dateCreated, b from A a inner join a.b b ' +
'where b.name=:name',
[name: 'X'])
This will give you a List of 3-element Object[] arrays containing A.id, A.dateCreated, and the list of B instances. I added the id to the query so you can group by it client-side:
def grouped = results.groupBy { it[0] }
This will be a Map where the keys are the A ids and the values are the Lists from the original results.
Ideally you'd do the grouping at the database, but it would complicate the query, and assuming you don't have a large number of results it should be fast.