Member 12224804 Ответов: 3

Быстрый поиск в больших объемах хранимых данных


У меня есть большие данные, хранящиеся в текстовом файле более 10000 следующим образом:
ID  Name  employment age  work_experience_year
12    XXX   UUU        22    BBBB

и так далее. Поэтому один из методов, использующих C#, который я сделал, - это прочитать все строки и поместить их в 2D-массив, а затем выполнить поиск строк. Две проблемы, с которыми я столкнулся сначала, когда я прочитал всю строку, память увеличится, а компьютер замедлится. Вторая проблема заключается в том, что поиск займет много времени.

Поиск, который я хочу, осуществляется по любым столбцам типа возраст, имя...
Итак, какой другой метод я использую, не хранясь в 2D-массиве?
Кроме того, а если использовать SQL лучше ?и что использовать ?

Что я уже пробовал:

линейный поиск и 2D массив были слишком медленными

PIEBALDconsult

Поместите данные в DataTable и используйте свойство RowFilter DefaultView.

PIEBALDconsult

Вы также можете построить любое количество индексов, используя словари.

3 Ответов

Рейтинг:
9

F-ES Sitecore

В дополнение к другим решениям вы можете использовать Lucene indexing для построения индекса данных и последующего их поиска. Люцин мощный, но немного неуклюжий в использовании.

CSV-файл

ID  Name  employment age  work_experience_year
1,XXX,UUU,22,BBBB
2,John,Manager,40,CCCC
3,Dave,CEO,50,DDDD
4,Peter Jones,Janitor,32,EEEE
5,John Simon,Tester,43,FFFF


Класс для хранения каждой строки данных

public class RowData
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Employment { get; set; }
}


Функция индексирования

static void IndexFile(string filename)
{
    var AppLuceneVersion = Lucene.Net.Util.Version.LUCENE_30;

    var indexLocation = @"C:\temp\Index";
    var dir = FSDirectory.Open(indexLocation);

    var fileStream = new StreamReader(filename);
    string line = fileStream.ReadLine();

    var splitChar = new char[] { ',' };

    using (var analyzer = new StandardAnalyzer(AppLuceneVersion))
    using (var writer = new IndexWriter(dir, analyzer, IndexWriter.MaxFieldLength.UNLIMITED))
    {
        writer.DeleteAll();
        while ((line = fileStream.ReadLine()) != null)
        {
            var values = line.Split(splitChar);

            Document document = new Document();

            document.Add(new Field("id", values[0], Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO));
            document.Add(new Field("name", values[1], Field.Store.NO, Field.Index.ANALYZED));
            document.Add(new Field("name_exact", values[1], Field.Store.YES, Field.Index.NOT_ANALYZED));
            document.Add(new Field("employment", values[2], Field.Store.YES, Field.Index.ANALYZED));
            var ageField = new NumericField("age", Field.Store.YES, true);
            ageField.OmitNorms = false;
                    
            ageField.OmitTermFreqAndPositions = false;
            ageField.SetLongValue(long.Parse(values[3]));
            document.Add(ageField);

            writer.AddDocument(document);
        }
    }
}


Функция поиска

static List<RowData> Search(int id = 0, string name = null, string exactName = null, int age = 0, int maxAge = 0)
{
    var indexLocation = @"C:\temp\Index";
    var dir = FSDirectory.Open(indexLocation);

    IndexSearcher searcher = new IndexSearcher(dir);

    var query = new BooleanQuery();

    if (id > 0)
    {
        Term searchTerm = new Term("id", id.ToString());
        query.Add(new BooleanClause(new TermQuery(searchTerm), Occur.MUST));
    }

    if (!string.IsNullOrWhiteSpace(name))
    {
        Term searchTerm = new Term("name", name.ToLowerInvariant());
        query.Add(new BooleanClause(new TermQuery(searchTerm), Occur.MUST));
    }

    if (!string.IsNullOrWhiteSpace(exactName))
    {
        Term searchTerm = new Term("name_exact", exactName);
        query.Add(new BooleanClause(new TermQuery(searchTerm), Occur.MUST));
    }

    if (age > 0 && maxAge == 0)
    {
        Term searchTerm = new Term("age", NumericUtils.LongToPrefixCoded(age));
        query.Add(new BooleanClause(new TermQuery(searchTerm), Occur.MUST));
    }

    if (age > 0 && maxAge > 0)
    {
        // search for any age greater than or equal to the given age
        var nrq = NumericRangeQuery.NewLongRange("age", age, maxAge, true, true);
        query.Add(nrq, Occur.MUST);
    }

    var hits = searcher.Search(query, 1000);

    var results = new List<RowData>();

    foreach (var hit in hits.ScoreDocs)
    {
        var doc = searcher.Doc(hit.Doc);

        results.Add(new RowData {
            ID = int.Parse(doc.Get("id")),
            Name = doc.Get("name_exact"),
            Employment = doc.Get("employment"),
            Age = int.Parse(doc.Get("age"))
        });
    }

    return results;
}


Использование

static void Main(string[] args)
{
    // You only need to index the file once, or every time it changes
    IndexFile(@"C:\Temp\data.csv");

    // Search by ID
    var results = Search(id: 1);

    ShowResults("Search by ID", results);

    // Do a partial search on a name
    results = Search(name: "John");

    ShowResults("Search by name", results);

    // Do a literal search on a name
    results = Search(exactName: "John Simon");

    ShowResults("Search by exact name", results);

    // Do a search for an exact age
    results = Search(age: 22);

    ShowResults("Search by age", results);

    // Do a search for an age range
    results = Search(age: 30, maxAge: 40);

    ShowResults("Search by age range", results);

    // Or combine any params you want
    results = Search(name: "Dave", age: 30, maxAge: 999);

    ShowResults("Search by name and age", results);

    Console.ReadLine();
}

