Firebase Summit で発表されたすべての情報をご覧ください。Firebase を使用してアプリ開発を加速し、自信を持ってアプリを実行する方法を紹介しています。詳細

データの取得

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

このドキュメントでは、データベース データの取得の基本、データの順序付け、およびデータに対して簡単なクエリを実行する方法について説明します。 Admin SDK でのデータ取得は、プログラミング言語によって実装が若干異なります。

  1. 非同期リスナー: Firebase Realtime Database に格納されたデータは、非同期リスナーをデータベース参照にアタッチすることによって取得されます。リスナーは、データの初期状態に対して 1 回トリガーされ、データが変更されるたびに再度トリガーされます。イベント リスナは、いくつかの異なるタイプのイベントを受け取る場合があります。このデータ取得モードは、Java、Node.js、および Python Admin SDK でサポートされています。
  2. 読み取りのブロック: Firebase Realtime Database に保存されているデータは、データベース参照でブロック メソッドを呼び出すことによって取得されます。これにより、参照に保存されているデータが返されます。各メソッド呼び出しは 1 回限りの操作です。つまり、SDK は後続のデータ更新をリッスンするコールバックを登録しません。このデータ取得モデルは、Python および Go Admin SDK でサポートされています。

入門

前回の記事のブログの例をもう一度見て、Firebase データベースからデータを読み取る方法を理解しましょう。サンプル アプリのブログ投稿は、データベース URL https://docs-examples.firebaseio.com/server/ Saving-data/fireblog/posts.jsonに保存されていることを思い出してください。投稿データを読み取るには、次の操作を実行できます。

ジャワ
public static class Post {

  public String author;
  public String title;

  public Post(String author, String title) {
    // ...
  }

}

// Get a reference to our posts
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("server/saving-data/fireblog/posts");

// Attach a listener to read the data at our posts reference
ref.addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    Post post = dataSnapshot.getValue(Post.class);
    System.out.println(post);
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    System.out.println("The read failed: " + databaseError.getCode());
  }
});
Node.js
// Get a database reference to our posts
const db = getDatabase();
const ref = db.ref('server/saving-data/fireblog/posts');

// Attach an asynchronous callback to read the data at our posts reference
ref.on('value', (snapshot) => {
  console.log(snapshot.val());
}, (errorObject) => {
  console.log('The read failed: ' + errorObject.name);
}); 
パイソン
# Import database module.
from firebase_admin import db

# Get a database reference to our posts
ref = db.reference('server/saving-data/fireblog/posts')

# Read the data at the posts reference (this is a blocking operation)
print(ref.get())
行け

// Post is a json-serializable type.
type Post struct {
	Author string `json:"author,omitempty"`
	Title  string `json:"title,omitempty"`
}

// Create a database client from App.
client, err := app.Database(ctx)
if err != nil {
	log.Fatalln("Error initializing database client:", err)
}

// Get a database reference to our posts
ref := client.NewRef("server/saving-data/fireblog/posts")

// Read the data at the posts reference (this is a blocking operation)
var post Post
if err := ref.Get(ctx, &post); err != nil {
	log.Fatalln("Error reading value:", err)
}

上記のコードを実行すると、コンソールに記録されたすべての投稿を含むオブジェクトが表示されます。 Node.js と Java の場合、新しいデータがデータベース参照に追加されるたびにリスナー関数が呼び出され、これを実現するために追加のコードを記述する必要はありません。

Java と Node.js では、コールバック関数はデータのスナップショットであるDataSnapshotを受け取ります。スナップショットは、ある時点における特定のデータベース参照でのデータの画像です。スナップショットでval() / getValue()を呼び出すと、データの言語固有のオブジェクト表現が返されます。参照の場所にデータが存在しない場合、スナップショットの値はnullです。 Python のget()メソッドは、データの Python 表現を直接返します。 Go のGet()関数は、データを特定のデータ構造に非整列化します。

上記の例ではvalueイベント タイプを使用していることに注意してください。これは、1 つのデータのみが変更された場合でも、Firebase データベース参照の内容全体を読み取ります。 valueは、データベースからデータを読み取るために使用できる、以下にリストされている 5 つの異なるイベント タイプの 1 つです。

Java および Node.js でのイベント タイプの読み取り

価値

