In SQLAlchemy, for example, I'd like to search for keywords "foo", "bar" and possibly but not necessarily more keywords in the "description" column of my Item class, so I build a list of such keywords:
["foo", "bar"],
and then use primitive looping to achieve my goal:
query_obj = session.query(Item)
for k in search_keywords:
query_obj = query_obj.filter(Item.description.like('%{0}%'.format(k)))
but this doesn't seem very elegant. I think there could be a better solution in sqlalchemy. Could someone share some tips other than looping and chaining? Thanks.
p.s. Also what is the best practice to achieve a "OR" search of multiple(indefinite number of) keywords?
Your code is as elegant as it gets, I do not think you can improve it significantly.
In order to solve the same for OR, you can collect the clauses into a list first, and then apply or_ with the filter(...):
q = session.query(Item)
clauses = [Item.description.like('%{0}%'.format(k)) for k in search_keywords]
q = q.filter(or_(*clauses))
If you find it more elegant, you can use it for AND case you have as well by calling and_ instead of or_.
Related
I'm trying to write a for loop to generate multiple functions. I would like to iterate on the name of the functions as well but I can't seem to make it work. Here's what I want to do:
for i = 1:n
h_[i](x) = i*x
end
I know this doesn't work but I would like something like that.
Thanks!
As #laborg mentioned, quite often you don't need/want to use metaprogramming. Since you already try to use array indexing syntax (h_[i]) perhaps you should simply create a vector of functions, for example like so:
h_ = [x->i*x for i in 1:n]
Here, x->i*x is an anonymous function - a function that we don't care to give a name. Afterwards you can use actual array indexing to access these different functions and call them.
Demo:
julia> n = 3;
julia> h_ = [x->i*x for i in 1:n];
julia> h_[1](3)
3
julia> h_[2](3)
6
julia> h_[3](3)
9
No metaprogramming involved.
(On a side note, in this particular example a single function h(x;i) = i*x with a keyword argument i would probably the best choice. But I assume this is a simplified example.)
You have to evaluate the name of your function:
for i = 1:n
f = Symbol(:h_,i)
#eval $f(x) = $i*x
end
More on this:
https://docs.julialang.org/en/v1/manual/metaprogramming/
As a general note on meta programming in Julia: It really helps to think twice if meta programming is the best solution, especially as Julia offers a lot of other cool features, e.g. multiple dispatch.
I have a database with Lab models. I want to be able to search them using multiple different methods.
I chose to use one input field and separate query into words array:
search = search.split(/[^[[:word:]]]+/).map{|val| val.downcase}
I use Acts-as-taggable gem so it would be nice to include those tags in search to:
tag_results = self.tagged_with(search, any: true, wild: true)
For methods down below it seemed to be necessary to use:
search = search.map{|val| "%#{val}%"}
Sunspot seemed also a great way to go for full-text search so
full_text_search = self.search {fulltext search}
full_text_results = full_text_search.results
I decided to go also with simple database query searching for a Lab name:
name_results = self.where("LOWER(name) ILIKE ANY ( array[?] )", search)
Lastly I need all of the results in one array so:
result = (tag_results + name_results + full_text_results).uniq
It works perfectly (what I mean is that the result is what I expect) but it returns a simple array and not ActiveRecord::Relation so there is no way for me to use method like .select() or .order() on the results.
I want to ask is there is some better way to implement such search? I was searching for search engines but it seems like there is nothing that would fit my idea.
If there is not - is there a way to convert an array into ActiveRecord::Relation? (SO says there is no way)
Answering this one:
is there a way to convert an array into ActiveRecord::Relation? (SO
says there is no way)
You can convert an array ob ActiveRecord objects into ActiveRecord::Relation by fetching ids from array and querying your AR model for objects with these ids:
Model.where(id: result.map(&:ids)) # returns AR, as expected.
It is the only way I am aware of.
I am using django with postgres, and have a bunch of JSON fields (some of them quite large and detailed) within my model. I'm in the process of switching from char based ones to jsonb fields, which allows me to filter on a key within the field, and I'm wondering if there is any way to get the equivalent benefit out of a call to the query set values method.
Example:
What I would like to do, given a Car model with options JSONField, is something like
qset = Car.objects.filter(options__interior__color='red')
vals = qset.values('options__interior__material')
Please excuse the lame toy problem, but hopefully it gets the idea across. Here the filter call does exactly what I want, but the call to values does not seem to be aware of the special nature of the JSON field. I get an error because values can't find the field called "interior" to join on. Is there some other syntax or option that I am missing that will make this work?
Seems like a pretty obvious extension to the existing functionality, but I have so far failed to find any reference to something similar in the docs or through stack overflow or google searches.
Edit - a workaround:
After playing around, looks like this could be fudged by inserting the following in between the two lines of code above:
qset=qset.annotate(options__interior__material=RawSQL("SELECT options->'interior'->'material'",()))
I say "fudged" because it seems like an abuse of notation and would require special treatment for integer indices.
Still hoping for a better answer.
I can suggest a bit cleaner way with using:
django's Func
https://docs.djangoproject.com/en/2.0/ref/models/expressions/#func-expressions
and postgres function jsonb_extract_path_text https://www.postgresql.org/docs/9.5/static/functions-json.html
from django.db.models import F, Func, CharField, Value
Car.objects.all().annotate(options__interior__material =
Func(
F('options'),
Value('interior'),
Value('material'),
function='jsonb_extract_path_text'
),
)
Perhaps a better solution (for Django >= 1.11) is to use something like this:
from django.contrib.postgres.fields.jsonb import KeyTextTransform
Car.objects.filter(options__interior__color='red').annotate(
interior_material=KeyTextTransform('material', KeyTextTransform('interior', 'options'))
).values('interior_material')
Note that you can nest KeyTextTransform expressions to pull out the value(s) you need.
Car.objects.extra(select={'interior_material': "options#>'{interior, material}'"})
.filter(options__interior__color='red')
.values('interior_material')
You can utilize .extra() and add postgres jsonb operators
Postgres jsonb operators: https://www.postgresql.org/docs/9.5/static/functions-json.html#FUNCTIONS-JSON-OP-TABLE
I've got a Product model which has_many OptionValue records, which describe color, size, etc.
Within my code, I need to query the Product model where the product.option_values.pluck(:id) array exactly matches an array of (e.g.) options = [1, 6, 4].
Running something like Product.includes(:option_values).where(option_values: { id: options_array }) returns all values that match at least one element of the options array, rather than all of them.
I've developed an inefficient way of getting the record I need, as follows:
Product.all.each { |v| return v if v.option_values.pluck(:id).sort == options_array.sort }
Obviously the above is way ott and I'm sure there's a simpler way to handle this, and I'm happy to use ActiveRecord or a straight SQL query (though I'm not too hot on the latter, so haven't come up with anything yet).
Any advice on the best way of achieving this greatly appreciated. Not sure I've explained this perfectly, so please comment if you've any questions.
Thanks in advance, Steve.
Clocked this in my old questions and threw together a quick and easy solution, so dropping it here for anyone else that might come this way:
product_options = product.option_values.pluck(:id)
unless options_array.length < product_options.length
(product_options & options_array).length == product_options.length
end
This:
checks the options_array is long enough to contain the necessary matches
looks for the elements in common across the two arrays ((product_options & options_array))
measures their length
checks this is the same length as the desired array product_options, meaning all the required options were found.
Alternatively, you can subtract one from the other and check there's nothing leftover (i.e. missing):
(product_options - options_array).empty?
Hope this helps someone at some point.
I have a table that stores queries that return a list of users.
I then have a method "get_public" to a "Banana" model that execute multiple queries using logic AND between them.
So, when I do
Banana.find(x).get_public I receive an Array of users (the ones suitable to that banana object).
The get_public method is like this:
def get_public
pb = []
banana_queries.each do |q|
pb << User.find_by_sql(q.query)
end
pb.inject(:'&')
end
But, would be great if I could get ActiveRecord::Relation instead. I want to do something like this after: Banana.find(x).get_public.where(...)
Any way to modify get_public and achieve this?
I am not sure I correcly undestood the problem, but I will try to help anyway.
As especified here
where returns an ActiveRecord::Relation
find (and its related dynamic methods) returns a single model object
So I suggest divide your queries into: 'joins' and 'where' fields. Your new code should look like something like this:
pb << User.joins(q.query_joins).where(q.query_where)
Also find methods will are deprecated in rails 4, so using where is recommended.
Hope I haven't missed the point too much :-)