How can I find a document and retrieve it if found, but insert and retrieve it if not found in one command?
I have an outline for the formats I wish my documents to look like for a user's data. Here is what it looks like
{
"username": "HeyAwesomePeople",
"uuid": "0f91ede5-54ed-495c-aa8c-d87bf405d2bb",
"global": {},
"servers": {}
}
When a user first logs in, I want to store the first two values of data (username and uuid) and create those empty values (global and servers. Both those global and servers will later on have more information filled into them, but for now they can be blank). But I also don't want to override any data if it already exists for the user.
I would normally use the insertOne or updateOne calls to the collection and then use the upsert (new UpdateOptions().upsert(true)) option to insert if it isn't found but in this case I also need to retrieve the user's document aswell.
So in a case in which the user isn't found in the database, I need to insert the outlined data into the database and return the document saved. In a case where the user is found in the database, I need to just return the document from the database.
How would I go about doing this? I am using the latest version of Mongo which has deprecated the old BasicDBObject types, so I can't find many places online that use the new 'Document' type. Also, I am using the Async driver for java and would like to keep the calls to the minimum.
How can I find a document and retrieve it if found, but insert and retrieve it if not found in one command?
You can use findOneAndUpdate() method to find and update/upsert.
The MongoDB Java driver exposes the same method name findOneAndUpdate(). For example:
// Example callback method for Async
SingleResultCallback<Document> printDocument = new SingleResultCallback<Document>() {
#Override
public void onResult(final Document document, final Throwable t) {
System.out.println(document.toJson());
}
};
Document userdata = new Document("username","HeyAwesomePeople")
.append("uuid", "0f91ede5")
.append("global", new Document())
.append("servers", new Document());
collection.findOneAndUpdate(userdata,
new Document("$set", userdata),
new FindOneAndUpdateOptions()
.upsert(true)
.returnDocument(ReturnDocument.AFTER),
printDocument);
The query above will try to find a document matching userdata; if found set it to the same value as userdata. If not found, the upsert boolean flag will insert it into the collection. The returnDocument option is to return the document after the action is performed.
The upsert and returnDocument flags are part of FindOneAndUpdateOptions
See also MongoDB Async Java Driver v3.4 for tutorials/examples. The above snippet was tested with current version of MongoDB v3.4.x.
Related
I am working on this legacy application (7 years old). I have many methods that do the same thing that I am trying to upgrade to a newer MongoDB Java driver, but it won't compile.
#Override
public void saveOrUpdatePrinter(Document printer) {
printer.put(PRINTER_COLUMNS.updateDate,new Date());
MongoCollection<Document> collection = mongoTemplate.getCollection("PRINTERS");
printer.remove("_id");
Document query = new Document().append(PRINTER_COLUMNS.internal_id, printer.get(PRINTER_COLUMNS.internal_id));
WriteResult result = collection.update(query, printer, true, false);
logger.debug("saveOrUpdatePrinter updeded records: " + result.getN());
}//
The error is:
The method update(Document, Document, boolean, boolean) is undefined
for the type MongoCollection<Document>
Why was this removed?
printer.remove("_id");
Also I would like to know how to do either update or save on the document in one go?
And what will be the proper way to update a single document in the new (MongoDB Java driver 4.7.0)?
Reading this code a little more seems like it was an attempt to do UPSERT operation (update or insert).
I will try to answer your questions.
Q : How to do either Update or Save on the Document in one go?
-> MongoDB's update method updates the values in the existing document whereas the save method replaces the existing document with the document passed. Nothing happens in one go.
update method only updates which are specific fields which are modified by comparing the fields from the modified document with the original document whereas the save method updates/replaces the values of all the fields of an original document by the taking values from the modified document and setting the values into the original document.
Q : What will be the proper way to update a single document in the new (Mongo Java driver 4.7.0)
-> You should be using updateOne(query, updates, options) to update a single document on a MongoCollection object.
From updateOne docs :
The method accepts a filter that matches the document you want to
update and an update statement that instructs the driver how to change
the matching document. The updateOne() method only updates the first
document that matches the filter.
To perform an update with the updateOne() method, you must pass a
query filter and an update document. The query filter specifies the
criteria for which document to perform the update on and the update
document provides instructions on what changes to make to it.
You can optionally pass an instance of UpdateOptions to the
updateOne() method in order to specify the method's behavior. For
example, if you set the upsert field of the UpdateOptions object to
true, the operation inserts a new document from the fields in both the
query and update document if no documents match the query filter.
Q : Is it seems like it was an attempt to do UPSERT operation (Update or Insert) ?
-> Yes, it's an upsert operation.
Q : Why the code is trying to remove _id from document ?
-> The update method will update the document if the document was found by internal_id. If the document was not found and also if there is no _id field in the document, then the mongoshell will consider it as a new document and will invoke insert method internally via the update method to insert the document. For the insertion to happen, that's why it was removed from document.
Just update the code to this.
#Override
public void saveOrUpdatePrinter(Document printer) {
MongoCollection<Document> collection = mongoTemplate.getCollection("PRINTERS");
Document query = new Document().append(PRINTER_COLUMNS.internal_id, printer.get(PRINTER_COLUMNS.internal_id));
UpdateOptions options = new UpdateOptions().upsert(true);
printer.put(PRINTER_COLUMNS.updateDate,new Date());
UpdateResult result = collection.updateOne(query, printer, options);
logger.debug("saveOrUpdatePrinter updated records: " + result.getModifiedCount());
}
You can update a document using the MongoCollection#updateOne() method
An example would be:
collection.updateOne(Filters.eq("_id", new ObjectId("1234")), Updates.set("date", new Date());
I have a big collection with several thousand documents. These documents have subcollections with documents inside. Now I deleted a lot of the documents on the highest level.
Structure:
MyCollection => MyDocument => MySubcollection => MySubdocument
Now I realized, that the files are deleted (not showing up in any query) but the subcollections and their documents still exist. Now I am not sure how I can delete them as well, as I don't know the ID's of my deleted documents.
When I would try to find out the ID's by just sending a query to my collection to read all documents, the deleted ones are (by design) not included anymore. So how can I figure out their IDs now to delete their subcollections?
Thanks for any advice!
It all depends on your exact goal.
If you want to delete ALL the docs in the MyCollection collection, including ALL the documents in ALL the sub-collection, you can use the Firebase CLI with the following command:
firebase firestore:delete MyCollection -r
Do firebase firestore:delete --help for more options.
Of course, this can only be done by an Owner of your Firebase project.
If you want to allow other users to do the same thing from the front-end (i.e. ALL the docs, including ALL the sub-collections), you can use the technique detailed in the "Delete data with a Callable Cloud Function" section in the doc.
As explained in this doc:
You can take advantage of the firestore:delete command in the Firebase Command Line Interface (CLI). You can import any function of the Firebase CLI into your Node.js application using the firebase-tools package.
The Firebase CLI uses the Cloud Firestore REST API to find all documents under the specified path and delete them individually. This implementation requires no knowledge of your app's specific data hierarchy and will even find and delete "orphaned" documents that no longer have a parent.
If you want to delete ONLY a subset of the documents in the MyCollection collection together with the documents in the sub-collection, you can use the same methods as above, with the path to the documents, e.g.:
firestore:delete MyCollection/MyDocument -r
Finally, if your problem is that you have already deleted "parent" documents and you don't know how to delete the documents in the (orphan) sub-collections (since you don't know the ID of the parent), you could use a Collection Group query to query all the MySubcollection subcollections and detect if the parent document exists or not. The following code, in JavaScript, would do the trick.
const db = firebase.firestore();
const parentDocReferences = [];
const deletedParentDocIds = [];
db.collectionGroup('MySubcollection')
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(doc.id);
console.log(doc.ref.parent.parent.path);
parentDocReferences.push(db.doc(doc.ref.parent.parent.path).get());
});
return Promise.all(parentDocReferences);
})
.then((docSnapshots) => {
docSnapshots.forEach((doc) => {
console.log(doc.id);
console.log(doc.exists);
if (!doc.exists && deletedParentDocIds.indexOf(doc.id) === -1) {
deletedParentDocIds.push(doc.id);
}
});
// Use the deletedParentDocIds array
// For example, get all orphan subcollections reference in order to delete all the documents in those collections (see https://firebase.google.com/docs/firestore/manage-data/delete-data#collections)
deletedParentDocIds.forEach(docId => {
const orphanSubCollectionRef = db.collection(`MyCollection/${docId}/MySubcollection`);
// ...
});
});
I'm looking to perform a query on my Couchbase database using the Java client SDK, which will return a list of results that include the document id for each result. Currently I'm using:
Statement stat = select("*").from(i("myBucket"))
.where(x(fieldIwantToGet).eq(s(valueIwantToGet)));
N1qlQueryResult result = bucket.query(stat);
However, N1qlQueryResult seems to only return a list of JsonObjects without any of the associated meta data. Looking at the documentation it seems like I want a method that returns a list of Document objects, but I can't see any bucket methods that I call that do the job.
Anyone know a way of doing this?
You need to use the below query to get Document Id:
Statement stat = select("meta(myBucket).id").from(i("myBucket"))
.where(x(fieldIwantToGet).eq(s(valueIwantToGet)));
The above would return you an array of Document Id.
How to query data from collection where document reference contain specific path?
Here is my Firestore database, filed user is a document reference type, mapped document reference form other collection.
Here I need to get all data from userinfo collection where user document reference contains specific path (eg : player/8SLuNWrI09UIuUfNe7ZR/playerinfo )
I have tried following query but seems not working:
Query query = mFirebaseFirestore.collection("userinfo").whereGreaterThanOrEqualTo("user", "player/8SLuNWrI09UIuUfNe7ZR/playerinfo");
Code to get data from query:
query.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot queryDocumentSnapshots, FirebaseFirestoreException e) {
//queried data here
}
});
No example available on document to query Firestore with document reference type.
Please help me to query list of document from collection where document reference having specific path.
Thanks in advance
You don't find any examples in the official documentation of Firestore because unfortunately, there is no contains() method which can help you verify if a String is apart of a reference, which is a property of a document.
However, if you want to get all document that exist under playerinfo collection, then just use a get() call. Don't use addSnapshotListener() unless you need to get data in real-time.
There is a solution in which you need to transform your user property to be of type String and use a query that look like this:
Query query = = mFirebaseFirestore.collection("userinfo").whereEqualTo("user", "player/8SLuNWrI09UIuUfNe7ZR/playerinfo");
But rememeber, this will work only if the property user holds a value of type String which is equal to:
player/8SLuNWrI09UIuUfNe7ZR/playerinfo`
and not:
player/8SLuNWrI09UIuUfNe7ZR/playerinfo/qY0D7Vef...
^ ^
There is one more in way in which you can solve this. Query the entire collection, get all the values of user property and add them to a List by converting them from Reference to String. In the end, just iterate over the list and use contains() method on each element.
You just want to get the document by its location in the database:
mFirebaseFirestore.collection("userinfo").document("8SLuNWrI09UIuUfNe7ZR").get()
No need for a query if you know the specific document to fetch.
Here is my database schema:
Database name: user
_id 74acd65e6eeb6d55809a950036000f50
_rev 1-f5ca343d0688d7a01b549e5c29a4a911
Budget dsds
user_Id abc123
Name ssdsd
Now what I want is to retrieve all the records who are having "user_Id":"ssdsd" using GWT.
Like in mysql: select * from user where user_Id=ssdsd
Please guide me in the following code
public String view(String user_id) throws IllegalArgumentException {
// TODO Auto-generated method stub
Session s1=new Session("127.0.0.1",5984);
Database db=s1.getDatabase("users");
return "";
Unfortunately I'm not familiar with GWT, but I think your question is more CouchDB-related.
If you plan on having one and exactly one document per user, then you should use the user_Id directly as the _id. This way you get the benefit of using the "primary" index of CouchDb to your advantage and enforce unique user ids at the same time.
It is good practice and a convention to also store the type of the document in the type property.
The document could look like this:
{
"_id": "user.abc123",
"type": "user",
"name": "ssdsd",
"budget": "dsds"
}
If you need to have multiple documents per user_Id, then you need to write a CouchDB view. You can read an introduction in the CouchDB Docs.
This would be some simple JavaScript code for the CouchDB map function:
function (doc) {
if (doc.user_Id) {
emit(doc.user_id, {budget: doc.budget});
}
}
You would then be able to query this view by calling the view with a key of "abc123", e.g. in the browser by calling this URL:
http://localhost:5984/users/_design/users/_view/users?key=[“abc123"]
P.S.: If you need authentication also, it might be worth considering to store the users in the built-in database _users. Just keep in mind that there are some restrictions in this system db: non-admins cannot read other users' documents in this db, and some special schema requirements have to be met when adding documents there.