Bulk import/export in Cloud Fire Store [duplicate] - firebase

Is there a way to import CSV or JSON to firebase cloud firestore like in firebase realtime database?

General Solution
I've found many takes on a script allowing to upload a JSON but none of them allowed sub-collections. My script above handles any level of nesting and sub-collections. It also handles the case where a document has its own data and sub-collections. This is based on the assumption that collection is array/object of objects (including an empty object or array).
To run the script make sure you have npm and node installed. Then run your code as node <name of the file>. Note, there is no need to deploy it as a cloud funciton.
const admin = require('../functions/node_modules/firebase-admin');
const serviceAccount = require("./service-key.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://<your-database-name>.firebaseio.com"
});
const data = require("./fakedb.json");
/**
* Data is a collection if
* - it has a odd depth
* - contains only objects or contains no objects.
*/
function isCollection(data, path, depth) {
if (
typeof data != 'object' ||
data == null ||
data.length === 0 ||
isEmpty(data)
) {
return false;
}
for (const key in data) {
if (typeof data[key] != 'object' || data[key] == null) {
// If there is at least one non-object item in the data then it cannot be collection.
return false;
}
}
return true;
}
// Checks if object is empty.
function isEmpty(obj) {
for(const key in obj) {
if(obj.hasOwnProperty(key)) {
return false;
}
}
return true;
}
async function upload(data, path) {
return await admin.firestore()
.doc(path.join('/'))
.set(data)
.then(() => console.log(`Document ${path.join('/')} uploaded.`))
.catch(() => console.error(`Could not write document ${path.join('/')}.`));
}
/**
*
*/
async function resolve(data, path = []) {
if (path.length > 0 && path.length % 2 == 0) {
// Document's length of path is always even, however, one of keys can actually be a collection.
// Copy an object.
const documentData = Object.assign({}, data);
for (const key in data) {
// Resolve each collection and remove it from document data.
if (isCollection(data[key], [...path, key])) {
// Remove a collection from the document data.
delete documentData[key];
// Resolve a colleciton.
resolve(data[key], [...path, key]);
}
}
// If document is empty then it means it only consisted of collections.
if (!isEmpty(documentData)) {
// Upload a document free of collections.
await upload(documentData, path);
}
} else {
// Collection's length of is always odd.
for (const key in data) {
// Resolve each collection.
await resolve(data[key], [...path, key]);
}
}
}
resolve(data);

You need a custom script to do that.
I wrote one based on the Firebase admin SDK, as long as firebase library does not allow you to import nested arrays of data.
const admin = require('./node_modules/firebase-admin');
const serviceAccount = require("./service-key.json");
const data = require("./data.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://YOUR_DB.firebaseio.com"
});
data && Object.keys(data).forEach(key => {
const nestedContent = data[key];
if (typeof nestedContent === "object") {
Object.keys(nestedContent).forEach(docTitle => {
admin.firestore()
.collection(key)
.doc(docTitle)
.set(nestedContent[docTitle])
.then((res) => {
console.log("Document successfully written!");
})
.catch((error) => {
console.error("Error writing document: ", error);
});
});
}
});
Update: I wrote an article on this topic - Filling Firestore with data

There is not, you'll need to write your own script at this time.

I used the General Solution provided by Maciej Caputa. Thank you (:
Here are a few hints. Assuming that you have an Ionic Firebase application installed with the required Firebase node modules in the functions folder inside that solution. This is a standard Ionic Firebase install. I created an import folder to hold the script and data at the same level.
Folder Hierarchy
myIonicApp
functions
node_modules
firebase-admin
ImportFolder
script.js
FirebaseIonicTest-a1b2c3d4e5.json
fileToImport.json
Script Parameters
const admin = require('../myIonicApp/functions/node_modules/firebase-admin'); //path to firebase-admin module
const serviceAccount = require("./FirebaseTest-xxxxxxxxxx.json"); //service account key file
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://fir-test-xxxxxx.firebaseio.com" //Your domain from the hosting tab
});
Creating the Service Account Key File
In the Firebase console for your project, next to the Project
Overwiew item, click on the gear icon and select Users and
permissions
At the bottom of the screen, click Advanced permission
settings
This opens another tab for the Google Cloud Platform Console
On the left select the Service Accounts item
Create a Service Account for an existing Service Account
I simply added a key to the App Engine default service account
The Create key function will offer to download the key to a JSON file
JSON Data Structure
To use the script provided, the data structure must be as follows:
{
"myCollection" : {
"UniqueKey1" : {
"field1" : "foo",
"field2" : "bar"
},{
"UniqueKey2" : {
"field1" : "fog",
"field2" : "buzz"
}...
}

For reference. I wrote a function that helps to import and export data in Firestore.
https://github.com/dalenguyen/firestore-import-export (archived)
Updated 2023:
Use this package instead. Thanks, #Webber
https://github.com/dalenguyen/firestore-backup-restore

https://gist.github.com/JoeRoddy/1c706b77ca676bfc0983880f6e9aa8c8
This should work for an object of objects (generally how old firebase json is set up). You can add that code to an app that's already configured with Firestore.
Just make sure you have it pointing to the correct JSON file.
Good luck!

This workaround in Python may help some people. First convert json or csv to dataframe using Pandas, then convert dataframe to dictionary and upload dictionary to firestore.
import firebase_admin as fb
from firebase_admin import firestore
import pandas as pd
cred = fb.credentials.Certificate('my_credentials_certificate.json')
default_app = fb.initialize_app(cred)
db = firestore.client()
df = pd.read_csv('data.csv')
dict = df.to_dict(orient='records')
my_doc_ref = db.collection('my_collection').document('my_document')
my_doc_ref.set(dict)
There could be similar workarounds in javascript and other languages using libraries similar to Pandas.

No as of now, you can't.. firestore structures data into a different format that is, using collections and each collection has a series of documents which then are stored in JSON format.. in future they might make a tool to convert JSON in to firestore.. for reference check this out
:https://cloud.google.com/firestore/docs/concepts/structure-data
****EDIT :****
You can automate the process up to some extent, that is, write a mock software which only pushes the fields of your CSV or JSON data into your Cloud Firestore DB. I migrated my whole database my making just a simple app which retrieved my DB and pushed it on Firestore

var admin = require('firebase-admin');
var serviceAccount = require('./serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'https://csvread-d149c.firebaseio.com'
});
const csv = require('csv-parser');
const fs = require('fs');
const firestore = admin.firestore();
// CSV FILE data.csv
// Name,Surname,Age,Gender
// John,Snow,26,M
// Clair,White,33,F
// Fancy,Brown,78,F
fs.createReadStream('data.csv')
.pipe(csv())
.on('data', (row) => {
console.log(row);
if(row) {
firestore.collection('csv').add({
name: row.Name,
surname: row.Surname,
age: row.Age,
sex: row.Gender
});
}
else {
console.log('No data')
}
})
.on('end', () => {
console.log('CSV file successfully processed');
});

There is now a paid GUI tool that does this, Firefoo. I haven't used it, but it seems quite powerful for managing Firestore - including importing csv files:

This is a small modification that copies the 'id' of the document, if exists, to its path. Otherwise it will use the "for"'s index.
...
...
} else {
// Collection's length of is always odd.
for (const key in data) {
// Resolve each collection.
if (data[key]['id']!==undefined)
await resolve(data[key], [...path,data[key]['id']])
else
await resolve(data[key], [...path, key]);
}
}
}
resolve(data);

