design-documents.coffee 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. # Design документы для CouchDB с поддержкой мультидоменности и мультиязычности
  2. # Версия 4.0 - с учетом последних изменений в правилах
  3. module.exports =
  4. # Design документ для мультидоменного поиска
  5. multi_domain_search:
  6. version: "4.0"
  7. views:
  8. # Универсальный поиск по всем доменам и языкам
  9. universal_search:
  10. map: ((doc) ->
  11. # Пропускаем системные документы
  12. if doc._id.startsWith('_design/') or doc.type is 'domain_settings'
  13. return
  14. domain = doc.domain or 'default'
  15. domains = if Array.isArray(domain) then domain else [domain]
  16. language = doc.language or 'ru'
  17. searchFields = {}
  18. searchText = ""
  19. # Определяем поля для поиска в зависимости от типа документа
  20. switch doc.type
  21. when 'blog_post'
  22. if doc.status is 'published'
  23. searchFields =
  24. title: doc.title
  25. content: doc.content
  26. excerpt: doc.excerpt
  27. author: doc.author
  28. tags: doc.tags
  29. type: 'blog_post'
  30. domain: domains
  31. language: language
  32. when 'event'
  33. searchFields =
  34. title: doc.title
  35. content: doc.content
  36. location: doc.location
  37. tags: doc.tags
  38. type: 'event'
  39. domain: domains
  40. language: language
  41. when 'product'
  42. if doc.status is 'available'
  43. searchFields =
  44. title: doc.title
  45. content: doc.content
  46. excerpt: doc.excerpt
  47. category: doc.category
  48. tags: doc.tags
  49. type: 'product'
  50. domain: domains
  51. language: language
  52. when 'category', 'theme'
  53. searchFields =
  54. name: doc.name
  55. description: doc.description
  56. type: doc.type
  57. domain: domains
  58. language: language
  59. when 'page'
  60. if doc.status is 'published'
  61. searchFields =
  62. title: doc.title
  63. content: doc.content
  64. type: 'page'
  65. domain: domains
  66. language: language
  67. # Создаем поисковый индекс для каждого домена
  68. if searchFields.title
  69. text = (
  70. searchFields.title + " " +
  71. (searchFields.content or "") + " " +
  72. (searchFields.excerpt or "") + " " +
  73. (searchFields.author or "") + " " +
  74. (searchFields.location or "") + " " +
  75. (searchFields.description or "")
  76. ).toLowerCase()
  77. if searchFields.tags
  78. text += " " + searchFields.tags.join(" ")
  79. words = text.split(/\W+/).filter (word) -> word.length > 2
  80. for domain in domains
  81. for word in words
  82. emit([domain, language, word], {
  83. _id: doc._id
  84. type: searchFields.type
  85. title: searchFields.title
  86. excerpt: searchFields.excerpt
  87. created_at: doc.created_at
  88. domain: domain
  89. language: language
  90. relevance: 1
  91. })
  92. ).toString()
  93. # Поиск по конкретному домену
  94. domain_specific_search:
  95. map: ((doc) ->
  96. if doc._id.startsWith('_design/') or doc.type is 'domain_settings'
  97. return
  98. domain = doc.domain or 'default'
  99. domains = if Array.isArray(domain) then domain else [domain]
  100. language = doc.language or 'ru'
  101. searchFields = {}
  102. switch doc.type
  103. when 'blog_post'
  104. if doc.status is 'published'
  105. searchFields = {title: doc.title, content: doc.content, excerpt: doc.excerpt}
  106. when 'event'
  107. searchFields = {title: doc.title, content: doc.content, location: doc.location}
  108. when 'product'
  109. if doc.status is 'available'
  110. searchFields = {title: doc.title, content: doc.content, excerpt: doc.excerpt}
  111. when 'page'
  112. if doc.status is 'published'
  113. searchFields = {title: doc.title, content: doc.content}
  114. if searchFields.title
  115. text = (searchFields.title + " " + (searchFields.content or "") + " " + (searchFields.excerpt or "")).toLowerCase()
  116. words = text.split(/\W+/).filter (word) -> word.length > 2
  117. for domain in domains
  118. for word in words
  119. emit([domain, word], {
  120. _id: doc._id
  121. type: doc.type
  122. title: searchFields.title
  123. language: language
  124. domain: domain
  125. })
  126. ).toString()
  127. # Design документ для блог постов с улучшенной мультидоменностью
  128. blog_posts:
  129. version: "4.0"
  130. views:
  131. # Все опубликованные блог посты с поддержкой массива доменов
  132. published_multidomain:
  133. map: ((doc) ->
  134. if doc.type is 'blog_post' and doc.status is 'published'
  135. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  136. language = doc.language or 'ru'
  137. for domain in domains
  138. emit([domain, language, doc.created_at], {
  139. _id: doc._id
  140. title: doc.title
  141. excerpt: doc.excerpt
  142. image: doc.image
  143. author: doc.author
  144. created_at: doc.created_at
  145. domain: domain
  146. language: language
  147. featured: doc.featured or false
  148. views: doc.views or 0
  149. })
  150. ).toString()
  151. # Блог посты по тегам с мультидоменностью
  152. by_tag_multidomain:
  153. map: ((doc) ->
  154. if doc.type is 'blog_post' and doc.status is 'published' and doc.tags
  155. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  156. language = doc.language or 'ru'
  157. for domain in domains
  158. for tag in doc.tags
  159. emit([domain, language, tag, doc.created_at], doc)
  160. ).toString()
  161. # Избранные посты с приоритетом доменов
  162. featured_multidomain:
  163. map: ((doc) ->
  164. if doc.type is 'blog_post' and doc.status is 'published' and doc.featured is true
  165. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  166. language = doc.language or 'ru'
  167. domainPriority = doc.domain_priority or domains
  168. for domain, index in domains
  169. priority = domainPriority.indexOf(domain)
  170. priority = if priority is -1 then 999 else priority
  171. emit([domain, priority, doc.created_at], doc)
  172. ).toString()
  173. # Переводы блог постов
  174. translations_advanced:
  175. map: ((doc) ->
  176. if doc.type is 'blog_post' and doc.translation_of
  177. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  178. language = doc.language or 'ru'
  179. for domain in domains
  180. emit([domain, doc.translation_of, language], {
  181. _id: doc._id
  182. title: doc.title
  183. language: doc.language
  184. translation_status: doc.translation_status
  185. domain: domain
  186. })
  187. ).toString()
  188. # Design документ для мероприятий с расширенной мультидоменностью
  189. events:
  190. version: "4.0"
  191. views:
  192. # Мероприятия по дате с поддержкой массива доменов
  193. by_date_multidomain:
  194. map: ((doc) ->
  195. if doc.type is 'event'
  196. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  197. language = doc.language or 'ru'
  198. for domain in domains
  199. emit([domain, language, doc.event_date], {
  200. _id: doc._id
  201. title: doc.title
  202. event_date: doc.event_date
  203. location: doc.location
  204. price: doc.price
  205. status: doc.status
  206. image: doc.image
  207. domain: domain
  208. language: language
  209. available_tickets: doc.available_tickets
  210. })
  211. ).toString()
  212. # Предстоящие мероприятия с приоритетом доменов
  213. upcoming_priority:
  214. map: ((doc) ->
  215. if doc.type is 'event' and doc.status is 'upcoming'
  216. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  217. language = doc.language or 'ru'
  218. domainPriority = doc.domain_priority or domains
  219. for domain, index in domains
  220. priority = domainPriority.indexOf(domain)
  221. priority = if priority is -1 then 999 else priority
  222. emit([domain, priority, doc.event_date], doc)
  223. ).toString()
  224. # Мероприятия по местоположению с мультидоменностью
  225. by_location_advanced:
  226. map: ((doc) ->
  227. if doc.type is 'event'
  228. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  229. language = doc.language or 'ru'
  230. for domain in domains
  231. locationKey = doc.location?.toLowerCase().replace(/\s+/g, '_') or 'unknown'
  232. emit([domain, language, locationKey, doc.event_date], doc)
  233. ).toString()
  234. # Design документ для статистики и аналитики
  235. statistics_advanced:
  236. version: "4.0"
  237. views:
  238. # Статистика по доменам и типам контента
  239. domain_content_stats:
  240. map: ((doc) ->
  241. if doc._id.startsWith('_design/')
  242. return
  243. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  244. docType = doc.type or 'unknown'
  245. language = doc.language or 'ru'
  246. for domain in domains
  247. # Статистика по типам документов
  248. emit([domain, 'type_count', docType], 1)
  249. # Статистика по языкам
  250. emit([domain, 'language_count', language], 1)
  251. # Статистика по датам создания
  252. if doc.created_at
  253. date = doc.created_at.split('T')[0] # YYYY-MM-DD
  254. emit([domain, 'creation_date', date], 1)
  255. # Статистика просмотров для блог постов
  256. if doc.type is 'blog_post' and doc.views
  257. emit([domain, 'blog_views', doc._id], doc.views)
  258. # Статистика доступных билетов для мероприятий
  259. if doc.type is 'event' and doc.available_tickets
  260. emit([domain, 'available_tickets', doc._id], doc.available_tickets)
  261. ).toString()
  262. reduce: ((keys, values) ->
  263. sum values
  264. ).toString()
  265. # Аналитика популярности контента
  266. content_popularity:
  267. map: ((doc) ->
  268. if doc.type is 'blog_post'
  269. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  270. views = doc.views or 0
  271. likes = doc.likes or 0
  272. shares = doc.shares or 0
  273. popularity = views + (likes * 2) + (shares * 3)
  274. for domain in domains
  275. emit([domain, popularity], {
  276. _id: doc._id
  277. title: doc.title
  278. views: views
  279. likes: likes
  280. shares: shares
  281. popularity: popularity
  282. created_at: doc.created_at
  283. })
  284. if doc.type is 'event'
  285. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  286. tickets_sold = (doc.total_tickets or 0) - (doc.available_tickets or 0)
  287. for domain in domains
  288. emit([domain, tickets_sold], {
  289. _id: doc._id
  290. title: doc.title
  291. tickets_sold: tickets_sold
  292. total_tickets: doc.total_tickets
  293. event_date: doc.event_date
  294. })
  295. ).toString()
  296. # Design документ для управления переводами
  297. translation_management:
  298. version: "4.0"
  299. views:
  300. # Все переводы по исходному документу и домену
  301. by_source_and_domain:
  302. map: ((doc) ->
  303. if doc.translation_of
  304. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  305. for domain in domains
  306. emit([domain, doc.translation_of, doc.language], {
  307. _id: doc._id
  308. type: doc.type
  309. title: doc.title
  310. language: doc.language
  311. translation_status: doc.translation_status
  312. domain: domain
  313. created_at: doc.created_at
  314. })
  315. ).toString()
  316. # Статусы переводов по доменам
  317. translation_status_by_domain:
  318. map: ((doc) ->
  319. if doc.translation_of and doc.translation_status
  320. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  321. for domain in domains
  322. emit([domain, doc.translation_status, doc.language], {
  323. _id: doc._id
  324. translation_of: doc.translation_of
  325. type: doc.type
  326. title: doc.title
  327. })
  328. ).toString()
  329. # Отсутствующие переводы
  330. missing_translations:
  331. map: ((doc) ->
  332. if doc.type and doc.language and not doc.translation_of
  333. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  334. supportedLanguages = ['ru', 'en', 'tj']
  335. for domain in domains
  336. for targetLang in supportedLanguages
  337. if targetLang != doc.language
  338. emit([domain, doc._id, targetLang], {
  339. original_id: doc._id
  340. original_language: doc.language
  341. target_language: targetLang
  342. domain: domain
  343. type: doc.type
  344. title: doc.title
  345. })
  346. ).toString()
  347. # Design документ для работы с заказами
  348. orders_management:
  349. version: "4.0"
  350. views:
  351. # Заказы по статусу и домену
  352. by_status_and_domain:
  353. map: ((doc) ->
  354. if doc.type is 'order'
  355. domain = doc.domain or 'default'
  356. emit([domain, doc.status, doc.created_at], {
  357. _id: doc._id
  358. total: doc.total
  359. currency: doc.currency
  360. customer_name: doc.customer_info?.name
  361. created_at: doc.created_at
  362. items_count: doc.items?.length or 0
  363. })
  364. ).toString()
  365. # Статистика продаж по доменам
  366. sales_statistics:
  367. map: ((doc) ->
  368. if doc.type is 'order' and doc.status is 'completed'
  369. domain = doc.domain or 'default'
  370. date = doc.created_at.split('T')[0] # YYYY-MM-DD
  371. # Общая сумма за день
  372. emit([domain, 'daily_sales', date], doc.total)
  373. # Количество заказов за день
  374. emit([domain, 'daily_orders', date], 1)
  375. # Статистика по товарам
  376. if doc.items
  377. for item in doc.items
  378. emit([domain, 'product_sales', item.product_id], item.quantity)
  379. emit([domain, 'product_revenue', item.product_id], item.total)
  380. ).toString()
  381. reduce: ((keys, values) ->
  382. sum values
  383. ).toString()
  384. # Design документ для системы уведомлений
  385. notifications:
  386. version: "4.0"
  387. views:
  388. # События для уведомлений по доменам
  389. domain_events:
  390. map: ((doc) ->
  391. if doc.type is 'event' and doc.status is 'upcoming'
  392. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  393. eventDate = new Date(doc.event_date)
  394. now = new Date()
  395. # Уведомление за 7 дней до события
  396. sevenDaysBefore = new Date(eventDate.getTime() - 7 * 24 * 60 * 60 * 1000)
  397. if now >= sevenDaysBefore and now < eventDate
  398. for domain in domains
  399. emit([domain, 'event_reminder_7d', doc.event_date], {
  400. _id: doc._id
  401. title: doc.title
  402. event_date: doc.event_date
  403. domain: domain
  404. notification_type: 'event_reminder_7d'
  405. })
  406. # Уведомление за 1 день до события
  407. oneDayBefore = new Date(eventDate.getTime() - 24 * 60 * 60 * 1000)
  408. if now >= oneDayBefore and now < eventDate
  409. for domain in domains
  410. emit([domain, 'event_reminder_1d', doc.event_date], {
  411. _id: doc._id
  412. title: doc.title
  413. event_date: doc.event_date
  414. domain: domain
  415. notification_type: 'event_reminder_1d'
  416. })
  417. ).toString()