이러한 키(및 기타 여러 키)가 포함된 수백만 개의 항목이 포함된 배열을 포함하는 매우 큰 JSON 파일이 있다고 가정해 보겠습니다.
{
"name": "assets/fUCcxWczWT0",
"displayName": "The House",
"authorName": "John Smith",
"resources": {"house" : "address","car":"bla"},
}
jq
특정 이름 키 앞에 있는 콘텐츠를 슬라이싱(제거)하는 방법은 무엇입니까 ? 예를 들어 고유하게 식별되는 요소 이전의 모든 것입니다 "name": "assets/fUCcxWczWT0"
.
위의 내용은 문서의 대표 요소를 단순화한 것입니다. 최상위 배열의 실제 요소는 다음과 같습니다.
{
"name": "assets/4vds6twPsb7",
"displayName": "pim",
"authorName": "Erwin Braak",
"createTime": "2017-12-06T11:52:40.557236Z",
"updateTime": "2020-10-07T09:49:08.752848Z",
"formats": [
{
"root": {
"relativePath": "Pim.gltf",
"url": "https://poly.googleapis.com/downloads/fp/1602064148752848/4vds6twPsb7/c8Ksvo0_VjG/Pim.gltf",
"contentType": "model/gltf+json"
},
"resources": [
{
"relativePath": "Pim.bin",
"url": "https://poly.googleapis.com/downloads/fp/1602064148752848/4vds6twPsb7/c8Ksvo0_VjG/Pim.bin",
"contentType": "application/octet-stream"
},
{
"relativePath": "C:/Users/PC/Desktop/pom/pimSurface_Color.png",
"url": "https://poly.googleapis.com/downloads/fp/1602064148752848/4vds6twPsb7/c8Ksvo0_VjG/C:/Users/PC/Desktop/pom/pimSurface_Color.png",
"contentType": "image/png"
}
],
"formatComplexity": {
"triangleCount": "586149"
},
"formatType": "GLTF2"
},
{
"root": {
"relativePath": "Pim.obj",
"url": "https://poly.googleapis.com/downloads/fp/1602064148752848/4vds6twPsb7/5QVGpvfauVK/Pim.obj",
"contentType": "text/plain"
},
"resources": [
{
"relativePath": "Pim.mtl",
"url": "https://poly.googleapis.com/downloads/fp/1602064148752848/4vds6twPsb7/5QVGpvfauVK/Pim.mtl",
"contentType": "text/plain"
},
{
"relativePath": "C:/Users/PC/Desktop/pom/pimSurface_Color.png",
"url": "https://poly.googleapis.com/downloads/fp/1602064148752848/4vds6twPsb7/5QVGpvfauVK/C:/Users/PC/Desktop/pom/pimSurface_Color.png",
"contentType": "image/png"
}
],
"formatComplexity": {
"triangleCount": "586149"
},
"formatType": "OBJ"
}
],
"thumbnail": {
"relativePath": "4vds6twPsb7.png",
"url": "https://lh3.googleusercontent.com/C_il4QubLWYMAyrnGPMjXWx4E7MVYLZAoX_Hf-qr4WyHfebvf2y3lndh71A350g",
"contentType": "image/png"
},
"license": "CREATIVE_COMMONS_BY",
"visibility": "PUBLIC",
"isCurated": true,
"presentationParams": {
"orientingRotation": {
"w": 1
},
"colorSpace": "LINEAR",
"backgroundColor": "#eeeeee"
}
}
답변1
파일 크기 문제 외에도 효과적인 솔루션을 방해하는 주요 장애물은 다음과 같습니다 jq
.모두객체, 이는 배열도 처리됨을 의미합니다.누구나전체적으로또는아무것도 없습니다. 따라서 요소를 포함하는 주요 큰 배열을 제거해야 후자를 원래 입력에서 (훨씬 더 작은) 개체의 스트림으로 처리할 수 있습니다.포함하다, 최종 출력.
이를 달성하려면 jq
최종 출력 배열에도 배열 처리 기능이 사용되지 않도록 일부 텍스트 조작이 필요합니다. 몇 가지 변형을 생각해 볼 수 있는데, 각 변형에는 텍스트 조작을 통해 수행해야 하는 어느 정도의 작업이 있지만 이 조작을 배열 구조 구문 최종 출력만 추가하는 최소한의 작업으로 제한하기로 선택했습니다.
이 텍스트 조작은 매우 최소화되어 이 목적을 위해 자체 텍스트 구성을 사용할 수 있습니다 jq
. 이를 통해 다른 도구에 의지하지 않고 순수한 솔루션을 가질 수 있을 뿐만 아니라 jq
필요한 텍스트 비트스트림 흐름을 따라가면서 추가할 수도 있습니다. 예상된 들여쓰기/줄 바꿈 또는 기타 형식 가정에 의존하지 않으며, 이는 모두 "수동으로" 최종 출력 배열을 구축하는 보다 강력한 방법을 용이하게 합니다. 이러한 장점의 대가는 sed
.
예제 입력의 "줄"은 다음과 같습니다.
jq -rn --stream --arg f name --arg q 'assets/fUCcxWczWT0' '"[", foreach fromstream(1|truncate_stream(inputs)) as $o ([null,null]; if .[1] or $o[$f]? == $q then [.[1], ","] else . end; .[0]//empty, if .[1] then $o else empty end), "]"'
사용된 옵션 jq
. 올바른 결과를 얻으려면 처음 세 개가 필요합니다.
-r
임의의 텍스트를 출력할 때 원시(예: JSON으로 변환되지 않음) 출력-n
내장된 소비를 통한inputs
입력 데이터 의 경우오직jq
그렇지 않으면 입력의 첫 번째 전체 개체가 다음의 일반 루프에 의해 먹혀집니다.--stream
jq
자체 스트리밍 모드를 사용하여 입력을 소비하는 데 사용됩니다.--arg f name
찾고 있는 값이 포함된 필드의 이름--arg q 'assets/fUCcxWczWT0'
각 객체의 필드에서 찾고 있는 값
jq
확장 기능과 설명을 포함하여 스크립트를 개별적으로 살펴보세요 .
# Firstly, the initial bit of arbitrary text: the opening bracket for the final output,
# as required for a valid JSON array syntax
"[",
# Then a foreach statement, looping over the objects provided singularly, one by one,
# by the streamlined input
# NOTE 1: jq's streaming mode is used for this solution primarily so that we
# can use `1 | truncate_stream()` here, which courteously (and natively)
# strips the first (1 | ...) structure of the original input along the course of
# the streamlined operation.
# The first structure is obviously the main huge array containing the
# objects, hence we receive these latter singularly in a truly streamlined
# fashion, freed by the containment of the array
# NOTE 2: using `inputs | tostream` here in place of `--stream`, although functioning,
# would not obtain the streaming goal, because it would first take the entire
# input as a whole rather than streaming it from the start
foreach fromstream( 1|truncate_stream(inputs) ) as $o (
# the initial state of the loop: we use 2 values as a "shifting 2-value state-machine"
# for the comma to be output (as text) along with all the elements except the
# first. We use the second value as an overall state too
[null, null];
# here we look for the wanted value in the wanted field unless already found
# previously according to the overall state, and we update the loop state "shifting"
# the 2-state for the comma as soon as the wanted value is found
if .[1] or $o[$f]? == $q then [.[1], ","] else . end;
# here we print the comma text (if need be according to its 2-state)
# as required for separating elements in a JSON array syntax, then we print
# the object element itself if the overall state says so
.[0]//empty, if .[1] then $o else empty end
),
# Lastly, the final bit of arbitrary text: the closing bracket for the output,
# as required for a valid JSON array syntax
"]"
몇 가지 최종 참고사항:
- 분명히 이 접근 방식은 원본 입력이 거대하기는 하지만 기본적으로 단순하고 단순하다고 가정합니다(실제로 이 사실을 활용합니다). 내부 값과 외부 값 사이의 상호 참조가 있는 더 복잡한 전체 구조라면 스크립트에 쉽게 필요할 수 있습니다. 똑같이 더 복잡하고 난해해지다
- 요소를 출력하고 싶다면에 따라대신 수배범에게에서 시작하다원하는 경우 스트리밍 논리는 더 간단할 수 있으며 상태가 전혀 필요하지 않을 수도 있습니다. 원칙적으로
halt
원하는 객체가 발견될 때 스크립트만 작성할 수 있으므로 부작용으로 속도도 더 빨라질 수 있습니다(완전 스트리밍 작업).
답변2
문제를 단순화하면 다음과 같습니다. 전체 문서를 읽지 않고 배열의 처음 몇 요소를 제거하는 방법입니다.
[7,10,8,6,5,4,0,9,3,2,1]
...예를 들어 다음을 얻을 수 있습니다.
[4,0,9,3,2,1]
... 4
솔루션에 대한 입력 쿼리는 어디에 있나요?
답변: 데이터가 메모리로 읽혀지는지 여부에 신경 쓰지 않고 먼저 입력 배열을 개별 숫자 집합으로 "폭발"한 다음 쿼리에 해당하는 첫 번째 숫자를 찾는 방법으로 이 문제를 더 간단하게 해결할 수 있습니다. 일단 발견되면, 발견된 요소와 모든 후속 숫자를 배열에 넣습니다. jq
파이프라인에서 이를 두 번 호출하여 이를 수행합니다. 여기서 첫 번째 호출 jq
은 한 줄에 하나씩 숫자 스트림을 생성합니다.
$ jq '.[]' file | jq -c --argjson query 4 'select(. == $query) as $elem | [$elem, inputs]'
[4,0,9,3,2,1]
문제는 입력 및 출력 데이터가 메모리에 저장되어야 한다는 것입니다. 입력은 jq
첫 번째 프로세스에 의해 완전히 구문 분석되는 반면 출력은 두 번째 프로세스에 의해 메모리에 수집됩니다.
입력 데이터를 메모리에 저장하지 않는 방법을 찾을 수 있다면 좋을 것입니다. 이를 위해 스트리밍 기능을 사용해 볼 수 있습니다 jq
. --stream
를 사용하면 jq
표현식은 전체 입력을 구문 분석하지 않고 입력 스트림의 현재 상태를 나타내는 배열 스트림을 수신합니다(참조:"스트림 미디어"매뉴얼 섹션 jq
).
$ jq -c --stream --argjson query 4 'fromstream(select(.[1] == $query) as $elem | $elem, inputs | .[0][0] -= $elem[0][0])' file
[4,0,9,3,2,1]
위 jq
명령은 배열의 스트림 표현을 읽고 쿼리에 해당하는 첫 번째 요소를 찾습니다. 전체 입력을 메모리에 저장하지 않고 이 작업을 수행합니다.
이 표현식은 배열 요소의 인덱스에 해당하는 .[1]
배열 요소의 값으로 평가됩니다 . 일반적 으로 데이터는.[0][0]
.[0]
길원래 비스트리밍 데이터 세트의 값). 출력으로 받은 각 요소에 대해 발견된 요소의 인덱스로 오프셋하여 해당 요소의 인덱스를 다시 계산해야 합니다. 이렇게 하면 저장이 방지됩니다.입력하다그러나 비스트리밍 형식으로 변환하려면 출력을 메모리에 저장해야 할 수도 있습니다.
원시 데이터에 사용할 수 있는 명령에 위의 명령을 적용하는 것은 매우 간단합니다. 또한 키 값을 비교하기 전에 객체의 올바른 키를 테스트해야 합니다. 요소의 키는 에 저장됩니다 .[0][1]
.그러나 우리가 받은 이후로 주의하십시오불완전한 객체 스트림,켜다충분히name
키를 기반으로 한 올바른 요소 name
는 다음과 같습니다.첫 번째각 요소의 핵심. 두 번째 또는 세 번째 키인 경우 객체의 초기 부분을 삭제합니다.
$ cat file
[
{
"name": "assets/1",
"authorName": "John Smith",
"displayName": "The House",
"resources": { "car": "bla", "house": "address" }
},
{
"name": "assets/6",
"authorName": "John Smith",
"displayName": "The House",
"resources": { "car": "bla", "house": "address" }
},
{
"name": "assets/0",
"authorName": "John Smith",
"displayName": "The House",
"resources": { "car": "bla", "house": "address" }
},
{
"name": "assets/2",
"authorName": "John Smith",
"displayName": "The House",
"resources": { "car": "bla", "house": "address" }
}
]
assets/0
다음은 값 으로 이 요소 앞에 있는 모든 요소를 제거합니다 name
.
$ jq --stream --arg query 'assets/0' 'fromstream(select(.[0][1] == "name" and .[1] == $query) as $elem | $elem, inputs | .[0][0] -= $elem[0][0])' file
[
{
"name": "assets/0",
"authorName": "John Smith",
"displayName": "The House",
"resources": {
"car": "bla",
"house": "address"
}
},
{
"name": "assets/2",
"authorName": "John Smith",
"displayName": "The House",
"resources": {
"car": "bla",
"house": "address"
}
}
]
쿼리 키가 각 객체의 첫 번째 키여야 한다는 제한 사항을 해결하려면 먼저 key를 기반으로 배열 요소의 첫 번째 인덱스를 결정한 name
다음 해당 키를 사용하여 배열을 추출하는 2단계 작업을 수행할 수 있습니다. 요소. 데이터:
jq --stream --arg query 'assets/0' \
'fromstream(select(.[0][1] == "name" and .[1] == $query) | [0,.[0][0]])' file |
head -n 1 |
jq --stream '.[1] as $index | fromstream(inputs | select(.[0][0] >= $index) | .[0][0] -= $index)' - file
위 파이프라인의 첫 번째는 jq
우리가 찾고 있는 특정 값을 가진 필드의 모든 요소에 대한 인덱스를 출력합니다. 이 인덱스 중 첫 번째 인덱스를 name
선택합니다 . head -n 1
두 번째는 jq
표준 입력의 정수를 내부 $index
변수로 읽은 다음 입력 파일에서 인덱스가 같거나 그보다 큰 요소를 추출합니다(두 번째 읽기) $index
.