Firebase Summit에서 발표된 모든 내용을 살펴보고 Firebase로 앱을 빠르게 개발하고 안심하고 앱을 실행하는 방법을 알아보세요. 자세히 알아보기

데이터 검색

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

이 문서에서는 데이터베이스 데이터 검색의 기본 사항, 데이터 정렬 방법 및 데이터에 대한 간단한 쿼리를 수행하는 방법을 다룹니다. Admin SDK의 데이터 검색은 프로그래밍 언어마다 약간 다르게 구현됩니다.

  1. 비동기 리스너: Firebase 실시간 데이터베이스에 저장된 데이터는 데이터베이스 참조에 비동기 리스너를 연결하여 검색됩니다. 리스너는 데이터의 초기 상태에 대해 한 번 트리거되고 데이터가 변경될 때마다 다시 트리거됩니다. 이벤트 리스너는 여러 가지 다른 유형의 이벤트를 수신할 수 있습니다. 이 데이터 검색 모드는 Java, Node.js 및 Python Admin SDK에서 지원됩니다.
  2. 차단 읽기: Firebase 실시간 데이터베이스에 저장된 데이터는 참조에 저장된 데이터를 반환하는 데이터베이스 참조에서 차단 메서드를 호출하여 검색됩니다. 각 메서드 호출은 일회성 작업입니다. 즉, 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 이벤트 유형을 사용했다는 점에 유의하십시오. 이 유형은 데이터가 하나만 변경된 경우에도 Firebase 데이터베이스 참조의 전체 콘텐츠를 읽습니다. value 은 데이터베이스에서 데이터를 읽는 데 사용할 수 있는 아래 나열된 다섯 가지 이벤트 유형 중 하나입니다.

Java 및 Node.js에서 이벤트 유형 읽기

value 이벤트는 주어진 데이터베이스 경로에서 내용의 정적 스냅샷을 읽는 데 사용됩니다. 이는 읽기 이벤트 시점에 존재했기 때문입니다. 초기 데이터로 한 번 트리거되고 데이터가 변경될 때마다 다시 트리거됩니다. 이벤트 콜백에는 하위 데이터를 포함하여 해당 위치의 모든 데이터가 포함된 스냅샷이 전달됩니다. 위의 코드 예제에서 value 는 앱의 모든 블로그 게시물을 반환했습니다. 새 블로그 게시물이 추가될 때마다 콜백 함수는 모든 게시물을 반환합니다.

자녀가 추가됨

child_added 이벤트는 일반적으로 데이터베이스에서 항목 목록을 검색할 때 사용됩니다. 위치의 전체 내용을 반환하는 value 과 달리 child_added 는 각 기존 자식에 대해 한 번 트리거된 다음 지정된 경로에 새 자식이 추가될 때마다 다시 트리거됩니다. 이벤트 콜백에는 새 자식 데이터가 포함된 스냅샷이 전달됩니다. 순서 지정을 위해 이전 자식의 키를 포함하는 두 번째 인수도 전달됩니다.

블로깅 앱에 추가된 각각의 새 게시물에 대한 데이터만 검색하려면 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 각각 호출하여 게시물의 작성자 및 제목 속성에 액세스할 수 있습니다. 또한 두 번째 prevChildKey 인수에서 이전 게시물 ID에 액세스할 수 있습니다.

아동이 변경됨

child_changed 이벤트는 자식 노드가 수정될 때마다 트리거됩니다. 여기에는 자식 노드의 자손에 대한 모든 수정 사항이 포함됩니다. 일반적으로 child_addedchild_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_addedchild_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();

데이터 한 번 읽기

경우에 따라 콜백이 한 번 호출된 후 즉시 제거되는 것이 유용할 수 있습니다. 이 작업을 쉽게 수행할 수 있도록 도우미 함수를 만들었습니다.

자바
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
  }
}

하위 키 기준 , 키 기준 또는 기준의 세 가지 방법으로 데이터를 주문할 수 있습니다. 기본 데이터베이스 쿼리는 이러한 주문 기능 중 하나로 시작하며 각 기능은 아래에 설명되어 있습니다.

지정된 하위 키로 주문

해당 키를 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 값으로 정렬됩니다. 즉, 순서에서 첫 번째가 됩니다. 데이터 정렬 방법에 대한 자세한 내용은 데이터 정렬 방법 섹션 을 참조하십시오.

질의는 한 수준 아래의 자식만이 아니라 깊이 중첩된 자식에 의해 정렬될 수도 있습니다. 이는 다음과 같이 깊이 중첩된 데이터가 있는 경우에 유용합니다.

{
  "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)
}

