Как лучше найти и удалить одну строку в очень большом файле
Примерно год назад собеседовался с немцами, на одном из этапов дали это легкое тестовое задание. Мое решение их устроило, но позже уже третье собеседование по алгоритмам и теории я завалил капитально.
Наткнулся на него случайно, разгребая старую почту. Оцените мое решение, кто в теме. Как часто спрашивают во всяких бизнес линчах "Стоит продолжать развиваться в этом направлении или забыть?" :) Условие: Представьте, что вы работаете в компании, которая помогает американским фирмам импортировать свои товары для продажи в ЕС. Давайте представим, что каждый заказ может быть представлен в виде кортежа(tuple по англ, надеюсь правильно перевел) - (orderId, companyName, customerAdress, orderedItem). Пример того, как может выглядеть такой набор данных в файле: 001, SuperTrader, Steindamm 80, Macbook 002, Cheapskates, Reeperbahn 153, Macbook 003, MegaCorp, Steindamm 80, Book "Guide to Hamburg" 004, SuperTrader, Sternstrasse 125, Book "Cooking 101" 005, SuperTrader, Ottenser Hauptstrasse 24, Inline Skates 006, MegaCorp, Reeperbahn 153, Playstation 007, Cheapskates, Lagerstrasse 11, Flux compensator 008, SuperTrader, Reeperbahn 153, Inline Skates Нужно реализовать: 1) Отобразить все заказы конкретной компании 2) Показать все заказы по конкретному адресу 3) Удалить какой-то заказ по его OrderId 4) Отобразить самые популярные товары по убыванию Мое решение (все лишнее вроде server.js, routes.js и тд опущены, ниже только сам контроллер): // app/controller.js var fs = require('fs'), byline = require('byline'); function find(position, value, next) { var content, temp, data = new Array(); value = value.trim(); var stream = byline(fs.createReadStream('sample', {encoding: 'utf8'})); stream.on('readable', function() { while ((content = stream.read()) !== null) { content = content.split(/\r?\n/); content.forEach(function(item) { if (!item) { return; } temp = item.split(','); // if there are some wrong format data // for example delivery address with comma "Reeperbahn, 153" // we can do nothing and just skip this broken line if (temp.length != 4) { return; } if (temp[position].trim() == value) { data.push(temp); } }); } }); stream.on('end', function() { next(null, data); return; }); } function getTopSellers(req, next) { var content, temp, data = {}, companies = new Array(); var stream = byline(fs.createReadStream('sample', {encoding: 'utf8'})); stream.on('readable', function() { while ((content = stream.read()) !== null) { content = content.split(/\r?\n/); content.forEach(function(item) { // empty line? if (!item) { return; } temp = item.split(','); if (temp.length != 4) { return; } // temp[3] is a product name if (data[temp[3]]) { data[temp[3]] += 1; } else { data[temp[3]] = 1; } // if not ajax, then create also companies list if (!req.xhr) { if (companies.indexOf(temp[1]) < 0 && typeof temp[1] != undefined) { companies.push(temp[1]); } } }); } }); stream.on('end', function() { var top = new Array(); var temp = {}; for(var key in data) { temp[key] = data[key]; top.push(temp); temp = {}; } top.sort(compare); next(null, top, companies); return; }); } function deleteOrder(id, next) { var content, temp, data = '', storage, found = false; // we are blocking the whole process, while deleting an order // to avoid loosing of data, if new orders appearing very quickly content = fs.readFileSync('sample', {encoding: 'utf8'}); content = content.split(/\r?\n/); content.forEach(function(item) { storage = item; temp = item.split(','); if (temp.length != 4) { return; } if (temp[0].trim() == id) { found = true; } else { data += storage + '\n'; } }); if (found) { fs.writeFileSync('sample', data); next(null, "ok"); return; } else { next(null, "error"); return; } } function compare(a,b) { for (var a_key in a) { for (var b_key in b) { if (a[a_key] > b[b_key]) return -1; if (a[a_key] < b[b_key]) return 1; return 0; } } } module.exports.getTopSellers = getTopSellers; module.exports.find = find; module.exports.deleteOrder = deleteOrder; Интересует в принципе общая оценка решения, но есть один главный вопрос. Как в таком случае нужно и вообще правильно делать удаление заказа? В моем решении я считываю по строке из файла и затем, после прохода по всему файлу, перезаписываю его полностью без той строки, в которой нужно было найти orderID и удалить. Для этого мне конечно же приходится блокировать весь процесс, чтобы параллельно не могло быть другой записи в файл из Ноды, чтобы избежать перезаписи \ потери данных. Для небольших файлов оно еще работает, но что если этот файлик с заказами будет 10GB, например? Скажем это все заказы текущего месяца, миллионы их. Стоит об этом вообще париться, или так вряд ли бывает и задача не корректна? Как лучше решить задачу для файла с 10GB данных, куда каждую минуту пишутся новые заказы, отменяются старые и тд (Амазон, например). |
похоже в случае 10 Гб нужно уже базу данных применять, а не парится делать аналог СУБД для файла)))
|
Часовой пояс GMT +3, время: 17:29. |