How to add new fields in all documents in Firestore? - firebase

Lets say I have 100 documents with fields
Name
Age
Address
Now suppose my business model is change and I want to add new field call PhoneNumber.
How to add field PhoneNumber in all 100 documents ?
Is is possible to such stuff on NoSQL database?

You will have to write code to iterate all the documents to update, then actually update a new value in each one of them. Firestore has no similar command as "update tablename set x=y where ..." in SQL.

Is is possible to such stuff on NoSQL database?
Yes it is! Assuming you have a User model class that look like this:
public class User {
private String name;
private int age;
private String address;
private String phoneNumber; //Property that is newly added
public User() {}
public User(String name, int age, String address, String phoneNumber) {
this.name = name;
this.age = age;
this.address = address;
this.phoneNumber = phoneNumber;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
public String getPhoneNumber() { return phoneNumber; }
public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; }
}
To actually add a new property and update it accordingly, you need to use setters. If you are setting the values directly onto the public fields, the setters are not mandatory.
How to add field PhoneNumber in all 100 documents?
As also #Doug Stevenson mentioned in his answer, to solve this, you need to iterate all the documents within your users collection. So please use the following lines of code:
db.collection("users").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
User user = document.toObject(User.class);
user.setPhoneNumber("+1-111-111-111"); //Use the setter
String id = document.getId();
db.collection("users").document(id).set(user); //Set user object
}
}
}
});
The result of this code would be to add the phoneNumber property to all you User objects with a default value of +1-111-111-111. You can also set the value to null if it's more convenient for you. At the end, the updated object is set right on the corresponding reference.
If you are not using a model class, please see my answer from this post.

Related

How to query DynamoDB using ONLY Partition Key [Java]?

