DbCreator.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. const InpxParser = require('./InpxParser');
  2. const utils = require('./utils');
  3. class DbCreator {
  4. constructor(config) {
  5. this.config = config;
  6. }
  7. async run(db, callback) {
  8. const config = this.config;
  9. //book
  10. await db.create({
  11. table: 'book'
  12. });
  13. callback({job: 'load inpx', jobMessage: 'Загрузка INPX'});
  14. const readFileCallback = async(readState) => {
  15. callback(readState);
  16. };
  17. //поисковые таблицы, ниже сохраним в БД
  18. let authorMap = new Map();//авторы
  19. let authorArr = [];
  20. let seriesMap = new Map();//серии
  21. let seriesArr = [];
  22. let titleMap = new Map();//названия
  23. let titleArr = [];
  24. let genreMap = new Map();//жанры
  25. let genreArr = [];
  26. let recsLoaded = 0;
  27. let id = 0;
  28. const parsedCallback = async(chunk) => {
  29. for (const rec of chunk) {
  30. rec.id = ++id;
  31. if (!rec.author)
  32. continue;
  33. //авторы
  34. const author = rec.author.split(',');
  35. if (author.length > 1)
  36. author.push(rec.author);
  37. const authorIds = [];
  38. for (const a of author) {
  39. let authorRec;
  40. if (authorMap.has(a)) {
  41. const authorId = authorMap.get(a);
  42. authorRec = authorArr[authorId];
  43. } else {
  44. authorRec = {id: authorArr.length, author: a, value: a.toLowerCase(), bookId: []};
  45. authorArr.push(authorRec);
  46. authorMap.set(a, authorRec.id);
  47. }
  48. authorRec.bookId.push(id);
  49. authorIds.push(authorRec.id);
  50. }
  51. //серии
  52. if (rec.series) {
  53. const series = rec.series;
  54. let seriesRec;
  55. if (seriesMap.has(series)) {
  56. const seriesId = seriesMap.get(series);
  57. seriesRec = seriesArr[seriesId];
  58. } else {
  59. seriesRec = {id: seriesArr.length, value: series.toLowerCase(), authorId: new Set()};
  60. seriesArr.push(seriesRec);
  61. seriesMap.set(series, seriesRec.id);
  62. }
  63. for (const id of authorIds) {
  64. seriesRec.authorId.add(id);
  65. }
  66. }
  67. //названия
  68. if (rec.title) {
  69. const title = rec.title;
  70. let titleRec;
  71. if (titleMap.has(title)) {
  72. const titileId = titleMap.get(title);
  73. titleRec = titleArr[titileId];
  74. } else {
  75. titleRec = {id: titleArr.length, value: title.toLowerCase(), authorId: new Set()};
  76. titleArr.push(titleRec);
  77. titleMap.set(title, titleRec.id);
  78. }
  79. for (const id of authorIds) {
  80. titleRec.authorId.add(id);
  81. }
  82. }
  83. //жанры
  84. if (rec.genre) {
  85. const genre = rec.genre.split(',');
  86. for (const g of genre) {
  87. let genreRec;
  88. if (genreMap.has(g)) {
  89. const genreId = genreMap.get(g);
  90. genreRec = genreArr[genreId];
  91. } else {
  92. genreRec = {id: genreArr.length, value: g, authorId: new Set()};
  93. genreArr.push(genreRec);
  94. genreMap.set(g, genreRec.id);
  95. }
  96. for (const id of authorIds) {
  97. genreRec.authorId.add(id);
  98. }
  99. }
  100. }
  101. }
  102. await db.insert({table: 'book', rows: chunk});
  103. recsLoaded += chunk.length;
  104. callback({recsLoaded});
  105. };
  106. //парсинг
  107. const parser = new InpxParser();
  108. await parser.parse(config.inpxFile, readFileCallback, parsedCallback);
  109. callback({job: 'config save', jobMessage: 'Сохранение конфигурации'});
  110. //чистка памяти, ибо жрет как не в себя
  111. authorMap = null;
  112. seriesMap = null;
  113. titleMap = null;
  114. genreMap = null;
  115. utils.freeMemory();
  116. //конфиг
  117. console.log('author:', authorArr.length);
  118. console.log('series:', seriesArr.length);
  119. console.log('title:', titleArr.length);
  120. console.log('genre:', genreArr.length);
  121. //сохраним поисковые таблицы
  122. const chunkSize = 10000;
  123. //author
  124. callback({job: 'author save', jobMessage: 'Сохранение авторов книг'});
  125. await db.create({
  126. table: 'author',
  127. index: {field: 'value', depth: config.indexDepth},
  128. });
  129. //вставка в БД по кусочкам, экономим память
  130. for (let i = 0; i < authorArr.length; i += chunkSize) {
  131. const chunk = authorArr.slice(i, i + chunkSize);
  132. await db.insert({table: 'author', rows: chunk});
  133. }
  134. authorArr = null;
  135. await db.close({table: 'author'});
  136. utils.freeMemory();
  137. //series
  138. callback({job: 'series save', jobMessage: 'Сохранение серий книг'});
  139. await db.create({
  140. table: 'series',
  141. index: {field: 'value', depth: config.indexDepth},
  142. });
  143. //вставка в БД по кусочкам, экономим память
  144. for (let i = 0; i < seriesArr.length; i += chunkSize) {
  145. const chunk = seriesArr.slice(i, i + chunkSize);
  146. for (const rec of chunk)
  147. rec.authorId = Array.from(rec.authorId);
  148. await db.insert({table: 'series', rows: chunk});
  149. }
  150. seriesArr = null;
  151. await db.close({table: 'series'});
  152. utils.freeMemory();
  153. //title
  154. callback({job: 'title save', jobMessage: 'Сохранение названий книг'});
  155. await db.create({
  156. table: 'title',
  157. index: {field: 'value', depth: config.indexDepth},
  158. });
  159. //вставка в БД по кусочкам, экономим память
  160. for (let i = 0; i < titleArr.length; i += chunkSize) {
  161. const chunk = titleArr.slice(i, i + chunkSize);
  162. for (const rec of chunk)
  163. rec.authorId = Array.from(rec.authorId);
  164. await db.insert({table: 'title', rows: chunk});
  165. }
  166. titleArr = null;
  167. await db.close({table: 'title'});
  168. utils.freeMemory();
  169. //genre
  170. callback({job: 'genre save', jobMessage: 'Сохранение жанров'});
  171. await db.create({
  172. table: 'genre',
  173. index: {field: 'value', depth: config.indexDepth},
  174. });
  175. //вставка в БД по кусочкам, экономим память
  176. for (let i = 0; i < genreArr.length; i += chunkSize) {
  177. const chunk = genreArr.slice(i, i + chunkSize);
  178. for (const rec of chunk)
  179. rec.authorId = Array.from(rec.authorId);
  180. await db.insert({table: 'genre', rows: chunk});
  181. }
  182. genreArr = null;
  183. await db.close({table: 'genre'});
  184. utils.freeMemory();
  185. callback({job: 'done', jobMessage: ''});
  186. }
  187. }
  188. module.exports = DbCreator;