valueイベントは、特定のデータベース パスにある内容の静的スナップショットを読み取るために使用されます。これらは、読み取りイベントの時点で存在していたものです。初期データで 1 回トリガーされ、データが変更されるたびに再度トリガーされます。イベント コールバックには、子データを含む、その場所にあるすべてのデータを含むスナップショットが渡されます。上記のコード例では、 valueはアプリ内のすべてのブログ投稿を返しました。新しいブログ投稿が追加されるたびに、コールバック関数はすべての投稿を返します。

子供が追加されました

child_addedイベントは通常、データベースからアイテムのリストを取得するときに使用されます。場所のコンテンツ全体を返すvalueとは異なり、 child_addedは既存の子ごとに 1 回トリガーされ、指定されたパスに新しい子が追加されるたびに再度トリガーされます。イベント コールバックには、新しい子のデータを含むスナップショットが渡されます。順序付けのために、前の子のキーを含む 2 番目の引数も渡されます。

ブログ アプリに追加された新しい投稿ごとにデータのみを取得する場合は、 child_addedを使用できます。

ジャワ
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    Post newPost = dataSnapshot.getValue(Post.class);
    System.out.println("Author: " + newPost.author);
    System.out.println("Title: " + newPost.title);
    System.out.println("Previous Post ID: " + prevChildKey);
  }

  @Override
  public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {}

  @Override
  public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
// Retrieve new posts as they are added to our database
ref.on('child_added', (snapshot, prevChildKey) => {
  const newPost = snapshot.val();
  console.log('Author: ' + newPost.author);
  console.log('Title: ' + newPost.title);
  console.log('Previous Post ID: ' + prevChildKey);
});

この例では、スナップショットには個別のブログ投稿を含むオブジェクトが含まれます。 SDK は値を取得して投稿をオブジェクトに変換するため、 authortitleそれぞれ呼び出すことで、投稿の author プロパティと title プロパティにアクセスできます。 2 番目のprevChildKey引数から前の投稿 ID にアクセスすることもできます。

子が変更されました

child_changedイベントは、子ノードが変更されるたびにトリガーされます。これには、子ノードの子孫への変更が含まれます。これは通常、 child_addedおよびchild_removedと組み合わせて使用​​され、アイテムのリストへの変更に対応します。イベント コールバックに渡されるスナップショットには、子の更新されたデータが含まれています。

child_changedを使用して、ブログ投稿が編集されたときに更新されたデータを読み取ることができます。

ジャワ
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {
    Post changedPost = dataSnapshot.getValue(Post.class);
    System.out.println("The updated post title is: " + changedPost.title);
  }

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {}

  @Override
  public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
// Get the data on a post that has changed
ref.on('child_changed', (snapshot) => {
  const changedPost = snapshot.val();
  console.log('The updated post title is ' + changedPost.title);
});

子供が削除されました

child_removedイベントは、直接の子が削除されたときにトリガーされます。これは通常、 child_addedおよびchild_changedと組み合わせて使用​​されます。イベント コールバックに渡されるスナップショットには、削除された子のデータが含まれています。

ブログの例では、 child_removedを使用して、削除された投稿に関する通知をコンソールに記録できます。

ジャワ
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {
    Post removedPost = dataSnapshot.getValue(Post.class);
    System.out.println("The blog post titled " + removedPost.title + " has been deleted");
  }

  @Override
  public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
// Get a reference to our posts
const ref = db.ref('server/saving-data/fireblog/posts');

// Get the data on a post that has been removed
ref.on('child_removed', (snapshot) => {
  const deletedPost = snapshot.val();
  console.log('The blog post titled \'' + deletedPost.title + '\' has been deleted');
});

子供が引っ越しました

child_movedイベントは、順序付けられたデータを操作するときに使用されます。これについては、次のセクションで説明します。

イベント保証

Firebase データベースは、イベントに関していくつかの重要な保証を行います。

データベース イベントの保証
ローカルの状態が変化すると、常にイベントがトリガーされます。
ネットワーク接続が一時的に失われた場合など、ローカル操作やタイミングによって一時的な違いが生じた場合でも、イベントは常に最終的にデータの正しい状態を反映します。
単一のクライアントからの書き込みは常にサーバーに書き込まれ、他のユーザーに順番にブロードキャストされます。
値イベントは常に最後にトリガーされ、そのスナップショットが作成される前に発生した他のイベントからの更新が含まれていることが保証されます。

値イベントは常に最後にトリガーされるため、次の例は常に機能します。