1 - Importing only collections
If the names of your collections are not only composed of numbers, then you can define the name of the document as follows.
...
...
} else {
// Collection's length of is always odd.
for (const key in data) {
// // Resolve each collection.
// If key is a number it means that it is not a collection
// The variable id in this case will be the name of the document field or you
// can generate randomly
let id = !isNaN(key) ? data[key].id : key;
await resolve(data[key], [...path, id]);
}
}
}
2 - Import collections and sub-collections
In the same way as in the example above, the name of the sub-collection can not contain only numbers.
...
...
for (const key in data) {
// Resolve each collection and remove it from document data.
if (isCollection(data[key], [...path, key])) {
// Remove a collection from the document data.
delete documentData[key];
// If key is a number it means that it is not a collection
// The variable id in this case will be the name of the document field or you
// can generate randomly
let id = !isNaN(key) ? data[key].id : key;
// Resolve a colleciton.
resolve(data[key], [...path, id]);
}
}
...
...
Note.: Changes in #Maciej Caputa's code

this library you can use https://www.npmjs.com/package/csv-to-firestore
install this library with this command npm i -g csv-to-firestore
put this script inside your ./scripts
module.exports = {
path: "./scripts/palabrasOutput.csv",
firebase: {
credential: "./scripts/serviceAccount.json",
collection: "collectionInFirestore",
},
mapper: (dataFromCSV) => {
return dataFromCSV;
},
};
and run:
csv-to-firestore --config ./scripts/csvToFirestore.js
btw: Generate new private key (it's a file in Service account section in firestore)
put that file in /scripts folder and rename serviceAccount.json

Related

Modular Firebase Admin Database

Is there or will there be modular Firebase Admin Database functions i.e. update(), get(), set(), ref() etc.? Maybe there is a workaround?
Else I would have to code equal functions twice, like "craftBuilding()" on server (admin) and one for client.
Tried to:
import { getDatabase, ref, set, update, child, onChildAdded } from 'firebase-admin/database';
Error:
The requested module 'firebase-admin/database' does not provide an export named 'ref'
I expected to be able to use firebase rtdb functions like on client-side, to not code identical functions twice.
The Admin SDK is not totally modular yet but has some function such a getDatabase() starting V10.
import { getDatabase } from 'firebase-admin/database';
// TODO: Initialize Admin SDK
const db = getDatabase();
// usage
const ref = db.ref('../../..')
I would recommend using latest version of the SDK along with the existing syntax but use newer modular imports.
Since Firebase Admin is namespaced, ref is available after you initialize.
import { ref, update, set } from '#path/yourAdminFunctions';
var admin = require("firebase-admin");
// Initialize Admin SDK
var db = admin.database();
export db;
const dbReference = ref("your reference here");
const IDSnapFromQuery = query(dbReference, ["ID", "==", 10]);
You could modularize it yourself by creating functions for the operations you require in a separate file:
import { db } from '#path/yourMainFile';
export function ref(databaseReference) {
return db.ref(databaseReference);
};
// for query pass in the db.ref instance you're using
export function query(dbReference, queryParams) {
// identify what query params are e.g. ID == 10 in this example
// note that you will have to code the queryParams filter, I have left it out
dbReference
.orderByChild('ID')
.equalTo(10).on('child_added',
(snapshot) => {
return snapshot;
});
}
// do the same for update, set etc
You can read more on the admin SDK documentation.

Export Firestore in JSON without Blaze plan

I have hundreds of entries in my firebase database and I'd like to try moving to SQL in case my Firebase free plan ever goes out of limit. Is there any way I can export the data in JSON format without buying the Blaze plan? (as my card doesn't work when I try to do payment)
I use firefoo... there is trial version for 14 days
To backup my collection manually I did this:
In the same project, created a file called backup.js that is called when I press a backup button.
In the backup.js endpoint, I fetch the collection and store every entry in the database, then I return this array of documents I just saved.
I console.log the returned data and in Browser's console, I right click the array output and click 'Copy Object' and save it in a text file.
backup.js (Next.js API Endpoint):
import { collection, doc, getDoc, getDocs } from "firebase/firestore";
import { StatusCodes } from "http-status-codes";
import { db } from "../../utils/firebase";
export default async function handler(req, res) {
const collectionName = "myCollection"
try {
// check firebase if slug exists
const documentSnapshot = await getDocs(collection(db, collectionName));
let backup = [];
documentSnapshot.forEach((doc) => {
backup.push(doc.data());
});
console.log(backup);
return res.status(StatusCodes.OK).send(backup);
} catch (err) {
console.log("Database Error", err);
}
}

Retrieve all images from firebase storage [duplicate]

