|
|
@@ -1,161 +1,318 @@
|
|
|
-# Design документы для CouchDB
|
|
|
+# Design документы для CouchDB с поддержкой мультидоменности
|
|
|
|
|
|
module.exports =
|
|
|
- # Design документ для блог постов
|
|
|
+ # Design документ для настроек доменов
|
|
|
+ domains:
|
|
|
+ version: "2.0"
|
|
|
+ views:
|
|
|
+ # Все настройки доменов
|
|
|
+ all_domains:
|
|
|
+ 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: "1.1"
|
|
|
+ version: "2.0"
|
|
|
views:
|
|
|
- # Все опубликованные блог посты
|
|
|
+ # Все опубликованные блог посты с фильтром по домену
|
|
|
published:
|
|
|
map: ((doc) ->
|
|
|
if doc.type is 'blog_post' and doc.status is 'published'
|
|
|
- emit(doc.created_at, doc)).toString()
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.created_at], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Блог посты по тегам
|
|
|
+ # Блог посты по тегам с фильтром по домену
|
|
|
by_tag:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'blog_post' and doc.status is 'published' and doc.tags
|
|
|
+ domain = doc.domain or 'default'
|
|
|
for tag in doc.tags
|
|
|
- emit([tag, doc.created_at], doc)
|
|
|
+ emit([domain, tag, doc.created_at], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Блог посты по автору
|
|
|
+ # Блог посты по автору с фильтром по домену
|
|
|
by_author:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'blog_post' and doc.status is 'published'
|
|
|
- emit([doc.author, doc.created_at], doc)
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.author, doc.created_at], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Поиск по заголовку и содержанию
|
|
|
+ # Поиск по заголовку и содержанию с фильтром по домену
|
|
|
search:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'blog_post' and doc.status is 'published'
|
|
|
- # Индексируем заголовок и содержание для поиска
|
|
|
+ domain = doc.domain or 'default'
|
|
|
text = (doc.title + " " + doc.content + " " + doc.excerpt).toLowerCase()
|
|
|
words = text.split(/\W+/).filter (word) -> word.length > 2
|
|
|
|
|
|
for word in words
|
|
|
- emit(word, {
|
|
|
+ emit([domain, word], {
|
|
|
_id: doc._id
|
|
|
title: doc.title
|
|
|
excerpt: doc.excerpt
|
|
|
created_at: doc.created_at
|
|
|
+ domain: doc.domain
|
|
|
})
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Design документ для мероприятий
|
|
|
+ # Design документ для мероприятий с мультидоменностью
|
|
|
events:
|
|
|
- version: "1.0"
|
|
|
+ version: "2.0"
|
|
|
views:
|
|
|
- # Все мероприятия по дате
|
|
|
+ # Все мероприятия по дате с фильтром по домену
|
|
|
by_date:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'event'
|
|
|
- emit(doc.event_date, doc)
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.event_date], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Предстоящие мероприятия
|
|
|
+ # Предстоящие мероприятия с фильтром по домену
|
|
|
upcoming:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'event' and doc.status is 'upcoming'
|
|
|
- emit(doc.event_date, doc)
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.event_date], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Активные мероприятия
|
|
|
+ # Активные мероприятия с фильтром по домену
|
|
|
ongoing:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'event' and doc.status is 'ongoing'
|
|
|
- emit(doc.event_date, doc)
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.event_date], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Мероприятия по тегам
|
|
|
+ # Мероприятия по тегам с фильтром по домену
|
|
|
by_tag:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'event' and doc.tags
|
|
|
+ domain = doc.domain or 'default'
|
|
|
for tag in doc.tags
|
|
|
- emit([tag, doc.event_date], doc)
|
|
|
+ emit([domain, tag, doc.event_date], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Мероприятия по местоположению
|
|
|
+ # Мероприятия по местоположению с фильтром по домену
|
|
|
by_location:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'event'
|
|
|
- emit([doc.location, doc.event_date], doc)
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.location, doc.event_date], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Design документ для слайдов
|
|
|
+ # Design документ для слайдов с мультидоменностью
|
|
|
slides:
|
|
|
- version: "1.0"
|
|
|
+ version: "2.0"
|
|
|
views:
|
|
|
- # Активные слайды по порядку
|
|
|
+ # Активные слайды по порядку с фильтром по домену
|
|
|
active_ordered:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'slide' and doc.active is true
|
|
|
- emit(doc.order, doc)
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.order], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Design документ для товаров
|
|
|
+ # Design документ для товаров с мультидоменностью
|
|
|
products:
|
|
|
- version: "1.0"
|
|
|
+ version: "2.0"
|
|
|
views:
|
|
|
- # Все доступные товары
|
|
|
+ # Все доступные товары с фильтром по домену
|
|
|
available:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'product' and doc.status is 'available'
|
|
|
- emit(doc.created_at, doc)
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.created_at], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Товары по категориям
|
|
|
+ # Товары по категориям с фильтром по домену
|
|
|
by_category:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'product' and doc.status is 'available'
|
|
|
- emit([doc.category, doc.created_at], doc)
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.category, doc.created_at], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Товары по тегам
|
|
|
+ # Товары по тегам с фильтром по домену
|
|
|
by_tag:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'product' and doc.tags
|
|
|
+ domain = doc.domain or 'default'
|
|
|
for tag in doc.tags
|
|
|
- emit([tag, doc.created_at], doc)
|
|
|
+ emit([domain, tag, doc.created_at], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Товары по цене
|
|
|
+ # Товары по цене с фильтром по домену
|
|
|
by_price:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'product' and doc.status is 'available'
|
|
|
- emit(doc.price, doc)
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.price], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Design документ для категорий и тем
|
|
|
+ # Design документ для категорий и тем с мультидоменностью
|
|
|
categories_themes:
|
|
|
- version: "1.0"
|
|
|
+ version: "2.0"
|
|
|
views:
|
|
|
- # Все категории по порядку
|
|
|
+ # Все категории по порядку с фильтром по домену
|
|
|
categories_ordered:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'category'
|
|
|
- emit(doc.order, doc)
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.order], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Категории по родителю (для иерархии)
|
|
|
+ # Категории по родителю с фильтром по домену
|
|
|
categories_by_parent:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'category'
|
|
|
+ domain = doc.domain or 'default'
|
|
|
parent = doc.parent or 'root'
|
|
|
- emit([parent, doc.order], doc)
|
|
|
+ emit([domain, parent, doc.order], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Все темы
|
|
|
+ # Все темы с фильтром по домену
|
|
|
themes:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'theme'
|
|
|
- emit(doc.name, doc)
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.name], doc)
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Design документ для страниц с мультидоменностью
|
|
|
+ pages:
|
|
|
+ version: "2.0"
|
|
|
+ views:
|
|
|
+ # Все страницы по slug с фильтром по домену
|
|
|
+ by_slug:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'page'
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.slug], doc)
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Опубликованные страницы с фильтром по домену
|
|
|
+ published:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'page' and doc.status is 'published'
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.order or 0], doc)
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Design документ для меню с мультидоменностью
|
|
|
+ menus:
|
|
|
+ version: "2.0"
|
|
|
+ views:
|
|
|
+ # Меню по локации с фильтром по домену
|
|
|
+ by_location:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'menu'
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.location], doc)
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Активные меню с фильтром по домену
|
|
|
+ active:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'menu' and doc.active is true
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.location], doc)
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Design документ для настроек с мультидоменностью
|
|
|
+ settings:
|
|
|
+ version: "2.0"
|
|
|
+ views:
|
|
|
+ # Настройки по ключу с фильтром по домену
|
|
|
+ by_key:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'setting'
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.key], doc)
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Design документ для глобального поиска
|
|
|
+ # Design документ для пользователей
|
|
|
+ users:
|
|
|
+ version: "2.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()
|
|
|
+
|
|
|
+ # Design документ для заказов с мультидоменностью
|
|
|
+ orders:
|
|
|
+ version: "2.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()
|
|
|
+
|
|
|
+ # Design документ для глобального поиска с мультидоменностью
|
|
|
global_search:
|
|
|
- version: "1.0"
|
|
|
+ version: "2.0"
|
|
|
views:
|
|
|
- # Глобальный поиск по всем типам документов
|
|
|
+ # Глобальный поиск по всем типам документов с фильтром по домену
|
|
|
all_content:
|
|
|
- map: (doc) ->
|
|
|
- # Индексируем различные типы документов для поиска
|
|
|
+ map: ((doc) ->
|
|
|
searchFields = {}
|
|
|
+ domain = doc.domain or 'default'
|
|
|
|
|
|
switch doc.type
|
|
|
when 'blog_post'
|
|
|
- searchFields =
|
|
|
- title: doc.title
|
|
|
- content: doc.content
|
|
|
- excerpt: doc.excerpt
|
|
|
- author: doc.author
|
|
|
- tags: doc.tags
|
|
|
- type: 'blog_post'
|
|
|
+ if doc.status is 'published'
|
|
|
+ searchFields =
|
|
|
+ title: doc.title
|
|
|
+ content: doc.content
|
|
|
+ excerpt: doc.excerpt
|
|
|
+ author: doc.author
|
|
|
+ tags: doc.tags
|
|
|
+ type: 'blog_post'
|
|
|
|
|
|
when 'event'
|
|
|
searchFields =
|
|
|
@@ -166,19 +323,27 @@ module.exports =
|
|
|
type: 'event'
|
|
|
|
|
|
when 'product'
|
|
|
- searchFields =
|
|
|
- title: doc.title
|
|
|
- content: doc.content
|
|
|
- excerpt: doc.excerpt
|
|
|
- category: doc.category
|
|
|
- tags: doc.tags
|
|
|
- type: 'product'
|
|
|
+ if doc.status is 'available'
|
|
|
+ searchFields =
|
|
|
+ title: doc.title
|
|
|
+ content: doc.content
|
|
|
+ excerpt: doc.excerpt
|
|
|
+ category: doc.category
|
|
|
+ tags: doc.tags
|
|
|
+ type: 'product'
|
|
|
|
|
|
when 'category', 'theme'
|
|
|
searchFields =
|
|
|
name: doc.name
|
|
|
description: doc.description
|
|
|
type: doc.type
|
|
|
+
|
|
|
+ when 'page'
|
|
|
+ if doc.status is 'published'
|
|
|
+ searchFields =
|
|
|
+ title: doc.title
|
|
|
+ content: doc.content
|
|
|
+ type: 'page'
|
|
|
|
|
|
# Создаем поисковый индекс
|
|
|
if searchFields.title
|
|
|
@@ -198,37 +363,85 @@ module.exports =
|
|
|
words = text.split(/\W+/).filter (word) -> word.length > 2
|
|
|
|
|
|
for word in words
|
|
|
- emit(word, {
|
|
|
+ emit([domain, word], {
|
|
|
_id: doc._id
|
|
|
type: searchFields.type
|
|
|
title: searchFields.title
|
|
|
excerpt: searchFields.excerpt
|
|
|
created_at: doc.created_at
|
|
|
+ domain: domain
|
|
|
})
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Design документ для статистики
|
|
|
+ # Design документ для статистики с мультидоменностью
|
|
|
statistics:
|
|
|
- version: "1.0"
|
|
|
+ version: "2.0"
|
|
|
views:
|
|
|
- # Статистика по типам документов
|
|
|
+ # Статистика по типам документов с фильтром по домену
|
|
|
by_type:
|
|
|
- map: (doc) ->
|
|
|
- emit(doc.type, 1)
|
|
|
- reduce: (keys, values) ->
|
|
|
+ map: ((doc) ->
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.type], 1)
|
|
|
+ ).toString()
|
|
|
+ reduce: ((keys, values) ->
|
|
|
sum values
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Статистика просмотров блог постов
|
|
|
+ # Статистика просмотров блог постов с фильтром по домену
|
|
|
blog_views:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'blog_post'
|
|
|
- emit(doc._id, doc.views or 0)
|
|
|
- reduce: (keys, values) ->
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc._id], doc.views or 0)
|
|
|
+ ).toString()
|
|
|
+ reduce: ((keys, values) ->
|
|
|
sum values
|
|
|
+ ).toString()
|
|
|
|
|
|
- # Статистика мероприятий по статусу
|
|
|
+ # Статистика мероприятий по статусу с фильтром по домену
|
|
|
events_by_status:
|
|
|
- map: (doc) ->
|
|
|
+ map: ((doc) ->
|
|
|
if doc.type is 'event'
|
|
|
- emit(doc.status, 1)
|
|
|
- reduce: (keys, values) ->
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ emit([domain, doc.status], 1)
|
|
|
+ ).toString()
|
|
|
+ reduce: ((keys, values) ->
|
|
|
+ sum values
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Статистика продаж товаров с фильтром по домену
|
|
|
+ product_sales:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'order' and doc.items
|
|
|
+ domain = doc.domain or 'default'
|
|
|
+ for item in doc.items
|
|
|
+ emit([domain, item.product_id], item.quantity)
|
|
|
+ ).toString()
|
|
|
+ reduce: ((keys, values) ->
|
|
|
sum values
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Design документ для ревизий и аудита
|
|
|
+ audit:
|
|
|
+ version: "2.0"
|
|
|
+ views:
|
|
|
+ # Логи изменений по дате
|
|
|
+ by_date:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'audit_log'
|
|
|
+ emit(doc.created_at, doc)
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Логи по пользователю
|
|
|
+ by_user:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'audit_log'
|
|
|
+ emit([doc.user_id, doc.created_at], doc)
|
|
|
+ ).toString()
|
|
|
+
|
|
|
+ # Логи по действию
|
|
|
+ by_action:
|
|
|
+ map: ((doc) ->
|
|
|
+ if doc.type is 'audit_log'
|
|
|
+ emit([doc.action, doc.created_at], doc)
|
|
|
+ ).toString()
|