znamenica/dneslov

View on GitHub
app/models/memory.rb

Summary

Maintainability
D
1 day
Test Coverage
F
42%
# council[string]       - соборы для памяти
# short_name[string]    - краткое имя
# quantity[string]      - количество
#
class Memory < ActiveRecord::Base
   extend TotalSize
   extend DefaultKey
   extend Informatible
   include WithTitles
   include WithDescriptions
   include WithLinks

   has_default_key :short_name

   has_one :slug, as: :sluggable, dependent: :destroy
   has_many :memory_names, dependent: :destroy
   has_many :nomina, through: :memory_names
   has_many :names, through: :nomina
   has_many :paterics, as: :info, dependent: :destroy, class_name: :PatericLink
   has_many :events, dependent: :destroy
   has_many :memos, through: :events
   has_many :service_scripta, through: :services
   has_many :scripta, through: :service_scripta
   has_many :calendaries, -> { distinct.reorder('id') }, through: :memos
   has_many :thumbs, -> { where(thumbs: { thumbable_type: "Memory" }) }, foreign_key: :thumbable_id, dependent: :destroy
   has_many :attitudes, as: :imageable, class_name: :ImageAttitude
   has_many :pictures, through: :attitudes
   has_many :notes, as: :describable, dependent: :destroy, class_name: :Note
   has_many :orders, -> { distinct.reorder('id') }, through: :memos, source: :orders
   has_many :slugs, -> { distinct.reorder('id') }, through: :orders, source: :slug
   has_many :memory_binds
   has_many :bond_memories, through: :memory_binds, foreign_key: :bond_to_id, class_name: :Memory
   has_many :coverings
   has_many :places, through: :coverings

   default_scope { order( base_year: :asc, short_name: :asc, id: :asc ) }

   scope :icons, -> { joins( :slugs ).where( slugs: { text: :обр } ) }

   scope :by_short_name, -> name { where( short_name: name ) }
   scope :by_slug, -> slug do
      unscoped.joins(:slug).where(slugs: {text: slug.split(",")})
   end

   scope :in_calendaries, -> calendaries_in do
      calendaries = calendaries_in.is_a?(String) && calendaries_in.split(',') || calendaries_in
      left_outer_joins( :memos ).merge( Memo.in_calendaries( calendaries )).distinct
   end

   scope :by_date, -> (date, julian = false) do
      left_outer_joins(:memos).merge(Memo.by_date(date, julian)).distinct
   end

   scope :by_token, -> text do
      left_outer_joins(:names, :descriptions).where( "short_name ~* ?", "\\m#{text}.*" ).or(
         where("unaccent(descriptions.text) ~* unaccent(?) OR unaccent(names.text) ~* unaccent(?)", "\\m#{text}.*", "\\m#{text}.*")).or(merge(
         Nomen.joins(:name).where("unaccent(names.text) ~* unaccent(?)", "\\m#{text}.*").by_root)).distinct
   end

   scope :by_tokens, -> string_in do
      return self if string_in.blank?
      # TODO fix the correctness of the query
      klass = self.model_name.name.constantize
      or_rel_tokens = string_in.split(/\//).map do |or_token|
         # OR operation
         or_token.strip.split(/\s+/).reduce(nil) do |rel, and_token|
            # AND operation
            and_rel = klass.by_token(and_token)
            rel && rel.merge(and_rel) || and_rel
         end
      end

      or_rel = or_rel_tokens.reduce { |sum_rel, rel| sum_rel.or(rel) }
      self.merge(or_rel).distinct
   end

   # required for short list
   scope :with_key, -> _ do
      selector = [ 'memories.id AS _key' ]

      select(selector).group('_key').reorder("_key")
   end

   scope :with_value, -> context do
      #TODO add search over names
      selector = [ 'memories.short_name AS _value' ]

      select(selector).group('_value').reorder("_value")
   end

   scope :with_names, -> (context) do
      language_codes = [context[:locales]].flatten
=begin
with recursive t(level,path,id,name_id,bind_kind_name,bond_to_id,root_id,name_alphabeth_code,name_language_code,name_text,state_code,mode,feasible) as (
        with recursive tt(level,path,id,name_id,bind_kind_name,bond_to_id,root_id,name_alphabeth_code,name_language_code,name_text,state_code,mode,feasible) as (SELECT 0,nm.text,n.id,n.name_id,n.bind_kind_name,n.bond_to_id,n.root_id,nm.alphabeth_code,nm.language_code,nm.text,mn.state_code,mn.mode,mn.feasible FROM nomina n JOIN names nm ON nm.id = n.name_id JOIN memory_names mn ON mn.nomen_id = n.id WHERE  mn.memory_id = 2989 UNION SELECT level + 1, path || ' > ' || nm.text, n.id,n.name_id,n.bind_kind_name,n.bond_to_id,n.root_id,nm.alphabeth_code,nm.language_code,nm.text,tt.state_code,tt.mode,tt.feasible FROM nomina n JOIN tt ON n.root_id = tt.root_id AND  tt.bond_to_id = n.name_id JOIN names nm ON nm.id = n.name_id JOIN names nm_t ON nm_t.id = tt.name_id AND nm_t.language_code <> nm.language_code LEFT OUTER JOIN memory_names mn ON n.root_id = mn.nomen_id AND mn.memory_id = 2989 ) select * from tt
    UNION
        SELECT DISTINCT
            level + 1,
            path || ' > ' || nm.text,
            n.id,n.name_id,n.bind_kind_name,n.bond_to_id,n.root_id,nm.alphabeth_code,nm.language_code,nm.text,t.state_code,t.mode,t.feasible
        FROM
            nomina n JOIN t
                ON n.bond_to_id = t.name_id AND n.root_id = t.root_id JOIN names nm ON nm.id = n.name_id JOIN names nm_t ON nm_t.id = t.name_id AND nm_t.language_code <> nm.language_code LEFT OUTER JOIN memory_names mn ON n.root_id = mn.nomen_id AND mn.memory_id = 2989
) select * from t  WHERE name_language_code IN ('ру')
=end

      selector = "COALESCE((
              WITH RECURSIVE __names(level,path,id,name_id,name_bind_kind_name,bond_to_id,root_id,name_alphabeth_code,name_language_code,name_text,state_code,mode,feasible) AS (
              WITH RECURSIVE tt(level,path,id,name_id,bind_kind_name,bond_to_id,root_id,name_alphabeth_code,name_language_code,name_text,state_code,mode,feasible) AS (
                      SELECT 0,
                             nm.text,
                             n.id,
                             n.name_id,
                             n.bind_kind_name,
                             n.bond_to_id,
                             n.root_id,
                             nm.alphabeth_code,
                             nm.language_code,
                             nm.text,
                             mn.state_code,
                             mn.mode,
                             mn.feasible
                        FROM nomina n
                        JOIN names nm
                          ON nm.id = n.name_id
                        JOIN memory_names mn
                          ON mn.nomen_id = n.id
                         AND mn.memory_id = memories.id
                       UNION

                      SELECT 0,
                             nm.text,
                             n.id,
                             n.name_id,
                             n.bind_kind_name,
                             n.bond_to_id,
                             n.root_id,
                             nm.alphabeth_code,
                             nm.language_code,
                             nm.text,
                             tt.state_code,
                             tt.mode,
                             tt.feasible
                        FROM nomina n
                        JOIN tt
                          ON n.root_id = tt.root_id AND tt.bond_to_id = n.name_id
                        JOIN names nm
                          ON nm.id = n.name_id
                        JOIN names nm_t
                          ON nm_t.id = tt.name_id
                         AND nm_t.language_code <> nm.language_code
             LEFT OUTER JOIN memory_names mn
                          ON n.root_id = mn.nomen_id
                         AND mn.memory_id = memories.id)
                      SELECT *
                        FROM tt

                       UNION
                      SELECT level + 1,
                             path || ' > ' || nm.text,
                             n.id,
                             n.name_id,
                             n.bind_kind_name,
                             n.bond_to_id,
                             n.root_id,
                             nm.alphabeth_code,
                             nm.language_code,
                             nm.text,
                             t.state_code,
                             t.mode,
                             t.feasible
                        FROM nomina n
                        JOIN __names t
                          ON n.bond_to_id = t.name_id
                         AND n.root_id = t.root_id
                        JOIN names nm
                          ON nm.id = n.name_id
                        JOIN names nm_t
                          ON nm_t.id = t.name_id
                         AND nm_t.language_code <> nm.language_code
             LEFT OUTER JOIN memory_names mn
                          ON n.root_id = mn.nomen_id
                         AND mn.memory_id = memories.id)
                      SELECT jsonb_agg(__names)
                        FROM __names
                       WHERE __names.name_language_code IN ('#{language_codes.join("', '")}')), '[]'::jsonb) AS _names"

      select(selector).group(:id)
   end

   scope :with_scripta, -> (context) do
      as = table.table_alias || table.name
      language_codes = [context[:locales]].flatten
      alphabeth_codes = context[:codes] || Languageble.alphabeth_list_for(language_codes).flatten
      calendaries_join =
         if context[:calendary_slugs]
             "JOIN calendaries AS #{as}_calendaries
                ON #{as}_calendaries.id = #{as}_memoes.calendary_id
              JOIN slugs AS #{as}_calendary_slugs
                ON #{as}_calendary_slugs.id = #{as}_calendaries.id
               AND #{as}_calendary_slugs.sluggable_type = 'Calendary'
               AND #{as}_calendary_slugs.text IN ('#{context[:calendary_slugs].join("','")}')"
         end

      selector = self.select_values.dup
      selector << "#{as}.*" if selector.empty?
      selector << "COALESCE((SELECT jsonb_agg(DISTINCT scripta)
                               FROM scripta
                               JOIN events
                                 ON memories.id = events.memory_id
                               JOIN memoes AS #{as}_memoes
                                 ON events.id = #{as}_memoes.event_id
                               JOIN memo_scripta
                                 ON memo_scripta.memo_id = #{as}_memoes.id
                                    #{calendaries_join}
                              WHERE scripta.id = memo_scripta.scriptum_id
                                AND scripta.language_code IN ('#{language_codes.join("', '")}')
                                AND scripta.alphabeth_code IN ('#{alphabeth_codes.join("', '")}')), '[]'::jsonb) AS _scripta"

      select(selector).group(:id)
   end

   scope :with_memory_binds, -> do
      as = table.table_alias || table.name

      selector = self.select_values.dup
      selector << "#{as}.*" if selector.empty?
      selector << "COALESCE((WITH __memory_binds AS (
                       SELECT #{as}_memory_binds.id AS id,
                              #{as}_memory_binds.bond_to_id AS bond_to_id,
                              #{as}_memory_binds.kind AS kind,
                              #{as}_bond_memories.short_name AS bond_to_name
                         FROM memory_binds AS #{as}_memory_binds
                         JOIN memories AS #{as}_bond_memories
                           ON #{as}_bond_memories.id = #{as}_memory_binds.bond_to_id
                        WHERE #{as}.id = #{as}_memory_binds.memory_id)
                       SELECT jsonb_agg(__memory_binds)
                         FROM __memory_binds), '[]'::jsonb) AS _memory_binds"

      select(selector).group(:id)
   end

   scope :with_covering_names, -> context do
      as = table.table_alias || table.name
      language_codes = [ context[:locales] ].flatten

      selector = self.select_values.dup
      selector << "#{as}.*" if selector.empty?
      selector << "COALESCE((WITH __coverings AS (
                       SELECT #{as}_coverings.id AS id,
                              #{as}_coverings.add_date AS add_date,
                              #{as}_coverings.place_id AS place_id,
                              #{as}_place_descriptions.text || ' (' || #{as}_place_descriptions.language_code || '_' || #{as}_place_descriptions.alphabeth_code || ')' AS place_name
                         FROM coverings AS #{as}_coverings
                         JOIN places AS #{as}_places
                           ON #{as}_places.id = #{as}_coverings.place_id
              LEFT OUTER JOIN descriptions AS #{as}_place_descriptions
                           ON #{as}_place_descriptions.describable_id = #{as}_places.id
                          AND #{as}_place_descriptions.describable_type = 'Place'
                          AND #{as}_place_descriptions.language_code IN ('#{language_codes.join("', '")}')
                        WHERE #{as}.id = #{as}_coverings.memory_id)
                       SELECT jsonb_agg(__coverings)
                         FROM __coverings), '[]'::jsonb) AS _coverings"

      select(selector).group(:id)
   end

   scope :with_coverings, -> context do
      as = table.table_alias || table.name
      language_codes = [ context[:locales] ].flatten

      selector = self.select_values.dup
      selector << "#{as}.*" if selector.empty?
      selector << "COALESCE((WITH __coverings AS (
                       SELECT #{as}_coverings.id AS id,
                              #{as}_coverings.add_date AS add_date,
                              #{as}_coverings.place_id AS place_id,
                              #{as}_place_descriptions.text AS name
                         FROM coverings AS #{as}_coverings
                         JOIN places AS #{as}_places
                           ON #{as}_places.id = #{as}_coverings.place_id
              LEFT OUTER JOIN descriptions AS #{as}_place_descriptions
                           ON #{as}_place_descriptions.describable_id = #{as}_places.id
                          AND #{as}_place_descriptions.describable_type = 'Place'
                          AND #{as}_place_descriptions.language_code IN ('#{language_codes.join("', '")}')
                        WHERE #{as}.id = #{as}_coverings.memory_id)
                       SELECT jsonb_agg(__coverings)
                         FROM __coverings), '[]'::jsonb) AS _coverings"

      select(selector).group(:id)
   end

   scope :with_memoes, -> context do
      as = table.table_alias || table.name
      language_codes = [ context[:locales] ].flatten
      cslugs_rule = context[:calendary_slugs] ? "AND calendary_slugs.text IN ('#{context[:calendary_slugs].join("','")}')" : ""

      selector = "COALESCE((WITH __memoes AS (
                       SELECT DISTINCT ON(memoes.id)
                              memoes.id AS id,
                              memoes.year_date AS year_date,
                              events.kind_code AS kind_code,
                              jsonb_object_agg(DISTINCT COALESCE(memo_slugs.text, 'Null'),
                                                        order_titles_memoes.text) AS orders,
                              memo_titles.text AS title,
                              memo_descriptions.text AS description,
                              memo_links.url AS url,
                              calendary_links.url AS calendary_url,
                              calendary_slugs.text AS calendary_slug,
                              calendary_titles.text AS calendary_title
                         FROM memoes
                         JOIN calendaries
                           ON calendaries.id = memoes.calendary_id
                         JOIN slugs AS calendary_slugs
                           ON calendary_slugs.sluggable_id = calendaries.id
                          AND calendary_slugs.sluggable_type = 'Calendary'
                              #{cslugs_rule}
              LEFT OUTER JOIN events
                           ON memoes.event_id = events.id
                          AND events.kind_code IN ('#{Event::NOTICE.join("','")}')
              LEFT OUTER JOIN links AS calendary_links
                           ON calendary_links.info_id = calendaries.id
                          AND calendary_links.info_type = 'Calendary'
              LEFT OUTER JOIN descriptions AS memo_titles
                           ON memo_titles.describable_id = memoes.id
                          AND memo_titles.describable_type = 'Memo'
                          AND memo_titles.type = 'Title'
                          AND memo_titles.language_code IN ('#{language_codes.join("', '")}')
              LEFT OUTER JOIN descriptions AS memo_descriptions
                           ON memo_descriptions.describable_id = memoes.id
                          AND memo_descriptions.describable_type = 'Memo'
                          AND memo_descriptions.type = 'Description'
                          AND memo_descriptions.language_code IN ('#{language_codes.join("', '")}')
              LEFT OUTER JOIN links AS memo_links
                           ON memo_links.info_id = memoes.id
                          AND memo_links.info_type = 'Memo'
              LEFT OUTER JOIN descriptions AS calendary_titles
                           ON calendary_titles.describable_id = calendaries.id
                          AND calendary_titles.describable_type = 'Calendary'
                          AND calendary_titles.type = 'Appellation'
                          AND calendary_titles.language_code IN ('#{language_codes.join("', '")}')
              LEFT OUTER JOIN memo_orders
                           ON memo_orders.memo_id = memoes.id
              LEFT OUTER JOIN orders
                           ON orders.id = memo_orders.order_id
              LEFT OUTER JOIN slugs AS memo_slugs
                           ON memo_slugs.sluggable_id = orders.id
                          AND memo_slugs.sluggable_type = 'Order'
              LEFT OUTER JOIN memo_orders AS memo_orders_memoes_join
                           ON memo_orders_memoes_join.memo_id = memoes.id
              LEFT OUTER JOIN orders AS orders_memoes_join
                           ON orders_memoes_join.id = memo_orders_memoes_join.order_id
              LEFT OUTER JOIN descriptions AS order_titles_memoes
                           ON order_titles_memoes.describable_id = orders_memoes_join.id
                          AND order_titles_memoes.describable_type = 'Order'
                          AND order_titles_memoes.type IN ('Tweet')
                          AND order_titles_memoes.language_code IN ('#{language_codes.join("', '")}')
                        WHERE events.memory_id = #{as}.id
                          AND memoes.bond_to_id IS NULL
                          AND memoes.id IS NOT NULL
                          AND calendaries.licit = 't'
                     GROUP BY memoes.id, year_date, title, description, calendary_slug,
                              calendary_title, calendary_url, memo_links.url, events.kind_code)
                       SELECT jsonb_agg(__memoes)
                         FROM __memoes), '[]'::jsonb) AS _memoes"

      select(selector).group(:id)
   end

   scope :with_bond_memories, ->(context) do
      as = table.table_alias || table.name
      language_codes = [context[:locales]].flatten
      alphabeth_codes = Languageble.alphabeth_list_for( language_codes ).flatten

      selector = self.select_values.dup
      selector << "#{as}.*" if selector.empty?
      selector << "COALESCE((WITH __bond_memories AS (
                      SELECT #{as}_memory_binds.id AS id,
                             #{as}_memory_binds.kind AS kind,
                             #{as}_memory_slugs.text AS slug,
                             COALESCE(#{as}_bond_memo_titles.text, #{as}_bond_memories.short_name) AS name
                        FROM memories AS #{as}_bond_memories
             LEFT OUTER JOIN slugs AS #{as}_memory_slugs
                          ON 'Memory' = #{as}_memory_slugs.sluggable_type
                         AND #{as}_bond_memories.id = #{as}_memory_slugs.sluggable_id
             LEFT OUTER JOIN memory_binds AS #{as}_memory_binds
                          ON #{as}_bond_memories.id = #{as}_memory_binds.bond_to_id
             LEFT OUTER JOIN events AS #{as}_events
                          ON #{as}.id = #{as}_events.memory_id
             LEFT OUTER JOIN memoes AS #{as}_memoes
                          ON #{as}_memoes.event_id = #{as}_events.id
             LEFT OUTER JOIN descriptions AS #{as}_bond_memo_titles
                          ON #{as}_bond_memo_titles.describable_id = #{as}_memoes.id
                         AND #{as}_bond_memo_titles.describable_type = 'Memo'
                         AND #{as}_bond_memo_titles.type = 'Name'
                         AND #{as}_bond_memo_titles.language_code IN ('#{language_codes.join("', '")}')
                         AND #{as}_bond_memo_titles.alphabeth_code IN ('#{alphabeth_codes.join("', '")}')
                       WHERE #{as}_memory_binds.memory_id = #{as}.id)
                      SELECT jsonb_agg(__bond_memories)
                        FROM __bond_memories), '[]'::jsonb) AS _bond_memories"

      select(selector).group(:id)
   end

   # https://efim360.ru/postgresql-tablicza-prisutstvuet-v-zaprose-no-soslatsya-na-neyo-iz-etoj-chasti-zaprosa-nelzya/?ysclid=lqs5tozisb568391387
   scope :with_icon, -> context do
      as = table.table_alias || table.name
      language_codes = [ context[:locales] ].flatten
      selector = self.select_values.dup
      selector << "#{as}.*" if selector.empty?
      selector << "jsonb_build_object(
         'url', #{as}_pictures.url,
         'thumb_url', #{as}_pictures.thumb_url,
         'width', #{as}_pictures.width,
         'height,', #{as}_pictures.height,
         'pos', #{as}_pictures.pos,
         'description', #{as}_pictures.description,
         'title', #{as}_pictures.title) AS _picture"
      join = "LEFT OUTER JOIN LATERAL (
                      SELECT #{as}_pictures.url AS url,
                             #{as}_pictures.thumb_url AS thumb_url,
                             #{as}_pictures.width AS width,
                             #{as}_pictures.height AS height,
                             #{as}_image_attitudes.pos::text AS pos,
                             #{as}_image_attitudes.imageable_id AS imageable_id,
                             #{as}_image_attitudes.imageable_type AS imageable_type,
                             #{as}_titles.text AS title,
                             #{as}_descriptions.text AS description
                        FROM pictures AS #{as}_pictures
             LEFT OUTER JOIN image_attitudes AS #{as}_image_attitudes
                          ON #{as}_image_attitudes.imageable_id = #{as}.id
                         AND #{as}_image_attitudes.imageable_type = 'Memory'
             LEFT OUTER JOIN descriptions AS #{as}_descriptions
                          ON #{as}_descriptions.describable_id = #{as}_pictures.id
                         AND #{as}_descriptions.describable_type = 'Picture'
                         AND #{as}_descriptions.type = 'Description'
                         AND #{as}_descriptions.language_code IN ('#{language_codes.join("', '")}')
             LEFT OUTER JOIN descriptions AS #{as}_titles
                          ON #{as}_titles.describable_id = #{as}_pictures.id
                         AND #{as}_titles.describable_type = 'Picture'
                         AND #{as}_titles.type = 'Title'
                         AND #{as}_titles.language_code IN ('#{language_codes.join("', '")}')
                       WHERE #{as}_image_attitudes.picture_id = #{as}_pictures.id
                         AND #{as}_pictures.type = 'Icon'
                    ORDER BY random()
                       LIMIT 1) AS #{as}_pictures
                          ON #{as}_pictures.imageable_id = #{as}.id
                         AND #{as}_pictures.imageable_type = 'Memory'"

      joins(join).select(selector).group(:id, "#{as}_pictures.url", "#{as}_pictures.thumb_url", "#{as}_pictures.width",
         "#{as}_pictures.height", "#{as}_pictures.pos", "#{as}_pictures.title", "#{as}_pictures.description")
   end

   scope :with_slug_text, -> do
      selector = self.select_values.dup
      if selector.empty?
         selector << 'memories.*'
      end
      selector << 'slugs.text AS _slug'

      left_outer_joins(:slug).select(selector.uniq).group('_slug').order('_slug')
   end

   scope :with_slug, -> do
      selector = self.select_values.dup
      if self.select_values.dup.empty?
         selector << 'memories.*'
      end

      selector << "jsonb_build_object('id', slugs.id, 'text', slugs.text) AS _slug"
      join = "LEFT OUTER JOIN slugs
                           ON slugs.sluggable_id = memories.id
                          AND slugs.sluggable_type = 'Memory'"

      joins(join).select(selector).group(:id, 'slugs.id', 'slugs.text')
   end

   scope :with_orders, -> context do
      language_codes = [ context[:locales] ].flatten
      selector = self.select_values.dup
      if self.select_values.dup.empty?
         selector << 'memories.*'
      end

      selector << "COALESCE((WITH __orders AS (
                       SELECT DISTINCT ON(orders.id)
                              orders.id AS id,
                              order_names.text AS name
                         FROM orders
                         JOIN memo_orders
                           ON memo_orders.order_id = orders.id
                         JOIN memoes
                           ON memoes.id = memo_orders.memo_id
                         JOIN events
                           ON events.id = event_id
              LEFT OUTER JOIN descriptions AS order_names
                           ON order_names.describable_id = orders.id
                          AND order_names.describable_type = 'Order'
                          AND order_names.language_code IN ('#{language_codes.join("', '")}')
                        WHERE events.memory_id = memories.id
                     GROUP BY orders.id, order_names.text)
                       SELECT jsonb_agg(__orders)
                         FROM __orders), '[]'::jsonb) AS _orders"

      select(selector).group(:id)
   end

   scope :with_events, -> context do
      as = table.table_alias || table.name
      language_codes = [context[:locales]].flatten
      alphabeth_codes = Languageble.alphabeth_list_for( language_codes ).flatten
      selector = self.select_values.dup
      if self.select_values.dup.empty?
         selector << 'memories.*'
      end

      selector << "COALESCE((WITH __events AS (
                      SELECT DISTINCT ON(events.id)
                             events.id AS id,
                             events.happened_at AS happened_at,
                             events.kind_code AS kind_code,
                             event_kind_titles.text AS kind_name,
                             events.place_id AS place_id,
                             place_names.text AS place,
                             events.item_id AS item_id,
                             COALESCE(item_names.text, #{as}_item_type_names.text) AS item,
                             events.person_name AS person_name,
                             events.type_number AS type_number,
                             events.about_string AS about_string,
                             events.tezo_string AS tezo_string,
                             events.order AS order,
                             events.council AS council,
              COALESCE((WITH __titles AS (
                      SELECT DISTINCT ON(titles.id)
                             titles.id AS id,
                             titles.text AS text,
                             titles.language_code AS language_code,
                             language_names.text AS language,
                             titles.alphabeth_code AS alphabeth_code,
                             alphabeth_names.text AS alphabeth
                        FROM descriptions AS titles
             LEFT OUTER JOIN subjects AS languages
                          ON languages.key = titles.language_code
             LEFT OUTER JOIN descriptions AS language_names
                          ON language_names.describable_id = languages.id
                         AND language_names.describable_type = 'Subject'
                         AND language_names.language_code IN ('#{language_codes.join("', '")}')
             LEFT OUTER JOIN subjects AS alphabeths
                          ON alphabeths.key = titles.alphabeth_code
             LEFT OUTER JOIN descriptions AS alphabeth_names
                          ON alphabeth_names.describable_id = alphabeths.id
                         AND alphabeth_names.describable_type = 'Subject'
                         AND alphabeth_names.alphabeth_code IN ('#{alphabeth_codes.join("', '")}')
                       WHERE titles.describable_id = events.id
                         AND titles.describable_type = 'Event'
                         AND titles.language_code IN ('#{language_codes.join("', '")}')
                         AND titles.type = 'Title'
                    GROUP BY titles.id, language_names.text, alphabeth_names.text)
                      SELECT jsonb_agg(__titles)
                        FROM __titles), '[]'::jsonb) AS titles,
              COALESCE((WITH __descriptions AS (
                      SELECT DISTINCT ON(descriptions.id)
                             descriptions.id AS id,
                             descriptions.text AS text,
                             descriptions.language_code AS language_code,
                             language_names.text AS language,
                             descriptions.alphabeth_code AS alphabeth_code,
                             alphabeth_names.text AS alphabeth
                        FROM descriptions
             LEFT OUTER JOIN subjects AS languages
                          ON languages.key = descriptions.language_code
             LEFT OUTER JOIN descriptions AS language_names
                          ON language_names.describable_id = languages.id
                         AND language_names.describable_type = 'Subject'
                         AND language_names.language_code IN ('#{language_codes.join("', '")}')
             LEFT OUTER JOIN subjects AS alphabeths
                          ON alphabeths.key = descriptions.alphabeth_code
             LEFT OUTER JOIN descriptions AS alphabeth_names
                          ON alphabeth_names.describable_id = alphabeths.id
                         AND alphabeth_names.describable_type = 'Subject'
                         AND alphabeth_names.alphabeth_code IN ('#{alphabeth_codes.join("', '")}')
                       WHERE descriptions.describable_id = events.id
                         AND descriptions.describable_type = 'Event'
                         AND descriptions.language_code IN ('#{language_codes.join("', '")}')
                         AND descriptions.type IN ('Description')
                    GROUP BY descriptions.id, language_names.text, alphabeth_names.text)
                      SELECT jsonb_agg(__descriptions)
                        FROM __descriptions), '[]'::jsonb) AS descriptions
                        FROM events
             LEFT OUTER JOIN descriptions AS place_names
                          ON place_names.describable_id = events.place_id
                         AND place_names.describable_type = 'Place'
                         AND place_names.language_code IN ('#{language_codes.join("', '")}')
             LEFT OUTER JOIN items AS #{as}_items
                          ON #{as}_items.id = events.item_id
             LEFT OUTER JOIN descriptions AS item_names
                          ON item_names.describable_id = #{as}_items.id
                         AND item_names.describable_type = 'Item'
                         AND item_names.language_code IN ('#{language_codes.join("', '")}')
             LEFT OUTER JOIN descriptions AS #{as}_item_type_names
                          ON #{as}_item_type_names.describable_id = #{as}_items.item_type_id
                         AND #{as}_item_type_names.describable_type = 'ItemType'
                         AND #{as}_item_type_names.language_code IN ('#{language_codes.join("', '")}')
             LEFT OUTER JOIN subjects AS event_kinds
                          ON event_kinds.kind_code = 'EventKind'
                         AND event_kinds.key = events.kind_code
             LEFT OUTER JOIN descriptions AS event_kind_titles
                          ON event_kind_titles.describable_id = event_kinds.id
                         AND event_kind_titles.describable_type = 'Subject'
                         AND event_kind_titles.type = 'Appellation'
                         AND event_kind_titles.language_code IN ('#{language_codes.join("', '")}')
                       WHERE events.memory_id = memories.id
                    GROUP BY events.id, place_names.text, item_names.text, #{as}_item_type_names.text, event_kind_titles.text)
                      SELECT jsonb_agg(__events)
                        FROM __events), '[]'::jsonb) AS _events"

      select(selector).group(:id)
   end

   scope :with_memory_names, -> context do
      language_codes = [ context[:locales] ].flatten
      selector = self.select_values.dup
      if self.select_values.dup.empty?
         selector << 'memories.*'
      end

      selector = "COALESCE((
                        WITH __memory_names AS (
                      SELECT memory_names.id AS id,
                             nomina.id AS nomen_id,
                             names.text || COALESCE(' < ' || bond_toes.text, '') || ' (' || names.language_code|| '_' || names.alphabeth_code || ')' AS name,
                             memory_names.state_code AS state_code,
                             memory_name_state_titles.text AS state_name,
                             memory_names.mode AS mode,
                             memory_names.feasible AS feasible
                        FROM memory_names
             LEFT OUTER JOIN subjects AS memory_name_states
                          ON memory_name_states.key = memory_names.state_code
             LEFT OUTER JOIN descriptions AS memory_name_state_titles
                          ON memory_name_state_titles.describable_id = memory_name_states.id
                         AND memory_name_state_titles.describable_type = 'Subject'
                         AND memory_name_state_titles.language_code IN ('#{language_codes.join("', '")}')
             LEFT OUTER JOIN nomina
                          ON nomina.id = memory_names.nomen_id
             LEFT OUTER JOIN names
                          ON names.id = nomina.name_id
             LEFT OUTER JOIN names AS bond_toes
                          ON bond_toes.id = nomina.bond_to_id
                       WHERE memory_names.memory_id = memories.id
                         AND memory_names.id IS NOT NULL
                    GROUP BY memory_names.id, names.text, bond_toes.text, memory_name_state_titles.text, names.language_code, names.alphabeth_code, nomina.id)
                      SELECT jsonb_agg(__memory_names)
                        FROM __memory_names), '[]'::jsonb) AS _memory_names"

      select(selector).group(:id)
   end

   singleton_class.send(:alias_method, :t, :by_token)
   singleton_class.send(:alias_method, :q, :by_tokens)
   singleton_class.send(:alias_method, :d, :by_date)
   singleton_class.send(:alias_method, :c, :in_calendaries)

   accepts_nested_attributes_for :memory_binds, reject_if: :all_blank, allow_destroy: true
   accepts_nested_attributes_for :memory_names, reject_if: :all_blank, allow_destroy: true
   accepts_nested_attributes_for :paterics, reject_if: :all_blank, allow_destroy: true
   accepts_nested_attributes_for :events, reject_if: :all_blank, allow_destroy: true
   accepts_nested_attributes_for :memos, reject_if: :all_blank, allow_destroy: true
   accepts_nested_attributes_for :coverings, reject_if: :all_blank, allow_destroy: true
   accepts_nested_attributes_for :notes, reject_if: :all_blank, allow_destroy: true
   accepts_nested_attributes_for :slug, reject_if: :all_blank

   validates_presence_of :short_name, :events
   validates :base_year, format: { with: /\A-?\d+\z/ }
   validates :quantity, format: { with: /\A[0-9?+]+?\z/ }, allow_blank: true

   before_create :set_slug
   before_validation :set_base_year, on: :create

   def set_base_year
      types = %w(Resurrection Repose Writing Appearance Translation Sanctification)

      event = self.events.to_a.sort_by { |x| (types.index(x.kind) || 100) }.first

      return unless event

      dates = event.happened_at.split(/[\/-]/)
      self.base_year ||=
         case dates.first
         when /([IVX]+)$/
            ($1.rom - 1) * 100 + 50
         when /\.\s*(\d+)$/
            $1
         when /(?:\A|\s|\()(\d+)$/
            $1
         when /(?:\A|\s|\(|\.)(\d+) до (?:нэ|РХ)/
            "-#{$1}"
         when /(:|сент)/
            dates.last.split(".").last
         when /давно/
            '-3760'
         else
            dates = event.happened_at.split(/[\/-]/)
            if /(?:\A|\s|\(|\.)(\d+) до (?:нэ|РХ)/ =~ dates.first
               "-#{$1}"
            else
               '0'
            end
         end

      self.base_year
   end

   def quantity= value_in
      value = value_in.present? && value_in || nil

      super(value)
   end

   def set_slug
      self.slug = Slug.new(base: self.short_name) if self.slug.blank?
   end

   def to_s
      memory_names.join( ' ' )
   end
end