I'm working on uploading images, everything works great, but I have 100 pictures and I would like to show all of them in my View, as I get the complete list of the images in a folder, I can not find any API for this work.
Since Firebase SDKs for JavaScript release 6.1, iOS release 6.4, and Android release version 18.1 all have a method to list files.
The documentation is a bit sparse so far, so I recommend checking out Rosário's answer for details.
Previous answer, since this approach can still be useful at times:
There currently is no API call in the Firebase SDK to list all files in a Cloud Storage folder from within an app. If you need such functionality, you should store the metadata of the files (such as the download URLs) in a place where you can list them. The Firebase Realtime Database and Cloud Firestore are perfect for this and allows you to also easily share the URLs with others.
You can find a good (but somewhat involved) sample of this in our FriendlyPix sample app. The relevant code for the web version is here, but there are also versions for iOS and Android.
As of May 2019, version 6.1.0 of the Firebase SDK for Cloud Storage now supports listing all objects from a bucket. You simply need to call listAll() in a Reference:
// Since you mentioned your images are in a folder,
// we'll create a Reference to that folder:
var storageRef = firebase.storage().ref("your_folder");
// Now we get the references of these images
storageRef.listAll().then(function(result) {
result.items.forEach(function(imageRef) {
// And finally display them
displayImage(imageRef);
});
}).catch(function(error) {
// Handle any errors
});
function displayImage(imageRef) {
imageRef.getDownloadURL().then(function(url) {
// TODO: Display the image on the UI
}).catch(function(error) {
// Handle any errors
});
}
Please note that in order to use this function, you must opt-in to version 2 of Security Rules, which can be done by making rules_version = '2'; the first line of your security rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
I'd recommend checking the docs for further reference.
Also, according to setup, on Step 5, this script is not allowed for Node.js since require("firebase/app"); won't return firebase.storage() as a function. This is only achieved using import * as firebase from 'firebase/app';.
Since Mar 2017: With the addition of Firebase Cloud Functions, and Firebase's deeper integration with Google Cloud, this is now possible.
With Cloud Functions you can use the Google Cloud Node package to do epic operations on Cloud Storage. Below is an example that gets all the file URLs into an array from Cloud Storage. This function will be triggered every time something's saved to google cloud storage.
Note 1: This is a rather computationally expensive operation, as it has to cycle through all files in a bucket / folder.
Note 2: I wrote this just as an example, without paying much detail into promises etc. Just to give an idea.
const functions = require('firebase-functions');
const gcs = require('#google-cloud/storage')();
// let's trigger this function with a file upload to google cloud storage
exports.fileUploaded = functions.storage.object().onChange(event => {
const object = event.data; // the object that was just uploaded
const bucket = gcs.bucket(object.bucket);
const signedUrlConfig = { action: 'read', expires: '03-17-2025' }; // this is a signed url configuration object
var fileURLs = []; // array to hold all file urls
// this is just for the sake of this example. Ideally you should get the path from the object that is uploaded :)
const folderPath = "a/path/you/want/its/folder/size/calculated";
bucket.getFiles({ prefix: folderPath }, function(err, files) {
// files = array of file objects
// not the contents of these files, we're not downloading the files.
files.forEach(function(file) {
file.getSignedUrl(signedUrlConfig, function(err, fileURL) {
console.log(fileURL);
fileURLs.push(fileURL);
});
});
});
});
I hope this will give you the general idea. For better cloud functions examples, check out Google's Github repo full of Cloud Functions samples for Firebase. Also check out their Google Cloud Node API Documentation
Since there's no language listed, I'll answer this in Swift. We highly recommend using Firebase Storage and the Firebase Realtime Database together to accomplish lists of downloads:
Shared:
// Firebase services
var database: FIRDatabase!
var storage: FIRStorage!
...
// Initialize Database, Auth, Storage
database = FIRDatabase.database()
storage = FIRStorage.storage()
...
// Initialize an array for your pictures
var picArray: [UIImage]()
Upload:
let fileData = NSData() // get data...
let storageRef = storage.reference().child("myFiles/myFile")
storageRef.putData(fileData).observeStatus(.Success) { (snapshot) in
// When the image has successfully uploaded, we get it's download URL
let downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
// Write the download URL to the Realtime Database
let dbRef = database.reference().child("myFiles/myFile")
dbRef.setValue(downloadURL)
}
Download:
let dbRef = database.reference().child("myFiles")
dbRef.observeEventType(.ChildAdded, withBlock: { (snapshot) in
// Get download URL from snapshot
let downloadURL = snapshot.value() as! String
// Create a storage reference from the URL
let storageRef = storage.referenceFromURL(downloadURL)
// Download the data, assuming a max size of 1MB (you can change this as necessary)
storageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
// Create a UIImage, add it to the array
let pic = UIImage(data: data)
picArray.append(pic)
})
})
For more information, see Zero to App: Develop with Firebase, and it's associated source code, for a practical example of how to do this.
I also encountered this problem when I was working on my project. I really wish they provide an end api method. Anyway, This is how I did it:
When you are uploading an image to Firebase storage, create an Object and pass this object to Firebase database at the same time. This object contains the download URI of the image.
trailsRef.putFile(file).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
Uri downloadUri = taskSnapshot.getDownloadUrl();
DatabaseReference myRef = database.getReference().child("trails").child(trail.getUnique_id()).push();
Image img = new Image(trail.getUnique_id(), downloadUri.toString());
myRef.setValue(img);
}
});
Later when you want to download images from a folder, you simply iterate through files under that folder. This folder has the same name as the "folder" in Firebase storage, but you can name them however you want to. I put them in separate thread.
#Override
protected List<Image> doInBackground(Trail... params) {
String trialId = params[0].getUnique_id();
mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("trails").child(trialId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
images = new ArrayList<>();
Iterator<DataSnapshot> iter = dataSnapshot.getChildren().iterator();
while (iter.hasNext()) {
Image img = iter.next().getValue(Image.class);
images.add(img);
}
isFinished = true;
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Now I have a list of objects containing the URIs to each image, I can do whatever I want to do with them. To load them into imageView, I created another thread.
#Override
protected List<Bitmap> doInBackground(List<Image>... params) {
List<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < params[0].size(); i++) {
try {
URL url = new URL(params[0].get(i).getImgUrl());
Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
bitmaps.add(bmp);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return bitmaps;
}
This returns a list of Bitmap, when it finishes I simply attach them to ImageView in the main activity. Below methods are #Override because I have interfaces created and listen for completion in other threads.
#Override
public void processFinishForBitmap(List<Bitmap> bitmaps) {
List<ImageView> imageViews = new ArrayList<>();
View v;
for (int i = 0; i < bitmaps.size(); i++) {
v = mInflater.inflate(R.layout.gallery_item, mGallery, false);
imageViews.add((ImageView) v.findViewById(R.id.id_index_gallery_item_image));
imageViews.get(i).setImageBitmap(bitmaps.get(i));
mGallery.addView(v);
}
}
Note that I have to wait for List Image to be returned first and then call thread to work on List Bitmap. In this case, Image contains the URI.
#Override
public void processFinish(List<Image> results) {
Log.e(TAG, "get back " + results.size());
LoadImageFromUrlTask loadImageFromUrlTask = new LoadImageFromUrlTask();
loadImageFromUrlTask.delegate = this;
loadImageFromUrlTask.execute(results);
}
Hopefully someone finds it helpful. It will also serve as a guild line for myself in the future too.
Combining some answers from this post and also from here, and after some personal research, for NodeJS with typescript I managed to accomplish this by using firebase-admin:
import * as admin from 'firebase-admin';
const getFileNames = () => {
admin.storage().bucket().getFiles(autoPaginate: false).then(([files]: any) => {
const fileNames = files.map((file: any) => file.name);
return fileNames;
})
}
In my case I also needed to get all the files inside a specific folder from firebase storage. According to google storage the folders don't exists but are rather a naming conventions. Anyway I managed to to this (without saving each file full path into DB) by adding { prefix: ${folderName}, autoPaginate: false } inside the getFiles function call so:
...
const getFileNames = (folderName: string) => {
admin.storage().bucket().getFiles({ prefix: `${folderName}`, autoPaginate: false })
.then(([files]: any) => {
...
You can list files in a directory of firebase storage by listAll() method.
To use this method, have to implement this version of firebase storage.
'com.google.firebase:firebase-storage:18.1.1'
https://firebase.google.com/docs/storage/android/list-files
Keep in mind that upgrade the Security Rules to version 2.
A workaround can be to create a file (i.e list.txt) with nothing inside, in this file you can set the custom metadata (that is a Map< String, String>) with the list of all the file's URL.So if you need to downlaod all the files in a fodler you first download the metadata of the list.txt file, then you iterate through the custom data and download all the files with the URLs in the Map.
One more way to add the image to Database using Cloud Function to track every uploaded image and store it in Database.
exports.fileUploaded = functions.storage.object().onChange(event => {
const object = event.data; // the object that was just uploaded
const contentType = event.data.contentType; // This is the image Mimme type\
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/')) {
console.log('This is not an image.');
return null;
}
// Get the Signed URLs for the thumbnail and original image.
const config = {
action: 'read',
expires: '03-01-2500'
};
const bucket = gcs.bucket(event.data.bucket);
const filePath = event.data.name;
const file = bucket.file(filePath);
file.getSignedUrl(config, function(err, fileURL) {
console.log(fileURL);
admin.database().ref('images').push({
src: fileURL
});
});
});
Full code here:
https://gist.github.com/bossly/fb03686f2cb1699c2717a0359880cf84
For node js, I used this code
const Storage = require('#google-cloud/storage');
const storage = new Storage({projectId: 'PROJECT_ID', keyFilename: 'D:\\keyFileName.json'});
const bucket = storage.bucket('project.appspot.com'); //gs://project.appspot.com
bucket.getFiles().then(results => {
const files = results[0];
console.log('Total files:', files.length);
files.forEach(file => {
file.download({destination: `D:\\${file}`}).catch(error => console.log('Error: ', error))
});
}).catch(err => {
console.error('ERROR:', err);
});
Actually this is possible but only with a Google Cloud API instead one from Firebase. It's because a Firebase Storage is a Google Cloud Storage Bucket which can be reached easily with the Google Cloud APIs however you need to use OAuth for Authentication instead of the Firebase one's.
#In Python
import firebase_admin
from firebase_admin import credentials
from firebase_admin import storage
import datetime
import urllib.request
def image_download(url, name_img) :
urllib.request.urlretrieve(url, name_img)
cred = credentials.Certificate("credentials.json")
# Initialize the app with a service account, granting admin privileges
app = firebase_admin.initialize_app(cred, {
'storageBucket': 'YOURSTORAGEBUCKETNAME.appspot.com',
})
url_img = "gs://YOURSTORAGEBUCKETNAME.appspot.com/"
bucket_1 = storage.bucket(app=app)
image_urls = []
for blob in bucket_1.list_blobs():
name = str(blob.name)
#print(name)
blob_img = bucket_1.blob(name)
X_url = blob_img.generate_signed_url(datetime.timedelta(seconds = 300), method='GET')
#print(X_url)
image_urls.append(X_url)
PATH = ['Where you want to save the image']
for path in PATH:
i = 1
for url in image_urls:
name_img = str(path + "image"+str(i)+".jpg")
image_download(url, name_img)
i+=1
Extending Rosário Pereira Fernandes' answer, for a JavaScript solution:
Install firebase on your machine
npm install -g firebase-tools
On firebase init set JavaScript as default language
On the root folder of created project execute npm installs
npm install --save firebase
npm install #google-cloud/storage
npm install #google-cloud/firestore
... <any other dependency needed>
Add non-default dependencies on your project like
"firebase": "^6.3.3",
"#google-cloud/storage": "^3.0.3"
functions/package.json
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "10"
},
"dependencies": {
"#google-cloud/storage": "^3.0.3",
"firebase": "^6.3.3",
"firebase-admin": "^8.0.0",
"firebase-functions": "^3.1.0"
},
"devDependencies": {
"eslint": "^5.12.0",
"eslint-plugin-promise": "^4.0.1",
"firebase-functions-test": "^0.1.6"
},
"private": true
}
Create sort of a listAll function
index.js
var serviceAccount = require("./key.json");
const functions = require('firebase-functions');
const images = require('./images.js');
var admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://<my_project>.firebaseio.com"
});
const bucket = admin.storage().bucket('<my_bucket>.appspot.com')
exports.getImages = functions.https.onRequest((request, response) => {
images.getImages(bucket)
.then(urls => response.status(200).send({ data: { urls } }))
.catch(err => console.error(err));
})
images.js
module.exports = {
getImages
}
const query = {
directory: 'images'
};
function getImages(bucket) {
return bucket.getFiles(query)
.then(response => getUrls(response))
.catch(err => console.error(err));
}
function getUrls(response) {
const promises = []
response.forEach( files => {
files.forEach (file => {
promises.push(getSignedUrl(file));
});
});
return Promise.all(promises).then(result => getParsedUrls(result));
}
function getSignedUrl(file) {
return file.getSignedUrl({
action: 'read',
expires: '09-01-2019'
})
}
function getParsedUrls(result) {
return JSON.stringify(result.map(mediaLink => createMedia(mediaLink)));
}
function createMedia(mediaLink) {
const reference = {};
reference.mediaLink = mediaLink[0];
return reference;
}
Execute firebase deploy to upload your cloud function
Call your custom function from your app
build.gradle
dependencies {
...
implementation 'com.google.firebase:firebase-functions:18.1.0'
...
}
kotlin class
private val functions = FirebaseFunctions.getInstance()
val cloudFunction = functions.getHttpsCallable("getImages")
cloudFunction.call().addOnSuccessListener {...}
Regarding the further development of this feature, I ran into some problems that might found here.
I am using AngularFire and use the following for get all of the downloadURL
getPhotos(id: string): Observable<string[]> {
const ref = this.storage.ref(`photos/${id}`)
return ref.listAll().pipe(switchMap(list => {
const calls: Promise<string>[] = [];
list.items.forEach(item => calls.push(item.getDownloadURL()))
return Promise.all(calls)
}));
}
I faced the same issue, mine is even more complicated.
Admin will upload audio and pdf files into storage:
audios/season1, season2.../class1, class 2/.mp3 files
books/.pdf files
Android app needs to get the list of sub folders and files.
The solution is catching the upload event on storage and create the same structure on firestore using cloud function.
Step 1: Create manually 'storage' collection and 'audios/books' doc on firestore
Step 2: Setup cloud function
Might take around 15 mins: https://www.youtube.com/watch?v=DYfP-UIKxH0&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=1
Step 3: Catch upload event using cloud function
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp(functions.config().firebase);
const path = require('path');
export const onFileUpload = functions.storage.object().onFinalize(async (object) => {
let filePath = object.name; // File path in the bucket.
const contentType = object.contentType; // File content type.
const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.
if (metageneration !== "1") return;
// Get the file name.
const fileName = path.basename(filePath);
filePath = filePath.substring(0, filePath.length - 1);
console.log('contentType ' + contentType);
console.log('fileName ' + fileName);
console.log('filePath ' + filePath);
console.log('path.dirname(filePath) ' + path.dirname(filePath));
filePath = path.dirname(filePath);
const pathArray = filePath.split("/");
let ref = '';
for (const item of pathArray) {
if (ref.length === 0) {
ref = item;
}
else {
ref = ref.concat('/sub/').concat(item);
}
}
ref = 'storage/'.concat(ref).concat('/sub')
admin.firestore().collection(ref).doc(fileName).create({})
.then(result => {console.log('onFileUpload:updated')})
.catch(error => {
console.log(error);
});
});
Step 4: Retrieve list of folders/files on Android app using firestore
private static final String STORAGE_DOC = "storage/";
public static void getMediaCollection(String path, OnCompleteListener onCompleteListener) {
String[] pathArray = path.split("/");
String doc = null;
for (String item : pathArray) {
if (TextUtils.isEmpty(doc)) doc = STORAGE_DOC.concat(item);
else doc = doc.concat("/sub/").concat(item);
}
doc = doc.concat("/sub");
getFirestore().collection(doc).get().addOnCompleteListener(onCompleteListener);
}
Step 5: Get download url
public static void downloadMediaFile(String path, OnCompleteListener<Uri> onCompleteListener) {
getStorage().getReference().child(path).getDownloadUrl().addOnCompleteListener(onCompleteListener);
}
Note
We have to put "sub" collection to each item since firestore doesn't support to retrieve the list of collection.
It took me 3 days to find out the solution, hopefully will take you 3 hours at most.
To do this with JS
You can append them directly to your div container, or you can push them to an array. The below shows you how to append them to your div.
1) When you store your images in storage create a reference to the image in your firebase database with the following structure
/images/(imageName){
description: "" ,
imageSrc : (imageSource)
}
2) When you load you document pull all your image source URLs from the database rather than the storage with the following code
$(document).ready(function(){
var query = firebase.database().ref('images/').orderByKey();
query.once("value").then(function(snapshot){
snapshot.forEach(function(childSnapshot){
var imageName = childSnapshot.key;
var childData = childSnapshot.val();
var imageSource = childData.url;
$('#imageGallery').append("<div><img src='"+imageSource+"'/></div>");
})
})
});
You can use the following code. Here I am uploading the image to firebase storage and then I am storing the image download url to firebase database.
//getting the storage reference
StorageReference sRef = storageReference.child(Constants.STORAGE_PATH_UPLOADS + System.currentTimeMillis() + "." + getFileExtension(filePath));
//adding the file to reference
sRef.putFile(filePath)
.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
//dismissing the progress dialog
progressDialog.dismiss();
//displaying success toast
Toast.makeText(getApplicationContext(), "File Uploaded ", Toast.LENGTH_LONG).show();
//creating the upload object to store uploaded image details
Upload upload = new Upload(editTextName.getText().toString().trim(), taskSnapshot.getDownloadUrl().toString());
//adding an upload to firebase database
String uploadId = mDatabase.push().getKey();
mDatabase.child(uploadId).setValue(upload);
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
progressDialog.dismiss();
Toast.makeText(getApplicationContext(), exception.getMessage(), Toast.LENGTH_LONG).show();
}
})
.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
#Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
//displaying the upload progress
double progress = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount();
progressDialog.setMessage("Uploaded " + ((int) progress) + "%...");
}
});
Now to fetch all the images stored in firebase database you can use
//adding an event listener to fetch values
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
//dismissing the progress dialog
progressDialog.dismiss();
//iterating through all the values in database
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
Upload upload = postSnapshot.getValue(Upload.class);
uploads.add(upload);
}
//creating adapter
adapter = new MyAdapter(getApplicationContext(), uploads);
//adding adapter to recyclerview
recyclerView.setAdapter(adapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
progressDialog.dismiss();
}
});
Fore more details you can see my post Firebase Storage Example.
In Swift
public func downloadData() async {
let imagesRef = storage.child("pictures/")
do {
let storageReference = try await storage.root().child("pictures").listAll()
print("storageReference: \(storageReference.items)")
} catch {
print(error)
}
}
Output
[
gs://<your_app_name>.appspot.com/pictures/IMG_1243.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1244.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1245.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1246.JPG
]
Here is the reference
So I had a project that required downloading assets from firebase storage, so I had to solve this problem myself. Here is How :
1- First, make a model data for example class Choice{}, In that class defines a String variable called image Name so it will be like that
class Choice {
.....
String imageName;
}
2- from a database/firebase database, go and hardcode the image names to the objects, so if you have image name called Apple.png, create the object to be
Choice myChoice = new Choice(...,....,"Apple.png");
3- Now, get the link for the assets in your firebase storage which will be something like that
gs://your-project-name.appspot.com/
like this one
4- finally, initialize your firebase storage reference and start getting the files by a loop like that
storageRef = storage.getReferenceFromUrl(firebaseRefURL).child(imagePath);
File localFile = File.createTempFile("images", "png");
storageRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
#Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
//Dismiss Progress Dialog\\
}
5- that's it
For Android the best pratice is to use FirebaseUI and Glide.
You need to add that on your gradle/app in order to get the library. Note that it already has Glide on it!
implementation 'com.firebaseui:firebase-ui-storage:4.1.0'
And then in your code use
// Reference to an image file in Cloud Storage
StorageReference storageReference = FirebaseStorage.getInstance().getReference();
// ImageView in your Activity
ImageView imageView = findViewById(R.id.imageView);
// Download directly from StorageReference using Glide
// (See MyAppGlideModule for Loader registration)
GlideApp.with(this /* context */)
.load(storageReference)
.into(imageView);