ジャワ
final AtomicInteger count = new AtomicInteger();

ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    // New child added, increment count
    int newCount = count.incrementAndGet();
    System.out.println("Added " + dataSnapshot.getKey() + ", count is " + newCount);
  }

  // ...
});

// The number of children will always be equal to 'count' since the value of
// the dataSnapshot here will include every child_added event triggered before this point.
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    long numChildren = dataSnapshot.getChildrenCount();
    System.out.println(count.get() + " == " + numChildren);
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
let count = 0;

ref.on('child_added', (snap) => {
  count++;
  console.log('added:', snap.key);
});

// length will always equal count, since snap.val() will include every child_added event
// triggered before this point
ref.once('value', (snap) => {
  console.log('initial data loaded!', snap.numChildren() === count);
});

コールバックの切り離し

コールバックは、次のようにイベント タイプと削除するコールバック関数を指定することで削除されます。

ジャワ
// Create and attach listener
ValueEventListener listener = new ValueEventListener() {
    // ...
};
ref.addValueEventListener(listener);

// Remove listener
ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);

スコープ コンテキストをon()に渡した場合は、コールバックをデタッチするときに渡す必要があります。

ジャワ
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);

特定の場所ですべてのコールバックを削除する場合は、次のようにします。

ジャワ
// No Java equivalent, listeners must be removed individually.
Node.js
// Remove all value callbacks
ref.off('value');

// Remove all callbacks of any type
ref.off();

データを 1 回読み取る

場合によっては、コールバックを一度呼び出してすぐに削除すると便利な場合があります。これを簡単にするヘルパー関数を作成しました。

ジャワ
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    // ...
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    // ...
  }
});
Node.js
ref.once('value', (data) => {
  // do some stuff once
});
パイソン
# Import database module.
from firebase_admin import db

# Get a database reference to our posts
ref = db.reference('server/saving-data/fireblog/posts')

# Read the data at the posts reference (this is a blocking operation)
print(ref.get())
行け
// Create a database client from App.
client, err := app.Database(ctx)
if err != nil {
	log.Fatalln("Error initializing database client:", err)
}

// Get a database reference to our posts
ref := client.NewRef("server/saving-data/fireblog/posts")

// Read the data at the posts reference (this is a blocking operation)
var post Post
if err := ref.Get(ctx, &post); err != nil {
	log.Fatalln("Error reading value:", err)
}

データのクエリ

Firebase データベース クエリを使用すると、さまざまな要因に基づいてデータを選択的に取得できます。データベースでクエリを作成するには、順序付け関数orderByChild()orderByKey() 、またはorderByValue()のいずれかを使用して、データの順序付け方法を指定することから始めます。次に、これらを他の 5 つのメソッドと組み合わせて、複雑なクエリを実行することができます: limitToFirst()limitToLast()startAt()endAt() 、およびequalTo()

Firebase の私たち全員が恐竜はとてもクールだと考えているので、恐竜のファクトのサンプル データベースのスニペットを使用して、Firebase データベースでデータをクエリする方法を示します。

{
  "lambeosaurus": {
    "height" : 2.1,
    "length" : 12.5,
    "weight": 5000
  },
  "stegosaurus": {
    "height" : 4,
    "length" : 9,
    "weight" : 2500
  }
}

子キーキー、またはの 3 つの方法でデータを並べ替えることができます。基本的なデータベース クエリは、以下で説明する順序付け関数の 1 つから始まります。

指定された子キーによる順序付け

そのキーをorderByChild()に渡すことで、共通の子キーでノードを並べ替えることができます。たとえば、高さ順に並べられたすべての恐竜を読み取るには、次のようにします。

ジャワ
public static class Dinosaur {

  public int height;
  public int weight;

  public Dinosaur(int height, int weight) {
    // ...
  }

}

final DatabaseReference dinosaursRef = database.getReference("dinosaurs");
dinosaursRef.orderByChild("height").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    Dinosaur dinosaur = dataSnapshot.getValue(Dinosaur.class);
    System.out.println(dataSnapshot.getKey() + " was " + dinosaur.height + " meters tall.");
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');

ref.orderByChild('height').on('child_added', (snapshot) => {
  console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall');
});
パイソン
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').get()
for key, val in snapshot.items():
    print('{0} was {1} meters tall'.format(key, val))
行け

// Dinosaur is a json-serializable type.
type Dinosaur struct {
	Height int `json:"height"`
	Width  int `json:"width"`
}

ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	var d Dinosaur
	if err := r.Unmarshal(&d); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	fmt.Printf("%s was %d meteres tall", r.Key(), d.Height)
}

