Firstore create multiple documents at once - javascript

I have given an array of document id's which I want to add to Firestore.
let documentIds = [
'San Francisco',
'New York',
'Las Vegas'
]
Each document should have a predefined set of properties.
let data = {
country: 'US',
language: 'English'
}
Is there some function inside the Firebase Admin SDK that can create all documents at once, without iterating through the array of documentIds?

This should help. Sam's answer will give you a good understanding of how to use batch writes. Be sure to check out the documentation that he linked to.
I don't want to upset Sam. He fixed my laptop, yesterday :-)
Setting a batch with an array
let documentIds = [
'San Francisco',
'New York',
'Las Vegas'
]
let data = {country: 'US', language: 'English'}
var batch = db.batch();
documentIds.forEach(docId => {
batch.set(db.doc(`cities/${docId}`), data);
})
batch.commit().then(response => {
console.log('Success');
}).catch(err => {
console.error(err);
})

You can do a batched write. You'll still need to iterate through the docs to add them all to the batch object, but it will be a single network call:
Example:
// Get a new write batch
var batch = db.batch();
// Set the value of 'NYC'
var nycRef = db.collection("cities").doc("NYC");
batch.set(nycRef, {name: "New York City"});
// Update the population of 'SF'
var sfRef = db.collection("cities").doc("SF");
batch.update(sfRef, {"population": 1000000});
// Delete the city 'LA'
var laRef = db.collection("cities").doc("LA");
batch.delete(laRef);
// Commit the batch
batch.commit().then(function () {
// ...
});
https://firebase.google.com/docs/firestore/manage-data/transactions#batched-writes

Related

Join object string values that exist and in a custom order

I am being passed an object containing address data (in alphabetical order) that I am wanting to extract a sub-set of to display, but in a different order.
Not all of the keys will have data in all of the time, sometimes having an empty string instead of nothing at all.
const address = {
buildingName: '',
company: 'My org',
county: 'My County',
postCode: 'My Postcode',
streetName: 'My street',
townCity: 'My Town'
};
I know that I can get all of the present values out by doing:
Object.keys(address).filter(Boolean).join(', ')
However I am wanting the order of the information being output to be: company, buildingName, streetName, townCity, county, postCode.
Is there any way in which I can alter my current solution, or do I need a different approach entirely?
I managed to get a solution combining Jan Pfeifer and Gog's solutions to some extent, along with my own experimentation.
It also transpired that fields with no value weren't being returned as empty strings, but weren't included, so that made things a bit easier.
const ADDRESS_ORDER = ['company', 'buildingName', 'streetName', 'townCity', 'county', 'postCode'];
return const addressString = ADDRESS_ORDER
.filter(detail => Object.keys(address).includes(detail))
.map(key => address[key])
.join(', ');
const ADDRESS_ORDER = ['company', 'buildingName', 'streetName', 'townCity', 'county', 'postCode'];
const address = {
company: 'My org',
county: 'My County',
postCode: 'My Postcode',
streetName: 'My street',
townCity: 'My Town'
};
const result = ADDRESS_ORDER
.filter(detail => Object.keys(address).includes(detail))
.map(key => address[key])
.join(', ');
console.log({result});
If this was any longer or more complex, I would consider tidying up by combining the fileter and map into a reduce, but I don't think it warrants this at the moment.
Because you need custom order you will have to create that object manually. You can use template array, but that is more or less the same solution.
const address = {
buildingName: '',
company: 'My org',
county: 'My County',
postCode: 'My Postcode',
streetName: 'My street',
townCity: 'My Town'
};
const tmp = ["company", "buildingName", "streetName", "townCity", "county", "postCode"];
let s = "";
tmp.forEach(key => {
const v = address[key];
if(v) {
if(s) s += ", ";
s += v;
}
});
console.log(s);
You could create an addressOrder array, to specify the order of the various address components, then use Array.reduce() to add to the output address components.
We'd only add the address to the output if it's present in the address object.
Finally we'd join the address components using the provided delimiter.
const address = {
buildingName: 'Nakatomi Towers',
company: 'Nakatomi Corp.',
county: 'LA County',
postCode: '90067',
streetName: 'Century City',
townCity: 'LA'
};
function formatAddress(address, addressOrder, delimiter = ', ') {
return addressOrder.reduce((outputFields, field) => {
if (address[field]) {
outputFields.push(address[field]);
}
return outputFields;
}, []).join(delimiter);
}
const order1 = ['company','buildingName','streetName','townCity','county','postCode']
const order2 = ['buildingName','streetName','townCity','postCode']
console.log('Address:', formatAddress(address, order1))
console.log('\nAddress (alt. format):\n' + formatAddress(address, order2, '\n'))
.as-console-wrapper { max-height: 100% !important; }

