【Firebase】Cloud Firestoreデータ抽出

Firebase

1. 概要

Cloud Firestoreのデータを抽出してみます。

2. プロジェクトを作成

こちらを参考

3. Firestoreのセットアップ

こちらを参考

4. プロジェクトのロケーションを変更

こちらを参考

5. Functionsのセットアップ

こちらを参考

6. ソースコード

6-1. functions/src/common.ts

import * as functions from "firebase-functions";

export const f = functions.region("asia-northeast1");

6-2. functions/src/database.ts

import * as admin from "firebase-admin";

admin.initializeApp();

export const db = () => admin.firestore();

6-3. functions/src/types.ts

import * as admin from "firebase-admin";

export type Timestamp = admin.firestore.Timestamp | admin.firestore.FieldValue;

6-4. functions/src/models.ts

import * as types from "./types";

export type RequestCity = {
    name: string,
    state: string,
    country: string,
    capital: boolean,
    population: number,
    regions: string[],
    updatedAt: types.Timestamp,
    createdAt: types.Timestamp
}

6-5. functions/src/utils.ts

import * as firestore from "firebase-admin/firestore";

export const serverTimestamp = () => firestore.FieldValue.serverTimestamp();

6-6. functions/src/simple_query.ts

import * as database from "./database";
import * as common from "./common";

export const getSimpleQuery01 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('state', '==', 'CA').get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getSimpleQuery02 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('capital', '==', true).get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getSimpleQuery03 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('population', '<', 1000000).get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getSimpleQuery04 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('name', '>=', 'San Francisco').get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getSimpleQuery05 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('capital', '!=', false).get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getSimpleQuery06 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('regions', 'array-contains', 'west_coast').get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getSimpleQuery07 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('country', 'in', ['USA', 'Japan']).get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getSimpleQuery08 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('country', 'not-in', ['USA', 'Japan']).get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getSimpleQuery09 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('regions', 'array-contains-any', ['west_coast', 'east_coast']).get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getSimpleQuery10 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('regions', 'in', [['west_coast', 'east_coast']]).get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getSimpleQuery11 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('regions', 'in', [['west_coast', 'socal']]).get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getSimpleQuery12 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('regions', 'in', [['socal', 'west_coast']]).get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

6-7. functions/src/composite_query.ts

import * as database from "./database";
import * as common from "./common";

export const getCompositeQuery01 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('state', '==', 'CA').where('name', '==', 'San Diego').get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getCompositeQuery02 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('state', '==', 'CA').where('population', '<', 1000000).get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

export const getCompositeQuery03 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const citiesRef = database.db().collection(collectionId);
  const allCaRes = await citiesRef.where('state', '>=', 'CA').where('state', '<=', 'IN').get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

6-8. functions/src/group_query.ts

import * as database from "./database";
import * as common from "./common";

export const getCollectionGroupQuery01 = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const allCaRes = await database.db().collectionGroup('landmarks').where('type', '==', 'museum').get().then(async (snapshot) => snapshot.docs.map((v) => v.data()));
  response.status(200).send(allCaRes);
});

6-9. functions/src/index.ts

import * as database from "./database";
import * as common from "./common";
import * as models from "./models";
import * as utils from "./utils";
export * as simple_query from "./simple_query";
export * as composite_query from "./composite_query";
export * as group_query from "./group_query";

export const addData = common.f.https.onRequest(async (request, response) => {
  if (request.method !== 'POST') {
    response.status(405).send('Please request as a POST!');
    return;
  }
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const docId = request.query.docId !== undefined ? request.query.docId.toString() : "";
  const requestCity : models.RequestCity = request.body;
  requestCity.updatedAt = utils.serverTimestamp();
  requestCity.createdAt = utils.serverTimestamp();
  try {
    await database.db().collection(collectionId).doc(docId).set(requestCity);
  } catch (e) {
    console.error(e);
    response.status(500).send(e);
  }
  response.status(200).send("OK");
});