dialogflow chatbot how to go to firestore database and return with support relevant questions?

I am using "firestore" database for my "dialogflow" chat bot which I already created for an online grocery store. The problem is: I want my chatbot to initially ask questions from users to find the proper item title in my database and then return to user by asking 3-4 support relevant questions about that item. the questions must be the item attributes (Brand, color, size...) and will vary from one item to another. so the chatbot will stream down the user to find the best item.
can you please help me to find the answer? I already created the codes but they don't work and I do not know what is wrong with that. If you already created this and have the index.js file, I appreciate to propose me here.
index.js:
'use strict';
const functions = require('firebase-functions');
// Import admin SDK
const admin = require('firebase-admin');
const {
WebhookClient
} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:*'; // enables lib debugging statements
admin.initializeApp(functions.config().firebase);
// here we get the database in a variable
const db = admin.firestore();
const data = {...};
// Add a new document in collection "dialogflow" with document ID 'agent'
const dialogflowAgentRef = db.collection('dialogflow').doc('agent').set(data);
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({
request,
response
});
function writeToDb(agent) {
// Get parameter from Dialogflow with the string to add to the database doc
const databaseEntry = agent.parameters.databaseEntry;
// Get the database collection 'dialogflow' and document 'agent' and store
// the document {entry: "<value of database entry>"} in the 'agent' document
const dialogflowAgentRef = db.collection('dialogflow').doc('agent').where('title', '==', title);
return db.runTransaction(t => {
t.set(dialogflowAgentRef, {
entry: databaseEntry
});
return Promise.resolve('Write complete');
}).then(doc => {
agent.add(`Wrote "${databaseEntry}" to the Firestore database.`);
}).catch(err => {
console.log(`Error writing to Firestore: ${err}`);
agent.add(`Failed to write "${databaseEntry}" to the Firestore database.`);
});
}
function readFromDb(agent) {
// Get the database collection 'dialogflow' and document 'agent'
const dialogflowAgentDoc = db.collection('dialogflow/agent/rss/channel/item'); // .doc('agent')
// Get the value of 'entry' in the document and send it to the user
return dialogflowAgentDoc.get()
.then(doc => {
if (!doc.exists) {
agent.add('No data found in the database!');
} else {
agent.add(doc.data().entry);
}
return Promise.resolve('Read complete');
}).catch(() => {
agent.add('Error reading entry from the Firestore database.');
agent.add('Please add a entry to the database first by saying, "Write <your phrase> to the database"');
});
}
// Map from Dialogflow intent names to functions to be run when the intent is matched
let intentMap = new Map();
intentMap.set('ReadFromFirestore', readFromDb);
intentMap.set('WriteToFirestore', writeToDb);
agent.handleRequest(intentMap);
});
There are a number of issues with your code as you've shown it that could cause problems reading and writing with the Firestore database.
It looks like you're trying to find an existing collection to write to with the line
const dialogflowAgentRef = db.collection('dialogflow').doc('agent').where('title', '==', title);
but title isn't defined anywhere, which I suspect causes an error. Furthermore, doc() returns a DocumentReference, but there is no where() method in a DocumentReference.
Remember that you need to structure Firestore using alternating collections and documents. So your "firebase" collection can contain a document named "agent", and that document may have subcollections.
When you're trying to read with
const dialogflowAgentDoc = db.collection('dialogflow/agent/rss/channel/item');
You're getting a collection, but then trying to treat it as a document. The comment suggests that you're trying to read a specific doc from this collection (which makes sense), but you're loading that document by a hard-coded string "agent", rather than trying to get the agent from the parameters passed to you from Dialogflow.
Finally - the paths in the read and write sections don't match. Using hard-coded paths are fine when testing, but make sure you're using matching paths and that they reflect the collection/doc/collection/doc/... path requirement.
So in both cases, you might have a reference that looks something like
const docTitle = agent.parameters.title;
const docRef = db.collection('dialogflow').doc(title);
Which, if you have defined a "title" parameter in your Intents in Dialogflow, will use this to reference the doc, which you can then read or write.
thanks for the answer I already changed my database to real time firebase instead of firestore. still having problem with support relevant questions. I want to go to my real time database to find the item by search using "oederByChild" and "equalTo" methods as I found these in people questions and answer in this website. still cannot find and item title through my database child. here is the codes are written:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {
WebhookClient
} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:*'; // enables lib debugging statements
admin.initializeApp(functions.config().firebase);
const db = admin.database();
// const ref = db.ref('server/saving-data/fireblog');
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({
request,
response
});
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
// Get the database collection 'dialogflow' and document 'agent' and store
// the document {entry: "<value of database entry>"} in the 'agent' document
function writeToDb(agent) {
const databaseEntry = agent.parameters.databaseEntry;
const acc = db.ref('rss/channel/item/4/title'); //**This worked! */
acc.set({
entry: databaseEntry
});
return Promise.resolve('write complete')
.then(_acc => {
agent.add(`Wrote ${databaseEntry} to the realtime database.`);
return false;
}).catch(err => {
console.log(`Error writing to Firestore: ${err}`);
agent.add(`Failed to write "${databaseEntry}" to the Firestore database.`);
});
}
// and this is when we want to write to in the same child, keeping the old values:
//const acc = db.ref('/rss/channel/item/5/color'); //**This worked! */
//const result = acc.child(databaseEntry).set({entry: databaseEntry});
//agent.add(`Wrote ${databaseEntry} to the realtime database.`);
//console.log(result.key);
//});
// to read data
function readFromDb(agent) {
const any = agent.parameters.any;
agent.add(`Thank you...`);
var rootRef = db.ref();
var childref = rootRef.child("rss/channel/item");
return childref.orderByChild("title").equalTo("Icebreaker").once("value").then(function(snapshot){ //has been taken from the bus example: https://stackoverflow.com/questions/51917390/dialogflow-how-do-i-pass-a-parameter-through-in-a-firebase-query
var colored = snapshot.child("color/__text").val();
var sized = snapshot.child("size/__text").val();
agent.add(`Your search result for ` + any + ` Throughout the database is ` + colored +
` Color and ` + sized + ` Size`);
return Promise.resolve('Read complete');
}).catch(() => {
agent.add('Error reading entry from the Firestore database.');
agent.add('Please add a entry to the database first by saying, "Write <your phrase> to the database"');
});
}
// Map from Dialogflow intent names to functions to be run when the intent is matched
let intentMap = new Map();
intentMap.set('IWannaBuy', readFromDb);
intentMap.set('WriteToFirebase', writeToDb);
agent.handleRequest(intentMap);
});
enter code here
[this is how my database is][1]
[1]: https://i.stack.imgur.com/QdFy5.png