I am new to DynamoDB and wanted to know how can we query on a table in DynamoDB by using ONLY partition key in JAVA
I have table called "ervive-pdi-data-invalid-qa" and it's Schema is :
partition key is "SubmissionId"
Sort key is "Id".
City (Attribute)
Errors (Attribute)
The table looks like this:
Table
I want to retrieve the sort key value and remaining attributes data by using Partition key using (software.amazon.awssdk) new version of AWS SDK DynamoDB classes.
is it possible to get it? If so, can any one post the answers?
Have tried this:
DynamoDbClient ddb =
DynamoDbClient.builder().region(Region.US_EAST_1).build();
DynamoDbEnhancedClient enhancedClient =
DynamoDbEnhancedClient.builder()
.dynamoDbClient(ddb)
.build();
//Define table
DynamoDbTable<ErvivePdiDataInvalidQa> table =
enhancedClient.table("ervive-pdi-data-invalid-qa",
TableSchema.fromBean(ErvivePdiDataInvalidQa.class));
Key key = Key.builder().partitionValue(2023).build();
ErvivePdiDataInvalidQa result = table.getItem(r->r.key(key));
System.out.println("The record id is "+result.getId());
ErvivePdiDataInvalidQa table class is in below comment*
and it is returning "The provided key element does not match the schema (Service: DynamoDb, Status Code: 400, Request ID: PE1MKPMQ9MLT51OLJQVDCURQGBVV4KQNSO5AEMVJF66Q9ASUAAJG, Extended Request ID: null)"
Query you need is documented in one of the examples of AWS Dynamodb Query API for Java.
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
.withRegion(Regions.US_WEST_2).build();
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("ervive-pdi-data-invalid-qa");
QuerySpec spec = new QuerySpec()
.withKeyConditionExpression("SubmissionId = :v_id")
.withValueMap(new ValueMap()
.withInt(":v_id", 2146));
ItemCollection<QueryOutcome> items = table.query(spec);
Iterator<Item> iterator = items.iterator();
Item item = null;
while (iterator.hasNext()) {
item = iterator.next();
System.out.println(item.toJSONPretty());
}
A single Query operation can retrieve a maximum of 1 MB of data, see documentation
I have been working with Padma on this issue. We first tried A. Khan's code but could not get passed authentication with v1. Instead we got "WARNING: Your profile name includes a 'profile ' prefix. This is considered part of the profile name in the Java SDK, so you will need to include this prefix in your profile name when you reference this profile from your Java code."
ultimately it could not get the credentials. Our credentials assume IAM roles in .aws/config-i2 file. It works fine in v2 but not v1.
So then we tried v2 of the SDK and have no problems with connecting but we get NULL returned on trying to fetch all records from the table.
In all of the below attempts using v2 of SDK, table data returns NULL
We created this table class
package data;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;
#DynamoDbBean
public class ErvivePdiDataInvalidQa {
private int submissionId;
private String id;
private String address1;
private String city;
private String dateOfBirth;
private String errors;
private String firstName;
private String firstNameNormalized;
private String gender;
private String lastName;
private String lastNameNormalized;
private String middleNameInitial;
private String postalCode;
private String rowNumber;
private String state;
private String submissionType;
#DynamoDbPartitionKey
public int getSubmissionId() {
return submissionId;
}
public void setSubmissionId(int submissionId) {
this.submissionId = submissionId;
}
#DynamoDbSortKey
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAddress1() {
return address1;
}
public void setAddress1(String Address1) {
this.address1 = Address1;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(String dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public String getErrors() {
return errors;
}
public void setErrors(String errors) {
this.errors = errors;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getFirstNameNormalized() {
return firstNameNormalized;
}
public void setFirstNameNormalized(String firstNameNormalized) {
this.firstNameNormalized = firstNameNormalized;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getLastNameNormalized() {
return lastNameNormalized;
}
public void setLastNameNormalized(String lastNameNormalized) {
this.lastNameNormalized = lastNameNormalized;
}
public String getMiddleNameInitial() {
return middleNameInitial;
}
public void setMiddleNameInitial(String middleNameInitial) {
this.middleNameInitial = middleNameInitial;
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public String getRowNumber() {
return rowNumber;
}
public void setRowNumber(String rowNumber) {
this.rowNumber = rowNumber;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getSubmissionType() {
return submissionType;
}
public void setSubmissionType(String submissionType) {
this.submissionType = submissionType;
}
}
DynamoDB code to get all records
//Connection
DynamoDbClient ddb = DynamoDbClient.builder().build();
DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
.dynamoDbClient(ddb)
.build();
//Define table
DynamoDbTable<ErvivePdiDataInvalidQa> table = enhancedClient.table("ervive-pdi-data-invalid-qa", TableSchema.fromBean(ErvivePdiDataInvalidQa.class));
//Get All Items from table - RETURNING NULL
Iterator<ErvivePdiDataInvalidQa> results = table.scan().items().iterator();
while (results.hasNext()) {
ErvivePdiDataInvalidQa rec = results.next();
System.out.println("The record id is "+rec.getId());
}
Also tried:
DynamoDB code to filter by SubmissionID
AttributeValue attr = AttributeValue.builder()
.n("1175")
.build();
// Get only Open items in the Work table
Map<String, AttributeValue> myMap = new HashMap<>();
myMap.put(":val1", attr);
Map<String, String> myExMap = new HashMap<>();
myExMap.put("#sid", "SubmissionId");
// Set the Expression so only Closed items are queried from the Work table
Expression expression = Expression.builder()
.expressionValues(myMap)
.expressionNames(myExMap)
.expression("#sid = :val1")
.build();
ScanEnhancedRequest enhancedRequest = ScanEnhancedRequest.builder()
.filterExpression(expression)
.limit(15)
.build();
// Get items in the Record table and write out the ID value
Iterator<ErvivePdiDataInvalidQa> results = table.scan(enhancedRequest).items().iterator();
while (results.hasNext()) {
ErvivePdiDataInvalidQa record = results.next();
System.out.println("The record id is " + record.getId());
}

How to use Firebase Query for data with anonymous keys

I have the following database data which I intend to display on a ListView with FirebaseListAdapter
My problem is creating a Query since the child elements after date are anonymous. Here is the query code
Query query = FirebaseDatabase.getInstance().getReference().child("Updates").child(refMail).child(day)
.orderByKey();
refMail and day are user email address and date respectively.
Here is also my Data Model Class
public class NotesDataModel {
private String Note;
private String uid;
private String time;
public NotesDataModel(){
}
public NotesDataModel(String Note, String uid, String time){
this.Note=Note;
this.uid=uid;
this.time=time;
}
public String getNote() {
return Note;
}
public void setNote(String note) {
Note = note;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}}
and finally the adapter initialization
FirebaseListOptions<NotesDataModel> options = new FirebaseListOptions.Builder<NotesDataModel>()
.setQuery(query, NotesDataModel.class)
.setLayout(R.layout.notes_cell_layout)
.build();
mAdapter = new FirebaseListAdapter<NotesDataModel>(options) {
#Override
protected void populateView(View view, NotesDataModel note, int position) { //some code }}; notesList.setAdapter(mAdapter);
Previous version worked like this
ref = FirebaseDatabase.getInstance().getReferenceFromUrl(FactoryDaftari.firebaseURL + "Updates/" + refMail + "/" + day);
And the Adapter initialization
mAdapter = new FirebaseListAdapter<NotesDataModel>(this, NotesDataModel.class, R.layout.notes_cell_layout, ref) {
#Override
protected void populateView(View view, NotesDataModel note, int position) { }};
You won't be able to make this query with the way your data is structured. It's common in NoSQL databases to make copies of data, structured for the purpose of specialized querying. So, if you want to query a list of notes, you'll need a structure where all the notes are children of the same parent, then make your query against that structure.
(Also, organizing your notes by a node with a date, like you have now, may not even be the best general structure in the first place.)

How to make TableCell editable , so it automatically updates the data class?

I am making a system for a school project , and one part of it is a TableView that is populated with rows using my own data class InventoryData that has properties correspondent to the table columns. I would like to make cells in some columns editable using a TextField, so that when an edit is committed, it will update the InventoryData object's relevant property.
I tried setting TextFieldTableCell.forTableColumn() as the cell factory of the columns. Although, now after committing the edit, the text in the cell will change, I don't think it is changing the property in the InventoryData object. The reason why I think that, is because when I try to edit that cell again ( after already being edited once), the TextField shows the former value ( before the first edit).
Did I do something wrong , or is that normal behavior and I have to implement the commits myself?
Here's the code for InventoryData :
package UILayer.TableData;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import ModelLayer.Product;
public class InventoryData {
// From Product
private Product productObj;
private SimpleIntegerProperty id;
private SimpleStringProperty name;
// Constructor - converts Product obj into InventoryData
public InventoryData(Product product)
{
this.productObj = product;
this.id = new SimpleIntegerProperty(product.getId());
this.name = new SimpleStringProperty(product.getName())
}
// GET & SET
public Product getProduct()
{
return productObj;
}
public int getId() {
return id.get();
}
public void setId(int id) {
this.id.set(id);
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
productObj.setName(name);
System.out.println(productObj.getName());
}
}
You need your InventoryData class to use the JavaFX Properties pattern. Specifically it needs property-type accessor methods in order to retrieve the property in the table cells. Without this, the cell value factory just calls the standard getName() or getId() method, and wraps the result in a ReadOnlyStringWrapper (or ReadOnlyIntegerWrapper): the table cell cannot change the values of those wrappers (since they are read only).
public class InventoryData {
// From Product
private Product productObj;
private IntegerProperty id;
private StringProperty name;
// Constructor - converts Product obj into InventoryData
public InventoryData(Product product)
{
this.productObj = product;
this.id = new SimpleIntegerProperty(product.getId());
this.name = new SimpleStringProperty(product.getName())
this.name.addListener((obs, oldName, newName) ->
productObj.setName(newName));
}
// GET & SET
public Product getProduct()
{
return productObj;
}
public IntegerProperty idProperty() {
return id ;
}
public final int getId() {
return idProperty().get();
}
public final void setId(int id) {
idProperty().set(id);
}
public StringProperty nameProperty() {
return name ;
}
public final String getName() {
return nameProperty().get();
}
public final void setName(String name) {
this.nameProperty().set(name);
// productObj.setName(name);
// System.out.println(productObj.getName());
}
}

Creating query and filtering for Coherence cache

I have a simple POJO:
class Person {
String name;
int age;
}
And I want to be able to create an index on a cache that will then allow me to execute the following pseudo-query:
find me all the people whose name is EQUAL to John and their age is GREATERTHAN 30
I've tried MultiExtractor which appears to create the index I want, but when I construct the query using the Filter objects, I seem to still end up with un-optimised queries.
Easiest solution is to add property accessors:
public class Person {
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
private String name;
private int age;
}
Now you can query:
// find me all the people whose name is EQUAL to
// John and their age is GREATERTHAN 30
Filter filter = new AndFilter(
new EqualsFilter("getName", "John"),
new GreaterFilter("getAge", Integer.valueOf(30));
Set setKeys = cache.keySet(filter);
You can also code that filter in SQL. See: https://community.oracle.com/message/11217211#11217211
Other resources:
http://vimeo.com/51314443
http://docs.oracle.com/middleware/1213/coherence/develop-applications/api_cq.htm#COHDG5263
https://www.youtube.com/user/OracleCoherence

How can I retrieve/store a result set to an ArrayList?

How do I use the JdbcTemplate.query()/queryForList() to run a query using namedParameter and store the result set into a List of 'User's?
User Class:
public class User {
String name = null;
String id = null;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return name;
}
public void setId(String id) {
this.id = id;
}
}
Query:
SELECT name, id FROM USERS where email=:email
I'm looking for something like:
ArrayList<User> userList = jdbcTemplate.query(sql_query,
...some_mapper..., etc);
Seems like the answer to the question is not available at one place, on the Internet. Here's what I found out:
For adding the resultset into a List<>, we can use the NamedParameterJdbcTemplate.query() function:
NamedParameterJdbcTemplate jdbcTemplate;
ArrayList<User> usersSearchResult = (ArrayList<User>) jdbcTemplate.query(
USER_LIST_TP_query,
namedParameters,
new RowMapperResultSetExtractor<User>(new UserRowMapper(), 20));
We also have to define a custom RowMapperResultSetExtractor so that JDBC can understand how to convert each row in the result set to the type User.
private class UserRowMapper implements RowMapper<User> {
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getString("ID"));
user.setName(rs.getString("NAME"));
return user;
}
}

Resources