Current Code
// items is an array.
// Array [
Object {
"id": "KQJfb2RkT",
"name": "first",
},
Object {
"id": "1mvshyh9H",
"name": "second",
},
]
storeSale = async ({ items }) => {
this.salesCollection.add({
status: 1,
created_at: new Date(),
updated_at: new Date(),
});
};
When adding a document in SalesCollection, I want to add items as subcollection to this document.
I would appreciate it if you could give me any advices.
I would like to save like this.
enter image description here
You can use a batched write, as follows:
// Get a new write batch
let batch = db.batch();
// Set the value of parent
const parentDocRef = db.collection("parentColl").doc();
batch.set(parentDocRef, {
status: 1,
created_at: new Date(),
updated_at: new Date(),
});
//Set the value of a sub-collection doc
const parentDocId = parentDocRef.id;
const subCollectionDocRef = db.collection("parentColl").doc(parentDocId).collection("subColl").doc();
batch.set(subCollectionDocRef, {
...
});
// Commit the batch
await batch.commit();
One key point to note: Actually, from a technical perspective, a parent collection and the sub-collections of the documents in this parent collection are not at all relating to each other.
Let's take an example: Imagine a doc1 document under the col1 collection
col1/doc1/
and another one subDoc1 under the subCol1 (sub-)collection
col1/doc1/subCol1/subDoc1
These two documents (and the two immediate parent collections, i.e. col1 and subCol1) just share a part of their path but nothing else.
One side effect of this is that if you delete a document, its sub-collection(s) still exist.
Related
I got stuck in this puzzle which doesn't seem to wanna be solved, I am kinda sure I am forgetting something since I just started learning react-native.
I have this code :
async componentDidMount() {
let user = await UserRepository.getUserRef(firebase.auth().currentUser.uid);
await firebase
.firestore()
.collection("reminder")
.where("user", "==", user)
.get()
.then((remindersRecord) => {
remindersRecord.forEach((reminderDoc) => {
console.log(reminderDoc.data());
});
});
I am trying to get the "reminders" data of the connected user, the query works since we got reminderDoc which contain a bunch of objects, and inside there is the data I want but when I call data() nothing changes, I don't get the document it returns the same object.
Reminder collection :
Any help is much appreciated!
I tried to replicate this on my side and I think this is working fine. I think that result that you get is related with fields boss and user which I guess are reference type in firestore. If you log to console such fields give results like this:
{
reference: DocumentReference {
_firestore: Firestore {
_settings: [Object],
_settingsFrozen: true,
_serializer: [Serializer],
_projectId: <PROJECT_ID>,
registeredListenersCount: 0,
bulkWritersCount: 0,
_backoffSettings: [Object],
_clientPool: [ClientPool]
},
_path: ResourcePath { segments: [Array] },
_converter: {
toFirestore: [Function: toFirestore],
fromFirestore: [Function: fromFirestore]
}
},
text_field: 'test',
...
}
So for presented example you will get 2 such fields and for those fields you will not see as a string. BTW the timestamp field will not be shown properly as well.
To avoid this issue you can use example path property of document reference or when it comes to timestamp you can use toDate() method. I have created small example to show the fields properly (looping over all the object fields):
remindersRecord.forEach((reminderDoc) => {
for (const [key, value] of Object.entries(reminderDoc.data())) {
if (key == 'boss' || key == 'user') console.log(`${key}: ${value.path}`)
else if (key == 'startAt') console.log(`${key}: ${value.toDate()}`)
else console.log(`${key}: ${value}`)
});
I tested this in nodejs directly, but it should work in componentDidMount as well.
I'm trying to execute the below SP
function createMyDocument() {
var collection = getContext().getCollection();
var doc = {
"someId": "123134444",
};
var options = {};
options['PartitionKey'] = ["someId"];
var isAccepted = collection.upsertDocument(collection.getSelfLink(), doc, options, function (error, resources, options) {
});
}
and cosmos keeps on complaining that there's something wrong with the partition key
{ code: 400,
body: '{"code":"BadRequest","message":"Message: {\\"Errors\\":
[\\"PartitionKey extracted from document doesn\'t match the one specified in the header\\"]}
}
Does anyone have any idea how to pass in the partion key in options so it gets pass this validation ?
Figured it out. The error was with how we call the stored proc.
How we were doing it
client.executeStoredProcedure('dbs/db1/colls/coll-1/sprocs/createMyDocument',
{},
{} //Here you have to pass in the partition key
);
How it has to be
client.executeStoredProcedure('dbs/db1/colls/coll-1/sprocs/createMyDocument',
{},
{"partitionKey": "43321"}
);
I think you misunderstand the meaning of partitionkey property in the options[].
For example , my container is created like this:
The partition key is "name" for my collection here. You could check your collection's partition key.
And my documents as below :
{
"id": "1",
"name": "jay"
}
{
"id": "2",
"name": "jay2"
}
My partitionkey is 'name', so here I have two paritions : 'jay' and 'jay1'.
So, here you should set the partitionkey property to '123134444' in your question, not 'someId'.
More details about cosmos db partition key.
Hope it helps you.
I have a dynamodb table that stores users videos.
It's structured like this:
{
"userid": 324234234234234234, // Hash key
"videoid": 298374982364723648 // Range key
"user": {
"username": "mario"
}
}
I want to update username for all videos of a specific user. It's possible with a simple update or i have to scan the complete table and update one item a time?
var params = {
TableName: DDB_TABLE_SCENE,
Key: {
userid: userid,
},
UpdateExpression: "SET username = :username",
ExpressionAttributeValues: { ":username": username },
ReturnValues: "ALL_NEW",
ConditionExpression: 'attribute_exists (userid)'
};
docClient.update(params, function(err, data) {
if (err) fn(err, null);
else fn(err, data.Attributes.username);
});
I receive the following error, I suppose the range key is necessary.
ValidationException: The provided key element does not match the schema
Dynamo does not support write operations across multiple items (ie. for more than one item at a time). You will have to first scan/query the table, or otherwise generate a list of all items you'd like to update, and then update them one by one.
Dynamo does provide a batching API but that is still just a way to group updates together in batches of 25 at a time. It's not a proxy for a multi-item update like you're trying to achieve.
I'm using Redux and ImmutableJS to manage the state of my app. I've created the following two Records:
export const OrderRecord = Record({
id: null,
productId: null,
amount: 1,
});
export const ProductRecord = Record({
id: null,
name: '',
price: 0,
});
My global state is normalized based on the normalizr approach like this:
const state = {
entities: {
orders: new OrderedMap(new Map({
1: new OrderRecord(createOrderItem(1, 1)),
})),
products: new OrderedMap(new Map({
1: new ProductRecord(createProductItem(1)),
})),
},
};
I'm using this specification for testing purposes.
Now I'm trying to make some selects with computed fields using Reselect.
export const getVisibleOrders = createSelector(
[getProducts, getOrders],
(products, orders) => {
orders.map(order => {
const product = products.get(order.productId.toString());
if (!product) {
return order;
}
const totalPrice = order.amount * product.price;
order.set('productName', product.name);
order.set('totalPrice', totalPrice);
return order;
});
}
);
, but I get the following error message:
Error: Cannot set unknown key "productName" on Record
I know the reason - Record cannot contain any undefined keys, but my question is: Is there any suggested approach how gracefully solved this problem?
I don't want to extend my Records to support this kind of computed parameters (product.name and totalPrice).
I don't want to keep the static and computed parameters in one place, because for example the 'productName' parametr is from "Product" entity and not from "Order" entity.
Thank you.
The whole point of using Immutable.Record is to not let you add new keys to your record, hence the error message you get. And the whole point of selectors is to expose such "computed" property if you want to consume them outside. In your case, you can simply return a new Map() object or a new record type if you need to use the dotted syntax :
return Map({
productName: 'foobar'
}).merge(order)
I'm getting the error "_id is not allowed by the schema" when trying to use an autoform to update a collection via a ValidatedMethod.
As far as I can see from this example and the official docs there is no expectation for my schema to include the _id field, and I wouldn't expect to be updating the id from an update statement, so I have no idea why this error is happening.
If I switch from using the validated method to writing directly to the collection (with a schema attached to the collection that doesn't have the id in) everything works as expected, so I'm assuming the issue is with my the validate in my ValidatedMethod.
Any idea what I'm doing wrong?
Template: customer-edit.html
<template name="updateCustomerEdit">
{{> quickForm
collection="CustomerCompaniesGlobal"
doc=someDoc
id="updateCustomerEdit"
type="method-update"
meteormethod="CustomerCompanies.methods.update"
singleMethodArgument=true
}}
</template>
Template 'code behind': customer-edit.js
Template.updateCustomerEdit.helpers({
someDoc() {
const customerId = () => FlowRouter.getParam('_id');
const instance = Template.instance();
instance.subscribe('CustomerCompany.get', customerId());
const company = CustomerCompanies.findOne({_id: customerId()});
return company;
}
});
Update Validated Method:
// The update method
update = new ValidatedMethod({
// register the name
name: 'CustomerCompanies.methods.update',
// register a method for validation, what's going on here?
validate: new SimpleSchema({}).validator(),
// the actual database updating part validate has already been run at this point
run( newCustomer) {
console.log("method: update");
return CustomerCompanies.update(newCustomer);
}
});
Schema:
Schemas = {};
Schemas.CustomerCompaniesSchema = new SimpleSchema({
name: {
type: String,
max: 100,
optional: false
},
email: {
type: String,
max: 100,
regEx: SimpleSchema.RegEx.Email,
optional: true
},
postcode: {
type: String,
max: 10,
optional: true
},
createdAt: {
type: Date,
optional: false
}
});
Collection:
class customerCompanyCollection extends Mongo.Collection {};
// Make it available to the rest of the app
CustomerCompanies = new customerCompanyCollection("Companies");
CustomerCompaniesGlobal = CustomerCompanies;
// Deny all client-side updates since we will be using methods to manage this collection
CustomerCompanies.deny({
insert() { return true; },
update() { return true; },
remove() { return true; }
});
// Define the expected Schema for data going into and coming out of the database
//CustomerCompanies.schema = Schemas.CustomerCompaniesSchema
// Bolt that schema onto the collection
CustomerCompanies.attachSchema(Schemas.CustomerCompaniesSchema);
I finally got to the bottom of this. The issue is that autoform passes in a composite object that represents the id of the record to be changed and also a modifier ($set) of the data, rather than just the data itself. So the structure of that object is along the lines of:
_id: '5TTbSkfzawwuHGLhy',
modifier:
{
'$set':
{ name: 'Smiths Fabrication Ltd',
email: 'info#smithsfab.com',
postcode: 'OX10 4RT',
createdAt: Wed Jan 27 2016 00:00:00 GMT+0000 (GMT Standard Time)
}
}
Once I figured that out, I changed my update method to this and everything then worked as expected:
// Autoform specific update method that knows how to unpack the single
// object we get from autoform.
update = new ValidatedMethod({
// register the name
name: 'CustomerCompanies.methods.updateAutoForm',
// register a method for validation.
validate(autoformArgs) {
console.log(autoformArgs);
// Need to tell the schema that we are passing in a mongo modifier rather than just the data.
Schemas.CustomerCompaniesSchema.validate(autoformArgs.modifier , {modifier: true});
},
// the actual database updating part
// validate has already been run at this point
run(autoformArgs)
{
return CustomerCompanies.update(autoformArgs._id, autoformArgs.modifier);
}
});
Excellent. Your post helped me out when I was struggling to find any other information on the topic.
To build on your answer, if for some reason you want to get the form data as a single block you can use the following in AutoForm.
type="method" meteormethod="myValidatedMethodName"
Your validated method then might look something like this:
export const myValidatedMethodName = new ValidatedMethod({
name: 'Users.methods.create',
validate(insertDoc) {
Schemas.NewUser.validate(insertDoc);
},
run(insertDoc) {
return Collections.Users.createUser(insertDoc);
}
});
NB: The Schema.validate() method then requires an Object, not the modifier as before.
I'm unclear if there are any clear advantages to either method in general.
The type="method-update" is obviously the way you want to go for updating documents because you get the modifier. The type="method" seems to be the best way to go for creating a new document. It would likely also be the best option in most cases where you're not intending to create a document from the form data.