Trong Firebase, có cách nào để lấy số lượng con của một nút mà không tải tất cả dữ liệu nút không?


132

Bạn có thể có được số lượng trẻ thông qua

firebase_node.once('value', function(snapshot) { alert('Count: ' + snapshot.numChildren()); });

Nhưng tôi tin rằng điều này tìm nạp toàn bộ cây con của nút đó từ máy chủ. Đối với các danh sách lớn, có vẻ như RAM và độ trễ chuyên sâu. Có cách nào để có được số đếm (và / hoặc danh sách tên con) mà không lấy toàn bộ nội dung không?


cảm ơn rất nhiều, tôi đã thử nó và nó đã làm việc cho tôi
Ahmed Mahmoud

mã của bạn không thể xử lý tập dữ liệu lớn. Tôi có lỗi gây ra bởi không gian heap Java. Tôi vẫn chờ đợi một số tính năng.
Panupong Kongarn

Câu trả lời:


98

Đoạn mã bạn đưa ra thực sự tải toàn bộ tập hợp dữ liệu và sau đó đếm phía máy khách, có thể rất chậm đối với số lượng lớn dữ liệu.

Firebase hiện không có cách đếm trẻ em mà không tải dữ liệu, nhưng chúng tôi có kế hoạch thêm nó.

Hiện tại, một giải pháp sẽ là duy trì số lượng trẻ em và cập nhật nó mỗi khi bạn thêm một đứa trẻ mới. Bạn có thể sử dụng một giao dịch để đếm các mục, như trong mã này theo dõi mã:

var upvotesRef = new Firebase('https://docs-examples.firebaseio.com/android/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction(function (current_value) {
  return (current_value || 0) + 1;
});

Để biết thêm thông tin, hãy xem https://www.firebase.com/docs/transilities.html

CẬP NHẬT: Firebase gần đây đã phát hành Chức năng đám mây. Với Chức năng đám mây, bạn không cần tạo Máy chủ của riêng mình. Bạn chỉ có thể viết các hàm JavaScript và tải nó lên Firebase. Firebase sẽ chịu trách nhiệm kích hoạt các chức năng mỗi khi có sự kiện xảy ra.

Nếu bạn muốn đếm upvote chẳng hạn, bạn nên tạo một cấu trúc tương tự như cấu trúc này:

{
  "posts" : {
    "-JRHTHaIs-jNPLXOQivY" : {
      "upvotes_count":5,
      "upvotes" : {
      "userX" : true,
      "userY" : true,
      "userZ" : true,
      ...
    }
    }
  }
}

Và sau đó viết một hàm javascript để tăng upvotes_countkhi có một ghi mới vào upvotesnút.

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.countlikes = functions.database.ref('/posts/$postid/upvotes').onWrite(event => {
  return event.data.ref.parent.child('upvotes_count').set(event.data.numChildren());
});

Bạn có thể đọc Tài liệu để biết cách bắt đầu với các chức năng đám mây .

Ngoài ra, một ví dụ khác về việc đếm bài đăng ở đây: https://github.com/firebase/fifts-samples/blob/master/child-count/fifts/index.js

Cập nhật tháng 1 năm 2018

Các tài liệu căn cứ hỏa lực đã thay đổi vì vậy thay vì eventbây giờ chúng ta có changecontext.

Ví dụ đã đưa ra một lỗi phàn nàn event.datakhông xác định. Mẫu này dường như hoạt động tốt hơn:

exports.countPrescriptions = functions.database.ref(`/prescriptions`).onWrite((change, context) => {
    const data = change.after.val();
    const count = Object.keys(data).length;
    return change.after.ref.child('_count').set(count);
});

