mobility icon indicating copy to clipboard operation
mobility copied to clipboard

Query with fallbacks

Open shioyama opened this issue 7 years ago • 12 comments

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

shioyama avatar Jul 08 '17 23:07 shioyama

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.

shioyama avatar Dec 12 '17 07:12 shioyama

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.

shioyama avatar Apr 11 '18 04:04 shioyama

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.

shioyama avatar May 27 '18 10:05 shioyama

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 avatar Oct 27 '18 10:10 hellekin

@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.

shioyama avatar Oct 27 '18 12:10 shioyama

Ah, thanks! Should this be moved to a new issue?

Changing config.i18n.fallbacks = [:en] explicitly indeed makes the fallbacks work.

hellekin avatar Oct 27 '18 15:10 hellekin

No need for issue since there is no "issue" :smile:

shioyama avatar Oct 27 '18 22:10 shioyama

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 avatar Feb 08 '19 20:02 molfar

@molfar Not supported yet, if it was I would have closed this issue.

shioyama avatar Feb 08 '19 22:02 shioyama

@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?

mrbrdo avatar Apr 12 '22 14:04 mrbrdo

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 avatar Jun 19 '22 01:06 shioyama

@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

mrbrdo avatar Jun 22 '22 11:06 mrbrdo

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.

shioyama avatar May 27 '23 09:05 shioyama