ใน Firebase มีวิธีรับจำนวนลูกของโหนดโดยไม่ต้องโหลดข้อมูลโหนดทั้งหมดหรือไม่


132

คุณสามารถรับจำนวนเด็กได้ทาง

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

แต่ฉันเชื่อว่าสิ่งนี้ดึงโครงสร้างย่อยทั้งหมดของโหนดนั้นจากเซิร์ฟเวอร์ สำหรับรายการขนาดใหญ่ดูเหมือนว่า RAM และเวลาแฝงจะเข้มข้น มีวิธีการนับ (และ / หรือรายชื่อเด็ก) โดยไม่ดึงข้อมูลทั้งหมดหรือไม่?


ขอบคุณมากที่ฉันลองและมันได้ผลสำหรับฉัน
Ahmed Mahmoud

รหัสของคุณไม่สามารถจัดการกับชุดข้อมูลขนาดใหญ่ได้ ฉันได้รับข้อผิดพลาดจาก Java heap space ฉันยังคงรอคุณสมบัติบางอย่าง
Panupong Kongarn

คำตอบ:


98

ข้อมูลโค้ดที่คุณให้จะโหลดชุดข้อมูลทั้งหมดจากนั้นนับเป็นฝั่งไคลเอ็นต์ซึ่งอาจช้ามากสำหรับข้อมูลจำนวนมาก

ปัจจุบัน Firebase ไม่มีวิธีนับลูกโดยไม่ต้องโหลดข้อมูล แต่เรามีแผนที่จะเพิ่มเข้าไป

สำหรับตอนนี้วิธีแก้ไขอย่างหนึ่งคือการรักษาจำนวนเด็กไว้และอัปเดตทุกครั้งที่คุณเพิ่มลูกใหม่ คุณสามารถใช้ธุรกรรมเพื่อนับรายการเช่นในรหัสติดตาม upvodes นี้:

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;
});

สำหรับข้อมูลเพิ่มเติมโปรดดูhttps://www.firebase.com/docs/transactions.html

อัปเดต: Firebase เพิ่งเปิดตัว Cloud Functions ด้วย Cloud Functions คุณไม่จำเป็นต้องสร้างเซิร์ฟเวอร์ของคุณเอง คุณสามารถเขียนฟังก์ชัน JavaScript และอัปโหลดไปยัง Firebase ได้ Firebase จะรับผิดชอบฟังก์ชันทริกเกอร์ทุกครั้งที่มีเหตุการณ์เกิดขึ้น

หากคุณต้องการนับคะแนนโหวตคุณควรสร้างโครงสร้างที่คล้ายกับโครงสร้างนี้:

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

จากนั้นเขียนฟังก์ชันจาวาสคริปต์เพื่อเพิ่มupvotes_countเมื่อมีการเขียนใหม่ไปยังupvotesโหนด

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());
});

คุณสามารถอ่านเอกสารที่จะรู้วิธีการที่จะเริ่มต้นทำงานกับฟังก์ชั่นคลาวด์

นอกจากนี้ตัวอย่างอื่นของการนับโพสต์อยู่ที่นี่: https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js

อัปเดตมกราคม 2018

เอกสาร Firebaseมีการเปลี่ยนแปลงดังนั้นแทนที่จะeventตอนนี้เรามีและchangecontext

ตัวอย่างที่ระบุแสดงข้อผิดพลาดในการบ่นที่event.dataไม่ได้กำหนด รูปแบบนี้ดูเหมือนจะทำงานได้ดีขึ้น:

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
คุณเคยเพิ่มการสนับสนุนสำหรับสิ่งนี้หรือไม่?
Jim Cooper

20
ตัวนับฝั่งไคลเอ็นต์ในธุรกรรมปลอดภัยหรือไม่? ดูเหมือนว่าอาจถูกแฮ็กได้อย่างง่ายดายเพื่อเพิ่มจำนวนปลอม ซึ่งอาจไม่ดีสำหรับระบบการลงคะแนน
Soviut

16
++ มันจะดีมากที่จะได้รับการนับโดยไม่ต้องเสียค่าใช้จ่ายในการโอน
Jared Forsyth

27
นี้เคยมีการเพิ่มหรือไม่?
Eliezer

25
มีข่าวเกี่ยวกับแผนงานคุณลักษณะนี้หรือไม่? ขอบคุณ
Pandaiolo

38