`` `


74
Bạn đã bao giờ thêm hỗ trợ cho điều này?
Jim Cooper

20
Là một bộ đếm phía khách hàng trong một giao dịch mặc dù an toàn? Có vẻ như nó có thể dễ dàng bị hack để tăng số lượng một cách giả tạo. Điều này có thể là xấu cho các hệ thống bỏ phiếu.
Soviut

16
++ sẽ rất tuyệt nếu được tính mà không phải chịu chi phí chuyển nhượng
Jared Forsyth

27
Điều này đã bao giờ được thêm vào?
Eliezer

25
Bất kỳ tin tức về lộ trình tính năng này? Cảm ơn
Pandaiolo

37

Điều này hơi muộn trong trò chơi vì một số người khác đã trả lời rất hay, nhưng tôi sẽ chia sẻ cách tôi có thể thực hiện nó.

Điều này xoay quanh thực tế là API REST của Firebase cung cấp một shallow=truetham số.

Giả sử bạn có một postđối tượng và mỗi đối tượng có thể có một số comments:

{
 "posts": {
  "$postKey": {
   "comments": {
     ...  
   }
  }
 }
}

Bạn rõ ràng không muốn lấy tất cả các bình luận, chỉ là số lượng bình luận.

Giả sử bạn có chìa khóa cho một bài đăng, bạn có thể gửi GETyêu cầu đến https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.

Điều này sẽ trả về một đối tượng của các cặp khóa-giá trị, trong đó mỗi khóa là khóa của một nhận xét và giá trị của nó là true:

{
 "comment1key": true,
 "comment2key": true,
 ...,
 "comment9999key": true
}

Kích thước của phản hồi này nhỏ hơn nhiều so với yêu cầu dữ liệu tương đương và bây giờ bạn có thể tính số lượng khóa trong phản hồi để tìm giá trị của mình (ví dụ: commentCount = Object.keys(result).length).

Điều này có thể không giải quyết được hoàn toàn vấn đề của bạn, vì bạn vẫn đang tính toán số lượng khóa được trả về và bạn không nhất thiết phải đăng ký giá trị khi nó thay đổi, nhưng nó làm giảm đáng kể kích thước của dữ liệu được trả về mà không yêu cầu bất kỳ thay đổi nào đối với lược đồ.


Có thể làm cho câu trả lời được chấp nhận vì nông = true là một bổ sung mới kể từ các câu trả lời trước. Chưa có thời gian để nhìn vào bản thân mình, như vậy sẽ chờ một vài ngày để xem những gì mọi người nghĩ ...
Josh

1
Shallow có lẽ là lựa chọn tốt nhất hiện tại, nhưng nó không được trả về khi nén và có thể trở nên khá chậm và trải nghiệm cho các tập dữ liệu lớn
Moustvda

Nếu các khóa nhận xét không có giá trị boolean mà thay vào đó có con, liệu nó có trả về các cặp khóa-giá trị không?
alltej

1
Bạn có thể muốn cẩn thận khi sử dụng API REST: startupsventurecapital.com/ từ
Remi Sture

3
Chỉ để chỉ ra rằng bạn phải nối .jsonvào cuối URL, ví dụ:https://yourapp.firebaseio.com/posts/comments.json?shallow=true
Osama Xäwãñz

22

Lưu số lượng khi bạn đi - và sử dụng xác nhận để thực thi nó. Tôi đã hack cái này cùng nhau - vì đã giữ một số phiếu và số lượng duy nhất tiếp tục tăng lên!. Nhưng lần này tôi đã thử nghiệm đề nghị của tôi! (mặc dù có lỗi cắt / dán!).

'Bí quyết' ở đây là sử dụng mức độ ưu tiên của nút để làm số phiếu ...

Dữ liệu là:

bỏ phiếu / $ vấn đề

,"vote": {
  ".read" : true
  ,".write" : true
  ,"$issue" : {
    "user" : {
      "$user" : {
        ".validate" : "!data.exists() && 
             newData.val()==data.parent().parent().child('count').getPriority()+1 &&
             newData.val()==newData.GetPriority()" 

người dùng chỉ có thể bình chọn một lần && số phải cao hơn số hiện tại && giá trị dữ liệu phải giống như mức ưu tiên.

      }
    }
    ,"count" : {
      ".validate" : "data.parent().child(newData.val()).val()==newData.getPriority() &&
             newData.getPriority()==data.getPriority()+1 "
    }

đếm (cử tri cuối cùng thực sự) - phiếu bầu phải tồn tại và số lượng mới bằng nhau, && newcount (ưu tiên) chỉ có thể tăng lên một.

  }
}

Kiểm tra tập lệnh để thêm 10 phiếu bầu bởi những người dùng khác nhau (ví dụ: giả mạo id, nên người dùng auth.uid trong sản xuất). Đếm ngược theo (i--) 10 để xem xác nhận thất bại.

<script src='https://cdn.firebase.com/v0/firebase.js'></script>
<script>
  window.fb = new Firebase('https:...vote/iss1/');
  window.fb.child('count').once('value', function (dss) {
    votes = dss.getPriority();
    for (var i=1;i<10;i++) vote(dss,i+votes);
  } );

function vote(dss,count)
{
  var user='user/zz' + count; // replace with auth.id or whatever
  window.fb.child(user).setWithPriority(count,count);
  window.fb.child('count').setWithPriority(user,count);
}
</script>

"Rủi ro" ở đây là một phiếu bầu được bỏ, nhưng số lượng không được cập nhật (thất bại hoặc thất bại về kịch bản). Đây là lý do tại sao các phiếu bầu có một 'mức độ ưu tiên' duy nhất - kịch bản nên thực sự bắt đầu bằng cách đảm bảo rằng không có phiếu bầu nào có mức độ ưu tiên cao hơn số lượng hiện tại, nếu có thì nên hoàn thành giao dịch đó trước khi thực hiện - làm cho khách hàng của bạn sạch sẽ lên cho bạn :)

Số lượng cần phải được khởi tạo với mức độ ưu tiên trước khi bạn bắt đầu - forge không cho phép bạn làm điều này, vì vậy cần có một kịch bản sơ khai (trước khi xác thực được kích hoạt!).


Điều này thật tuyệt!!! Điều gì xảy ra trên các xung đột, mặc dù? Tức là, hai người bỏ phiếu cùng một lúc? Lý tưởng nhất là bạn muốn tự động giải quyết điều đó, thay vì chỉ loại bỏ một trong số phiếu bầu của họ ... có thể thực hiện bỏ phiếu trong một giao dịch?
Josh

Xin chào Josh, về mặt logic, một phiếu bầu chính hãng chỉ có thể thất bại nếu phiếu bầu trước đó đã được bỏ, nhưng tổng số chưa được cập nhật (chưa). Lần thứ 2 đến lần cuối cùng của tôi bao gồm điều đó - Tôi chỉ cần thực hiện cập nhật tổng số cho các cử tri trước đó bỏ phiếu (mọi lúc) - nếu không cần thiết, vậy thì sao? và sau đó phiếu này cập nhật. Miễn là phiếu bầu hoạt động tốt. Nếu cập nhật 'tổng' của bạn không thành công, cử tri tiếp theo sẽ sửa nó, vậy một lần nữa - vậy thì sao?
pperrin

Tôi thực sự rất muốn nói rằng nút 'đếm' phải là nút 'phiếu bầu trước' - vì vậy mỗi cử tri / khách hàng cập nhật / sửa chữa / sửa chữa nút / giá trị đó và sau đó thêm phiếu bầu của riêng mình - (để cho cử tri tiếp theo cập nhật tổng cộng bao gồm 'phiếu này'). - Nếu bạn nhận được sự trôi dạt của tôi ...
pperrin

4

viết một hàm đám mây và cập nhật số nút.

// below function to get the given node count.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.userscount = functions.database.ref('/users/')
    .onWrite(event => {

      console.log('users number : ', event.data.numChildren());


      return event.data.ref.parent.child('count/users').set(event.data.numChildren());
    }); 

Tham khảo: https://firebase.google.com/docs/fifts/database-events

gốc-- | | -users (nút này chứa tất cả danh sách người dùng) |
| -count | -userscount: (nút này được thêm động bởi chức năng đám mây với số lượng người dùng)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.