export const addSubData = common.f.https.onRequest(async (request, response) => {
  const collectionId = request.get('X-CollectionId');
  if (collectionId === undefined) {
    response.status(400).send('Please request with a Collection Id');
    return;
  }
  const subCollectionId = 'landmarks';
  try {
    await database.db().collection(collectionId).doc('SF').collection(subCollectionId).doc().set({'name': 'Golden Gate Bridge','type': 'bridge','updatedAt': utils.serverTimestamp(),'createdAt': utils.serverTimestamp()});
    await database.db().collection(collectionId).doc('SF').collection(subCollectionId).doc().set({'name': 'Legion of Honor','type': 'museum','updatedAt': utils.serverTimestamp(),'createdAt': utils.serverTimestamp()});
    await database.db().collection(collectionId).doc('LA').collection(subCollectionId).doc().set({'name': 'Griffith Park','type': 'park','updatedAt': utils.serverTimestamp(),'createdAt': utils.serverTimestamp()});
    await database.db().collection(collectionId).doc('LA').collection(subCollectionId).doc().set({'name': 'The Getty','type': 'museum','updatedAt': utils.serverTimestamp(),'createdAt': utils.serverTimestamp()});
    await database.db().collection(collectionId).doc('DC').collection(subCollectionId).doc().set({'name': 'Lincoln Memorial','type': 'memorial','updatedAt': utils.serverTimestamp(),'createdAt': utils.serverTimestamp()});
    await database.db().collection(collectionId).doc('DC').collection(subCollectionId).doc().set({'name': 'National Air and Space Museum','type': 'museum','updatedAt': utils.serverTimestamp(),'createdAt': utils.serverTimestamp()});
    await database.db().collection(collectionId).doc('TOK').collection(subCollectionId).doc().set({'name': 'Ueno Park','type': 'park','updatedAt': utils.serverTimestamp(),'createdAt': utils.serverTimestamp()});
    await database.db().collection(collectionId).doc('TOK').collection(subCollectionId).doc().set({'name': 'National Museum of Nature and Science','type': 'museum','updatedAt': utils.serverTimestamp(),'createdAt': utils.serverTimestamp()});
    await database.db().collection(collectionId).doc('BJ').collection(subCollectionId).doc().set({'name': 'Jingshan Park','type': 'park','updatedAt': utils.serverTimestamp(),'createdAt': utils.serverTimestamp()});
    await database.db().collection(collectionId).doc('BJ').collection(subCollectionId).doc().set({'name': 'Beijing Ancient Observatory','type': 'museum','updatedAt': utils.serverTimestamp(),'createdAt': utils.serverTimestamp()});
  } catch (e) {
    console.error(e);
    response.status(500).send(e);
  }
  response.status(200).send("OK");
});

7. エミュレータ スイートのセットアップ

7-1. 初期化

7-2. functions/package.json

{
  "name": "functions",
  "scripts": {
    "lint": "eslint --ext .js,.ts .",
    "build": "tsc",
    "build:watch": "tsc --watch",
    "serve": "npm run build && firebase emulators:start",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "18"
  },
  "main": "lib/index.js",
  "dependencies": {
    "firebase-admin": "^11.5.0",
    "firebase-functions": "^4.2.0"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^5.48.2",
    "@typescript-eslint/parser": "^5.48.2",
    "eslint": "^8.32.0",
    "eslint-config-google": "^0.14.0",
    "eslint-plugin-import": "^2.27.5",
    "firebase-functions-test": "^3.0.0",
    "typescript": "^4.9.4"
  },
  "private": true
}

7-3. エミュレータを起動

cd functions
npm run serve
> serve
> npm run build && firebase emulators:start


> build
> tsc

