{"id":28042347,"url":"https://github.com/researchgate/crossref-snapshot-mount","last_synced_at":"2025-05-11T14:26:24.126Z","repository":{"id":288111500,"uuid":"966851656","full_name":"researchgate/crossref-snapshot-mount","owner":"researchgate","description":"This repository contains the code useed to mount the April 2025 crossref snapshot to Bigquery","archived":false,"fork":false,"pushed_at":"2025-04-23T07:30:44.000Z","size":30,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-23T08:29:28.781Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/researchgate.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-15T14:49:14.000Z","updated_at":"2025-04-23T07:30:49.000Z","dependencies_parsed_at":"2025-04-16T09:17:39.988Z","dependency_job_id":null,"html_url":"https://github.com/researchgate/crossref-snapshot-mount","commit_stats":null,"previous_names":["researchgate/crossref-snapshot-mount"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/researchgate%2Fcrossref-snapshot-mount","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/researchgate%2Fcrossref-snapshot-mount/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/researchgate%2Fcrossref-snapshot-mount/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/researchgate%2Fcrossref-snapshot-mount/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/researchgate","download_url":"https://codeload.github.com/researchgate/crossref-snapshot-mount/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253578836,"owners_count":21930611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-11T14:26:23.596Z","updated_at":"2025-05-11T14:26:24.112Z","avatar_url":"https://github.com/researchgate.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# crossref-snapshot-mount\nThis repository contains the code useed to mount the April 2025 crossref snapshot to Bigquery\n\n# Create a VM (with name crossref):\n\n## why?\nWe will be working primarily with VM to download the latest snapshot and upload the cleaned up data to BigQuery.\n\n## Configuration and setup\n* consider enough space while creating VM ~700 GB (can be reduced later)\n* while creating the VM, under `Identity and API access` -\u003e Allow full access to all Cloud APIs. This will be required to write\n  to bucket.\n* Once your VM is setup, find out your service account\n  ```\n  gcloud compute instances describe crossref --zone=\"northamerica-northeast2-b\" --format='value(serviceAccounts.email)'\n  ```\n* grant permission to your service account to be able to manage objects in bucket via VM\n  ```\n      gcloud storage buckets add-iam-policy-binding gs://crossref \\\n      --member=serviceAccount:830983394603-compute@developer.gserviceaccount.com \\\n      --role=roles/storage.objectAdmin\n* **restart** the VM for IAM policy to take effect\n\n## Download the crossref yearly dump and set up the VM to process it\n\n* Once your VM  is setup. Start the VM and install awscli\n\n  ```sudo apt install awscli```\n\n* Configure and download the snapshot\n    ```\n       aws configure\n       AWS Access Key ID [None]:  $give_the_key_id\n       AWS Secret Access Key [None]: $give_the_secret_access_key\n       Default region name [None]: \n       Default output format [None]: \n  ```\n* List the files by crossref on aws:\n  ```\n       aws s3 ls --request-payer requester s3://api-snapshots-reqpays-crossref\n\n       should show something like:  \n       2023-05-09 15:14:12 185897154560 April_2023_Public_Data_File_from_Crossref.tar\n       2024-05-09 19:56:17 212156057600 April_2024_Public_Data_File_from_Crossref.tar\n  ```\n\n* Download the file to current location in your VM:\n\n  ```\n  aws s3api get-object --bucket api-snapshots-reqpays-crossref --request-payer requester --key  April_2024_Public_Data_File_from_Crossref.tar ./April_2024_Public_Data_File_from_Crossref.tar\n  ```\n\n* upload the file to the crossref bucket to prevent any mishaps in case of VM deletion and data loss\n\n  ```\n  gsutil cp April_2024_Public_Data_File_from_Crossref.tar  gs://crossref\n  ```\n\n* unzip the tar file in VM because we will cleanup the data with a python script that accesses extracted files in VM:\n\n  ```\n  tar -xvf April_2024_Public_Data_File_from_Crossref.tar\n  ```\n\n* Python set up in VM\n  ```\n      sudo apt install python3-pip\n      sudo apt install python3-venv\n      python3 -m venv myenv\n      source myenv/bin/activate\n  ```\n\n* BQ package install:\n\n  ```\n   pip3 install google-cloud-storage google-cloud-bigquery\n  ```\n\n## Processing the yearly dump\n\nWe divide this process into two-steps\n* cleaning the data\n* loading the data to BQ\n\n### Why?\nThe VM tends to lose session while the data is being cleaned/uploaded to buckets for loading to BQ. Therefore, it is much better to clean\nthe data first and then attempt the loading to BQ , because even the loading step may have several failures and with 30k+ files it is easy\nto lose track of successful and failed file cleanups/uploads.\n\n### Cleanup\n\n* The minimum amount of cleanup that is a must is flattening the field \"date-parts\". In the crossref snapshots, date-parts are double arrays [[\"YYYY-MM-DDTHH:MM:S]] fields which BQ does not allow.\n  We run a python script to flatten the date-parts field.\n\n#### Running the cleanup script\n\n* Setup gcloud CLI local machine.\n  https://cloud.google.com/sdk/docs/install\n\n* Make sure your VM is started. Now upload the script to your VM\n\n``` \n  gcloud compute scp $path_to_cleanup_script \\\n   rashmi_varma@crossref:$path_to_where_you_want_your_script_in_VM \\\n   --zone=\"$zone-of-your-VM\" \\\n   --tunnel-through-iap\n```\n   \n   \n* Make sure your venv environment is activated(source myenv/bin/activate) in the VM. Run the script in your VM\n  ```\n  python3 crossref_cleanup.py\n  ``` \n  This can take very long time depending on the number of files and the size of the files.\n  Full disclosure, this script was written with the help of Gemini. Feel free to improve it and write your own :).\n\n* The script will create a folder called \"processed_for_bq\"  as specified by the variable : gcs_processed_folder \n  in the  bucket named crossref and shall upload all the cleaned up files potentially ready to be uploaded to a BQ table.\n\n* Try your luck:  If you are feeling lucky , and the crossref data is super clean , you can directly upload all the .jsonl.gz files to a BQ table with the command\n  ``` \n    bq load \\\n    --autodetect \\\n    --source_format=NEWLINE_DELIMITED_JSON \\\n    --max_bad_records=1000 \\   # optional  I usually omit this argument to catch all the errors and get all the data\n    dataset_id.table_name \\\n    'gs://crossref/extracted/*.jsonl.gz'`\n  ```\nUnfortunately, more often than not , the data is not clean and you will have to do further clean ups.\n\n### Upload to BQ (patience required!!!)\n\n* Now that first round of cleanup is done and the luck did not work out , you are here!!\n* So, we can start uploading the cleaned up files step-wise to BQ , eliminating the problematic ones and handling them at the end.\n\n* Upload the schema file to the VM (preferably in same location as script)\n``` \n  gcloud compute scp $path_to_schema_file \\\n   rashmi_varma@crossref:$path_to_where_you_want_your_schema_in_VM \\\n   --zone=\"$zone-of-your-VM\" \\\n   --tunnel-through-iap\n```\n* Upload the BQ load script\n  ``` \n  gcloud compute scp /Users/rvarma/Documents/repositories/crossref-snapshot-mount/scripts/loadtobq.py \n  rashmi_varma@crossref:/home/rashmi_varma  \\\n  --zone=\"northamerica-northeast2-b\" \\\n  --tunnel-through-iap\n  ```\n\n* **BQ caveats and limitations**\n    BigQuery has rate limits on table update operations: \n    Max: 1,500 load jobs per table per day ✅ \n\n    BUT ALSO:\n    ⛔️ Max: 100 table update operations per 10 seconds per table\n\n   We try to circumvent those by diving the total number of files into batches , so that the \n   number of load jobs on the table we are creating is \u003c1500\n   We try to deal with the time limitation between table update operations by adding a DELAY. \n   I used a delay of 10 seconds and there were still several errors:\n\n#### Running the upload script\n\n  ```\n  python3 loadtobq.py\n  ``` \n\n  * Common Errors\n\n   ```\n   Rate Limiting: \n   FAILED: gs://crossref/processed_for_bq/10012.jsonl.gz - 429 Exceeded rate limits: too many table update operations for this table. For more information, see https://cloud.google.com/bigquery/docs/troubleshoot-quotas; reason: rateLimitExceeded, location: table.write, message: Exceeded rate limits: too many table update ope\n  \n   data error:\n   \n  FAILED: gs://crossref/processed_for_bq/14822.jsonl.gz - 400 Cannot return an invalid timestamp value of 569552083200000000 microseconds relative to the Unix epoch. The range of valid timestamp values is [0001-01-01 00:00:00, 9999-12-31 23:59:59.999999]; error in writing field updated-by.updated.date-time; error in writing field updated-by.updated; error in writing field updated-by; reason: invalidQuery, location: query, message: Cannot return an invalid timestamp value of 569552083200000000 microseconds relative to the Unix epoch. The range of valid timestamp values is [0001-01-01 00:00:00, 9999-12-31 23:59:59.999999]; error in writing field updated-by.updated.date-time; error in writing field updated-by.updated; error in writing field updated-by\n  ``` \n  \n* For the rate-limiting error, upload the **loadtobq_retry.py** script.\n  This will attempt to reload the files listed in failed_uploads.txt. \n  For speed you can increase the size of the batch by increasing $FILES_PER_BATCH\n  \n  DISCLAIMER: you will have to re-run the **loadtobq_retry.py** couple of times until all that remains of the errors is bad data i.e. the 400s.\n\n### Dealing with 400  (manual)\n\n* Download the .jsonl.gz in question to your local machine from the \"processed_for_bq\" bucket in GCP\n* Go through each file and error in the file referenced by $RETRY_ERROR_LOG in loadtobq_retry.py script.\n  and fix the error manually. Sometimes there are only 1 or two records that have errors.\n* re-upload the .jsonl to the bucket in a different folder(like manually_cleanedup) in crossref bucket for easier tracking.\n* Run this command on GCP terminal. Upload the schema.json file to the terminal.\n  ```\n    bq load --source_format=NEWLINE_DELIMITED_JSON --schema=schema.json  dataset_id.table_id  gs://crossref/manually_cleanedup/14822.jsonl\n  ```\n  if it shows more errors , cleanup.\n* do the process for all the files in question. \n  If the files in question are too many with a pattern in the errors, you can use `jq` tool to fix them via commandline.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresearchgate%2Fcrossref-snapshot-mount","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fresearchgate%2Fcrossref-snapshot-mount","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresearchgate%2Fcrossref-snapshot-mount/lists"}