design-documents.coffee 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. # Design документы для CouchDB с поддержкой иерархических категорий
  2. # Версия 5.0 - с иерархической структурой категорий
  3. module.exports =
  4. # Design документ для работы с иерархическими категориями
  5. categories_hierarchical:
  6. version: "5.0"
  7. views:
  8. # Все категории по уровню иерархии
  9. by_level:
  10. map: ((doc) ->
  11. if doc.type is 'category'
  12. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  13. level = doc.level or 0
  14. for domain in domains
  15. emit([domain, level, doc.order], {
  16. _id: doc._id
  17. name: doc.name
  18. slug: doc.slug
  19. parent_id: doc.parent_id
  20. level: level
  21. order: doc.order
  22. active: doc.active
  23. featured: doc.featured
  24. show_in_menu: doc.show_in_menu
  25. })
  26. ).toString()
  27. # Категории по родителю (для построения дерева)
  28. by_parent:
  29. map: ((doc) ->
  30. if doc.type is 'category'
  31. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  32. parent_id = doc.parent_id or 'root'
  33. for domain in domains
  34. emit([domain, parent_id, doc.order], {
  35. _id: doc._id
  36. name: doc.name
  37. slug: doc.slug
  38. level: doc.level
  39. children_count: doc.children_count
  40. order: doc.order
  41. active: doc.active
  42. })
  43. ).toString()
  44. # Полное дерево категорий с путями
  45. by_path:
  46. map: ((doc) ->
  47. if doc.type is 'category'
  48. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  49. parent_path = doc.parent_path or []
  50. for domain in domains
  51. # Эмитим каждый сегмент пути для быстрого поиска
  52. for path_segment, index in parent_path
  53. emit([domain, path_segment, doc.level, doc.order], doc)
  54. # Эмитим саму категорию
  55. emit([domain, doc._id, doc.level, doc.order], doc)
  56. ).toString()
  57. # Корневые категории
  58. root_categories:
  59. map: ((doc) ->
  60. if doc.type is 'category' and (not doc.parent_id or doc.parent_id is null)
  61. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  62. for domain in domains
  63. emit([domain, doc.order], {
  64. _id: doc._id
  65. name: doc.name
  66. slug: doc.slug
  67. level: 0
  68. children_count: doc.children_count
  69. order: doc.order
  70. active: doc.active
  71. featured: doc.featured
  72. })
  73. ).toString()
  74. # Категории для меню
  75. menu_categories:
  76. map: ((doc) ->
  77. if doc.type is 'category' and doc.show_in_menu is true
  78. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  79. level = doc.level or 0
  80. for domain in domains
  81. emit([domain, level, doc.menu_order or doc.order], {
  82. _id: doc._id
  83. name: doc.name
  84. slug: doc.slug
  85. parent_id: doc.parent_id
  86. level: level
  87. menu_order: doc.menu_order
  88. icon: doc.icon
  89. color: doc.color
  90. })
  91. ).toString()
  92. # Design документ для контента с иерархическими категориями
  93. content_with_hierarchical_categories:
  94. version: "5.0"
  95. views:
  96. # Контент по полному пути категории
  97. by_category_path:
  98. map: ((doc) ->
  99. if (doc.type is 'blog_post' or doc.type is 'event') and doc.category_path
  100. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  101. for domain in domains
  102. # Эмитим для каждого сегмента пути категории
  103. for category_id in doc.category_path
  104. emit([domain, category_id, doc.created_at], {
  105. _id: doc._id
  106. type: doc.type
  107. title: doc.title
  108. category_id: doc.category_id
  109. category_path: doc.category_path
  110. created_at: doc.created_at
  111. status: doc.status
  112. featured: doc.featured
  113. })
  114. ).toString()
  115. # Контент по конечной категории (самой глубокой)
  116. by_leaf_category:
  117. map: ((doc) ->
  118. if (doc.type is 'blog_post' or doc.type is 'event') and doc.category_path
  119. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  120. leaf_category = doc.category_path[doc.category_path.length - 1]
  121. for domain in domains
  122. emit([domain, leaf_category, doc.created_at], {
  123. _id: doc._id
  124. type: doc.type
  125. title: doc.title
  126. category_id: doc.category_id
  127. created_at: doc.created_at
  128. status: doc.status
  129. })
  130. ).toString()
  131. # Контент по корневой категории
  132. by_root_category:
  133. map: ((doc) ->
  134. if (doc.type is 'blog_post' or doc.type is 'event') and doc.category_path
  135. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  136. root_category = doc.category_path[0]
  137. for domain in domains
  138. emit([domain, root_category, doc.created_at], {
  139. _id: doc._id
  140. type: doc.type
  141. title: doc.title
  142. root_category: root_category
  143. created_at: doc.created_at
  144. status: doc.status
  145. })
  146. ).toString()
  147. # Design документ для поиска с учетом иерархии категорий
  148. search_with_category_hierarchy:
  149. version: "5.0"
  150. views:
  151. # Поиск по контенту с информацией о категории
  152. content_with_category_info:
  153. map: ((doc) ->
  154. if (doc.type is 'blog_post' and doc.status is 'published') or doc.type is 'event'
  155. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  156. search_text = (doc.title + " " + (doc.content or "") + " " + (doc.excerpt or "")).toLowerCase()
  157. words = search_text.split(/\W+/).filter (word) -> word.length > 2
  158. for domain in domains
  159. for word in words
  160. emit([domain, word], {
  161. _id: doc._id
  162. type: doc.type
  163. title: doc.title
  164. excerpt: doc.excerpt
  165. category_id: doc.category_id
  166. category_path: doc.category_path
  167. created_at: doc.created_at
  168. domain: domain
  169. })
  170. ).toString()
  171. # Поиск по категориям и тегам
  172. by_category_and_tags:
  173. map: ((doc) ->
  174. if (doc.type is 'blog_post' and doc.status is 'published') or doc.type is 'event'
  175. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  176. for domain in domains
  177. # По категории
  178. if doc.category_id
  179. emit([domain, 'category', doc.category_id, doc.created_at], {
  180. _id: doc._id
  181. type: doc.type
  182. title: doc.title
  183. })
  184. # По тегам
  185. if doc.tags
  186. for tag in doc.tags
  187. emit([domain, 'tag', tag, doc.created_at], {
  188. _id: doc._id
  189. type: doc.type
  190. title: doc.title
  191. })
  192. ).toString()
  193. # Design документ для статистики иерархических категорий
  194. category_statistics:
  195. version: "5.0"
  196. views:
  197. # Статистика контента по категориям
  198. content_count_by_category:
  199. map: ((doc) ->
  200. if (doc.type is 'blog_post' or doc.type is 'event') and doc.category_id
  201. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  202. for domain in domains
  203. # По конечной категории
  204. emit([domain, 'leaf_category', doc.category_id, doc.type], 1)
  205. # По всем категориям в пути
  206. if doc.category_path
  207. for category_id in doc.category_path
  208. emit([domain, 'path_category', category_id, doc.type], 1)
  209. ).toString()
  210. reduce: ((keys, values) ->
  211. sum values
  212. ).toString()
  213. # Статистика популярности категорий
  214. category_popularity:
  215. map: ((doc) ->
  216. if doc.type is 'blog_post'
  217. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  218. views = doc.views or 0
  219. for domain in domains
  220. if doc.category_id
  221. emit([domain, doc.category_id], views)
  222. if doc.category_path
  223. for category_id in doc.category_path
  224. emit([domain, category_id], views)
  225. else if doc.type is 'event'
  226. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  227. tickets_sold = (doc.total_tickets or 0) - (doc.available_tickets or 0)
  228. for domain in domains
  229. if doc.category_id
  230. emit([domain, doc.category_id], tickets_sold)
  231. ).toString()
  232. reduce: ((keys, values) ->
  233. sum values
  234. ).toString()
  235. # Design документ для навигации по иерархии
  236. category_navigation:
  237. version: "5.0"
  238. views:
  239. # Соседние категории (одного уровня)
  240. siblings_categories:
  241. map: ((doc) ->
  242. if doc.type is 'category'
  243. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  244. parent_id = doc.parent_id or 'root'
  245. for domain in domains
  246. emit([domain, parent_id, doc.order], {
  247. _id: doc._id
  248. name: doc.name
  249. slug: doc.slug
  250. order: doc.order
  251. level: doc.level
  252. })
  253. ).toString()
  254. # Дочерние категории
  255. children_categories:
  256. map: ((doc) ->
  257. if doc.type is 'category'
  258. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  259. for domain in domains
  260. emit([domain, doc._id, 'children'], {
  261. _id: doc._id
  262. name: doc.name
  263. children_count: doc.children_count
  264. has_children: doc.children_count > 0
  265. })
  266. ).toString()
  267. # Хлебные крошки для категорий
  268. breadcrumbs:
  269. map: ((doc) ->
  270. if doc.type is 'category' and doc.parent_path
  271. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  272. for domain in domains
  273. # Эмитим для каждого родителя в пути
  274. for parent_id, index in doc.parent_path
  275. emit([domain, doc._id, 'parent', index], {
  276. parent_id: parent_id
  277. position: index
  278. current_category: doc._id
  279. })
  280. ).toString()
  281. # Design документ для мультидоменного поиска (обновленный)
  282. multi_domain_search:
  283. version: "5.0"
  284. views:
  285. # Универсальный поиск с учетом иерархии категорий
  286. universal_search_with_categories:
  287. map: ((doc) ->
  288. if doc._id.startsWith('_design/') or doc.type is 'domain_settings'
  289. return
  290. domain = doc.domain or 'default'
  291. domains = if Array.isArray(domain) then domain else [domain]
  292. language = doc.language or 'ru'
  293. searchFields = {}
  294. searchText = ""
  295. switch doc.type
  296. when 'blog_post'
  297. if doc.status is 'published'
  298. searchFields =
  299. title: doc.title
  300. content: doc.content
  301. excerpt: doc.excerpt
  302. author: doc.author
  303. tags: doc.tags
  304. category_path: doc.category_path
  305. type: 'blog_post'
  306. when 'event'
  307. searchFields =
  308. title: doc.title
  309. content: doc.content
  310. location: doc.location
  311. tags: doc.tags
  312. category_path: doc.category_path
  313. type: 'event'
  314. if searchFields.title
  315. text = (
  316. searchFields.title + " " +
  317. (searchFields.content or "") + " " +
  318. (searchFields.excerpt or "") + " " +
  319. (searchFields.author or "") + " " +
  320. (searchFields.location or "")
  321. ).toLowerCase()
  322. if searchFields.tags
  323. text += " " + searchFields.tags.join(" ")
  324. words = text.split(/\W+/).filter (word) -> word.length > 2
  325. for domain in domains
  326. for word in words
  327. emit([domain, language, word], {
  328. _id: doc._id
  329. type: searchFields.type
  330. title: searchFields.title
  331. excerpt: searchFields.excerpt
  332. category_path: searchFields.category_path
  333. created_at: doc.created_at
  334. domain: domain
  335. language: language
  336. })
  337. ).toString()
  338. # Design документ для блог постов (обновленный)
  339. blog_posts:
  340. version: "5.0"
  341. views:
  342. # Блог посты с информацией о категориях
  343. published_with_categories:
  344. map: ((doc) ->
  345. if doc.type is 'blog_post' and doc.status is 'published'
  346. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  347. language = doc.language or 'ru'
  348. for domain in domains
  349. emit([domain, language, doc.created_at], {
  350. _id: doc._id
  351. title: doc.title
  352. excerpt: doc.excerpt
  353. image: doc.image
  354. author: doc.author
  355. category_id: doc.category_id
  356. category_path: doc.category_path
  357. created_at: doc.created_at
  358. domain: domain
  359. language: language
  360. featured: doc.featured or false
  361. views: doc.views or 0
  362. })
  363. ).toString()
  364. # Design документ для мероприятий (обновленный)
  365. events:
  366. version: "5.0"
  367. views:
  368. # Мероприятия с информацией о категориях
  369. by_date_with_categories:
  370. map: ((doc) ->
  371. if doc.type is 'event'
  372. domains = if Array.isArray(doc.domain) then doc.domain else [doc.domain or 'default']
  373. language = doc.language or 'ru'
  374. for domain in domains
  375. emit([domain, language, doc.event_date], {
  376. _id: doc._id
  377. title: doc.title
  378. event_date: doc.event_date
  379. location: doc.location
  380. price: doc.price
  381. status: doc.status
  382. image: doc.image
  383. category_id: doc.category_id
  384. category_path: doc.category_path
  385. domain: domain
  386. language: language
  387. available_tickets: doc.available_tickets
  388. })
  389. ).toString()