i  emulators: Starting emulators: functions, firestore, storage
⚠  functions: The following emulators are not running, calls to these services from the Functions emulator will affect production: auth, database, hosting, pubsub
✔  functions: Using node@18 from host.
i  firestore: Firestore Emulator logging to firestore-debug.log
✔  firestore: Firestore Emulator UI websocket is running on 9150.
i  ui: Emulator UI logging to ui-debug.log
i  functions: Watching "/home/sondon/dev/wsl/gcp/firebase/sample003/functions" for Cloud Functions...
⚠  functions: package.json indicates an outdated version of firebase-functions. Please upgrade using npm install --save firebase-functions@latest in your functions directory.
✔  functions: Loaded functions definitions from source: simple_query.getSimpleQuery01, simple_query.getSimpleQuery02, simple_query.getSimpleQuery03, simple_query.getSimpleQuery04, simple_query.getSimpleQuery05, simple_query.getSimpleQuery06, simple_query.getSimpleQuery07, simple_query.getSimpleQuery08, simple_query.getSimpleQuery09, simple_query.getSimpleQuery10, simple_query.getSimpleQuery11, simple_query.getSimpleQuery12, composite_query.getCompositeQuery01, composite_query.getCompositeQuery02, composite_query.getCompositeQuery03, group_query.getCollectionGroupQuery01, addData, addSubData.
✔  functions[asia-northeast1-simple_query-getSimpleQuery01]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery01).
✔  functions[asia-northeast1-simple_query-getSimpleQuery02]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery02).
✔  functions[asia-northeast1-simple_query-getSimpleQuery03]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery03).
✔  functions[asia-northeast1-simple_query-getSimpleQuery04]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery04).
✔  functions[asia-northeast1-simple_query-getSimpleQuery05]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery05).
✔  functions[asia-northeast1-simple_query-getSimpleQuery06]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery06).
✔  functions[asia-northeast1-simple_query-getSimpleQuery07]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery07).
✔  functions[asia-northeast1-simple_query-getSimpleQuery08]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery08).
✔  functions[asia-northeast1-simple_query-getSimpleQuery09]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery09).
✔  functions[asia-northeast1-simple_query-getSimpleQuery10]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery10).
✔  functions[asia-northeast1-simple_query-getSimpleQuery11]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery11).
✔  functions[asia-northeast1-simple_query-getSimpleQuery12]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery12).
✔  functions[asia-northeast1-composite_query-getCompositeQuery01]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/composite_query-getCompositeQuery01).
✔  functions[asia-northeast1-composite_query-getCompositeQuery02]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/composite_query-getCompositeQuery02).
✔  functions[asia-northeast1-composite_query-getCompositeQuery03]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/composite_query-getCompositeQuery03).
✔  functions[asia-northeast1-group_query-getCollectionGroupQuery01]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/group_query-getCollectionGroupQuery01).
✔  functions[asia-northeast1-addData]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/addData).
✔  functions[asia-northeast1-addSubData]: http function initialized (http://127.0.0.1:5001/isub-sample-003/asia-northeast1/addSubData).

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://127.0.0.1:4000/               │
└─────────────────────────────────────────────────────────────┘

┌───────────┬────────────────┬─────────────────────────────────┐
│ Emulator  │ Host:Port      │ View in Emulator UI             │
├───────────┼────────────────┼─────────────────────────────────┤
│ Functions │ 127.0.0.1:5001 │ http://127.0.0.1:4000/functions │
├───────────┼────────────────┼─────────────────────────────────┤
│ Firestore │ 127.0.0.1:8080 │ http://127.0.0.1:4000/firestore │
├───────────┼────────────────┼─────────────────────────────────┤
│ Storage   │ 127.0.0.1:9199 │ http://127.0.0.1:4000/storage   │
└───────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at 127.0.0.1:4400
  Other reserved ports: 4500, 9150

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.

8. データの登録

8-1. 登録

curl -X POST -H "Content-Type: application/json" -H "X-CollectionId:cities" -d '{"name":"San Francisco","state":"CA","country":"USA","capital":false,"population":860000,"regions":["west_coast","norcal"]}' http://127.0.0.1:5001/isub-sample-003/asia-northeast1/addData?docId=SF
curl -X POST -H "Content-Type: application/json" -H "X-CollectionId:cities" -d '{"name":"Los Angeles","state":"CA","country":"USA","capital":false,"population":3900000,"regions":["west_coast","socal"]}' http://127.0.0.1:5001/isub-sample-003/asia-northeast1/addData?docId=LA
curl -X POST -H "Content-Type: application/json" -H "X-CollectionId:cities" -d '{"name":"Washington, D.C.","state":null,"country":"USA","capital":true,"population":680000,"regions":["east_coast"]}' http://127.0.0.1:5001/isub-sample-003/asia-northeast1/addData?docId=DC
curl -X POST -H "Content-Type: application/json" -H "X-CollectionId:cities" -d '{"name":"Tokyo","state":null,"country":"Japan","capital":true,"population":9000000,"regions":["kanto","honshu"]}' http://127.0.0.1:5001/isub-sample-003/asia-northeast1/addData?docId=TOK
curl -X POST -H "Content-Type: application/json" -H "X-CollectionId:cities" -d '{"name":"Beijing","state":null,"country":"China","capital":true,"population":21500000,"regions":["jingjinji","hebei"]}' http://127.0.0.1:5001/isub-sample-003/asia-northeast1/addData?docId=BJ

8-2. 登録(サブコレクション)

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/addSubData

8-3. 登録データ

CollectionDocumentData or CollectionDocumentData
1citiesSF{
“name”: “San Francisco”,
“state”: “CA”,
“country”: “USA”,
“capital”: false,
“population”: 860000,
“regions”: [
“west_coast”,
“norcal”
]
}
2landmarksAUTO{
name: ‘Golden Gate Bridge’,
type: ‘bridge’
}
3landmarksAUTO{
name: ‘Legion of Honor’,
type: ‘museum’
}
4LA{
“name”: “Los Angeles”,
“state”: “CA”,
“country”: “USA”,
“capital”: false,
“population”: 3900000,
“regions”: [
“west_coast”,
“socal”
]
}
5landmarksAUTO{
name: ‘Griffith Park’,
type: ‘park’
}
6landmarksAUTO{
name: ‘The Getty’,
type: ‘museum’
}
7DC{
“name”: “Washington, D.C.”,
“state”: null,
“country”: “USA”,
“capital”: true,
“population”: 680000,
“regions”: [
“east_coast”
]
}
8landmarksAUTO{
name: ‘Lincoln Memorial’,
type: ‘memorial’
}
9landmarksAUTO{
name: ‘National Air and Space Museum’,
type: ‘museum’
}
10TOK{
“name”: “Tokyo”,
“state”: null,
“country”: “Japan”,
“capital”: true,
“population”: 9000000,
“regions”: [
“kanto”,
“honshu”
]
}
11landmarksAUTO{
name: ‘Ueno Park’,
type: ‘park’
}
12landmarksAUTO{
name: ‘National Museum of Nature and Science’,
type: ‘museum’
}
13BJ{
“name”: “Beijing”,
“state”: null,
“country”: “China”,
“capital”: true,
“population”: 21500000,
“regions”: [
“jingjinji”,
“hebei”
]
}
14landmarksAUTO{
name: ‘Jingshan Park’,
type: ‘park’
}
15landmarksAUTO{
name: ‘Beijing Ancient Observatory’,
type: ‘museum’
}

9. 単純なクエリ

9-1. stateがCAのすべての都市を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery01
[
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"socal"
		],
		"name": "Los Angeles",
		"state": "CA",
		"population": 3900000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		}
	},
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"norcal"
		],
		"name": "San Francisco",
		"state": "CA",
		"population": 860000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		}
	}
]