クエリ対象の子キーを持たないノードは、 nullの値でソートされます。つまり、順序で最初に来ることを意味します。データの順序付け方法の詳細については、 「データの順序付け方法」セクションを参照してください。

クエリは、1 レベル下の子だけでなく、深くネストされた子によっても順序付けできます。これは、次のように深くネストされたデータがある場合に役立ちます。

{
  "lambeosaurus": {
    "dimensions": {
      "height" : 2.1,
      "length" : 12.5,
      "weight": 5000
    }
  },
  "stegosaurus": {
    "dimensions": {
      "height" : 4,
      "length" : 9,
      "weight" : 2500
    }
  }
}

高さを照会するには、単一のキーではなく、オブジェクトへのフル パスを使用できます。

ジャワ
dinosaursRef.orderByChild("dimensions/height").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    // ...
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('dimensions/height').on('child_added', (snapshot) => {
  console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall');
});
パイソン
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('dimensions/height').get()
for key, val in snapshot.items():
    print('{0} was {1} meters tall'.format(key, val))
行け
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("dimensions/height").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	var d Dinosaur
	if err := r.Unmarshal(&d); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	fmt.Printf("%s was %d meteres tall", r.Key(), d.Height)
}

クエリは、一度に 1 つのキーでのみ並べ替えることができます。同じクエリでorderByChild()を複数回呼び出すと、エラーがスローされます。

キーによる注文

orderByKey()メソッドを使用して、キーでノードを並べ替えることもできます。次の例では、すべての恐竜をアルファベット順に読み取ります。

ジャワ
dinosaursRef.orderByKey().addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
var ref = db.ref('dinosaurs');
ref.orderByKey().on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
パイソン
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().get()
print(snapshot)
行け
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByKey().GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
snapshot := make([]Dinosaur, len(results))
for i, r := range results {
	var d Dinosaur
	if err := r.Unmarshal(&d); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	snapshot[i] = d
}
fmt.Println(snapshot)

値による並べ替え

orderByValue()メソッドを使用して、子キーの値でノードを並べ替えることができます。恐竜が恐竜のスポーツ大会を開催していて、そのスコアを次の形式で追跡しているとします。

{
  "scores": {
    "bruhathkayosaurus" : 55,
    "lambeosaurus" : 21,
    "linhenykus" : 80,
    "pterodactyl" : 93,
    "stegosaurus" : 5,
    "triceratops" : 22
  }
}

恐竜をスコアで並べ替えるには、次のクエリを作成できます。

ジャワ
DatabaseReference scoresRef = database.getReference("scores");
scoresRef.orderByValue().addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue());
  }

  // ...
});
Node.js
const scoresRef = db.ref('scores');
scoresRef.orderByValue().on('value', (snapshot) => {
  snapshot.forEach((data) => {
    console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val());
  });
});
パイソン
ref = db.reference('scores')
snapshot = ref.order_by_value().get()
for key, val in snapshot.items():
    print('The {0} dinosaur\'s score is {1}'.format(key, val))
行け
ref := client.NewRef("scores")

results, err := ref.OrderByValue().GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	var score int
	if err := r.Unmarshal(&score); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score)
}

orderByValue()を使用したときにnull 、ブール値、文字列、およびオブジェクトの値がどのようにソートされるかについては、 「データの順序付け」セクションを参照してください。

複雑なクエリ

データがどのように並べられているかが明確になったので、以下で説明するlimitメソッドまたはrangeメソッドを使用して、より複雑なクエリを作成できます。

クエリを制限する

limitToFirst()およびlimitToLast()クエリは、特定のコールバックで同期される子の最大数を設定するために使用されます。制限を 100 に設定すると、最初は最大 100 のchild_addedイベントしか受け取りません。データベースに保存されているメッセージが 100 件未満の場合、メッセージごとにchild_addedイベントが発生します。ただし、100 を超えるメッセージがある場合は、100 のメッセージに対してのみchild_addedイベントを受け取ります。これらは、 limitToFirst() を使用している場合は順序付けされた最初の 100 個のメッセージ、またはlimitToFirst() limitToLast()使用している場合は順序付けされた最後の 100 個のメッセージです。アイテムが変更されると、クエリに入るアイテムのchild_removed child_addedを受け取り、合計数が 100 のままになるようにします。

