2025, Dec 15 12:02

Как импортировать PDF с метаданными в Google Cloud Discovery Engine без ошибки CONTENT REQUIRED

Разбираем ошибку CONTENT REQUIRED при импорте JSONL в Google Cloud Discovery Engine и даем решение: data_schema=documents, ссылки на PDF в GCS, без id_field.

Загрузка неструктурированных документов вместе с метаданными в хранилище Google Cloud Discovery Engine звучит просто, пока конвейер импорта не начинает отвечать загадочными ошибками. Если вы направляете манифест JSONL на PDF-файлы в Cloud Storage и сталкиваетесь со сбоями, связанными с содержимым, почти наверняка причина — несоответствие между вашей схемой данных и конфигурацией контента в хранилище.

Обзор проблемы

Сценарий простой: создаёте хранилище данных, кладёте в бакет GCS PDF-файлы и манифест JSONL, затем запускаете импорт через Python SDK. В JSONL для каждого документа указаны метаданные и content.uri со ссылкой на PDF. Хранилище настроено как CONTENT REQUIRED. Во время импорта операция падает с сообщениями вида «To create document without content, content config of data store must be NO_CONTENT», повторяющимися для каждой строки JSONL.

Проблемный пример кода

Следующий фрагмент Python вызывает ошибку, если JSONL содержит неструктурированные документы с метаданными, а хранилище настроено как CONTENT REQUIRED:

from google.api_core.client_options import ClientOptions
from google.cloud import discoveryengine

REGION = "your-region"
PROJECT = "your-project-id"
STORE_ID = "your-store-id"
META_URI = "gs://your-bucket/metadata.jsonl"

client_opts = (
    ClientOptions(api_endpoint=f"{REGION}-discoveryengine.googleapis.com")
    if REGION != "global"
    else None
)

svc = discoveryengine.DocumentServiceClient(client_options=client_opts)

parent_branch = svc.branch_path(
    project=PROJECT,
    location=REGION,
    data_store=STORE_ID,
    branch="default_branch",
)

req = discoveryengine.ImportDocumentsRequest(
    parent=parent_branch,
    gcs_source=discoveryengine.GcsSource(
        input_uris=[META_URI],
        data_schema="custom",
    ),
    id_field="id",
    reconciliation_mode=discoveryengine.ImportDocumentsRequest.ReconciliationMode.FULL,
)

op = svc.import_documents(request=req)
print(f"Waiting for operation to complete: {op.operation.name}")
res = op.result()
print(res)

Манифест JSONL выглядит так: в каждой строке описаны метаданные и content.uri, указывающий на PDF в GCS:

{"id": "1", "structData": {"title": "Coldsmokesubmittal", "category": "212027"}, "content": {"mimeType": "application/pdf", "uri": "gs://meta-data-testing/ColdSmokeSubmittal.pdf"}}
{"id": "2", "structData": {"title": "Defssubmittal", "category": "212027"}, "content": {"mimeType": "application/pdf", "uri": "gs://meta-data-testing/DEFSSubmittal.pdf"}}
{"id": "3", "structData": {"title": "Cmu Submittal", "category": "222039"}, "content": {"mimeType": "application/pdf", "uri": "gs://meta-data-testing/CMU_Submittal.pdf"}}
{"id": "4", "structData": {"title": "Concrete Mix Submittal", "category": "222039"}, "content": {"mimeType": "application/pdf", "uri": "gs://meta-data-testing/Concrete_Mix_Submittal.pdf"}}

Что происходит на самом деле

Сообщение об ошибке корректно. Использование data_schema="custom" означает, что вы импортируете только метаданные. В этом режиме Discovery Engine ожидает, что хранилище принимает документы без содержимого, то есть конфигурацию NO_CONTENT. Если же хранилище требует контент (CONTENT REQUIRED), система отклоняет такие записи как неполные. Поэтому переключение хранилища на NO_CONTENT позволяет пройти импорт, но оставляет документы непоисковыми — потому что контент не загружается и, соответственно, не индексируется.

Исправление

Переключите импорт на data_schema="documents", чтобы конвейер загрузил и метаданные, и связанное неструктурированное содержимое. Параметр id_field также нужно опустить. Импорт по‑прежнему указывает на манифест JSONL, где содержатся ссылки content.uri на PDF в Cloud Storage. Схема "documents" правильно интерпретирует ваш JSONL и импортирует PDF вместе с метаданными. Чёткие определения параметра data_schema приведены в официальной документации.

Исправленный пример кода

Ниже приведён скорректированный фрагмент Python, который успешно импортирует PDF вместе с метаданными в хранилище с конфигурацией CONTENT REQUIRED:

from google.api_core.client_options import ClientOptions
from google.cloud import discoveryengine

REGION = "your-region"
PROJECT = "your-project-id"
STORE_ID = "your-store-id"
META_URI = "gs://your-bucket/metadata.jsonl"

client_opts = (
    ClientOptions(api_endpoint=f"{REGION}-discoveryengine.googleapis.com")
    if REGION != "global"
    else None
)

svc = discoveryengine.DocumentServiceClient(client_options=client_opts)

parent_branch = svc.branch_path(
    project=PROJECT,
    location=REGION,
    data_store=STORE_ID,
    branch="default_branch",
)

req = discoveryengine.importDocumentsRequest(
    parent=parent_branch,
    gcs_source=discoveryengine.GcsSource(
        input_uris=[META_URI],
        data_schema="documents",
    ),
    reconciliation_mode=discoveryengine.ImportDocumentsRequest.ReconciliationMode.FULL,
)

op = svc.import_documents(request=req)
print(f"Waiting for operation to complete: {op.operation.name}")
res = op.result()
print(res)

Почему это важно

От правильного выбора схемы данных зависит, загрузите ли вы только метаданные или действительно внесёте неструктурированный контент для поиска. Схема "documents" обрабатывает content.uri, подтягивает PDF‑файлы и индексирует их текст, сохраняя ваши кастомные поля в structData. Это позволяет обогащать выдачу полями вроде project_number и фильтровать результаты поиска через API на основе этих метаданных.

Результат

После перехода на data_schema="documents" и удаления id_field импорт завершается успешно. Пользовательское поле project_number из манифеста корректно разбирается, и по нему можно фильтровать результаты поиска через API.

Вывод

Если в вашем манифесте JSONL указаны content.uri для неструктурированных файлов, а хранилище ожидает реальный контент, не импортируйте с data_schema="custom". Используйте data_schema="documents" и оставьте конфигурацию хранилища CONTENT REQUIRED. Этот подход импортирует и содержимое файлов, и ваши метаданные, делая документы доступными для поиска и сохраняя поля, необходимые для фильтрации и управления релевантностью.