|
|
@@ -1,475 +1,26 @@
|
|
|
# Design документы для CouchDB с поддержкой мультидоменности и мультиязычности
|
|
|
+# Версия 4.0 - с учетом последних изменений в правилах
|
|
|
|
|
|
module.exports =
|
|
|
- # Design документ для настроек доменов
|
|
|
- domains:
|
|
|
- version: "3.0"
|
|
|
+ # Design документ для мультидоменного поиска
|
|
|
+ multi_domain_search:
|
|
|
+ version: "4.0"
|
|
|
views:
|
|
|
- # Все настройки доменов
|
|
|
- all_domains:
|
|
|
+ # Универсальный поиск по всем доменам и языкам
|
|
|
+ universal_search:
|
|
|
map: ((doc) ->
|
|
|
- if doc.type is 'domain_settings'
|
|
|
- emit(doc.domain, doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Активные домены
|
|
|
- active_domains:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'domain_settings' and doc.active is true
|
|
|
- emit(doc.domain, doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Домены по приоритету
|
|
|
- by_priority:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'domain_settings' and doc.active is true
|
|
|
- emit(doc.priority or 0, doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для блог постов с мультидоменностью и мультиязычностью
|
|
|
- blog_posts:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Все опубликованные блог посты с фильтром по домену и языку
|
|
|
- published:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'blog_post' and doc.status is 'published'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.created_at], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Блог посты по тегам с фильтром по домену и языку
|
|
|
- by_tag:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'blog_post' and doc.status is 'published' and doc.tags
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- for tag in doc.tags
|
|
|
- emit([domain, language, tag, doc.created_at], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Блог посты по автору с фильтром по домену и языку
|
|
|
- by_author:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'blog_post' and doc.status is 'published'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.author, doc.created_at], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Блог посты по категории с фильтром по домену и языку
|
|
|
- by_category:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'blog_post' and doc.status is 'published' and doc.category_id
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.category_id, doc.created_at], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Переводы блог постов
|
|
|
- translations:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'blog_post' and doc.translation_of
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, doc.translation_of, language], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Поиск по заголовку и содержанию с фильтром по домену и языку
|
|
|
- search:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'blog_post' and doc.status is 'published'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- text = (doc.title + " " + doc.content + " " + doc.excerpt).toLowerCase()
|
|
|
- words = text.split(/\W+/).filter (word) -> word.length > 2
|
|
|
-
|
|
|
- for word in words
|
|
|
- emit([domain, language, word], {
|
|
|
- _id: doc._id
|
|
|
- title: doc.title
|
|
|
- excerpt: doc.excerpt
|
|
|
- created_at: doc.created_at
|
|
|
- domain: doc.domain
|
|
|
- language: doc.language
|
|
|
- })
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для мероприятий с мультидоменностью и мультиязычностью
|
|
|
- events:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Все мероприятия по дате с фильтром по домену и языку
|
|
|
- by_date:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'event'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.event_date], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Предстоящие мероприятия с фильтром по домену и языку
|
|
|
- upcoming:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'event' and doc.status is 'upcoming'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.event_date], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Активные мероприятия с фильтром по домену и языку
|
|
|
- ongoing:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'event' and doc.status is 'ongoing'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.event_date], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Мероприятия по тегам с фильтром по домену и языку
|
|
|
- by_tag:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'event' and doc.tags
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- for tag in doc.tags
|
|
|
- emit([domain, language, tag, doc.event_date], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Мероприятия по местоположению с фильтром по домену и языку
|
|
|
- by_location:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'event'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.location, doc.event_date], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Переводы мероприятий
|
|
|
- translations:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'event' and doc.translation_of
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, doc.translation_of, language], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для слайдов с мультидоменностью и мультиязычностью
|
|
|
- slides:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Активные слайды по порядку с фильтром по домену и языку
|
|
|
- active_ordered:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'slide' and doc.active is true
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.order], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Переводы слайдов
|
|
|
- translations:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'slide' and doc.translation_of
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, doc.translation_of, language], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для товаров с мультидоменностью и мультиязычностью
|
|
|
- products:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Все доступные товары с фильтром по домену и языку
|
|
|
- available:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'product' and doc.status is 'available'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.created_at], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Товары по категориям с фильтром по домену и языку
|
|
|
- by_category:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'product' and doc.status is 'available'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.category_id, doc.created_at], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Товары по тегам с фильтром по домену и языку
|
|
|
- by_tag:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'product' and doc.tags
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- for tag in doc.tags
|
|
|
- emit([domain, language, tag, doc.created_at], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Товары по цене с фильтром по домену и языку
|
|
|
- by_price:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'product' and doc.status is 'available'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.price], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Переводы товаров
|
|
|
- translations:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'product' and doc.translation_of
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, doc.translation_of, language], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для категорий и тем с мультидоменностью и мультиязычностью
|
|
|
- categories_themes:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Все категории по порядку с фильтром по домену и языку
|
|
|
- categories_ordered:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'category'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.order], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Категории по родителю с фильтром по домену и языку
|
|
|
- categories_by_parent:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'category'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- parent = doc.parent_id or 'root'
|
|
|
- emit([domain, language, parent, doc.order], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Все темы с фильтром по домену и языку
|
|
|
- themes:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'theme'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.name], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Переводы категорий
|
|
|
- category_translations:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'category' and doc.translation_of
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, doc.translation_of, language], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Переводы тем
|
|
|
- theme_translations:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'theme' and doc.translation_of
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, doc.translation_of, language], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для страниц с мультидоменностью и мультиязычностью
|
|
|
- pages:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Все страницы по slug с фильтром по домену и языку
|
|
|
- by_slug:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'page'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.slug], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Опубликованные страницы с фильтром по домену и языку
|
|
|
- published:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'page' and doc.status is 'published'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.order or 0], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Страницы по родителю с фильтром по домену и языку
|
|
|
- by_parent:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'page'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- parent = doc.parent_id or 'root'
|
|
|
- emit([domain, language, parent, doc.order or 0], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Переводы страниц
|
|
|
- translations:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'page' and doc.translation_of
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, doc.translation_of, language], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для меню с мультидоменностью и мультиязычностью
|
|
|
- menus:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Меню по локации с фильтром по домену и языку
|
|
|
- by_location:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'menu'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.location], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Активные меню с фильтром по домену и языку
|
|
|
- active:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'menu' and doc.active is true
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.location], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Переводы меню
|
|
|
- translations:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'menu' and doc.translation_of
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, doc.translation_of, language], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для настроек с мультидоменностью и мультиязычностью
|
|
|
- settings:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Настройки по ключу с фильтром по домену и языку
|
|
|
- by_key:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'setting'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.key], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Глобальные настройки (без привязки к языку)
|
|
|
- global:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'setting' and doc.is_global is true
|
|
|
- domain = doc.domain or 'default'
|
|
|
- emit([domain, doc.key], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для пользователей
|
|
|
- users:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Пользователи по email
|
|
|
- by_email:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'user'
|
|
|
- emit(doc.email, doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Пользователи по роли
|
|
|
- by_role:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'user'
|
|
|
- emit(doc.role, doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Активные пользователи
|
|
|
- active:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'user' and doc.active is true
|
|
|
- emit(doc.email, doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Пользователи по языку предпочтения
|
|
|
- by_language:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'user' and doc.preferences?.language
|
|
|
- emit(doc.preferences.language, doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для заказов с мультидоменностью
|
|
|
- orders:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Заказы по статусу с фильтром по домену
|
|
|
- by_status:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'order'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- emit([domain, doc.status, doc.created_at], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Заказы по пользователю с фильтром по домену
|
|
|
- by_user:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'order'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- emit([domain, doc.user_id, doc.created_at], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Заказы по языку
|
|
|
- by_language:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.type is 'order'
|
|
|
- domain = doc.domain or 'default'
|
|
|
- language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.created_at], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для переводов контента
|
|
|
- translations:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Все переводы по исходному документу
|
|
|
- by_source:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.translation_of
|
|
|
- domain = doc.domain or 'default'
|
|
|
- emit([domain, doc.translation_of, doc.language], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Переводы по типу контента
|
|
|
- by_content_type:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.translation_of
|
|
|
- domain = doc.domain or 'default'
|
|
|
- emit([domain, doc.type, doc.translation_of, doc.language], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Статусы переводов
|
|
|
- by_status:
|
|
|
- map: ((doc) ->
|
|
|
- if doc.translation_of and doc.translation_status
|
|
|
- domain = doc.domain or 'default'
|
|
|
- emit([domain, doc.translation_status, doc.language], doc)
|
|
|
- ).toString()
|
|
|
-
|
|
|
- # Design документ для глобального поиска с мультидоменностью и мультиязычностью
|
|
|
- global_search:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Глобальный поиск по всем типам документов с фильтром по домену и языку
|
|
|
- all_content:
|
|
|
- map: ((doc) ->
|
|
|
- searchFields = {}
|
|
|
+ # Пропускаем системные документы
|
|
|
+ if doc._id.startsWith('_design/') or doc.type is 'domain_settings'
|
|
|
+ return
|
|
|
+
|
|
|
domain = doc.domain or 'default'
|
|
|
+ domains = if Array.isArray(domain) then domain else [domain]
|
|
|
language = doc.language or 'ru'
|
|
|
|
|
|
+ searchFields = {}
|
|
|
+ searchText = ""
|
|
|
+
|
|
|
+ # Определяем поля для поиска в зависимости от типа документа
|
|
|
switch doc.type
|
|
|
when 'blog_post'
|
|
|
if doc.status is 'published'
|
|
|
@@ -480,6 +31,8 @@ module.exports =
|
|
|
author: doc.author
|
|
|
tags: doc.tags
|
|
|
type: 'blog_post'
|
|
|
+ domain: domains
|
|
|
+ language: language
|
|
|
|
|
|
when 'event'
|
|
|
searchFields =
|
|
|
@@ -488,6 +41,8 @@ module.exports =
|
|
|
location: doc.location
|
|
|
tags: doc.tags
|
|
|
type: 'event'
|
|
|
+ domain: domains
|
|
|
+ language: language
|
|
|
|
|
|
when 'product'
|
|
|
if doc.status is 'available'
|
|
|
@@ -498,12 +53,16 @@ module.exports =
|
|
|
category: doc.category
|
|
|
tags: doc.tags
|
|
|
type: 'product'
|
|
|
+ domain: domains
|
|
|
+ language: language
|
|
|
|
|
|
when 'category', 'theme'
|
|
|
searchFields =
|
|
|
name: doc.name
|
|
|
description: doc.description
|
|
|
type: doc.type
|
|
|
+ domain: domains
|
|
|
+ language: language
|
|
|
|
|
|
when 'page'
|
|
|
if doc.status is 'published'
|
|
|
@@ -511,8 +70,10 @@ module.exports =
|
|
|
title: doc.title
|
|
|
content: doc.content
|
|
|
type: 'page'
|
|
|
+ domain: domains
|
|
|
+ language: language
|
|
|
|
|
|
- # Создаем поисковый индекс
|
|
|
+ # Создаем поисковый индекс для каждого домена
|
|
|
if searchFields.title
|
|
|
text = (
|
|
|
searchFields.title + " " +
|
|
|
@@ -523,115 +84,390 @@ module.exports =
|
|
|
(searchFields.description or "")
|
|
|
).toLowerCase()
|
|
|
|
|
|
- # Добавляем теги
|
|
|
if searchFields.tags
|
|
|
text += " " + searchFields.tags.join(" ")
|
|
|
|
|
|
words = text.split(/\W+/).filter (word) -> word.length > 2
|
|
|
|
|
|
- for word in words
|
|
|
- emit([domain, language, word], {
|
|
|
- _id: doc._id
|
|
|
- type: searchFields.type
|
|
|
- title: searchFields.title
|
|
|
- excerpt: searchFields.excerpt
|
|
|
- created_at: doc.created_at
|
|
|
- domain: domain
|
|
|
- language: language
|
|
|
- })
|
|
|
+ for domain in domains
|
|
|
+ for word in words
|
|
|
+ emit([domain, language, word], {
|
|
|
+ _id: doc._id
|
|
|
+ type: searchFields.type
|
|
|
+ title: searchFields.title
|
|
|
+ excerpt: searchFields.excerpt
|
|
|
+ created_at: doc.created_at
|
|
|
+ domain: domain
|
|
|
+ language: language
|
|
|
+ relevance: 1
|
|
|
+ })
|
|
|
).toString()
|
|
|
|
|
|
- # Design документ для статистики с мультидоменностью и мультиязычностью
|
|
|
- statistics:
|
|
|
- version: "3.0"
|
|
|
- views:
|
|
|
- # Статистика по типам документов с фильтром по домену и языку
|
|
|
- by_type:
|
|
|
+ # Поиск по конкретному домену
|
|
|
+ domain_specific_search:
|
|
|
map: ((doc) ->
|
|
|
+ if doc._id.startsWith('_design/') or doc.type is 'domain_settings'
|
|
|
+ return
|
|
|
+
|
|
|
domain = doc.domain or 'default'
|
|
|
+ domains = if Array.isArray(domain) then domain else [domain]
|
|
|
language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.type], 1)
|
|
|
+
|
|
|
+ searchFields = {}
|
|
|
+
|
|
|
+ switch doc.type
|
|
|
+ when 'blog_post'
|
|
|
+ if doc.status is 'published'
|
|
|
+ searchFields = {title: doc.title, content: doc.content, excerpt: doc.excerpt}
|
|
|
+ when 'event'
|
|
|
+ searchFields = {title: doc.title, content: doc.content, location: doc.location}
|
|
|
+ when 'product'
|
|
|
+ if doc.status is 'available'
|
|
|
+ searchFields = {title: doc.title, content: doc.content, excerpt: doc.excerpt}
|
|
|
+ when 'page'
|
|
|
+ if doc.status is 'published'
|
|
|
+ searchFields = {title: doc.title, content: doc.content}
|
|
|
+
|
|
|
+ if searchFields.title
|
|
|
+ text = (searchFields.title + " " + (searchFields.content or "") + " " + (searchFields.excerpt or "")).toLowerCase()
|
|
|
+ words = text.split(/\W+/).filter (word) -> word.length > 2
|
|
|
+
|
|
|
+ for domain in domains
|
|
|
+ for word in words
|
|
|
+ emit([domain, word], {
|
|
|
+ _id: doc._id
|
|
|
+ type: doc.type
|
|
|
+ title: searchFields.title
|
|
|
+ language: language
|
|
|
+ domain: domain
|
|
|
+ })
|
|
|
).toString()
|
|
|
- reduce: ((keys, values) ->
|
|
|
- sum values
|
|
|
+
|
|
|
+ # Design документ для блог постов с улучшенной мультидоменностью
|
|
|
+ blog_posts:
|
|
|
+ version: "4.0"
|
|
|
+ views:
|
|
|
+ # Все опубликованные блог посты с поддержкой массива доменов
|
|
|
+ published_multidomain:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'blog_post' and doc.status is 'published'
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
+ language = doc.language or 'ru'
|
|
|
+
|
|
|
+ for domain in domains
|
|
|
+ emit([domain, language, doc.created_at], {
|
|
|
+ _id: doc._id
|
|
|
+ title: doc.title
|
|
|
+ excerpt: doc.excerpt
|
|
|
+ image: doc.image
|
|
|
+ author: doc.author
|
|
|
+ created_at: doc.created_at
|
|
|
+ domain: domain
|
|
|
+ language: language
|
|
|
+ featured: doc.featured or false
|
|
|
+ views: doc.views or 0
|
|
|
+ })
|
|
|
).toString()
|
|
|
|
|
|
- # Статистика просмотров блог постов с фильтром по домену и языку
|
|
|
- blog_views:
|
|
|
+ # Блог посты по тегам с мультидоменностью
|
|
|
+ by_tag_multidomain:
|
|
|
map: ((doc) ->
|
|
|
- if doc.type is 'blog_post'
|
|
|
- domain = doc.domain or 'default'
|
|
|
+ if doc.type is 'blog_post' and doc.status is 'published' and doc.tags
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc._id], doc.views or 0)
|
|
|
+
|
|
|
+ for domain in domains
|
|
|
+ for tag in doc.tags
|
|
|
+ emit([domain, language, tag, doc.created_at], doc)
|
|
|
).toString()
|
|
|
- reduce: ((keys, values) ->
|
|
|
- sum values
|
|
|
+
|
|
|
+ # Избранные посты с приоритетом доменов
|
|
|
+ featured_multidomain:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'blog_post' and doc.status is 'published' and doc.featured is true
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
+ language = doc.language or 'ru'
|
|
|
+ domainPriority = doc.domain_priority or domains
|
|
|
+
|
|
|
+ for domain, index in domains
|
|
|
+ priority = domainPriority.indexOf(domain)
|
|
|
+ priority = if priority is -1 then 999 else priority
|
|
|
+ emit([domain, priority, doc.created_at], doc)
|
|
|
).toString()
|
|
|
|
|
|
- # Статистика мероприятий по статусу с фильтром по домену и языку
|
|
|
- events_by_status:
|
|
|
+ # Переводы блог постов
|
|
|
+ translations_advanced:
|
|
|
map: ((doc) ->
|
|
|
- if doc.type is 'event'
|
|
|
- domain = doc.domain or 'default'
|
|
|
+ if doc.type is 'blog_post' and doc.translation_of
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
language = doc.language or 'ru'
|
|
|
- emit([domain, language, doc.status], 1)
|
|
|
+
|
|
|
+ for domain in domains
|
|
|
+ emit([domain, doc.translation_of, language], {
|
|
|
+ _id: doc._id
|
|
|
+ title: doc.title
|
|
|
+ language: doc.language
|
|
|
+ translation_status: doc.translation_status
|
|
|
+ domain: domain
|
|
|
+ })
|
|
|
).toString()
|
|
|
- reduce: ((keys, values) ->
|
|
|
- sum values
|
|
|
+
|
|
|
+ # Design документ для мероприятий с расширенной мультидоменностью
|
|
|
+ events:
|
|
|
+ version: "4.0"
|
|
|
+ views:
|
|
|
+ # Мероприятия по дате с поддержкой массива доменов
|
|
|
+ by_date_multidomain:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'event'
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
+ language = doc.language or 'ru'
|
|
|
+
|
|
|
+ for domain in domains
|
|
|
+ emit([domain, language, doc.event_date], {
|
|
|
+ _id: doc._id
|
|
|
+ title: doc.title
|
|
|
+ event_date: doc.event_date
|
|
|
+ location: doc.location
|
|
|
+ price: doc.price
|
|
|
+ status: doc.status
|
|
|
+ image: doc.image
|
|
|
+ domain: domain
|
|
|
+ language: language
|
|
|
+ available_tickets: doc.available_tickets
|
|
|
+ })
|
|
|
).toString()
|
|
|
|
|
|
- # Статистика продаж товаров с фильтром по домену и языку
|
|
|
- product_sales:
|
|
|
+ # Предстоящие мероприятия с приоритетом доменов
|
|
|
+ upcoming_priority:
|
|
|
map: ((doc) ->
|
|
|
- if doc.type is 'order' and doc.items
|
|
|
- domain = doc.domain or 'default'
|
|
|
+ if doc.type is 'event' and doc.status is 'upcoming'
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
language = doc.language or 'ru'
|
|
|
- for item in doc.items
|
|
|
- emit([domain, language, item.product_id], item.quantity)
|
|
|
- ).toString()
|
|
|
- reduce: ((keys, values) ->
|
|
|
- sum values
|
|
|
+ domainPriority = doc.domain_priority or domains
|
|
|
+
|
|
|
+ for domain, index in domains
|
|
|
+ priority = domainPriority.indexOf(domain)
|
|
|
+ priority = if priority is -1 then 999 else priority
|
|
|
+ emit([domain, priority, doc.event_date], doc)
|
|
|
).toString()
|
|
|
|
|
|
- # Статистика по языкам
|
|
|
- by_language:
|
|
|
+ # Мероприятия по местоположению с мультидоменностью
|
|
|
+ by_location_advanced:
|
|
|
map: ((doc) ->
|
|
|
- if doc.language
|
|
|
- domain = doc.domain or 'default'
|
|
|
- emit([domain, doc.language, doc.type], 1)
|
|
|
+ if doc.type is 'event'
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
+ language = doc.language or 'ru'
|
|
|
+
|
|
|
+ for domain in domains
|
|
|
+ locationKey = doc.location?.toLowerCase().replace(/\s+/g, '_') or 'unknown'
|
|
|
+ emit([domain, language, locationKey, doc.event_date], doc)
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Design документ для статистики и аналитики
|
|
|
+ statistics_advanced:
|
|
|
+ version: "4.0"
|
|
|
+ views:
|
|
|
+ # Статистика по доменам и типам контента
|
|
|
+ domain_content_stats:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc._id.startsWith('_design/')
|
|
|
+ return
|
|
|
+
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
+ docType = doc.type or 'unknown'
|
|
|
+ language = doc.language or 'ru'
|
|
|
+
|
|
|
+ for domain in domains
|
|
|
+ # Статистика по типам документов
|
|
|
+ emit([domain, 'type_count', docType], 1)
|
|
|
+
|
|
|
+ # Статистика по языкам
|
|
|
+ emit([domain, 'language_count', language], 1)
|
|
|
+
|
|
|
+ # Статистика по датам создания
|
|
|
+ if doc.created_at
|
|
|
+ date = doc.created_at.split('T')[0] # YYYY-MM-DD
|
|
|
+ emit([domain, 'creation_date', date], 1)
|
|
|
+
|
|
|
+ # Статистика просмотров для блог постов
|
|
|
+ if doc.type is 'blog_post' and doc.views
|
|
|
+ emit([domain, 'blog_views', doc._id], doc.views)
|
|
|
+
|
|
|
+ # Статистика доступных билетов для мероприятий
|
|
|
+ if doc.type is 'event' and doc.available_tickets
|
|
|
+ emit([domain, 'available_tickets', doc._id], doc.available_tickets)
|
|
|
).toString()
|
|
|
reduce: ((keys, values) ->
|
|
|
sum values
|
|
|
).toString()
|
|
|
|
|
|
- # Design документ для ревизий и аудита
|
|
|
- audit:
|
|
|
- version: "3.0"
|
|
|
+ # Аналитика популярности контента
|
|
|
+ content_popularity:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'blog_post'
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
+ views = doc.views or 0
|
|
|
+ likes = doc.likes or 0
|
|
|
+ shares = doc.shares or 0
|
|
|
+
|
|
|
+ popularity = views + (likes * 2) + (shares * 3)
|
|
|
+
|
|
|
+ for domain in domains
|
|
|
+ emit([domain, popularity], {
|
|
|
+ _id: doc._id
|
|
|
+ title: doc.title
|
|
|
+ views: views
|
|
|
+ likes: likes
|
|
|
+ shares: shares
|
|
|
+ popularity: popularity
|
|
|
+ created_at: doc.created_at
|
|
|
+ })
|
|
|
+
|
|
|
+ if doc.type is 'event'
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
+ tickets_sold = (doc.total_tickets or 0) - (doc.available_tickets or 0)
|
|
|
+
|
|
|
+ for domain in domains
|
|
|
+ emit([domain, tickets_sold], {
|
|
|
+ _id: doc._id
|
|
|
+ title: doc.title
|
|
|
+ tickets_sold: tickets_sold
|
|
|
+ total_tickets: doc.total_tickets
|
|
|
+ event_date: doc.event_date
|
|
|
+ })
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Design документ для управления переводами
|
|
|
+ translation_management:
|
|
|
+ version: "4.0"
|
|
|
views:
|
|
|
- # Логи изменений по дате
|
|
|
- by_date:
|
|
|
+ # Все переводы по исходному документу и домену
|
|
|
+ by_source_and_domain:
|
|
|
map: ((doc) ->
|
|
|
- if doc.type is 'audit_log'
|
|
|
- emit(doc.created_at, doc)
|
|
|
+ if doc.translation_of
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
+ for domain in domains
|
|
|
+ emit([domain, doc.translation_of, doc.language], {
|
|
|
+ _id: doc._id
|
|
|
+ type: doc.type
|
|
|
+ title: doc.title
|
|
|
+ language: doc.language
|
|
|
+ translation_status: doc.translation_status
|
|
|
+ domain: domain
|
|
|
+ created_at: doc.created_at
|
|
|
+ })
|
|
|
).toString()
|
|
|
|
|
|
- # Логи по пользователю
|
|
|
- by_user:
|
|
|
+ # Статусы переводов по доменам
|
|
|
+ translation_status_by_domain:
|
|
|
map: ((doc) ->
|
|
|
- if doc.type is 'audit_log'
|
|
|
- emit([doc.user_id, doc.created_at], doc)
|
|
|
+ if doc.translation_of and doc.translation_status
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
+ for domain in domains
|
|
|
+ emit([domain, doc.translation_status, doc.language], {
|
|
|
+ _id: doc._id
|
|
|
+ translation_of: doc.translation_of
|
|
|
+ type: doc.type
|
|
|
+ title: doc.title
|
|
|
+ })
|
|
|
).toString()
|
|
|
|
|
|
- # Логи по действию
|
|
|
- by_action:
|
|
|
+ # Отсутствующие переводы
|
|
|
+ missing_translations:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type and doc.language and not doc.translation_of
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
+ supportedLanguages = ['ru', 'en', 'tj']
|
|
|
+
|
|
|
+ for domain in domains
|
|
|
+ for targetLang in supportedLanguages
|
|
|
+ if targetLang != doc.language
|
|
|
+ emit([domain, doc._id, targetLang], {
|
|
|
+ original_id: doc._id
|
|
|
+ original_language: doc.language
|
|
|
+ target_language: targetLang
|
|
|
+ domain: domain
|
|
|
+ type: doc.type
|
|
|
+ title: doc.title
|
|
|
+ })
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Design документ для работы с заказами
|
|
|
+ orders_management:
|
|
|
+ version: "4.0"
|
|
|
+ views:
|
|
|
+ # Заказы по статусу и домену
|
|
|
+ by_status_and_domain:
|
|
|
map: ((doc) ->
|
|
|
- if doc.type is 'audit_log'
|
|
|
- emit([doc.action, doc.created_at], doc)
|
|
|
+ if doc.type is 'order'
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.status, doc.created_at], {
|
|
|
+ _id: doc._id
|
|
|
+ total: doc.total
|
|
|
+ currency: doc.currency
|
|
|
+ customer_name: doc.customer_info?.name
|
|
|
+ created_at: doc.created_at
|
|
|
+ items_count: doc.items?.length or 0
|
|
|
+ })
|
|
|
).toString()
|
|
|
|
|
|
- # Логи по языку
|
|
|
- by_language:
|
|
|
+ # Статистика продаж по доменам
|
|
|
+ sales_statistics:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'order' and doc.status is 'completed'
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ date = doc.created_at.split('T')[0] # YYYY-MM-DD
|
|
|
+
|
|
|
+ # Общая сумма за день
|
|
|
+ emit([domain, 'daily_sales', date], doc.total)
|
|
|
+
|
|
|
+ # Количество заказов за день
|
|
|
+ emit([domain, 'daily_orders', date], 1)
|
|
|
+
|
|
|
+ # Статистика по товарам
|
|
|
+ if doc.items
|
|
|
+ for item in doc.items
|
|
|
+ emit([domain, 'product_sales', item.product_id], item.quantity)
|
|
|
+ emit([domain, 'product_revenue', item.product_id], item.total)
|
|
|
+ ).toString()
|
|
|
+ reduce: ((keys, values) ->
|
|
|
+ sum values
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Design документ для системы уведомлений
|
|
|
+ notifications:
|
|
|
+ version: "4.0"
|
|
|
+ views:
|
|
|
+ # События для уведомлений по доменам
|
|
|
+ domain_events:
|
|
|
map: ((doc) ->
|
|
|
- if doc.type is 'audit_log' and doc.language
|
|
|
- emit([doc.language, doc.created_at], doc)
|
|
|
+ if doc.type is 'event' and doc.status is 'upcoming'
|
|
|
+ domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
|
|
|
+ eventDate = new Date(doc.event_date)
|
|
|
+ now = new Date()
|
|
|
+
|
|
|
+ # Уведомление за 7 дней до события
|
|
|
+ sevenDaysBefore = new Date(eventDate.getTime() - 7 * 24 * 60 * 60 * 1000)
|
|
|
+ if now >= sevenDaysBefore and now < eventDate
|
|
|
+ for domain in domains
|
|
|
+ emit([domain, 'event_reminder_7d', doc.event_date], {
|
|
|
+ _id: doc._id
|
|
|
+ title: doc.title
|
|
|
+ event_date: doc.event_date
|
|
|
+ domain: domain
|
|
|
+ notification_type: 'event_reminder_7d'
|
|
|
+ })
|
|
|
+
|
|
|
+ # Уведомление за 1 день до события
|
|
|
+ oneDayBefore = new Date(eventDate.getTime() - 24 * 60 * 60 * 1000)
|
|
|
+ if now >= oneDayBefore and now < eventDate
|
|
|
+ for domain in domains
|
|
|
+ emit([domain, 'event_reminder_1d', doc.event_date], {
|
|
|
+ _id: doc._id
|
|
|
+ title: doc.title
|
|
|
+ event_date: doc.event_date
|
|
|
+ domain: domain
|
|
|
+ notification_type: 'event_reminder_1d'
|
|
|
+ })
|
|
|
).toString()
|