9-2. すべての首都を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery02
[
	{
		"country": "China",
		"capital": true,
		"regions": [
			"jingjinji",
			"hebei"
		],
		"name": "Beijing",
		"state": null,
		"population": 21500000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 875000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 875000000
		}
	},
	{
		"country": "USA",
		"capital": true,
		"regions": [
			"east_coast"
		],
		"name": "Washington, D.C.",
		"state": null,
		"population": 680000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		}
	},
	{
		"country": "Japan",
		"capital": true,
		"regions": [
			"kanto",
			"honshu"
		],
		"name": "Tokyo",
		"state": null,
		"population": 9000000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 796000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 796000000
		}
	}
]

9-3. 人口数が100万未満を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery03
[
	{
		"country": "USA",
		"capital": true,
		"regions": [
			"east_coast"
		],
		"name": "Washington, D.C.",
		"state": null,
		"population": 680000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		}
	},
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"norcal"
		],
		"name": "San Francisco",
		"state": "CA",
		"population": 860000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		}
	}
]

9-4. nameがSan Franciscoより大きいデータを返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery04
[
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"norcal"
		],
		"name": "San Francisco",
		"state": "CA",
		"population": 860000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		}
	},
	{
		"country": "Japan",
		"capital": true,
		"regions": [
			"kanto",
			"honshu"
		],
		"name": "Tokyo",
		"state": null,
		"population": 9000000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 796000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 796000000
		}
	},
	{
		"country": "USA",
		"capital": true,
		"regions": [
			"east_coast"
		],
		"name": "Washington, D.C.",
		"state": null,
		"population": 680000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		}
	}
]