static void ShowResults(string name, List<RowData> results)
{
    Console.WriteLine($"\r\n{name}\r\n");

    foreach (var result in results)
    {
        Console.WriteLine($"{result.ID} {result.Name} ({result.Age}), {result.Employment}");
    }
}


Member 12224804

так вы сделали индексный файл? или Вы читаете весь файл в память?

F-ES Sitecore

Да, он помещает кучу файлов в указанный вами каталог (не уверен, что я упоминал, но "temp\index" - это каталог, а не файл), поэтому он постоянен, и когда вы запускаете запросы, он запускает их против индекса в этой папке, он не загружает все данные в память, эти индексы могут иметь много миллионов строк.

Member 12224804

Ах да, я видел, что он действительно использует индексацию один раз, так как данные не изменятся.это замечательно в моем случае.
кроме того, если я хочу использовать базу данных вместо того, какая из них лучше подходит для автономного и онлайн-сервера?

F-ES Sitecore

Для вашего использования вам, вероятно, понадобится что-то вроде SQLLite, поскольку он легче, повторно распространяется и т. д. Когда вы переходите к "правильным" базам данных, таким как SQL Server, вам нужно установить их и так далее, они просто сложнее начать работать.

Member 12224804

таким образом, sqlite может использовать их в качестве автономного и онлайн-сервера? поскольку мое программное обеспечение будет веб-и автономным настольным приложением, с той же базой данных.

F-ES Sitecore

Любая база данных может быть доступна "в автономном режиме", базы данных редко доступны через интернет. Если вы используете веб-сайт, он будет обращаться к базе данных локально, если вы используете настольное приложение, вам нужно будет убедиться, что машина, на которой он работает, также имеет доступ к этой базе данных каким-то образом, не связанным с интернетом, например, если она находится в той же сети.

Рейтинг:
1

Patrice T

Цитата:
У меня есть большие данные, хранящиеся в текстовом файле

Да, текстовые файлы или CSV-файлы хороши для эффективного хранения пространства, но не для быстрого извлечения данных.
Именно по этой причине существуют базы данных, они не очень хороши для эффективного хранения пространства, но хороши для быстрого извлечения данных.
Цитата:
Кроме того, а если использовать SQL лучше ?и что использовать ?

SQL или нет-это ваш выбор среди возможных решений.
Вам нужно изучить базы данных- это обширная тема , которой мы не можем научить вас в пространстве этого форума.
Цитата:
линейный поиск и 2D массив были слишком медленными

2D-массив в памяти возможен только в том случае, если он используется с такими методами баз данных, как индексы.
Цитата:
более 10000

Базы данных обычно обрабатывают миллионы записей без заметного замедления.


Member 12224804

ну ладно, значит, база данных-это лучшее решение ? однако что рекомендует ( SQL, MySQL...)для wpf C# и python для web-базирования?

Рейтинг:
0

OriginalGriff

Это зависит от того, что вы готовы принять. 10 000 строк-это не так уж много, поэтому загрузка их в память не должна быть слишком плохой.
Я могу прочитать 10 000 строк из CSV за 312 МС на i5-3330 @ 2.00 Ghz с 8 ГБ оперативной памяти и 1 000 000 за 18 994 МС (позже использовалось всего 345 Мб или память процесса)
Это использование CsvHelper[^] который может быть моим новым любимым читателем CSV!

List<Foo> records;
using (var reader = new StreamReader(@"D:\Test data\SR1M.csv"))
    {
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
        {
        records = csv.GetRecords<Foo>().ToList();
        }
    }

Для поиска я не вижу никаких проблем: 10K row DB может найти запись за 1 мс:
Stopwatch s2 = new Stopwatch();
s2.Start();
Foo found = records.FirstOrDefault(r => r.Region == "Asia" &&
                                        r.Country == "Mongolia" &&
                                        r.ItemType == "Snacks" &&
                                        r.SalesChannel == "Offline" &&
                                        r.OrderPriority == "M" &&
                                        r.OrderDate == new DateTime(2016, 4, 12));
s2.Stop();
Console.WriteLine(s2.ElapsedMilliseconds);

Для версии 1 000 000 строк потребовалось 75 мс, чтобы найти ту же строку.
В обоих случаях не имело значения, где в файле находится строка.

Испытания проводились с использованием 10К и 1М выборки строк CSV-файла отсюда: Загрузки 18 - примеры CSV-файлов / наборов данных для тестирования (до 1,5 млн записей) - Sales | E for Excel | Пробуждение Microsoft Excel Student Inside You[^]

Я подозреваю, что любое значительно чрезмерное использование времени или памяти связано с тем, как Вы читаете и обрабатываете данные, а не с внутренней особенностью формата данных на основе файлов!

Имейте в виду, что переход на что - то вроде SQL Server потребует много ресурсов - диск, память и процессор, - поэтому, если ваш существующий код работает на плохо специфицированном оборудовании, это не улучшит проблему!


PIEBALDconsult

Для чтения CSV я всегда использовал свою функцию Rive, но недавно я узнал, что .net имеет встроенный читатель/писатель CSV.

George Swan

Вы пробовали пакет Nu Get SoftCircuits?CsvParser? Он имеет очень похожий способ работы с CsvHelper, но выделяет меньше памяти и примерно на 25% быстрее читает.