Пытаюсь загрузить данные в MongoDB

Я пытаюсь загрузить данные в свою базу данных MongoDB Atlas. Данные представляют собой файлы .csv из набора данных Kaggle Movies https://www.kaggle.com/datasets/rounakbanik/the-movies-dataset. Я пробовал разные методы, но ничего не работает так, как должно.

Способ 1 Сначала я попытался загрузить данные с помощью текстового редактора Python следующим образом:

from pymongo.mongo_client import MongoClient
from pymongo.server_api import ServerApi
import pandas as pd

uri = "..."
client = MongoClient(uri, server_api=ServerApi('1'))
db = client['db']

def load_csv_to_mongodb_batch(collection_name, file_path, batch_size=1000):
    df = pd.read_csv(file_path)
    data = df.to_dict(orient='records')
    collection = db[collection_name]
    
    # Insert data in batches
    for i in range(0, len(data), batch_size):
        batch = data[i:i + batch_size]
        collection.insert_many(batch)
        print(f"Inserted batch {i//batch_size + 1} into {collection_name} collection.")

datasets = {
    "Credits": "/path_to/credits.csv",
    "Keywords": "/path_to/keywords.csv",
    "Films": "/path_to/movies_metadata.csv",
}

for collection, file_path in datasets.items():
    load_csv_to_mongodb_batch(collection, file_path)