9-5. 首都じゃなくない都市(=首都)を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery05
[
	{
		"country": "China",
		"capital": true,
		"regions": [
			"jingjinji",
			"hebei"
		],
		"name": "Beijing",
		"state": null,
		"population": 21500000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 875000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 875000000
		}
	},
	{
		"country": "USA",
		"capital": true,
		"regions": [
			"east_coast"
		],
		"name": "Washington, D.C.",
		"state": null,
		"population": 680000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		}
	},
	{
		"country": "Japan",
		"capital": true,
		"regions": [
			"kanto",
			"honshu"
		],
		"name": "Tokyo",
		"state": null,
		"population": 9000000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 796000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 796000000
		}
	}
]

9-6. regionsがwest_coastの都市を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery06
[
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"socal"
		],
		"name": "Los Angeles",
		"state": "CA",
		"population": 3900000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		}
	},
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"norcal"
		],
		"name": "San Francisco",
		"state": "CA",
		"population": 860000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		}
	}
]

9-7. 国がUSAとJapanの都市を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery07
[
	{
		"country": "USA",
		"capital": true,
		"regions": [
			"east_coast"
		],
		"name": "Washington, D.C.",
		"state": null,
		"population": 680000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		}
	},
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"socal"
		],
		"name": "Los Angeles",
		"state": "CA",
		"population": 3900000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		}
	},
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"norcal"
		],
		"name": "San Francisco",
		"state": "CA",
		"population": 860000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		}
	},
	{
		"country": "Japan",
		"capital": true,
		"regions": [
			"kanto",
			"honshu"
		],
		"name": "Tokyo",
		"state": null,
		"population": 9000000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 796000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 796000000
		}
	}
]

9-8. 国がUSAとJapanじゃない都市を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery08
[
	{
		"country": "China",
		"capital": true,
		"regions": [
			"jingjinji",
			"hebei"
		],
		"name": "Beijing",
		"state": null,
		"population": 21500000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 875000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 875000000
		}
	}
]

9-9. regionsがwest_coastかeast_coastの都市を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery09
[
	{
		"country": "USA",
		"capital": true,
		"regions": [
			"east_coast"
		],
		"name": "Washington, D.C.",
		"state": null,
		"population": 680000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 722000000
		}
	},
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"socal"
		],
		"name": "Los Angeles",
		"state": "CA",
		"population": 3900000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		}
	},
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"norcal"
		],
		"name": "San Francisco",
		"state": "CA",
		"population": 860000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		}
	}
]

