CompletableFutureの備忘録
CompletableFutureの使い方をよく忘れるので備忘録。
Memo
staticメソッド
completedFuture
CompletableFuture<U> completedFuture(U value)
CompletableFuture.completedFuture("OK")
supplyAsync
CompletableFuture<U> supplyAsync(Supplier<U> supplier)
CompletableFuture.supplyAsync(() -> "OK")
runAsync
CompletableFuture<Void> runAsync(Runnable runnable)
CompletableFuture.runAsync(() -> System.out.println("OK"))
allOf
CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
戻り値はVoid。
CompletableFuture.allOf(CompletableFuture.runAsync(() -> System.out.println("OK")), CompletableFuture.runAsync(() -> System.out.println("OK")))
anyOf
CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
戻り値を返せる。
CompletableFuture.anyOf(CompletableFuture.completedFuture(1), CompletableFuture.completedFuture(2)) .thenAccept(System.out::println);
thenXxxx
thenRun
CompletableFuture<Void> thenRun(Runnable action)
Runnableを実行する。
CompletableFuture.runAsync(() -> System.out.println("OK")) .thenRun(() -> System.out.println("OK"));
thenAccept
CompletableFuture<Void> thenAccept(Consumer<? super T> action)
Consumerを実行する。
CompletableFuture.completedFuture("OK")
.thenAccept(System.out::println);
thenApply
CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
Functionを実行する。
CompletableFuture.completedFuture("OK") .thenApply(value -> value + "OK") .thenAccept(System.out::println);
thenCompose
CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
Functionを実行する。thenApplyとの違いは、CompletionStageを返す。
CompletableFuture.completedFuture("OK") .thenCompose(value -> CompletableFuture.completedFuture(value + "OK")) .thenAccept(System.out::println);
thenCombine
CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn)
2つの実行結果をBiFunctionで受け取る。
CompletableFuture.completedFuture("OK") .thenCombine(CompletableFuture.completedFuture("OK"), (value1, value2) -> value1 + value2) .thenAccept(System.out::println);
xxxxEither
runAfterEither
CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action)
どちらかが実行された後で、Runnableを実行する。
CompletableFuture.runAsync(() -> System.out.println(1)) .runAfterEither(CompletableFuture.runAsync(() -> System.out.println(2)), () -> System.out.println("OK"));
acceptEither
CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other,
Consumer<? super T> action)
どちらかの実行結果をConsumerで受け取る。
CompletableFuture.completedFuture(1) .acceptEither(CompletableFuture.completedFuture(2), System.out::println);
applyToEither
CompletableFuture<U> applyToEither(CompletionStage<? extends T> other,
Function<? super T, U> fn)
どちらかの実行結果をFunctionで受け取る。
CompletableFuture.completedFuture(1) .applyToEither(CompletableFuture.completedFuture(2), value -> value) .thenAccept(System.out::println);
xxxxBoth
runAfterBoth
CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action)
両方が実行された後で、Runnableを実行する。
CompletableFuture.runAsync(() -> System.out.println(1)) .runAfterBoth(CompletableFuture.runAsync(() -> System.out.println(2)), () -> System.out.println("OK"));
thenAcceptBoth
CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action)
両方の実行結果をBiConsumerで受け取る。
CompletableFuture.completedFuture(1) .thenAcceptBoth(CompletableFuture.completedFuture(2), (value1, value2) -> System.out.println(value1 + value2));
例外ハンドリング
exceptionally
CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
例外発生時にのみ呼ばれて、値を返せる。
CompletableFuture.failedFuture(new RuntimeException()) .exceptionally(throwable -> "Error") .thenAccept(System.out::println)
handle
CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
例外発生時に値を返せる。Tを受け取ってUを返すので、別の型を返すこともできる。
CompletableFuture.failedFuture(new RuntimeException()) .handle((value, throwable) -> { if (throwable == null) { return value; } else { return "Error"; } }) .thenAccept(System.out::println);
whenComplete
CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
例外発生時の処理を追加できるが、BiConsumerなので値は返せない。
CompletableFuture.failedFuture(new RuntimeException()) .whenComplete((value, throwable) -> { if (throwable == null) { System.out.println(value); } else { System.err.println("Error"); } });
参考
WiresharkでHTTPSを復号化する
Wiresharkでhttps通信を復号化するときの備忘録。
今回はMacOSのChromeとlocalhostのnginxでhttpsの通信をする。
準備
証明書
mkcertでlocalhostの証明書と秘密鍵を用意する。コマンドを実行するとlocalhost.pem
とlocalhost-key.pem
が作成されるので、これをnginxに設定する。
% brew install mkcert
% mkcert -install
% mkcert localhost
NGINX
localhost.conf
server { listen 443 ssl; server_name localhost; root /usr/share/nginx/html; ssl_certificate localhost.pem; ssl_certificate_key localhost-key.pem; }
compose.yaml
services: nginx: image: nginx:latest ports: - "443:443" volumes: - ./localhost.conf:/etc/nginx/conf.d/localhost.conf - ./localhost-key.pem:/etc/nginx/localhost-key.pem - ./localhost.pem:/etc/nginx/localhost.pem
Dockerでnginxを起動する。
% docker compose up
Google Chrome
環境変数SSLKEYLOGFILE
かコマンドライン引数の--ssl-key-log-file
でSSL session keyのログファイルを指定して、コマンドラインからChromeを起動する。今回はコマンドライン引数でsslkey.log
ファイルを指定してChromeを起動する。
% /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --ssl-key-log-file=./sslkey.log
Wireshark
Preferences -> Protocols -> TLS から「(Pre)-Master-Secret log filename」にsslkey.log
ファイルを設定後、Wiresharkを再起動する。
Demo
コマンドラインから起動したChromeで https://localhost にアクセスすると、復号化されたHTTPSのパケットが確認できる。
参考
PrometheusのHTTP Service Discoveryを試す
PrometheusのHTTP Service Discoveryを試したときのメモ。
準備
Prometheus
DockerのPrometheusとNode exporter2台を使う。
prometheus.yml
global: scrape_interval: 30s scrape_configs: - job_name: prometheus static_configs: - targets: ['host.docker.internal:9090'] - job_name: http-sd http_sd_configs: - url: 'http://host.docker.internal:8080/http-sd'
compose.yml
services: prometheus: image: prom/prometheus:latest ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml node-exporter1: image: quay.io/prometheus/node-exporter:latest ports: - "9101:9100" node-exporter2: image: quay.io/prometheus/node-exporter:latest ports: - "9102:9100"
PromethusとNod exporterを起動する。
% docker compose up
HTTP SD endpoint
HTTP SD endpointはspring bootで実装する。
build.gradle
plugins { id 'java' id 'org.springframework.boot' version '3.0.2' id 'io.spring.dependency-management' version '1.1.0' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'io.micrometer:micrometer-registry-prometheus' testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { useJUnitPlatform() }
application.yml
management: endpoints: web: exposure: include: prometheus
HttpSdController.java
ドキュメントに記載されているフォーマットで、Node exporter2台とこのspring bootアプリの情報を返す。
import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; @RestController public class HttpSdController { private static final Logger logger = LoggerFactory.getLogger(HttpSdController.class); public record HttpSd(List<String> targets, Map<String, String> labels) {} @GetMapping("/http-sd") public List<HttpSd> httpSd(@RequestHeader("X-Prometheus-Refresh-Interval-Seconds") String refreshInterval) { logger.info("Refresh-Interval-Seconds: {}", refreshInterval); return List.of(new HttpSd(List.of("host.docker.internal:9101", "host.docker.internal:9102"), Map.of("job", "node-exporter")), new HttpSd(List.of("host.docker.internal:8080"), Map.of("job", "spring-boot", "__metrics_path__", "/actuator/prometheus"))); } }
具体的にはこんなJSONを返す。
[ { "targets": [ "host.docker.internal:9101", "host.docker.internal:9102" ], "labels": { "job": "node-exporter" } }, { "targets": [ "host.docker.internal:8080" ], "labels": { "job": "spring-boot", "__metrics_path__": "/actuator/prometheus" } } ]
動作確認
spring bootアプリを起動後、http://localhost:9090/targets にアクセスすると、SD endpointのtargetsがprometheusに反映されていることが確認できた。
http_sd_config.refresh_interval
のデフォルトは1mなので、1分ごとにprometheusがSD endpointにアクセスしていることも確認できた。
参考
MongoDBのクエリの備忘録 (Element)
準備
使用するMongoDBはこちらと同じ。
MongoDB Shellで接続して、マニュアルと同じテストデータを挿入する。
test> db.inventory.insertMany([ ... { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] }, ... { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] }, ... { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] }, ... { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] }, ... { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] } ... ]); { acknowledged: true, insertedIds: { '0': ObjectId("63945c181ce8aac5f4f6e632"), '1': ObjectId("63945c181ce8aac5f4f6e633"), '2': ObjectId("63945c181ce8aac5f4f6e634"), '3': ObjectId("63945c181ce8aac5f4f6e635"), '4': ObjectId("63945c181ce8aac5f4f6e636") } }
テストデータを変更して、"notebook"のqty
fieldを削除、"paper"のqty
にnullを設定する。
test> db.inventory.updateOne({item: "notebook"}, {$unset: {qty: ""}}) { acknowledged: true, insertedId: null, matchedCount: 1, modifiedCount: 1, upsertedCount: 0 } test> db.inventory.updateOne({item: "paper"}, {$set: {qty: undefined}}) { acknowledged: true, insertedId: null, matchedCount: 1, modifiedCount: 1, upsertedCount: 0 }
Element Query Operators
exists
qty
fieldがあるdocumentを取得
(valueがnullのdocumentも含む)
test> db.inventory.find({qty: {$exists: true}}) [ { _id: ObjectId("63945c181ce8aac5f4f6e632"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("63945c181ce8aac5f4f6e634"), item: 'paper', qty: null, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("63945c181ce8aac5f4f6e635"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] }, { _id: ObjectId("63945c181ce8aac5f4f6e636"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ]
qty
fieldがないdocumentを取得
test> db.inventory.find({qty: {$exists: false}}) [ { _id: ObjectId("63945c181ce8aac5f4f6e633"), item: 'notebook', tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] } ]
type
qty
のvalueがnumberのdocumentを取得
test> db.inventory.find({qty: {$type: "number"}}) [ { _id: ObjectId("63945c181ce8aac5f4f6e632"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("63945c181ce8aac5f4f6e635"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] }, { _id: ObjectId("63945c181ce8aac5f4f6e636"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ]
qty
のvalueがnullのdocumentを取得
test> db.inventory.find({qty: {$type: "null"}}) [ { _id: ObjectId("63945c181ce8aac5f4f6e634"), item: 'paper', qty: null, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] } ]
参考
MongoDBのクエリの備忘録 (Array)
準備
使用するMongoDBはこちらと同じ。
MongoDB Shellで接続して、マニュアルと同じテストデータを挿入する。
test> db.inventory.insertMany([ ... { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] }, ... { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] }, ... { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] }, ... { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] }, ... { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] } ... ]); { acknowledged: true, insertedIds: { '0': ObjectId("634137309de97ee89b8130a8"), '1': ObjectId("634137309de97ee89b8130a9"), '2': ObjectId("634137309de97ee89b8130aa"), '3': ObjectId("634137309de97ee89b8130ab"), '4': ObjectId("634137309de97ee89b8130ac") } }
Array Query Operators
all
指定した全ての要素を含むarrayを取得
red
とblank
を含むtags
test> db.inventory.find( { tags: { $all: ["red", "blank"] } } ) [ { _id: ObjectId("634137309de97ee89b8130a8"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130aa"), item: 'paper', qty: 100, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130ab"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] } ]
このクエリと同じ
test> db.inventory.find( { $and: [ { tags: "red" }, { tags: "blank" } ] } ) [ { _id: ObjectId("634137309de97ee89b8130a8"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130aa"), item: 'paper', qty: 100, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130ab"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] } ]
これらのクエリは完全一致
test> db.inventory.find( { tags: { $all: [["red", "blank"]] } } ) [ { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] } ]
test> db.inventory.find( { $and: [ { tags: ["red", "blank"] } ] } ) [ { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] } ]
test> db.inventory.find( { tags: ["red", "blank"] } ) [ { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] } ]
elemMatch
指定した全てのクエリに一致する要素を少なくとも1つは含んでいるarrayを取得
dim_cm
でvalue > 15 and value < 20
は15.25のみ
test> db.inventory.find( { dim_cm: { $elemMatch: { $gt: 15, $lt: 20 } } } ) [ { _id: ObjectId("634137309de97ee89b8130ac"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ]
これは、arrayのいずれかの要素が指定したクエリのいずれかに一致する場合
dim_cm: [ 22.85, 30 ]
のみ20未満の要素がないので該当しない
test> db.inventory.find( { dim_cm: { $gt: 15, $lt: 20 } } ) [ { _id: ObjectId("634137309de97ee89b8130a8"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130aa"), item: 'paper', qty: 100, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130ac"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ]
size
指定したサイズのarrayを取得
test> db.inventory.find( { tags: { $size: 1 } } ) [ { _id: ObjectId("634137309de97ee89b8130ac"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ] test> db.inventory.find({ tags: { $size: 2 } } ) [ { _id: ObjectId("634137309de97ee89b8130a8"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130ab"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] } ] test> db.inventory.find({ tags: { $size: 3 } } ) [ { _id: ObjectId("634137309de97ee89b8130aa"), item: 'paper', qty: 100, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] } ]
Array Index Position
クエリの条件にarrayのindexを指定する場合
test> db.inventory.find( { "dim_cm.0": { $gt: 15 } } ) [ { _id: ObjectId("634137309de97ee89b8130ab"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] } ] test> db.inventory.find( { "dim_cm.1": { $lt: 20 } } ) [ { _id: ObjectId("634137309de97ee89b8130ac"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ]
参考
jfrコマンドの備忘録
Memo
準備
JFRファイルのサイズを大きくしたいので、大量のJFRイベントを書き込むデモアプリを用意する。
Main.java
import java.time.LocalDateTime; import jdk.jfr.Category; import jdk.jfr.Description; import jdk.jfr.Event; import jdk.jfr.Label; import jdk.jfr.Name; public class Main { @Name("hirakida.Hello") @Label("Hello") static class HelloEvent extends Event { @Label("Message") String message; @Label("DateTime") LocalDateTime dateTime; } public static void main(String[] args) { while (true) { HelloEvent event = new HelloEvent(); event.message = "Hello!"; event.dateTime = LocalDateTime.now(); event.begin(); event.commit(); } } }
JFRを有効にして、デモアプリを実行する。
% sdk use java 17.0.4.1-tem % java -XX:StartFlightRecording:filename=recording.jfr Main.java [1.240s][info][jfr,startup] Started recording 1. No limit specified, using maxsize=250MB as default. [1.240s][info][jfr,startup] [1.240s][info][jfr,startup] Use jcmd 6058 JFR.dump name=1 to copy recording data to file.
コマンド
flight recordingの内容を表示する。
% jfr print --events hirakida.Hello recording.jfr hirakida.Hello { startTime = 14:53:33.505 duration = 0.000011 ms message = "Hello!" eventThread = "main" (javaThreadId = 1) stackTrace = [ Main.main(String[]) line: 29 jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Object, Object[]) line: 77 jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 java.lang.reflect.Method.invoke(Object, Object[]) line: 568 ... ] } ...
JSON形式
% jfr print --json --events hirakida.Hello recording.jfr { "recording": { "events": [{ "type": "hirakida.Hello", "values": { "startTime": "2022-09-17T14:53:33.505442889+09:00", "duration": "PT0.000000011S", "eventThread": { "osName": "main", "osThreadId": 6659, "javaName": "main", "javaThreadId": 1, "group": { "parent": { "parent": null, "name": "system" }, "name": "main" } }, ...
metadata
イベントの情報を表示する。
% jfr metadata recording.jfr class boolean { } class byte { } class char { } class double { } class float { } class int { } class long { } class short { } @Name("java.lang.Class") @Label("Java Class") class Class { @Label("Class Loader") ClassLoader classLoader; @Label("Name") String name; @Label("Package") Package package; @Label("Access Modifiers") int modifiers; @Label("Hidden") boolean hidden; } ...
disassemble
JFRファイルを分割する。
% mkdir out % jfr disassemble --output out recording.jfr Examining recording /Users/hirakida/work/recording.jfr ... File consists of 19 chunks. The recording will be split into 4 files Writing /Users/hirakida/work/out/recording_0.jfr ... 66784534 Writing /Users/hirakida/work/out/recording_1.jfr ... 66796485 Writing /Users/hirakida/work/out/recording_2.jfr ... 66693161 Writing /Users/hirakida/work/out/recording_3.jfr ... 50546118
assemble
JFRファイルを統合する。
% jfr assemble out assemble.jfr Assembling files... /Users/hirakida/work/out/recording_0.jfr /Users/hirakida/work/out/recording_1.jfr /Users/hirakida/work/out/recording_2.jfr /Users/hirakida/work/out/recording_3.jfr Finished.
summary
サマリーを表示する。
% jfr summary recording.jfr Version: 2.1 Chunks: 19 Start: 2022-09-17 05:53:33 (UTC) Duration: 19 s Event Type Count Size (bytes) ================================================================== hirakida.Hello 11686763 245434096 jdk.BooleanFlag 9975 304950 jdk.NativeLibrary 9728 858382 jdk.ModuleExport 9063 96786 jdk.GCPhaseParallel 6668 177786 jdk.ActiveSetting 6308 194047 jdk.SystemProcess 4645 373916 jdk.LongFlag 3762 125020 jdk.ModuleRequire 2869 28690 jdk.UnsignedLongFlag 2774 94696 jdk.ObjectAllocationSample 2428 38397 ...
参考
MongoDBのクエリの備忘録 (Comparison, Logical)
準備
Docker ComposeでMongoDBを起動する。
% docker compose up -d
compose.yml
services: mongo: image: mongo:6.0 restart: always ports: - 27017:27017 environment: MONGO_INITDB_ROOT_USERNAME: user1 MONGO_INITDB_ROOT_PASSWORD: xxxx
MongoDB Shellで接続する。
% mongosh "mongodb://localhost:27017" --username user1 Enter password: **** Current Mongosh Log ID: 633acb1ef03fa39a9f8e3ec1 Connecting to: mongodb://<credentials>@localhost:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.6.0 Using MongoDB: 6.0.2 Using Mongosh: 1.6.0 For mongosh info see: https://docs.mongodb.com/mongodb-shell/ test>
テストデータを挿入する。
test> db.product.insertMany([ ... {name: "product1", price: 100}, ... {name: "product2", price: 200}, ... {name: "product3", price: 300}, ... {name: "product4", price: 400}, ... {name: "product5", price: 500} ... ]) { acknowledged: true, insertedIds: { '0': ObjectId("633acbf5f03fa39a9f8e3ec2"), '1': ObjectId("633acbf5f03fa39a9f8e3ec3"), '2': ObjectId("633acbf5f03fa39a9f8e3ec4"), '3': ObjectId("633acbf5f03fa39a9f8e3ec5"), '4': ObjectId("633acbf5f03fa39a9f8e3ec6") } }
Comparison Query Operators
eq (equal)
price = 300
test> db.product.find({price: {$eq: 300}}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec4"), name: 'product3', price: 300 } ]
ne (not equal)
price <> 300
test> db.product.find({price: {$ne: 300}}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec2"), name: 'product1', price: 100 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec3"), name: 'product2', price: 200 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec5"), name: 'product4', price: 400 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec6"), name: 'product5', price: 500 } ]
gt (greater than)
price > 300
test> db.product.find({price: {$gt: 300}}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec5"), name: 'product4', price: 400 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec6"), name: 'product5', price: 500 } ]
gte (greater than or equal)
price >= 300
test> db.product.find({price: {$gte: 300}}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec4"), name: 'product3', price: 300 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec5"), name: 'product4', price: 400 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec6"), name: 'product5', price: 500 } ]
lt (less than)
price < 300
test> db.product.find({price: {$lt: 300}}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec2"), name: 'product1', price: 100 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec3"), name: 'product2', price: 200 } ]
lte (less than or equal)
price <= 300
test> db.product.find({price: {$lte: 300}}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec2"), name: 'product1', price: 100 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec3"), name: 'product2', price: 200 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec4"), name: 'product3', price: 300 } ]
in
price in (200, 300)
test> db.product.find({price: {$in: [200,300]}}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec3"), name: 'product2', price: 200 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec4"), name: 'product3', price: 300 } ]
nin
price not in (200, 300)
test> db.product.find({price: {$nin: [200,300]}}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec2"), name: 'product1', price: 100 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec5"), name: 'product4', price: 400 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec6"), name: 'product5', price: 500 } ]
Logical Query Operators
and
price >= 300 and price <= 400
test> db.product.find({$and: [{price: {$gte: 300}}, {price: {$lte: 400}}]}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec4"), name: 'product3', price: 300 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec5"), name: 'product4', price: 400 } ]
or
price = 300 or price = 400
test> db.product.find({$or: [{price: {$eq: 300}}, {price: {$eq: 400}}]}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec4"), name: 'product3', price: 300 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec5"), name: 'product4', price: 400 } ]
nor
not (price = 300 or price = 400)
test> db.product.find({$nor: [{price: {$eq: 300}}, {price: {$eq: 400}}]}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec2"), name: 'product1', price: 100 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec3"), name: 'product2', price: 200 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec6"), name: 'product5', price: 500 } ]
not
not price >= 300
test> db.product.find({price: {$not: {$gte: 300}}}) [ { _id: ObjectId("633acbf5f03fa39a9f8e3ec2"), name: 'product1', price: 100 }, { _id: ObjectId("633acbf5f03fa39a9f8e3ec3"), name: 'product2', price: 200 } ]