What is the exact size of a Firestore document?

前端 未结 1 391
灰色年华
灰色年华 2020-12-22 01:46

The docs says that the size of a document is composed of:

  1. document name size
  2. The sum of the string size of each field name
  3. The su
相关标签:
1条回答
  • 2020-12-22 01:59

    TL;DR

    The original question did not involve Geopoints but as more discussion took place that was the ultimate goal. The issue is not that Firestore Documents can't hold 1Mb of data (because they can as clearly shown below) but the actual issue is how the OP is calculating how much data they want to store.

    A Geopoint takes 16 bytes but there is also the rest of the calculation that should be added in. So here's a summary to calculate the size of a document

    docNameSize = 8 //suppose it's called 'geoArray'
    fieldNameSize = 5 // this is an array, so the first element name is 0 = 1 byte, 
                      // element 10 name would be two bytes
                      // up to 5 bytes for the higher numbers
    geoPointSize = 16 * number of geopoints
    addlSize = 32
    

    So suppose there are 1000 geopoints

    8 + (bytes depending on the field name length) + (16 * # of geopoints) + addl Size

    So as you can see, the discussion is not around how much data a document will hold but about how the document size for a geopoint is calculated.

    quick calculation

    var s = ""
    for i in 0..<10000 {
        s += String(i)
    }
    print(s.count)
    

    shows that if you want to store 10000 Geopoints, 38890 bytes goes just to field names alone.

    Discussion

    This answer shows how to calculate the size of a Firestore document as well as the code to demonstrate how a file (an image in this case) of size 1Mb can be uploaded to a Firestore document.

    Note that this is NOT how it should be done in real world use! - images and files should be stored in Storage, not Firestore, so take this as an example case.

    An additional note that storing datasets that max out the capacity of a document may hinder overall performance and negates the ability to query or sort that data server side which puts a lot more strain on the apps resources. If there is concern about cost per number of writes/reads, I suggest looking at the Real Time Data Base as the costs are per amount of data, not reads/writes.

    First we start with a 1Mb jpg called Mountain

    To calculate the actual amount of data being uploaded, we use the following from the Firebase Storage and Calculations

    The size of a document is the sum of:

    • The document name size
    • The sum of the string size of each field name
    • The sum of the size of each field value (we have only one field in
      this example)
    • 32 additional bytes

    In the following code, the document name is 'mountain_image' which is 14, the field name is 'imageData' 9, the size of the field value is calculated (shown below) plus 32 bytes.

    For this example, I've dragged the 1Mb image into my App bundle. Here's the (macOS) code that reads that image, converts it to a NSData type for Firestore and uploads the file.

    func uploadImageToFirestre() {
        let image = NSImage(imageLiteralResourceName: "Mountain.jpeg")
    
        guard let asTiffData = image.tiffRepresentation else { return }
        let data = NSData(data: asTiffData)
    
        let imgRep = NSBitmapImageRep(data: data as Data)
        guard let jpgData = imgRep?.representation(using: NSBitmapImageRep.FileType.jpeg, properties:  [:]) else { return }
    
        let docNameSize = 14
        let fieldNameSize = 9
        let dataSize = jpgData.count
        let addlSize = 32
    
        let totalSize = docNameSize + fieldNameSize + dataSize + addlSize
    
        print("allowed size: \(1048487)")
        print("total size:   \(totalSize)")
        let imageCollection = self.db.collection("images")
        let thisImage = imageCollection.document("mountain_image")
        let dict:[String:Any] = ["imageData": jpgData]
        thisImage.setData(dict, completion: { error in
            if let err = error {
                print(err.localizedDescription)
                return
            }
    
            print("upload success")
        })
    }
    

    the output to console is this

    allowed size: 1048487
    total size:   1040221
    upload success
    

    So as can be seen, the total size is just under the allowed size in a Firestore document.

    To summarize, this code uploads a 1Mb file to a Firestore Document

    For completeness, here's the code that reads back that data object, converts back to an image and displays in the UI

    func readImageFromFirestore() {
        let imageCollection = self.db.collection("images")
        imageCollection.getDocuments(completion: { snapshot, error in
            if let err = error {
                print(err.localizedDescription)
                return
            }
    
            guard let snap = snapshot else { return }
    
            for doc in snap.documents {
                let imageData = doc.get("imageData") as! Data
                let image = NSImage(data: imageData)
                self.myImageView.image = image
            }
    
        })
    }
    

    Keep in mind that Text Strings sizes are the number of UTF-8 encoded bytes + 1 so 'Hello' would be 6 total, 5 + 1

    EDIT:

    The OP added some additional information about storing Geopoints. A Geopoint is a specific data type in Firestore and requires a single field to store a geopoint. Attempting to store multiple geopoints in a single field is not an option.

    That being said, if you want to store 1Mb of geopoints, it can still be done.

    Here's some math: the total bytes allowed in a document is 1048487 and if each geopoint uses 16 bytes, quick division shows that approximately 65530 worth of geopoint data can be stored.

    So if I can upload 65530 bytes then it shows that a document can hold approximately 1Mb of data. Right? Here's the code that does that

    The following code creates almost 65530 geopoints, converts them to a string and stores them in a single Firestore document.

    func uploadGeopoints() {
        var geoArray = [GeoPoint]()
        let point = GeoPoint(latitude: 1.0, longitude: 1.0)
        for i in 0..<65530 {
            geoArray.append(point)
        }
    
        let geoString = geoArray.map { String("\($0.latitude)\($0.longitude)") }
    
        let combinedString = geoString.joined()
    
        let geoCollection = self.db.collection("geoStrings")
        let thisGeoString = geoCollection.document()
        let dict:[String: Any] = ["geoString": combinedString]
        thisGeoString.setData(dict, completion: { error in
            if let err = error {
                print(err.localizedDescription)
                return
            }
    
            print("upload success")
        })
    }
    
    0 讨论(0)
提交回复
热议问题