Я пытаюсь загрузить данные в свою базу данных 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
Не могли бы вы добавить к своему вопросу несколько строк из CSV?
... в необработанном CSV-файле, вставленном в блок кода?
Извините, сейчас его следует добавить как необработанный CSV!






Вы можете использовать mongoimport для импорта с помощью этой строки:
mongoimport --uri "mongodb+srv://..." --collection Films --type=csv --headerline --drop --file movies_metadata.csv
Вам просто не хватает --type=csv --headerline, который сообщает mongoimport использовать CSV и что первая строка содержит названия полей.
Спасибо за ваш ответ, это очень полезно! Теперь я могу загрузить данные, но у меня все еще есть проблема с загрузкой данных в виде строк, а не вложенных документов.
Попробуйте использовать конвейер агрегации, чтобы изменить данные, затем используйте $out, чтобы вывести результат в новую коллекцию и удалить исходную.
На что мне следует изменить данные? Знаете ли вы, какой формат позволит загружать данные в виде вложенных документов?
Импортируйте данные как есть из файлов CSV. Это создаст коллекцию фильмов. Затем, используя конвейер агрегации и $addFields/$project, вы можете удалять и добавлять поля и получать нужный вам формат. Как только вы запустите конвейер агрегирования и получите документы в нужном формате, перейдите в новую коллекцию «FilmsOK». Затем удалите старый, переименуйте этот новый.
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 Это может сработать для вас (из оболочки bash): csvjson -S -y0 -I --stream credits.csv |jq 'with_entries(.value = (.value|gsub("'"'"'";"\"")|fromjson))'.
Лично я бы придерживался метода 1, хотя я не знаком с Pandas, поскольку этот метод позволяет обрабатывать CSV-файлы построчно. Попробуйте просмотреть данные и проверить, является ли значение допустимым JSON, и преобразовать его. Способ 2. Попробуйте добавить
--type=csvи посмотрите, сработает ли это.