При этом мои данные загружаются в правильные коллекции, однако некоторые из моих данных в файлах .csv имеют формат [{'id': 1, 'name': 'Action'}, {'id':2, 'name ':'Adenture}], и эти записи данных загружаются в виде строк, а не вложенных документов (см. поле жанров на скриншоте), что делает запросы трудными/невозможными

Способ 2 Я также пытался использовать mongoimport следующим образом: mongoimport --uri "..." --collection Films --drop --file /path_to/movies_metadata.csv. Однако здесь я получаю следующее сообщение:

connected to: mongodb+srv://...
dropping: test.Films
Failed: error processing document #1: invalid character 'a' looking for beginning of value
0 document(s) imported successfully. 0 document(s) failed to import.

Итак, я устанавливаю соединение, удаляю коллекцию, но затем не удается импортировать какие-либо мои данные.

Обновлено: Основываясь на комментариях, я добавил ключевые слова: --type=csv --headerlineв приглашение, которое позволяет мне загружать данные, но в этом методе данные по-прежнему загружаются в виде строк, а не вложенных документов.

Способ 3 Наконец, я даже попытался преобразовать свои файлы CSV в файлы JSON, заключив все ключи и значения в двойные кавычки. Здесь я преобразовал данные следующим образом, а затем сохранил их в виде файлов json:

def preprocess_columns(field):
    if isinstance(field, str) and field.startswith('[') and field.endswith(']'): # deals with JSON lists
        field = field.replace("'", '"') # Replace single quotes with double quotes
        field = re.sub(r'(\b\w+\b):', r'"\1":', field) # Add double quotes around keys if missing
        return field
    elif isinstance(field, str) and field.startswith('{') and field.endswith('}'): # deals with JSON dictionaries
        field = field.replace("'", '"') 
        field = re.sub(r'(\b\w+\b):', r'"\1":', field) 
        return field
    elif isinstance(field, (int, float, bool)): # if single boolean or number --> does not need ".."
        return field
    elif not isinstance(field, (int, float, bool)): # if single string value --> need ".."
        field = field.replace('"', "'") # in case there are double quotations are part of text
        return f'"{field}"' 
    else: 
        return field

Однако в этом случае при использовании mongoimport я получаю сообщение: «Ошибка: невозможно декодировать массив в примитив.D».

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

Я уже давно застрял на этом и пробовал разные способы, поэтому буду очень признателен за любую помощь!

Обновлено: Вот первые два фильма файла Movies_metadata.csv.

adult,belongs_to_collection,budget,genres,homepage,id,imdb_id,original_language,original_title,overview,popularity,poster_path,production_companies,production_countries,release_date,revenue,runtime,spoken_languages,status,tagline,title,video,vote_average,vote_count
FALSE,"{'id': 10194, 'name': 'Toy Story Collection', 'poster_path': '/7G9915LfUQ2lVfwMEEhDsn3kT4B.jpg', 'backdrop_path': '/9FBwqcd9IRruEDUrTdcaafOMKUq.jpg'}",30000000,"[{'id': 16, 'name': 'Animation'}, {'id': 35, 'name': 'Comedy'}, {'id': 10751, 'name': 'Family'}]",http://toystory.disney.com/toy-story,862,tt0114709,en,Toy Story,"Led by Woody, Andy's toys live happily in his room until Andy's birthday brings Buzz Lightyear onto the scene. Afraid of losing his place in Andy's heart, Woody plots against Buzz. But when circumstances separate Buzz and Woody from their owner, the duo eventually learns to put aside their differences.",21.946943,/rhIRbceoE9lR4veEXuwCC2wARtG.jpg,"[{'name': 'Pixar Animation Studios', 'id': 3}]","[{'iso_3166_1': 'US', 'name': 'United States of America'}]",30/10/1995,373554033,81,"[{'iso_639_1': 'en', 'name': 'English'}]",Released,,Toy Story,FALSE,7.7,5415
FALSE,,65000000,"[{'id': 12, 'name': 'Adventure'}, {'id': 14, 'name': 'Fantasy'}, {'id': 10751, 'name': 'Family'}]",,8844,tt0113497,en,Jumanji,"When siblings Judy and Peter discover an enchanted board game that opens the door to a magical world, they unwittingly invite Alan -- an adult who's been trapped inside the game for 26 years -- into their living room. Alan's only hope for freedom is to finish the game, which proves risky as all three find themselves running from giant rhinoceroses, evil monkeys and other terrifying creatures.",17.015539,/vzmL6fP7aPKNKPRTFnZmiUfciyV.jpg,"[{'name': 'TriStar Pictures', 'id': 559}, {'name': 'Teitler Film', 'id': 2550}, {'name': 'Interscope Communications', 'id': 10201}]","[{'iso_3166_1': 'US', 'name': 'United States of America'}]",15/12/1995,262797249,104,"[{'iso_639_1': 'en', 'name': 'English'}, {'iso_639_1': 'fr', 'name': 'Français'}]",Released,Roll the dice and unleash the excitement!,Jumanji,FALSE,6.9,2413

Вот первая запись файла Credits.csv (чтобы уместиться в текстовый лимит, мне пришлось сократить списки)

cast,crew,id
"[{'cast_id': 14, 'character': 'Woody (voice)', 'credit_id': '52fe4284c3a36847f8024f95', 'gender': 2, 'id': 31, 'name': 'Tom Hanks', 'order': 0, 'profile_path': '/pQFoyx7rp09CJTAb932F2g8Nlho.jpg'}, {'cast_id': 15, 'character': 'Buzz Lightyear (voice)', 'credit_id': '52fe4284c3a36847f8024f99', 'gender': 2, 'id': 12898, 'name': 'Tim Allen', 'order': 1, 'profile_path': '/uX2xVf6pMmPepxnvFWyBtjexzgY.jpg'}]", "[{'credit_id': '52fe4284c3a36847f8024f49', 'department': 'Directing', 'gender': 2, 'id': 7879, 'job': 'Director', 'name': 'John Lasseter', 'profile_path': '/7EdqiNbr4FRjIhKHyPPdFfEEEFG.jpg'}, {'credit_id': '52fe4284c3a36847f8024f4f', 'department': 'Writing', 'gender': 2, 'id': 12891, 'job': 'Screenplay', 'name': 'Joss Whedon', 'profile_path': '/dTiVsuaTVTeGmvkhcyJvKp2A5kr.jpg'}, {'credit_id': '52fe4284c3a36847f8024f55', 'department': 'Writing', 'gender': 2, 'id': 7, 'job': 'Screenplay', 'name': 'Andrew Stanton', 'profile_path': '/pvQWsu0qc8JFQhMVJkTHuexUAa1.jpg'}, {'credit_id': '52fe4284c3a36847f8024f5b', 'department': 'Writing', 'gender': 2, 'id': 12892, 'job': 'Screenplay', 'name': 'Joel Cohen', 'profile_path': '/dAubAiZcvKFbboWlj7oXOkZnTSu.jpg'}]", 862

Лично я бы придерживался метода 1, хотя я не знаком с Pandas, поскольку этот метод позволяет обрабатывать CSV-файлы построчно. Попробуйте просмотреть данные и проверить, является ли значение допустимым JSON, и преобразовать его. Способ 2. Попробуйте добавить --type=csv и посмотрите, сработает ли это.

Interwebber 06.06.2024 17:01

Не могли бы вы добавить к своему вопросу несколько строк из CSV?

rickhg12hs 06.06.2024 17:14

... в необработанном CSV-файле, вставленном в блок кода?

rickhg12hs 06.06.2024 17:49

Извините, сейчас его следует добавить как необработанный CSV!

Q.Ask 06.06.2024 18:00
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
4
104
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете использовать mongoimport для импорта с помощью этой строки:

mongoimport --uri "mongodb+srv://..." --collection Films --type=csv --headerline  --drop --file movies_metadata.csv

Вам просто не хватает --type=csv --headerline, который сообщает mongoimport использовать CSV и что первая строка содержит названия полей.

Спасибо за ваш ответ, это очень полезно! Теперь я могу загрузить данные, но у меня все еще есть проблема с загрузкой данных в виде строк, а не вложенных документов.

Q.Ask 06.06.2024 18:05

Попробуйте использовать конвейер агрегации, чтобы изменить данные, затем используйте $out, чтобы вывести результат в новую коллекцию и удалить исходную.

Diego Freniche 06.06.2024 19:01

На что мне следует изменить данные? Знаете ли вы, какой формат позволит загружать данные в виде вложенных документов?

Q.Ask 06.06.2024 19:23

Импортируйте данные как есть из файлов CSV. Это создаст коллекцию фильмов. Затем, используя конвейер агрегации и $addFields/$project, вы можете удалять и добавлять поля и получать нужный вам формат. Как только вы запустите конвейер агрегирования и получите документы в нужном формате, перейдите в новую коллекцию «FilmsOK». Затем удалите старый, переименуйте этот новый.

Diego Freniche 10.06.2024 09:41
Ответ принят как подходящий

movies_metadata.csv имеет неудобный формат, по-видимому, смесь типичного CSV и строк JSON.

Это довольно хрупкий способ, но вот способ с помощью csvjson (из csvkit ) и jq преобразовать ваш пример CSV в JSON, который mongoimport должен принять.

Это для командной строки bash (в другой оболочке, вероятно, будет альтернативный синтаксис расширения/интерполяции параметров):

csvjson --stream -I ./movies_metadata.csv |jq -c $'with_entries(if IN(.key;"belongs_to_collection", "genres", "production_companies", "production_countries", "spoken_languages") then .value = ((.value|gsub("\'";"\\"")? // .)|fromjson? // .) elif .key= = "release_date" then .value = {"$date": (.value|strptime("%d/%m/%Y")|todate)} else .value=(.value|(.|tonumber? // .= = "TRUE" // if .= = "FALSE" then false else . end))  end )' > ./movies_metadata.json

Предполагая, что формат приемлем для mongoimport, вы можете пропустить запись файла JSON и просто передать вывод вышеуказанного канала в mongoimport.

Вот что получается:

csvjson --stream -I ./movies_metadata.csv |jq $'with_entries(if IN(.key;"belongs_to_collection", "genres", "production_companies", "production_countries", "spoken_languages") then .value = ((.value|gsub("\'";"\\"")? // .)|fromjson? // .) elif .key= = "release_date" then .value = {"$date": (.value|strptime("%d/%m/%Y")|todate)} else .value=(.value|(.|tonumber? // .= = "TRUE" // if .= = "FALSE" then false else . end))  end )'

... выглядит так (обратите внимание на использование "$date" для правильного типа даты MongoDB):

{
  "adult": false,
  "belongs_to_collection": {
    "id": 10194,
    "name": "Toy Story Collection",
    "poster_path": "/7G9915LfUQ2lVfwMEEhDsn3kT4B.jpg",
    "backdrop_path": "/9FBwqcd9IRruEDUrTdcaafOMKUq.jpg"
  },
  "budget": 30000000,
  "genres": [
    {
      "id": 16,
      "name": "Animation"
    },
    {
      "id": 35,
      "name": "Comedy"
    },
    {
      "id": 10751,
      "name": "Family"
    }
  ],
  "homepage": "http://toystory.disney.com/toy-story",
  "id": 862,
  "imdb_id": "tt0114709",
  "original_language": "en",
  "original_title": "Toy Story",
  "overview": "Led by Woody, Andy's toys live happily in his room until Andy's birthday brings Buzz Lightyear onto the scene. Afraid of losing his place in Andy's heart, Woody plots against Buzz. But when circumstances separate Buzz and Woody from their owner, the duo eventually learns to put aside their differences.",
  "popularity": 21.946943,
  "poster_path": "/rhIRbceoE9lR4veEXuwCC2wARtG.jpg",
  "production_companies": [
    {
      "name": "Pixar Animation Studios",
      "id": 3
    }
  ],
  "production_countries": [
    {
      "iso_3166_1": "US",
      "name": "United States of America"
    }
  ],
  "release_date": {
    "$date": "1995-10-30T00:00:00Z"
  },
  "revenue": 373554033,
  "runtime": 81,
  "spoken_languages": [
    {
      "iso_639_1": "en",
      "name": "English"
    }
  ],
  "status": "Released",
  "tagline": null,
  "title": "Toy Story",
  "video": false,
  "vote_average": 7.7,
  "vote_count": 5415
}
{
  "adult": false,
  "belongs_to_collection": null,
  "budget": 65000000,
  "genres": [
    {
      "id": 12,
      "name": "Adventure"
    },
    {
      "id": 14,
      "name": "Fantasy"
    },
    {
      "id": 10751,
      "name": "Family"
    }
  ],
  "homepage": null,
  "id": 8844,
  "imdb_id": "tt0113497",
  "original_language": "en",
  "original_title": "Jumanji",
  "overview": "When siblings Judy and Peter discover an enchanted board game that opens the door to a magical world, they unwittingly invite Alan -- an adult who's been trapped inside the game for 26 years -- into their living room. Alan's only hope for freedom is to finish the game, which proves risky as all three find themselves running from giant rhinoceroses, evil monkeys and other terrifying creatures.",
  "popularity": 17.015539,
  "poster_path": "/vzmL6fP7aPKNKPRTFnZmiUfciyV.jpg",
  "production_companies": [
    {
      "name": "TriStar Pictures",
      "id": 559
    },
    {
      "name": "Teitler Film",
      "id": 2550
    },
    {
      "name": "Interscope Communications",
      "id": 10201
    }
  ],
  "production_countries": [
    {
      "iso_3166_1": "US",
      "name": "United States of America"
    }
  ],
  "release_date": {
    "$date": "1995-12-15T00:00:00Z"
  },
  "revenue": 262797249,
  "runtime": 104,
  "spoken_languages": [
    {
      "iso_639_1": "en",
      "name": "English"
    },
    {
      "iso_639_1": "fr",
      "name": "Français"
    }
  ],
  "status": "Released",
  "tagline": "Roll the dice and unleash the excitement!",
  "title": "Jumanji",
  "video": false,
  "vote_average": 6.9,
  "vote_count": 2413
}

Спасибо! Это отлично работает! Сейчас я пытаюсь сделать то же самое с другим файлом CSV из набора данных Kaggle: Credits.csv (я вставил код CSV из этого файла в свой исходный пост), но этот подход, похоже, здесь не работает? Я изменил запрос на рассмотрение столбцов актерского состава и съемочной группы, но получаю ошибку: ValueError: в строке 0 содержится 847 значений, а в таблице — только 3 столбца. Может быть, вы поможете мне решить и эту проблему? Спасибо!

Q.Ask 15.06.2024 16:52

@Q.Ask Это может сработать для вас (из оболочки bash): csvjson -S -y0 -I --stream credits.csv |jq 'with_entries(.value = (.value|gsub("'"'"'";"\"")|fromjson))'.

rickhg12hs 19.06.2024 02:59

Другие вопросы по теме