恐竜ファクト データベースとorderByChild()を使用すると、2 つの最も重い恐竜を見つけることができます。

ジャワ
dinosaursRef.orderByChild("weight").limitToLast(2).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('weight').limitToLast(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
パイソン
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('weight').limit_to_last(2).get()
for key in snapshot:
    print(key)
行け
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("weight").LimitToLast(2).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

データベースに保存されている恐竜が 2 つ未満でない限り、 child_addedコールバックは正確に 2 回トリガーされます。また、データベースに追加される新しい、より重い恐竜ごとに起動されます。 Python では、クエリは 2 つの最も重い恐竜を含むOrderedDictを直接返します。

同様に、 limitToFirst()を使用して、2 つの最も短い恐竜を見つけることができます。

ジャワ
dinosaursRef.orderByChild("weight").limitToFirst(2).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').limitToFirst(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
パイソン
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').limit_to_first(2).get()
for key in snapshot:
    print(key)
行け
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").LimitToFirst(2).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

データベースに保存されている恐竜が 2 つ未満でない限り、 child_addedコールバックは正確に 2 回トリガーされます。最初の 2 つの恐竜のうちの 1 つがデータベースから削除された場合も、新しい恐竜が 2 番目に短いため、再び発生します。 Python では、クエリは最短の恐竜を含むOrderedDictを直接返します。

orderByValue()を使用して制限クエリを実行することもできます。上位 3 位の最高得点の恐竜スポーツ恐竜のリーダーボードを作成する場合は、次のようにします。

ジャワ
scoresRef.orderByValue().limitToFirst(3).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue());
  }

  // ...
});
Node.js
const scoresRef = db.ref('scores');
scoresRef.orderByValue().limitToLast(3).on('value', (snapshot)  =>{
  snapshot.forEach((data) => {
    console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val());
  });
});
パイソン
scores_ref = db.reference('scores')
snapshot = scores_ref.order_by_value().limit_to_last(3).get()
for key, val in snapshot.items():
    print('The {0} dinosaur\'s score is {1}'.format(key, val))
行け
ref := client.NewRef("scores")

results, err := ref.OrderByValue().LimitToLast(3).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	var score int
	if err := r.Unmarshal(&score); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score)
}

範囲クエリ

startAt()endAt() 、およびequalTo()を使用すると、クエリの任意の開始点と終了点を選択できます。たとえば、少なくとも 3 メートルの高さの恐竜をすべて見つけたい場合は、 orderByChild()startAt() ) を組み合わせることができます。

ジャワ
dinosaursRef.orderByChild("height").startAt(3).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').startAt(3).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
パイソン
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').start_at(3).get()
for key in snapshot:
    print(key)
行け
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").StartAt(3).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

endAt()を使用して、辞書順でプテロダクティルより前に名前が来るすべての恐竜を見つけることができます。

ジャワ
dinosaursRef.orderByKey().endAt("pterodactyl").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByKey().endAt('pterodactyl').on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
パイソン
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().end_at('pterodactyl').get()
for key in snapshot:
    print(key)
行け
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByKey().EndAt("pterodactyl").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

startAt()endAt()を組み合わせて、クエリの両端を制限できます。次の例では、名前が文字 "b" で始まるすべての恐竜を検索します。

ジャワ
dinosaursRef.orderByKey().startAt("b").endAt("b\uf8ff").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
var ref = db.ref('dinosaurs');
ref.orderByKey().startAt('b').endAt('b\uf8ff').on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
パイソン
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().start_at('b').end_at(u'b\uf8ff').get()
for key in snapshot:
    print(key)
行け
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByKey().StartAt("b").EndAt("b\uf8ff").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

equalTo()メソッドを使用すると、完全一致に基づいてフィルタリングできます。他の範囲クエリの場合と同様に、一致する子ノードごとに起動します。たとえば、次のクエリを使用して、高さ 25 メートルのすべての恐竜を見つけることができます。

ジャワ
dinosaursRef.orderByChild("height").equalTo(25).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').equalTo(25).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
パイソン
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').equal_to(25).get()
for key in snapshot:
    print(key)
行け
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").EqualTo(25).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

範囲クエリは、データのページ分割が必要な場合にも役立ちます。

すべてを一緒に入れて

これらの手法をすべて組み合わせて、複雑なクエリを作成できます。たとえば、ステゴサウルスよりも短い恐竜の名前を見つけることができます。

