CosmosDB insert item with specific ID - javascript

I was wondering if it is possible to specific the ID when inserting a new object on Cosmos DB.
this.container.items
.create<Session>(session)
.then(() => callback(null))
.catch((error) => callback(error));
Is it possible to specify the ID when inserting it?

The id is specified in the object, in your case, the session object:
interface Session{
id: string;
// other properties
}
let session : Session = {
id: "myId",
// other properties
};
this.container.items
.create<Session>(session)
.then(() => callback(null))
.catch((error) => callback(error));

It is possible as far as I know. Please find the below code as reference:
const documentClient = new DocumentClient({ endpoint, authKey });
const databaseId = 'your_database_id';
const collectionId = 'your_collection_id';
const document = {
id: 'your_specific_id',
property1: 'value1',
property2: 'value2',
};
documentClient.createDocument(
`dbs/${databaseId}/colls/${collectionId}`,
document,
(err, result) => {
if (err) {
console.log(`Error: ${JSON.stringify(err)}`);
} else {
console.log(`Inserted document with ID: ${result.id}`);
}
}
);
In this above code, documentClient is a new DocumentClient object that is created with your CosmosDB endpoint and authentication key. After that, you can define the databaseId and collectionId variables to specify the target database and collection. And then you have to define the document variable that contains the properties of the new item, including the id property with your specific ID.

Related

How to get data from 2 collection in firebase at a time?(similar to aggregate lookup in MongoDB) [duplicate]