9-10. regionsがwest_coast且つeast_coastの都市を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery10
[]

9-11. regionsがwest_coast且つsocalの都市を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery11
[
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"socal"
		],
		"name": "Los Angeles",
		"state": "CA",
		"population": 3900000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		}
	}
]
  • 「in」は、「array-contains-any」とは異なり、句は配列の長さ、順序、および値が完全に一致する場合に一致する

9-12. regionsがsocal且つwest_coastの都市を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/simple_query-getSimpleQuery12
[]

10. 複合クエリ

10-1. stateがCA且つnameがSan Diegoの都市を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/composite_query-getCompositeQuery01
[]

10-2. stateがCA且つ人口数が100万未満の都市を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/composite_query-getCompositeQuery02
[
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"norcal"
		],
		"name": "San Francisco",
		"state": "CA",
		"population": 860000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		}
	}
]

10-3. stateがCA以上かつstateがIN以下の都市を返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/composite_query-getCompositeQuery03
[
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"socal"
		],
		"name": "Los Angeles",
		"state": "CA",
		"population": 3900000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 645000000
		}
	},
	{
		"country": "USA",
		"capital": false,
		"regions": [
			"west_coast",
			"norcal"
		],
		"name": "San Francisco",
		"state": "CA",
		"population": 860000,
		"createdAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		},
		"updatedAt": {
			"_seconds": 1675484699,
			"_nanoseconds": 472000000
		}
	}
]

※異なるフィールドの範囲フィルターはNG

citiesRef.where('state', '>=', 'CA').where('population', '>', 1000000); // NG

11. コレクション グループ クエリ

11-1. すべての都市のすべてのmuseumのランドマークを返す

curl -H "X-CollectionId:cities" http://127.0.0.1:5001/isub-sample-003/asia-northeast1/group_query-getCollectionGroupQuery01
[
	{
		"name": "Beijing Ancient Observatory",
		"type": "museum",
		"createdAt": {
			"_seconds": 1675484747,
			"_nanoseconds": 242000000
		},
		"updatedAt": {
			"_seconds": 1675484747,
			"_nanoseconds": 242000000
		}
	},
	{
		"name": "National Air and Space Museum",
		"type": "museum",
		"createdAt": {
			"_seconds": 1675484747,
			"_nanoseconds": 207000000
		},
		"updatedAt": {
			"_seconds": 1675484747,
			"_nanoseconds": 207000000
		}
	},
	{
		"name": "The Getty",
		"type": "museum",
		"createdAt": {
			"_seconds": 1675484747,
			"_nanoseconds": 192000000
		},
		"updatedAt": {
			"_seconds": 1675484747,
			"_nanoseconds": 192000000
		}
	},
	{
		"name": "Legion of Honor",
		"type": "museum",
		"createdAt": {
			"_seconds": 1675484747,
			"_nanoseconds": 167000000
		},
		"updatedAt": {
			"_seconds": 1675484747,
			"_nanoseconds": 167000000
		}
	},
	{
		"name": "National Museum of Nature and Science",
		"type": "museum",
		"createdAt": {
			"_seconds": 1675484747,
			"_nanoseconds": 219000000
		},
		"updatedAt": {
			"_seconds": 1675484747,
			"_nanoseconds": 219000000
		}
	}
]

12. 備考

13. 参考

投稿者プロフィール

Sondon
開発好きなシステムエンジニアです。
卓球にハマってます。

関連記事

  1. Firebase

    【Firebase】Cloud Functionsを使ってみる

  2. flutter

    【Flutter】Firebase Dynamic Linksを使う

  3. Firebase

    【Firebase】CloudFirestoreトリガーを使ってみる

  4. flutter

    【Flutter】Cloud Firestoreと連携

  5. Firebase

    【Firebase】Cloud Firestoreを使ってみる

  6. Firebase

    【Firebase】Firestore Security Rulesを…

最近の記事

  1. raspberrypi

制作実績一覧

  1. Checkeys