How to get access to the PromiseResult in React when calling Azure Cosmos DB api

I'm trying to pull some rows from a Cosmos DB and then map through them to display a component for each. All of the documentation I can find for the Azure Cosmos DB API logs rows to the console one by one but I can't find one then tells you how to return the whole string.
I'm new to this so am probably doing it horribly wrong but I am now stuck and cannot move on. Hope you can help ...
In my App.js I have this
function App() {
const [newMembers, setNewMembers] = useState(dataFetch());
In my dataFetch.js I have
export default async function dataFetch() {
const { endpoint, key, databaseId, containerId } = config;
const client = new CosmosClient({ endpoint, key });
const database = client.database(databaseId);
const container = database.container(containerId);
// Make sure Tasks database is already setup. If not, create it.
// await dbContext.create(client, databaseId, containerId);
try {
console.log(`Querying container: Items`);
// query to return all items
const querySpec = {
query: "SELECT * from c",
};
// read all items in the Items container
const { resources: items } = await container.items
.query(querySpec)
.fetchAll();
return items;
} catch (err) {
console.log(err.message);
}
}
When I console.log the result back it says
Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Array(3)
0: {id: '154', forename: 'Fred', surname: 'Robson', address1: 'Some address', address2: 'Kingsmead', …}
1: {id: '416', forename: 'Lee', surname: 'Robson', address1: 'Some address', address2: 'Kingsmead', …}
2: {id: '900', forename: 'Kathryn', surname: 'Robson', address1: 'Some address', address2: 'Kingsmead', …}
length: 3
[[Prototype]]: Array(0)
I saw something elsewhere about using .then but when I tried
const { resources: items } = await container.items
.query(querySpec)
.fetchAll()
.then(() => {
return items;
});
It said "dataFetch.js:33 Cannot access 'items' before initialization"
What you're doing in the first two code blocks is perfectly fine. However with putting the result of dataFetch() into the newMembers state, you're just storing the promise in there which just get's resolved at some point and you can retrieve the results at any point with newMembers.then(actualResult => ...).
However, I think you would rather like to keep the actual members array in the newMembers state. This can be done by e.g.:
function App() {
const [newMembers, setNewMembers] = useState([]); // initially set to an empty array
useEffect(() => {
dataFetch().then(members => setMembers(members));
}, []); // providing an empty array means: run this effect only once when this component is created.
// do something with the newMembers. Initially, it will be []
// but as soon as the data is retrieved from the DB, the actual data will be
// in the state and the component will be rerendered.
}

Objects with same ID pushed into same array

how can i get all the customers with the same id in each Object into One array (unless you have a better idea)
so i can get the start and end time to show in a table for that specific customer.
the idea here is to show a table with fixed columns, and each row would be filled with customer name and start time for each of these columns instead of having multiple rows.
im looping over my API data in which it's returning multiple Objects with each having its own values for Example:
Better to use Object, where the key is the actual ID identifier: {ID1: [], ID2: []}- that way by just targeting the ID you get back all the data for that customer ID:
const response = [
{customerId:"11", customerName:"John", serviceName: "foo"},
{customerId:"88", customerName:"Anne", serviceName: "bar"},
{customerId:"11", customerName:"John", serviceName: "baz"},
];
const customers = response.reduce((dict, data) => {
if (!dict[data.customerId]) dict[data.customerId] = [];
dict[data.customerId].push(data);
return dict;
}, {});
// Get specific customer data (customerId "11"):
console.log(customers["11"]);
// Get all customers data:
console.log(customers);
This can also be done using the Map Object and its has, get and set methods:
const response = [
{customerId:"11", customerName:"John", serviceName: "foo"},
{customerId:"88", customerName:"Anne", serviceName: "bar"},
{customerId:"11", customerName:"John", serviceName: "baz"},
];
const customers = response.reduce((m, {customerId:id, ...r}) => {
if (!m.has(id)) m.set(id, []);
return (m.get(id).push(r), m);
}, new Map());
// Get specific customer data (customerId "11"):
console.log(customers.get("11"));
// Get all customers data:
console.log([...customers]);

Write data starting from a specific row/column using module exceljs with node.js

I want to write data to a xlsx file starting from a specific row/column. I've found a lot of websites about exceljs but none helps me.
for (let i = 0; i < people.length; i++) {
let {name, age} = people[i]
worksheet.addRow({name: name, age: age})
}
worksheet.eachRow(function(row, rowNumber) {
row.eachCell(function(cell, colNumber) {
// some code
}
}
I don't know other way but could managed it like this:
const ExcelJS = require('exceljs');
const workbook = new ExcelJS.Workbook();
const sheet = workbook.addWorksheet('Node-Cheat');
// keep {} where you wan to skip the column
sheet.columns = [{}, {key: 'first', header: 'First'}, {}, {key: 'last', header: 'Last'}];
// keep {} where you wan to skip the row
const data = [{first:'John', last: 'Doe'}, {}, {first:'Jenny', last: 'Smith'}];
data.forEach((item, i) => {
sheet.addRow(item);
});
workbook.xlsx.writeFile('node-cheat.xlsx').then(() => {
console.log('Finished...');
});
For complete running example clone node-cheat and run node write_xlsx.js.
P.S. Using this approach requirements could be fulfilled as far as I understand.
Output screenshot:
You could use Excel JS API, the code example shows how to write the data to a special row/column. the gist can be found at:https://gist.github.com/lumine2008/796915d73a56a9e89ff9393c77845759
await Excel.run(async (context) => {
context.workbook.worksheets.getItemOrNullObject("Sample").delete();
const sheet = context.workbook.worksheets.add("Sample");
const data = [
["Product", "Qty", "Unit Price", "Total Price"],
["Almonds", 2, 7.5, "=C3 * D3"],
["Coffee", 1, 34.5, "=C4 * D4"],
["Chocolate", 5, 9.56, "=C5 * D5"]
];
const range = sheet.getRange("B2:E5");
range.values = data;
range.format.autofitColumns();
const header = range.getRow(0);
header.format.fill.color = "#4472C4";
header.format.font.color = "white";
sheet.activate();
await context.sync();
I also ran up against this. You want to use insertRow, not addRow. If you already have your header defined with column keys, you can pass your data object along with a position number.
From the documentation:
// Insert a couple of Rows by key-value, shifting down rows every time
worksheet.insertRow(1, {id: 1, name: 'John Doe', dob: new Date(1970,1,1)});
worksheet.insertRow(1, {id: 2, name: 'Jane Doe', dob: new Date(1965,1,7)});
that first number parameter (position) will insert the row and shift down any existing row, as opposed to overwriting what's already there. So if you wanted to, for example, insert a row at the bottom of your existing worksheet, you would use the number that indicates the desired position.
worksheet.insertRow(12, {id: 3, name: 'John Doe', dob: new Date(1970,1,1)});
In the above example, the new row would be inserted at position 12 (the twelfth row down).
https://www.npmjs.com/package/exceljs#insert-rows

How to retrieve Firebase pushed key?

I have an event I'm storing in my database. It is stored as
events
my_party123
info
-KZbdiR_PBrAwxYvUR4U
name: 'John'
city: 'Los Angeles'
my_party124
...
However, I'm trying to retrieve this info and I can't quite get it.
I tried
ref
.child('events')
.child('my_party123') // I also tried .push('my_party123')
.once('value')
.then(snapshot => {
console.log(snapshot.key) // my_party123
console.log(snapshot.val().info)
// this shows object with
// "-KZbdiR_PBrAwxYvUR4U" property, this is the pushed key property
im trying to access
})
.pushed is creating new keys when i called it - how do i just access the value stored in my database?
If you're looking to have unique event URLs that point at events, I'd recommend a change to your data structure to be something more like:
events
-KZ123PUSHID
name: 'John'
location: 'Los Angeles'
-KZ124PUSHID
name: 'Morgan'
location: 'New York City'
eventNames
my_party_123: '-KZ123PUSHID'
sweet_nyc_part: '-KZ124PUSHID'
Then, in your code, you could do something like:
var eventNamesRef = firebase.database().ref('eventNames');
var eventsRef = firebase.database().ref('events');
var event;
eventNamesRef.child(nameFromUrl).once('value').then(nameSnap => {
eventsRef.child(nameSnap.val()).on('value', eventSnap => {
event = eventSnap.val();
});
});

Categories

Resources