นี่เป็นเกมที่ล่าช้าเล็กน้อยเนื่องจากคนอื่น ๆ หลายคนได้ตอบอย่างดีไปแล้ว แต่ฉันจะแบ่งปันวิธีที่ฉันจะนำไปใช้

สิ่งนี้ขึ้นอยู่กับข้อเท็จจริงที่ว่าFirebase REST APIเสนอshallow=trueพารามิเตอร์

สมมติว่าคุณมีpostวัตถุและแต่ละชิ้นสามารถมีจำนวนcomments:

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

เห็นได้ชัดว่าคุณไม่ต้องการดึงความคิดเห็นทั้งหมดเพียงแค่จำนวนความคิดเห็น

สมมติว่าคุณมีสำคัญสำหรับการโพสต์คุณสามารถส่งคำขอไปยังGET https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true

สิ่งนี้จะส่งคืนออบเจ็กต์ของคู่คีย์ - ค่าโดยที่แต่ละคีย์เป็นคีย์ของความคิดเห็นและค่าของมันคือtrue:

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

ขนาดของการตอบกลับนี้เล็กกว่าการร้องขอข้อมูลที่เทียบเท่ากันมากและตอนนี้คุณสามารถคำนวณจำนวนคีย์ในการตอบกลับเพื่อค้นหาค่าของคุณได้ (เช่น commentCount = Object.keys(result).length)

วิธีนี้อาจไม่สามารถแก้ปัญหาของคุณได้อย่างสมบูรณ์เนื่องจากคุณยังคงคำนวณจำนวนคีย์ที่ส่งคืนและคุณไม่สามารถสมัครรับค่าได้เนื่องจากมีการเปลี่ยนแปลง แต่จะช่วยลดขนาดของข้อมูลที่ส่งคืนได้อย่างมากโดยไม่ต้องมีการเปลี่ยนแปลงใด ๆ กับ สคี


อาจทำให้คำตอบนี้เป็นที่ยอมรับเนื่องจากตื้น = จริงเป็นการเพิ่มใหม่ตั้งแต่คำตอบก่อนหน้านี้ ยังไม่มีเวลาดูด้วยตัวเองเลยจะรอสักสองสามวันว่าใครจะคิดยังไง ...
Josh

1
ตื้นน่าจะเป็นตัวเลือกที่ดีที่สุดสำหรับตอนนี้ แต่จะไม่ส่งคืนพร้อมกับการบีบอัดและอาจค่อนข้างช้าและเป็นประสบการณ์สำหรับชุดข้อมูลขนาดใหญ่
Mย่อดา

หากคีย์ความคิดเห็นไม่มีค่าบูลีน แต่มีลูกแทนคีย์จะยังคงส่งคืนคู่คีย์ - ค่าของคีย์หรือไม่
alltej

1
คุณอาจต้องการใช้ REST API อย่างระมัดระวัง: startupsventurecapital.com/…
Remi Sture

3
เพียงเพื่อชี้ให้เห็นว่าคุณต้องต่อ.jsonท้าย URL เช่นhttps://yourapp.firebaseio.com/posts/comments.json?shallow=true
Osama Xäwãñz

22

บันทึกการนับเมื่อคุณไป - และใช้การตรวจสอบความถูกต้องเพื่อบังคับใช้ ฉันแฮ็คสิ่งนี้เข้าด้วยกัน - เพื่อเก็บคะแนนโหวตที่ไม่ซ้ำกันและจำนวนที่จะมา! แต่คราวนี้ฉันได้ทดสอบคำแนะนำของฉันแล้ว! (แม้ว่าจะมีข้อผิดพลาดในการตัด / วาง!)

'เคล็ดลับ' ในที่นี้คือการใช้ลำดับความสำคัญของโหนดเพื่อนับคะแนน ...

ข้อมูลคือ:

โหวต / $ issueBeingVotedOn / user / $ uniqueIdOfVoter = thisVotesCount ลำดับความสำคัญ = thisVotesCount โหวต / $ issueBeingVotedOn / count = 'user /' + $ idOfLastVoter ลำดับความสำคัญ = CountofLastVote

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

ผู้ใช้สามารถลงคะแนนได้เพียงครั้งเดียวเท่านั้น && จำนวนต้องสูงกว่าค่าข้อมูลจำนวน && ปัจจุบันหนึ่งค่าต้องเท่ากับลำดับความสำคัญ

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