쿼리는 한 번에 하나의 키로만 주문할 수 있습니다. 동일한 쿼리에서 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 , boolean, string 및 object 값이 정렬되는 방법에 대한 설명 은 데이터 정렬 방법 섹션을 참조하세요.

복잡한 쿼리

이제 데이터 순서가 명확해졌으므로 아래에 설명된 제한 또는 범위 방법을 사용하여 더 복잡한 쿼리를 구성할 수 있습니다.

쿼리 제한

limitToFirst()limitToLast() 쿼리는 지정된 콜백에 대해 동기화할 최대 하위 수를 설정하는 데 사용됩니다. 한도를 100으로 설정하면 처음에는 child_added 이벤트를 최대 100개까지만 수신하게 됩니다. 데이터베이스에 저장된 메시지가 100개 미만인 경우 각 메시지에 대해 child_added 이벤트가 발생합니다. 그러나 100개 이상의 메시지가 있는 경우 해당 메시지 중 100개에 대해서만 child_added 이벤트를 받게 됩니다. limitToFirst() 를 사용하는 경우 처음 100개의 주문된 메시지이거나 limitToFirst() limitToLast() 사용하는 경우 마지막 100개의 주문된 메시지입니다. 항목이 변경되면 쿼리를 입력하는 항목에 대해 child_added 이벤트를 수신하고 쿼리를 떠나는 항목에 대해 child_removed 이벤트를 수신하여 총 개수가 100으로 유지됩니다.

공룡 정보 데이터베이스와 orderByChild() 를 사용하여 가장 무거운 두 공룡을 찾을 수 있습니다.

자바
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())
}

child_added 콜백은 데이터베이스에 저장된 공룡이 두 마리 미만이 아닌 한 정확히 두 번 트리거됩니다. 또한 데이터베이스에 추가되는 모든 새롭고 무거운 공룡에 대해 실행됩니다. Python에서 쿼리는 두 개의 가장 무거운 공룡이 포함된 OrderedDict 를 직접 반환합니다.

마찬가지로, limitToFirst() 를 사용하여 키가 가장 작은 두 공룡을 찾을 수 있습니다.

자바
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())
}

child_added 콜백은 데이터베이스에 저장된 공룡이 두 마리 미만이 아닌 한 정확히 두 번 트리거됩니다. 새로운 공룡이 이제 두 번째로 키가 작기 때문에 처음 두 공룡 중 하나가 데이터베이스에서 제거되면 다시 실행됩니다. 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() 를 사용하여 이름이 사전순으로 Pterodactyl보다 앞에 오는 모든 공룡을 찾을 수 있습니다.

자바
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")
}

데이터 정렬 방법

이 섹션에서는 네 가지 정렬 기능을 각각 사용할 때 데이터가 정렬되는 방식에 대해 설명합니다.

orderByChild

orderByChild() 를 사용할 때 지정된 하위 키를 포함하는 데이터는 다음과 같이 정렬됩니다.

  1. 지정된 자식 키에 대해 null 값이 있는 자식이 먼저 옵니다.
  2. 지정된 하위 키에 대해 값이 false 인 하위가 다음에 옵니다. 여러 자식의 값이 false 인 경우 키를 기준으로 사전순 으로 정렬됩니다.
  3. 지정된 자식 키에 대해 true 값을 가진 자식이 다음에 옵니다. 여러 자식의 값이 true 인 경우 키를 기준으로 사전순으로 정렬됩니다.
  4. 숫자 값이 있는 하위 항목은 오름차순으로 정렬되어 다음에 옵니다. 여러 자식이 지정된 자식 노드에 대해 동일한 숫자 값을 갖는 경우 키별로 정렬됩니다.
  5. 문자열은 숫자 다음에 오며 사전순으로 오름차순으로 정렬됩니다. 여러 자식이 지정된 자식 노드에 대해 동일한 값을 갖는 경우 키를 기준으로 사전순으로 정렬됩니다.
  6. 객체는 마지막에 오름차순으로 키별로 사전순으로 정렬됩니다.

orderByKey

orderByKey() 를 사용하여 데이터를 정렬하면 다음과 같이 데이터가 키별로 오름차순으로 반환됩니다. 키는 문자열만 될 수 있음을 명심하십시오.

  1. 32비트 정수로 구문 분석할 수 있는 키가 있는 자식이 먼저 오름차순으로 정렬됩니다.
  2. 키로 문자열 값이 있는 자식이 다음에 오며 사전순으로 오름차순으로 정렬됩니다.

orderByValue

orderByValue() 를 사용할 때 자식은 값에 따라 정렬됩니다. 순서 지정 기준은 orderByChild() 와 동일하지만 지정된 하위 키 값 대신 노드 값이 사용됩니다.