mobility
mobility copied to clipboard
Query with fallbacks
i.e.
Mobility.locale = :en
Post.i18n.where(title: "foo")
...should return the post with title "foo" in English if a post with title in English exists, if not it should find a post with title "foo" in one of the fallback locales defined for the title
attributes.
Ref: shioyama/friendly_id-mobility#4
So I've looked into this a bit today, and I think before doing anything like this it might be good to take a few steps back and think about how querying is currently implemented.
Currently, the locale is always implicit when querying, so Post.i18n.where(title: "foo")
will implicitly look for posts whose title in English is "foo"
.
I'm thinking that it would be useful to allow options ("query options") to be passed into the query method, which could e.g. include which locales to match for:
Post.i18n(locales: [:en, :ja]).where(title: "foo")
#=> finds posts whose title in English or Japanese is "foo"
This seems like a logical step toward something like fallbacks, which could be a different option.
Where I'm hesitating a bit is whether to couple accessor fallbacks (the fallbacks plugin) to querying fallbacks. Globalize does this but I'm thinking of keeping them separate and isolated, but open to feedback.
I'm leaning now toward implementing this with a simple locale
key in the query hash, like this:
Post.i18n.where(title: "foo", locale: [:en, :fr])
One advantage here is that you can apply locales to specific attributes, allowing you to potentially build up more complex multi-locale queries:
Post.i18n.where(title: "foo", locale: [:en, :fr]).where(content: "bar", locale: :de)
This is pretty far-fetched, but all else equal I think localizing the scope of the locale is better and this shouldn't be any more complicated than passing the locale to i18n
.
On the other hand, this means that locale
becomes a reserved key inside the i18n
scope. I think this is fine, it pretty much is a reserved key anyway.
So #233 now implements passing the locale in the options to where
. It only accepts a single locale, but accepting an array of locales should not be so hard given that the entire querying interface has been rewritten.
It looks to me like fallbacks do not work with :container
backend:
[10] pry(main)> Mobility.config
=> #<Mobility::Configuration:0x000055bc07a2a468
@accessor_method=:translates,
@default_accessor_locales=#<Proc:0x000055bc07a2a3f0@/home/how/.gem/ruby/2.5.3/gems/mobility-0.8.4/lib/mobility/configuration.rb:107 (lambda)>,
@default_backend=:container,
@default_options={:cache=>true, :presence=>true, :query=>true, :default=>#<Object:0x000055bc0729a608>, :fallbacks=>true},
@fallbacks_generator=#<Proc:0x000055bc07a2a418@/home/how/.gem/ruby/2.5.3/gems/mobility-0.8.4/lib/mobility/configuration.rb:106 (lambda)>,
@plugins=[:query, :cache, :dirty, :fallbacks, :presence, :default, :attribute_methods, :fallthrough_accessors, :locale_accessors],
@query_method=:i18n>
[11] pry(main)> I18n.default_locale
=> :en
[12] pry(main)> I18n.locale
=> :en
[13] pry(main)> Mobility.locale
=> :fr
[14] pry(main)> a[:name]
=> nil
[15] pry(main)> a[:translations]
=> {"en"=>{"name"=>"Dewey Maps", "summary"=>"Collective Agent for Dewey Maps"}, "fr"=>{"summary"=>"Un agent collectif pour Dewey Maps"}}
[16] pry(main)> a.name_backend.read(:fr)
=> nil
[17] pry(main)> a.name
=> nil
[18] pry(main)> a.name_backend.read(:en)
=> "Dewey Maps"
[19] pry(main)> a.name == a.name_backend.read(:en)
=> false
As fallbacks are set in the default configuration, I would expect a.name
to fallback to the default locale, which is English.
I just read the roadmap and found a slot for "Migrating from untranslated to translated + writing to untranslated column" and another for "Querying with fallbacks" which leads to this issue. I guess @shioyama is working on it :)
@hellekin This is really not the right place for a question about fallbacks on accessors, this issue is about fallbacks on querying (i.e. search).
But just quickly: I assume you're using I18n version 1.1, right? As of 1.1 I18n no longer falls back to the default locale, see svenfuchs/i18n#415.
Ah, thanks! Should this be moved to a new issue?
Changing config.i18n.fallbacks = [:en]
explicitly indeed makes the fallbacks work.
No need for issue since there is no "issue" :smile:
Back to the issue subject, is it supported fallbacks in queries? I'm trying to use mobility-friendly_id gem, and this question is actual.
@molfar Not supported yet, if it was I would have closed this issue.
@shioyama so 3 years later, any chance we could see this supported any time soon? The friendly_id-mobility is kinda useless without this fallback support, and I'm sure it would benefit other use-cases as well. Globalize does support his, right?
This is not easy to support for all backends.
You have to understand that Globalize can support this kind of tricky thing because it only has one backend. Mobility has a half dozen of them. So querying with fallbacks would require some new abstraction at the backend layer, which would mean all backends would have to change. That's not something I'm going to do anytime soon.
Also though, even if it was possible, I don't want to add new functionality which then needs to be supported. I'd welcome someone to implement this for the table backend and either make a gem out of it or paste the code here, or it could go in the wiki.
@shioyama For reference here is how I implemented it for my use case with ActiveRecord table backend. It's not generalized/automatic though, but when used it will use fallback locales to perform the query. It uses COALESCE in SQL to use the first non-null value. It's not ideal but it works and is not too slow.
https://github.com/mrbrdo/spree_mobility/blob/949987fa86e4e36e03a0eff86881b872d6ee6894/lib/spree_mobility/translation_query.rb https://github.com/mrbrdo/spree_mobility/blob/949987fa86e4e36e03a0eff86881b872d6ee6894/lib/spree_mobility/core_ext/spree/variant_decorator.rb#L9
I'm closing this one. It would be "nice to have" but it's difficult to implement for all backends and I've scaled back my ambitions for mobility to maintaining current functionality.
This feels like something that could be implemented as a plugin.