การนับ (ผู้มีสิทธิเลือกตั้งคนสุดท้ายจริงๆ) - การลงคะแนนจะต้องมีอยู่และการนับจำนวนใหม่เท่ากับจำนวนใหม่ && จำนวนใหม่ (ลำดับความสำคัญ) จะเพิ่มขึ้นได้เพียงหนึ่ง

  }
}

สคริปต์ทดสอบเพื่อเพิ่มการโหวต 10 ครั้งโดยผู้ใช้ที่แตกต่างกัน (สำหรับตัวอย่างนี้การปลอมแปลงรหัสควรให้ผู้ใช้ auth.uid ในการผลิต) นับถอยหลังด้วย (i--) 10 เพื่อดูว่าการตรวจสอบล้มเหลว

<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>

'ความเสี่ยง' ในที่นี้คือการลงคะแนน แต่การนับไม่ได้รับการอัปเดต (haking หรือ script ล้มเหลว) นี่คือเหตุผลที่การโหวตมี 'ลำดับความสำคัญ' ที่ไม่ซ้ำกัน - สคริปต์ควรเริ่มต้นด้วยการตรวจสอบให้แน่ใจว่าไม่มีการโหวตที่มีลำดับความสำคัญสูงกว่าจำนวนปัจจุบันหากมีควรทำธุรกรรมนั้นให้เสร็จสิ้นก่อนที่จะดำเนินการเอง - ให้ลูกค้าของคุณทำความสะอาด ขึ้นสำหรับคุณ :)

การนับจะต้องเริ่มต้นด้วยลำดับความสำคัญก่อนที่คุณจะเริ่ม - forge ไม่อนุญาตให้คุณทำเช่นนี้ดังนั้นจึงจำเป็นต้องมีสคริปต์ต้นขั้ว (ก่อนที่การตรวจสอบจะทำงาน!)


นี่มันเจ๋งมาก!!! จะเกิดอะไรขึ้นกับความขัดแย้ง? คือคนสองคนลงคะแนนพร้อมกัน? ตามหลักการแล้วคุณต้องการแก้ไขโดยอัตโนมัติแทนที่จะทิ้งเพียงหนึ่งในการโหวตของพวกเขา ... อาจเป็นการลงคะแนนในธุรกรรมหรือไม่?
Josh

สวัสดี Josh ตามเหตุผลแล้วการโหวตของแท้จะล้มเหลวก็ต่อเมื่อมีการโหวตก่อนหน้านี้ แต่จำนวนทั้งหมดยังไม่อัปเดต (ยัง) วรรคที่ 2 ถึงสุดท้ายของฉันครอบคลุมสิ่งนั้น - ฉันจะอัปเดตทั้งหมดสำหรับผู้มีสิทธิเลือกตั้งก่อนหน้านี้ (ทุกครั้ง) - ถ้าไม่จำเป็นแล้วจะเป็นอย่างไร จากนั้นการโหวตนี้จะอัปเดต ตราบเท่าที่การโหวตทำงานได้ดี หากการอัปเดต "ทั้งหมด" ของคุณล้มเหลวผู้มีสิทธิเลือกตั้งคนถัดไปจะแก้ไขอีกครั้ง - แล้วยังไงต่อ
pperrin

ฉันอยากจะบอกว่าโหนด 'นับ' ควรเป็นโหนด 'การโหวตครั้งสุดท้าย' - ดังนั้นผู้มีสิทธิเลือกตั้ง / ไคลเอ็นต์แต่ละคนจะอัปเดต / แก้ไข / ซ่อมแซมโหนด / ค่านั้นจากนั้นเพิ่มการโหวตของตัวเอง - (ให้ผู้มีสิทธิเลือกตั้งคนถัดไปอัปเดต รวมเพื่อรวมการโหวต "นี้") - ถ้าคุณได้รับของฉัน ...
pperrin

4

เขียนฟังก์ชันระบบคลาวด์และอัปเดตจำนวนโหนด

// 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());
    }); 

อ้างอิง: https://firebase.google.com/docs/functions/database-events

root-- | | - ผู้ใช้ (โหนดนี้มีรายชื่อผู้ใช้ทั้งหมด) |
| -count | -userscount: (โหนดนี้เพิ่มแบบไดนามิกโดยฟังก์ชันคลาวด์พร้อมจำนวนผู้ใช้)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.