How to import CSV or JSON to firebase cloud firestore

Is there a way to import CSV or JSON to firebase cloud firestore like in firebase realtime database?
General Solution
I've found many takes on a script allowing to upload a JSON but none of them allowed sub-collections. My script above handles any level of nesting and sub-collections. It also handles the case where a document has its own data and sub-collections. This is based on the assumption that collection is array/object of objects (including an empty object or array).
To run the script make sure you have npm and node installed. Then run your code as node <name of the file>. Note, there is no need to deploy it as a cloud funciton.
const admin = require('../functions/node_modules/firebase-admin');
const serviceAccount = require("./service-key.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://<your-database-name>.firebaseio.com"
});
const data = require("./fakedb.json");
/**
* Data is a collection if
* - it has a odd depth
* - contains only objects or contains no objects.
*/
function isCollection(data, path, depth) {
if (
typeof data != 'object' ||
data == null ||
data.length === 0 ||
isEmpty(data)
) {
return false;
}
for (const key in data) {
if (typeof data[key] != 'object' || data[key] == null) {
// If there is at least one non-object item in the data then it cannot be collection.
return false;
}
}
return true;
}
// Checks if object is empty.
function isEmpty(obj) {
for(const key in obj) {
if(obj.hasOwnProperty(key)) {
return false;
}
}
return true;
}
async function upload(data, path) {
return await admin.firestore()
.doc(path.join('/'))
.set(data)
.then(() => console.log(`Document ${path.join('/')} uploaded.`))
.catch(() => console.error(`Could not write document ${path.join('/')}.`));
}
/**
*
*/
async function resolve(data, path = []) {
if (path.length > 0 && path.length % 2 == 0) {
// Document's length of path is always even, however, one of keys can actually be a collection.
// Copy an object.
const documentData = Object.assign({}, data);
for (const key in data) {
// Resolve each collection and remove it from document data.
if (isCollection(data[key], [...path, key])) {
// Remove a collection from the document data.
delete documentData[key];
// Resolve a colleciton.
resolve(data[key], [...path, key]);
}
}
// If document is empty then it means it only consisted of collections.
if (!isEmpty(documentData)) {
// Upload a document free of collections.
await upload(documentData, path);
}
} else {
// Collection's length of is always odd.
for (const key in data) {
// Resolve each collection.
await resolve(data[key], [...path, key]);
}
}
}
resolve(data);
You need a custom script to do that.
I wrote one based on the Firebase admin SDK, as long as firebase library does not allow you to import nested arrays of data.
const admin = require('./node_modules/firebase-admin');
const serviceAccount = require("./service-key.json");
const data = require("./data.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://YOUR_DB.firebaseio.com"
});
data && Object.keys(data).forEach(key => {
const nestedContent = data[key];
if (typeof nestedContent === "object") {
Object.keys(nestedContent).forEach(docTitle => {
admin.firestore()
.collection(key)
.doc(docTitle)
.set(nestedContent[docTitle])
.then((res) => {
console.log("Document successfully written!");
})
.catch((error) => {
console.error("Error writing document: ", error);
});
});
}
});
Update: I wrote an article on this topic - Filling Firestore with data
There is not, you'll need to write your own script at this time.
I used the General Solution provided by Maciej Caputa. Thank you (:
Here are a few hints. Assuming that you have an Ionic Firebase application installed with the required Firebase node modules in the functions folder inside that solution. This is a standard Ionic Firebase install. I created an import folder to hold the script and data at the same level.
Folder Hierarchy
myIonicApp
functions
node_modules
firebase-admin
ImportFolder
script.js
FirebaseIonicTest-a1b2c3d4e5.json
fileToImport.json
Script Parameters
const admin = require('../myIonicApp/functions/node_modules/firebase-admin'); //path to firebase-admin module
const serviceAccount = require("./FirebaseTest-xxxxxxxxxx.json"); //service account key file
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://fir-test-xxxxxx.firebaseio.com" //Your domain from the hosting tab
});
Creating the Service Account Key File
In the Firebase console for your project, next to the Project
Overwiew item, click on the gear icon and select Users and
permissions
At the bottom of the screen, click Advanced permission
settings
This opens another tab for the Google Cloud Platform Console
On the left select the Service Accounts item
Create a Service Account for an existing Service Account
I simply added a key to the App Engine default service account
The Create key function will offer to download the key to a JSON file
JSON Data Structure
To use the script provided, the data structure must be as follows:
{
"myCollection" : {
"UniqueKey1" : {
"field1" : "foo",
"field2" : "bar"
},{
"UniqueKey2" : {
"field1" : "fog",
"field2" : "buzz"
}...
}
For reference. I wrote a function that helps to import and export data in Firestore.
https://github.com/dalenguyen/firestore-import-export (archived)
Updated 2023:
Use this package instead. Thanks, #Webber
https://github.com/dalenguyen/firestore-backup-restore
https://gist.github.com/JoeRoddy/1c706b77ca676bfc0983880f6e9aa8c8
This should work for an object of objects (generally how old firebase json is set up). You can add that code to an app that's already configured with Firestore.
Just make sure you have it pointing to the correct JSON file.
Good luck!
This workaround in Python may help some people. First convert json or csv to dataframe using Pandas, then convert dataframe to dictionary and upload dictionary to firestore.
import firebase_admin as fb
from firebase_admin import firestore
import pandas as pd
cred = fb.credentials.Certificate('my_credentials_certificate.json')
default_app = fb.initialize_app(cred)
db = firestore.client()
df = pd.read_csv('data.csv')
dict = df.to_dict(orient='records')
my_doc_ref = db.collection('my_collection').document('my_document')
my_doc_ref.set(dict)
There could be similar workarounds in javascript and other languages using libraries similar to Pandas.
No as of now, you can't.. firestore structures data into a different format that is, using collections and each collection has a series of documents which then are stored in JSON format.. in future they might make a tool to convert JSON in to firestore.. for reference check this out
:https://cloud.google.com/firestore/docs/concepts/structure-data
****EDIT :****
You can automate the process up to some extent, that is, write a mock software which only pushes the fields of your CSV or JSON data into your Cloud Firestore DB. I migrated my whole database my making just a simple app which retrieved my DB and pushed it on Firestore
var admin = require('firebase-admin');
var serviceAccount = require('./serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'https://csvread-d149c.firebaseio.com'
});
const csv = require('csv-parser');
const fs = require('fs');
const firestore = admin.firestore();
// CSV FILE data.csv
// Name,Surname,Age,Gender
// John,Snow,26,M
// Clair,White,33,F
// Fancy,Brown,78,F
fs.createReadStream('data.csv')
.pipe(csv())
.on('data', (row) => {
console.log(row);
if(row) {
firestore.collection('csv').add({
name: row.Name,
surname: row.Surname,
age: row.Age,
sex: row.Gender
});
}
else {
console.log('No data')
}
})
.on('end', () => {
console.log('CSV file successfully processed');
});
There is now a paid GUI tool that does this, Firefoo. I haven't used it, but it seems quite powerful for managing Firestore - including importing csv files:
This is a small modification that copies the 'id' of the document, if exists, to its path. Otherwise it will use the "for"'s index.
...
...
} else {
// Collection's length of is always odd.
for (const key in data) {
// Resolve each collection.
if (data[key]['id']!==undefined)
await resolve(data[key], [...path,data[key]['id']])
else
await resolve(data[key], [...path, key]);
}
}
}
resolve(data);
1 - Importing only collections
If the names of your collections are not only composed of numbers, then you can define the name of the document as follows.
...
...
} else {
// Collection's length of is always odd.
for (const key in data) {
// // Resolve each collection.
// If key is a number it means that it is not a collection
// The variable id in this case will be the name of the document field or you
// can generate randomly
let id = !isNaN(key) ? data[key].id : key;
await resolve(data[key], [...path, id]);
}
}
}
2 - Import collections and sub-collections
In the same way as in the example above, the name of the sub-collection can not contain only numbers.
...
...
for (const key in data) {
// Resolve each collection and remove it from document data.
if (isCollection(data[key], [...path, key])) {
// Remove a collection from the document data.
delete documentData[key];
// If key is a number it means that it is not a collection
// The variable id in this case will be the name of the document field or you
// can generate randomly
let id = !isNaN(key) ? data[key].id : key;
// Resolve a colleciton.
resolve(data[key], [...path, id]);
}
}
...
...
Note.: Changes in #Maciej Caputa's code
this library you can use https://www.npmjs.com/package/csv-to-firestore
install this library with this command npm i -g csv-to-firestore
put this script inside your ./scripts
module.exports = {
path: "./scripts/palabrasOutput.csv",
firebase: {
credential: "./scripts/serviceAccount.json",
collection: "collectionInFirestore",
},
mapper: (dataFromCSV) => {
return dataFromCSV;
},
};
and run:
csv-to-firestore --config ./scripts/csvToFirestore.js
btw: Generate new private key (it's a file in Service account section in firestore)
put that file in /scripts folder and rename serviceAccount.json

Resources