I have a Cloud Firestore DB with the following structure:
users
[uid]
name: "Test User"
posts
[id]
content: "Just some test post."
timestamp: (Dec. 22, 2017)
uid: [uid]
There is more data present in the actual DB, the above just illustrates the collection/document/field structure.
I have a view in my web app where I'm displaying posts and would like to display the name of the user who posted. I'm using the below query to fetch the posts:
let loadedPosts = {};
posts = db.collection('posts')
.orderBy('timestamp', 'desc')
.limit(3);
posts.get()
.then((docSnaps) => {
const postDocs = docSnaps.docs;
for (let i in postDocs) {
loadedPosts[postDocs[i].id] = postDocs[i].data();
}
});
// Render loadedPosts later
What I want to do is query the user object by the uid stored in the post's uid field, and add the user's name field into the corresponding loadedPosts object. If I was only loading one post at a time this would be no problem, just wait for the query to come back with an object and in the .then() function make another query to the user document, and so on.
However because I'm getting multiple post documents at once, I'm having a hard time figuring out how to map the correct user to the correct post after calling .get() on each post's user/[uid] document due to the asynchronous way they return.
Can anyone think of an elegant solution to this issue?
It seems fairly simple to me:
let loadedPosts = {};
posts = db.collection('posts')
.orderBy('timestamp', 'desc')
.limit(3);
posts.get()
.then((docSnaps) => {
docSnaps.forEach((doc) => {
loadedPosts[doc.id] = doc.data();
db.collection('users').child(doc.data().uid).get().then((userDoc) => {
loadedPosts[doc.id].userName = userDoc.data().name;
});
})
});
If you want to prevent loading a user multiple times, you can cache the user data client side. In that case I'd recommend factoring the user-loading code into a helper function. But it'll be a variation of the above.
I would do 1 user doc call and the needed posts call.
let users = {} ;
let loadedPosts = {};
db.collection('users').get().then((results) => {
results.forEach((doc) => {
users[doc.id] = doc.data();
});
posts = db.collection('posts').orderBy('timestamp', 'desc').limit(3);
posts.get().then((docSnaps) => {
docSnaps.forEach((doc) => {
loadedPosts[doc.id] = doc.data();
loadedPosts[doc.id].userName = users[doc.data().uid].name;
});
});
After trying multiple solution I get it done with RXJS combineLatest, take operator. Using map function we can combine result.
Might not be an optimum solution but here its solve your problem.
combineLatest(
this.firestore.collection('Collection1').snapshotChanges(),
this.firestore.collection('Collection2').snapshotChanges(),
//In collection 2 we have document with reference id of collection 1
)
.pipe(
take(1),
).subscribe(
([dataFromCollection1, dataFromCollection2]) => {
this.dataofCollection1 = dataFromCollection1.map((data) => {
return {
id: data.payload.doc.id,
...data.payload.doc.data() as {},
}
as IdataFromCollection1;
});
this.dataofCollection2 = dataFromCollection2.map((data2) => {
return {
id: data2.payload.doc.id,
...data2.payload.doc.data() as {},
}
as IdataFromCollection2;
});
console.log(this.dataofCollection2, 'all feeess');
const mergeDataFromCollection =
this.dataofCollection1.map(itm => ({
payment: [this.dataofCollection2.find((item) => (item.RefId === itm.id))],
...itm
}))
console.log(mergeDataFromCollection, 'all data');
},
my solution as below.
Concept: You know user id you want to get information, in your posts list, you can request user document and save it as promise in your post item. after promise resolve then you get user information.
Note: i do not test below code, but it is simplify version of my code.
let posts: Observable<{}[]>; // you can display in HTML directly with | async tag
this.posts = this.listenPosts()
.map( posts => {
posts.forEach( post => {
post.promise = this.getUserDoc( post.uid )
.then( (doc: DocumentSnapshot) => {
post.userName = doc.data().name;
});
}); // end forEach
return posts;
});
// normally, i keep in provider
listenPosts(): Observable<any> {
let fsPath = 'posts';
return this.afDb.collection( fsPath ).valueChanges();
}
// to get the document according the user uid
getUserDoc( uid: string ): Promise<any> {
let fsPath = 'users/' + uid;
return this.afDb.doc( fsPath ).ref.get();
}
Note: afDb: AngularFirestore it is initialize in constructor (by angularFire lib)
If you want to join observables instead of promises, use combineLatest. Here is an example joining a user document to a post document:
getPosts(): Observable<Post[]> {
let data: any;
return this.afs.collection<Post>('posts').valueChanges().pipe(
switchMap((r: any[]) => {
data = r;
const docs = r.map(
(d: any) => this.afs.doc<any>(`users/${d.user}`).valueChanges()
);
return combineLatest(docs).pipe(
map((arr: any) => arr.reduce((acc: any, cur: any) => [acc].concat(cur)))
);
}),
map((d: any) => {
let i = 0;
return d.map(
(doc: any) => {
const t = { ...data[i], user: doc };
++i;
return t;
}
);
})
);
}
This example joins each document in a collection, but you could simplify this if you wanted to just join one single document to another.
This assumes your post document has a user variable with the userId of the document.
J

How to retrieve a document from firestore using firebase function and add new data from another firestore document?

I'm new with firebase cloud function, and have some trouble to get() et set() data from firestore documents within a firebase function.
Here what I try to do within a firebase function :
Access the data of the new document "doc1" when its created in firestore;
Access the value associated with the "user" field of "doc1";
This value is of type "reference", i.e. a path pointing to another document in another firestore collection "col2/doc2"
Use this path to access the second document "doc2" and retrieve two new values belonging to this second document to add it to the first document "doc1";
Final goal is to add the values belonging to the fields "name" and "city" of "doc2" to "doc1" ;
Here what I try up to now, I'm sure I have few problems with syntax and use of then() chain, but the main idea is there :
exports.addDataFromDoc2ToDoc1 = functions.firestore
.document('col1/{doc1Id}')
.onCreate((change, context) => {
const doc1Id = context.params.doc1Id
const doc1 = change.data()
const refToDoc2 = doc1.refField
const doc2Data = refToDoc2.get()
.then(function (documentSnapshot) {
if (documentSnapshot.exists) {
doc2Data = documentSnapshot.data()
return doc2Data
}
})
const doc1Name = doc2Data.doc1Name
const doc1City = doc2Data.doc1City
db.collection('col1')
.doc(doc1Id)
.set({
name: doc1Name,
city: doc1City
});
})
I start from firebase documentation :
https://firebase.google.com/docs/functions/firestore-events
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.writeToFirestore = functions.firestore
.document('some/doc')
.onWrite((change, context) => {
db.doc('some/otherdoc').set({ ... });
});
It would be appreciated if someone could help me with this task, and how I can restructure my algorithm to be more efficient maybe?
Thank you very much for your help and your time!
Since the field is of type Reference, you need to use the path property of the DocumentReference object, as follows:
exports.writeToFirestore = functions.firestore
.document('col1/{doc1Id}')
.onCreate((snap, context) => {
const newValue = snap.data();
const refToDoc2 = newValue.refField;
return db.doc(refToDoc2.path).get()
.then((doc) => {
if (doc.exists) {
const name = doc.data().name;
const city = doc.data().city;
return snap.ref.update({ name, city })
} else {
throw new Error('No document corresponding to the Reference!')
}
})
.catch(error => {
console.log(error);
return null;
});
});
In addition, note how we chain the promises returned by the asynchronous Firestore methods and, very important, how we return this promise chain.
Also note that we use the update() method, instead of the set() one.

How can i update nested data by using mongoose findByIdAndUpdate

i can not able to update nested data in my mongodb. here is my "update" module at back-end side.
exports.updateOne = (req, res) => {
if (!req.body) {
return res.status(400).send({
message: "Data to update can not be empty!"
});
}
const {id} = req.params;
console.log(req.body);
User.findByIdAndUpdate(id, req.body, { useFindAndModify: false, new: true}).populate('basic')
.then(data => {
if (!data) {
res.status(404).send({
message: `Cannot update User with id=${id}. Maybe User was not found!`
});
} else
res.send({ message: "User was dupdated successfully." , data});
})
.catch(err => {
res.status(500).send({
message:
err.message || "Error updating User with id=" + id
});
});
};
and my front-end side is;
onChangePosition(e) {
const position = e.target.value;
this.setState(prevState => ({
currentStaff: {
...prevState.currentStaff,
basic:
{
...prevState.currentStaff.basic,
position:position
}
}
}));
}
onChangeEmail(e) {
const emailBusiness = e.target.value;
this.setState(prevState => ({
currentStaff: {
...prevState.currentStaff,
emailBusiness:emailBusiness
}
}));
}
updateStaff() {
StaffDataService.updateOne(
this.state.currentStaff.id,
this.state.currentStaff
).then(response => {
console.log(response.data);
})
.catch(e => {
console.log(e);
})
}
i can change state properly, and my sending data "req.body" is what i want (it is an object). There is no problem.
as you see above, i can update "email" because it is on the main body of object, but can not update "position" (nested element) because it is inside of basic (populated data).
i tried different methods by mongoose, and tried "$set" command.
Can anyone solve this?
To update, the nested value/object in your document, you should use dot notations, so it depends from the req.body variable value.
req.body shouldn't be a Mongoose doc. In such case you mongoose.toObject.
Second thing is:
[update] Object should be: field_with_subdocument.key_value: updated_propery
like this:
/** Example doc */
{
_id: 1,
parent_field: {
baby_field: value
}
}
/** Inside async function */
...await Model.findByIdAndUpdate(id, { "parent_field.baby_field": value })
Also, take a look at [`{overwrite: true}`](https://mongoosejs.com/docs/api/model.html#model_Model.findByIdAndUpdate) option. It might be useful to you.
I faced the same issue, In my case, the defined mongoose schema for that model did not match the nested Object I was passing to the findByIdAndUpdate method. let me simplify it, here is the model
import { model, Schema } from 'mongooose';
const UserModel = model('user', new Schema({
profile: {
avatar: String,
bio: String,
}
}));
And here is the update query:
async function getDefaultProfile() {
const user = await UserModel.findById(process.env.DEFAULT_USER);
return user.profile;
}
const profile = await getDefaultProfile();
UserModel.findByIdAndUpdate('user id', {
$set: {
profile: profile
}
});
The important note was that my getDefaultProfile function returns a mongoose nested object, not a pure object. So in the returned object, I had $set, $get, etc function. So as you know this object is not what we define in the mongoose model, therefore the mongoose ignores it.
So I guess you have the same problem, or something close to my issue.
What should I do?
Run your project in debugging mode.
then check req.body or whatever that gives you the nested object (in my case getDefaultProfile).
Check it with your model, Are they equal?
And if that solution does not work for you, please try this solution, write a utility function:
export async function flatObjectAndSeparateThemByDot(
object: any,
): Promise<any> {
const res: any = {};
(function recurse(obj: any, current?: string) {
for (const key in obj) {
const value = obj[key];
// joined keys with dot
const newKey = current ? current + '.' + key : key;
if (value && typeof value === 'object') {
// it's a nested object, so do it again
recurse(value, newKey);
} else {
// it's not an object, so set the property
res[newKey] = value;
}
}
})(object);
return res;
}
then you can pass your nested object to this function and you will get something like this: { "profile.avatar": "lorem ipsum", "profile.bio": "bio temp" }. So to show you how this function works I will write a sample code:
const sampleProfile = {
profile: {
avatar: "asd",
bio: "sample"
}
}
const profile = await flatObjectAndSeparateThemByDot(sampleProfile);
await UserModel.findByIdAndUpdate('user id', {
$set: {
// other fields,
...profile
}
});

How to use dataloader?

Im trying to figure this out.
I want to get all my users from my database, cache them
and then when making a new request I want to get those that Ive cached + new ones that have been created.
So far:
const batchUsers = async ({ user }) => {
const users = await user.findAll({});
return users;
};
const apolloServer = new ApolloServer({
schema,
playground: true,
context: {
userLoader: new DataLoader(() => batchUsers(db)),// not sending keys since Im after all users
},
});
my resolver:
users: async (obj, args, context, info) => {
return context.userLoader.load();
}
load method requiers a parameter but in this case I dont want to have a specific user I want all of them.
I dont understand how to implement this can someone please explain.
If you're trying to just load all records, then there's not much of a point in utilizing DataLoader to begin in. The purpose behind DataLoader is to batch multiple calls like load(7) and load(22) into a single call that's then executed against your data source. If you need to get all users, then you should just call user.findAll directly.
Also, if you do end up using DataLoader, make sure you pass in a function, not an object as your context. The function will be ran on each request, which will ensure you're using a fresh instance of DataLoader instead of one with a stale cache.
context: () => ({
userLoader: new DataLoader(async (ids) => {
const users = await User.findAll({
where: { id: ids }
})
// Note that we need to map over the original ids instead of
// just returning the results of User.findAll because the
// length of the returned array needs to match the length of the ids
return ids.map(id => users.find(user => user.id === id) || null)
}),
}),
Note that you could also return an instance of an error instead of null inside the array if you want load to reject.
Took me a while but I got this working:
const batchUsers = async (keys, { user }) => {
const users = await user.findAll({
raw: true,
where: {
Id: {
// #ts-ignore
// eslint-disable-next-line no-undef
[op.in]: keys,
},
},
});
const gs = _.groupBy(users, 'Id');
return keys.map(k => gs[k] || []);
};
const apolloServer = new ApolloServer({
schema,
playground: true,
context: () => ({
userLoader: new DataLoader(keys => batchUsers(keys, db)),
}),
});
resolver:
user: {
myUsers: ({ Id }, args, { userLoader }) => {
return userLoader.load(Id);
},
},
playground:
{users
{Id
myUsers
{Id}}
}
playground explained:
users basically fetches all users and then myusers does the same thing by inhereting the id from the first call.
I think I choose a horrible example here since I did not see any gains in performence by this. I did see however that the query turned into:
SELECT ... FROM User WhERE ID IN(...)

How to get the unique id (key) of a document in Firebase database collection?

I am trying to fetch the documents of a collection in my React-Native app but I dont know how to fetch them by ID (key).
P.S: I dont have any field called unique id or id inside the document but as I have understood, the unique id is the auto-generated key when I create a document which stands as the name of that document (20 characters id).
This is how I fetch all the fields inside a document:
var ref = firebase.firestore().collection('discounts')
.orderBy('rest_id')
EDIT:
getDiscounts = () => {
try {
this.setState({
loading: true
})
var ref = firebase.firestore().collection('discounts')
.orderBy('rest_id')
.limit(this.state.limit)
ref.onSnapshot((querySnapshot => {
var discounts = querySnapshot.docs.map(document => document.data());
var lastVisibleDiscount = discounts[discounts.length - 1].rest_id;
this.setState({
discounts: discounts,
lastVisibleDiscount: lastVisibleDiscount,
loading: false,
});
}));
}
catch (error) {
console.log(error);
}
}
To print the keys of the documents in the collection in the order of the value of their rest_id field, you can do something like this:
firebase.firestore().collection("discounts").orderBy('rest_id').get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id);
});
});
This is an almost literal copy of the code in the documentation on getting all documents from a collection, so I recommend spending some time there.
You can use (using await/async)
const ref = await ref.get()
It will have an array called docs that you can map over to get the id and data of the document:
const data = ref.docs.map(doc => {return {id: doc.id, data: doc.data()} })

Categories

Resources