attributes concatenation with thinking-sphinx V3 - thinking-sphinx

I have a User model.
Each user can speak 2 languages.
So I have 2 integer columns speak_1 and speak_2 in my DB.
Each integer correspond to one language :
1 -> french
2 -> english
etc.
I'd like to filter by facets all the users who speak one particular language (= all the user whith speak_1 or speak_2 attribute set to 1)
So I defined this indice :
has [speak_1, speak_2], as: :speak, type: :integer, facet: true
I was hoping it will work because this kind of attributes does :
has mobilities(:city_id), :as => :mobile_cities_ids
But it only consider the speak_1 values in my facets search then
Any idea ?

If you add :multi => true to the attribute definition and then run ts:rebuild, does that get things working? Although I would hope TS is smart enough to realise multiple columns means multiple values.

Related

Database Agnostic 'or' Query in Rails 4.2 Active Record

I have a scope on a Conversation model which maintains an encapsulation of messages between 2 users. I created a scope to help detect a 'Conversation' (to find or check exist) from two user objects.
I used to have:
validates_uniqueness_of :from, scope: :to
scope :between, -> (from,to) { where('("conversations"."from" = ? AND "conversations"."to" = ?) OR ("conversations"."from" = ? AND "conversations"."to" = ?)', from, to, to, from) }
However, while this worked for SQLite and Postgres; it wasn't working for MySQL. I need to convert this explicit query to either be completely database agnostic, or to be made out of active record relations instead.
I have had a shot at doing it myself using '.or' but I suspect this is only available from Rails 5+ because it gave me a undefined method error when I tried to use it.
Instead I have used:
validates_uniqueness_of :from, scope: :to
scope :between, -> (from,to) { where(from: [from, to], to: [to, from]) }
This code works great (and its actually really fast and succinct) but I am worried that it might be 'weak' - in that technically it could allow for a situation where from == from, to == from - which is supposed to be impossible for my application.
If a conversation is created between 'X' and 'Y', then from: X and to: Y = conversation_id: 3 and if Y messages X, then it should find conversation_id: 3 (since semantically they are the same).
Im happy enough to leave the new 'between' scope as is, but how can I strengthen the validation to ensure that from != to when the records are created (and then it doesn't matter that the query could potentially find conversations to and from the same user).
You could try rails_or, a gem which supports #or method in Rails 3, 4, 5.
Then write your scope as:
scope :between, -> (from,to) { where(from: from, to: to).or(from: to, to: from) }

Rails advanced search query

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.

Check if model has locale_restriction in scope

Consider a model Post which has a title, description and a locale_restrictions field.
The locale restrictions field specifies in which locales the post should be displayed. It contains a CSV value: en,de,be,nl.
What I would like to do is use either a default_scope or a named scope to only return the model instances for a specific locale. Something like (with a localized scope): Post.localized.all. This scope then looks at the current locale I18n.locale and returns the posts that have that locale in their locale_restrictions CSV.
I cannot seem to get this working, having tried quite a couple of options. The closest I came was with a SQL LIKE expression:
default_scope -> { where("locale_restrictions LIKE (?)", "%#{I18n.locale.to_s}%") }
However, this fails when there's, for example, both a :en and :benl locale, since %en% will match :benl.
Apparently you can't get access to self.locale_restrictions within a scope. self returns the class instead of the instance. I can't figure out a way to split the locale_restrictions and check them.
What would be the best way to go about this using scopes, or are there any best practices regarding localizing database that I'm missing out on?
I'm basically looking for an easy way to scope my controller instance variables to a specific locale. Any help would be greatly appreciated.
Instead of using LIKE you can use REGEXP and include beginning-of-word and end-of-word boundries in the regular expression. This should do the trick:
default_scope -> { where("locale_restrictions REGEXP (?)", "[[:<:]]#{I18n.locale.to_s}[[:>:]]") }

Best way to declare a rails array field?

What is the best way/practice to declare a Rails(4) array field (with mysql database)? I need to store some ids into that array. I tried to do this using the ActiveRecord Serializer and I customized the attribute accessors so my field can behave like an array.
class OfficeIds < ActiveRecord::Base
serialize :office_ids
def office_ids=(ids)
ids = ids.join(",") if ids.is_a?(Array)
write_attribute(:office_ids, ids)
end
def office_ids
(read_attribute(:office_ids) || "").split(",")
end
end
I feel that this is not the best approach for this kind of situation. Any help would be appreciated. Thanks!
If you're using the serializer, there's no need to write a wrapper method for this. You should be able to assign arbitrary objects to that field:
ids = OfficeIds.new
ids.office_ids = [ 1, 2, 3 ]
ids.save
It is rather odd to have a model called OfficeIds though, as a plural name for this willc cause all kinds of trouble. Are you sure you don't want a traditional has_many relationship for these?

How can I store an array of boolean values in a MySql database?

In my case, every "item" either has a property , or not. The properties can be some hundreds, so I will need , say, max 1000 true/false bits per item.
Is there a way to store those bits in one field of the item ?
If you're looking for a way to do this in a way that's searchable, then no.
A couple searchable methods (involving more than 1 column and/or table):
Use a bunch of SET columns. You're limited to 64 items (on/offs) in a set, but you cna probably figure out a way to group them.
Use 3 tables: Items (id, ...), FlagNames(id, name), and a pivot table ItemFlags(item_id, flag_id). You can then query for items with joins.
If you don't need it to be searchable, then all you need is a method to serialize your data before you put it in the database, and a unserialize it when you pull it out, then use a char, or varchar column.
Use facilities built in to your language (PHP's serialize/unserialize).
Concatenate a series of "y" and "n" characters together.
Bit-pack your values into a string (8 bits per character) in the client before making a call to the MySQL database, and unpack them when retrieving data out of the database. This is the most efficient storage mechanism (if all rows are the same, use char[x], not varchar[x]) at the expense of the data not being searchable and slightly more complicated code.
I would rather go with something like:
Properties
ID, Property
1, FirsProperty
2, SecondProperty
ItemProperties
ID, Property, Item
1021, 1, 10
1022, 2, 10
Then it would be easy to retrieve which properties are set or not with a query for any particular item.
At worst you would have to use a char(1000) [ynnynynnynynnynny...] or the like. If you're willing to pack it (for example, into hex isn't too bad) you could do it with a char(64) [hexadecimal chars].
If it is less than 64, then the SET type will work, but it seems like that's not enough.
You could use a binary type, but that's designed more for stuff like movies, etc.. so I'd not.
So yeah, it seems like your best bet is to pack it into a string, and then store that.
It should be noted that a VARCHAR would be wasting space, since you do know precisely how much space your data will take, and can allocate it exactly. (Having fixed-width rows is a good thing)
Strictly speaking you can accomplish this using the following:
$bools = array(0,1,1,0,1,0,0,1);
$for_db = serialize($array);
// Insert the serialized $for_db string into the database. You could use a text type
// make certain it could hold the entire string.
// To get it back out:
$bools = unserialize($from_db);
That said, I would strongly recommend looking at alternative solutions.
Depending on the use case you might try creating an "item" table that has a many-to-many relationship with values from an "attributes" table. This would be a standard implementation of the common Entity Attribute Value database design pattern for storing variable points of data about a common set of objects.