ジャワ
dinosaursRef.child("stegosaurus").child("height").addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot stegoHeightSnapshot) {
    Integer favoriteDinoHeight = stegoHeightSnapshot.getValue(Integer.class);
    Query query = dinosaursRef.orderByChild("height").endAt(favoriteDinoHeight).limitToLast(2);
    query.addValueEventListener(new ValueEventListener() {
      @Override
      public void onDataChange(DataSnapshot dataSnapshot) {
        // Data is ordered by increasing height, so we want the first entry
        DataSnapshot firstChild = dataSnapshot.getChildren().iterator().next();
        System.out.println("The dinosaur just shorter than the stegosaurus is: " + firstChild.getKey());
      }

      @Override
      public void onCancelled(DatabaseError databaseError) {
        // ...
      }
    });
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    // ...
  }
});
Node.js
  const ref = db.ref('dinosaurs');
  ref.child('stegosaurus').child('height').on('value', (stegosaurusHeightSnapshot) => {
    const favoriteDinoHeight = stegosaurusHeightSnapshot.val();

    const queryRef = ref.orderByChild('height').endAt(favoriteDinoHeight).limitToLast(2);
    queryRef.on('value', (querySnapshot) => {
      if (querySnapshot.numChildren() === 2) {
        // Data is ordered by increasing height, so we want the first entry
        querySnapshot.forEach((dinoSnapshot) => {
          console.log('The dinosaur just shorter than the stegasaurus is ' + dinoSnapshot.key);

          // Returning true means that we will only loop through the forEach() one time
          return true;
        });
      } else {
        console.log('The stegosaurus is the shortest dino');
      }
    });
});
パイソン
ref = db.reference('dinosaurs')
favotire_dino_height = ref.child('stegosaurus').child('height').get()
query = ref.order_by_child('height').end_at(favotire_dino_height).limit_to_last(2)
snapshot = query.get()
if len(snapshot) == 2:
    # Data is ordered by increasing height, so we want the first entry.
    # Second entry is stegosarus.
    for key in snapshot:
        print('The dinosaur just shorter than the stegosaurus is {0}'.format(key))
        return
else:
    print('The stegosaurus is the shortest dino')
行け
ref := client.NewRef("dinosaurs")

var favDinoHeight int
if err := ref.Child("stegosaurus").Child("height").Get(ctx, &favDinoHeight); err != nil {
	log.Fatalln("Error querying database:", err)
}

query := ref.OrderByChild("height").EndAt(favDinoHeight).LimitToLast(2)
results, err := query.GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
if len(results) == 2 {
	// Data is ordered by increasing height, so we want the first entry.
	// Second entry is stegosarus.
	fmt.Printf("The dinosaur just shorter than the stegosaurus is %s\n", results[0].Key())
} else {
	fmt.Println("The stegosaurus is the shortest dino")
}

データの順序付け方法

このセクションでは、4 つの順序付け関数のそれぞれを使用するときに、データがどのように順序付けられるかについて説明します。

orderByChild

orderByChild()を使用すると、指定された子キーを含むデータは次のように並べられます。

  1. 指定された子キーのnull値を持つ子が最初に来ます。
  2. 指定された子キーの値がfalseの子が次に来ます。複数の子の値がfalseの場合、それらはキーによって辞書順にソートされます。
  3. 指定された子キーの値がtrueの子が次に来ます。複数の子の値がtrueの場合、それらは辞書式にキーでソートされます。
  4. 次に、数値を持つ子が昇順でソートされます。複数の子が指定された子ノードに対して同じ数値を持つ場合、それらはキーでソートされます。
  5. 文字列は数値の後に続き、辞書順で昇順にソートされます。指定された子ノードに対して複数の子が同じ値を持つ場合、それらはキーによって辞書順に並べられます。
  6. オブジェクトは最後に来て、辞書順でキーの昇順にソートされます。

orderByKey

orderByKey()を使用してデータをソートすると、データは次のようにキーの昇順で返されます。キーは文字列のみであることに注意してください。

  1. 32 ビット整数として解析できるキーを持つ子が最初に来て、昇順でソートされます。
  2. 次にキーとして文字列値を持つ子が来て、辞書式に昇順にソートされます。

orderByValue

orderByValue()を使用すると、子はその値によって並べ替えられます。順序付けの基準はorderByChild()と同じですが、指定された子キーの値の代わりにノードの値が使用されます。