MongoDB "Invalid BSON Field Name" - java

I know that there's probably a better way to do this however I'm completely stumped. I'm writing a Discord bot in which a user is able to add points to other users, however I can't figure out how to replace a user's "points". My code is as follows:
BasicDBObject cursor = new BasicDBObject();
cursor.put(user.getAsMember().getId(), getMongoPoints(user.getAsMember()));
if(cursor.containsKey(user.getAsMember().getId())) {
Document old = new Document(user.getAsMember().getId(), getMongoPoints(user.getAsMember()));
Document doc = new Document(user.getAsMember().getId(), getMongoPoints(user.getAsMember()) + Integer.parseInt(amount.getAsString()));
collection.findOneAndUpdate(old, doc);
}
My getMongoPoints function:
public static int getMongoPoints(Member m) {
ConnectionString connectionString = new ConnectionString("database");
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.build();
MongoClient mongoClient = MongoClients.create(settings);
MongoDatabase database = mongoClient.getDatabase("SRU");
MongoCollection<Document> collection = database.getCollection("points");
DistinctIterable<Integer> docs = collection.distinct(m.getId(), Integer.class);
MongoCursor<Integer> result = docs.iterator();
return result.next();
}
I've tried findOneAndReplace, however that simply makes a new entry without deleting the old one. The error I receive is: Invalid BSON field name 262014495440896000
Everything else works, include writing to the database itself which is why I'm stumped. Any help would be greatly appreciated and I apologize if this is written poorly.

BSON field names must be string. From the spec:
Zero or more modified UTF-8 encoded characters followed by '\x00'. The (byte*) MUST NOT contain '\x00', hence it is not full UTF-8.
To use 262014495440896000 as a field name, convert it to string first.

Related

Trying to update a document using MongoDB Java Driver

Thank you
I just want to thank you for clicking on this question! I've tried my best to make this as thorough as possible.
but still, feel free to let me know if you need to clarify anything further!
if you think the question is too long. you can just read the third & fourth part and post your own solution down here!
Setup
Mongodb Java driver: org.mongodb:mongo-java-driver:3.11.0-rc0
What I want to do
find a specific document with a specific "name" field.
then update the other field or the whole document.
Example Document
// the document that I am trying to find in db
{
"_id":"5de6af7cfa42833bd9849477",
"name":"Richard Koba",
"skills":[]
}
// the document that I have
{
"name":"Richard Koba",
"skills":[jump, dance, sing]
}
// final result in db
{
"_id":"5de6af7cfa42833bd9849477",
"name":"Richard Koba",
"skills":[jump, dance, sing]
}
What I am doing now
// finding a document with same "name" field as input doc and update it with doc
public MongoCollection updateDocument(Document doc, String colName) {
MongoCollection collection;
// make sure collection exist
try {
collection = connectCollection(colName); // returns MongoCollection Obj
} catch (CollectionNotFoundException e) {
MessageHandler.errorMessage(e.getMessage());
return null;
}
// trying to find the document.
if (collection.find(eq("name", doc.get("name"))).first() == null) {
// if document not found, insert a new one
collection.insertOne(doc);
} else {
// if the document found, replace/update it with the one I have
collection.replaceOne(eq("name", doc.get("name")), doc);
}
return collection;
}
What I found about my false solution
collection.find(eq("name", doc.get("name"))).first() never returns null.
Java only tells me it returns an Object. MongoDB Documentation tells me it is a TResult, which point back to MongoIterable<TResult>. I am stuck here.
the code outcome is that none of the documents is inserted/updated in the end.
Reference
https://mongodb.github.io/mongo-java-driver/3.11/javadoc/com/mongodb/client/MongoIterable.html#first()
I tried some code and this works fine. This is not much different from your code.
Created a document from mongo shell:
MongoDB Enterprise > db.users.findOne()
{
"_id" : "5de6af7cfa42833bd9849477",
"name" : "Richard Koba",
"skills" : [ ]
}
My Java Code:
// Test input documents
private Document doc1 = new Document()
.append("name", "Richard Koba")
.append("skills", Arrays.asList("jump", "dance", "sing"));
private Document doc2 = new Document()
.append("name", "Richard K")
.append("skills", Arrays.asList("sings"));
When doc1 is passed to the following method the result is: "### Doc FOUND". And, with doc2 the result is "### Doc NOT found".
private void checkDocument(Document doc) {
MongoClient mongoClient = MongoClients.create("mongodb://localhost/");
MongoDatabase database = mongoClient.getDatabase("javadb");
MongoCollection<Document> collection = database.getCollection("users");
if (collection.find(eq("name", doc.get("name"))).first() == null) {
System.out.println("### Doc NOT found");
}
else {
System.out.println("### Doc FOUND");
}
}
I also tried this, with the same results.
Document d = collection.find(eq("name", doc.get("name"))).first();
if (d == null) { // ... }
I also tried this; works fine too.
if (collection.find(queryFilter).iterator().tryNext() == null) { // ... }
I think there might be some other issue with your code or the database / collection. Some debugging and testing with new data might reveal the real issue.
Did you check if the document already exists in the collection, from mongo shell or Compass tools?
Are you using the right database and collection names?
After each test run are you verifying the data in the database if it is updated / inserted?
collection.find(eq("name", doc.get("name"))).first() never returns
null.
With the code I posted above, the find query did return null when the users collection was empty.
collection.find() return an array of documents. What you actually want here is collection.findOneAndUpdate()
After you get a TDoucment object from findOneAndUpdate, you can use a ObjectMapper e.g.jackson to map it back to a java object

retrieve docs from monogdb collection with field condition

I am trying to query a mongodb collection and retrieve certain documents based on a field value but also only retrieve a single field per record. I tried the following but no getting the solution I want:
MongoCollection<Document> collection =
database.getCollection("client_data");
//Document document = collection
// .find(new BasicDBObject("sampleUser", "myDb"))
//.projection(Projections.fields(Projections.include("address"),
//Projections.excludeId())).first();
BasicDBObject aQuery = new BasicDBObject();
aQuery.put("clientId",567);
FindIterable<Document> iterDoc = collection.find(aQuery);
The following retrieves all documents for clientid = 567, but I only want to show the address field.
The commented out code was also what I tried but not sure how to combine that with the query.
EDIT:
I am now able to iterate through all the results but would like to parse each document; I tried parsing the document into my class object but it immediately gives an error:
Unrecognized field "_id" (class
model.Client), not marked as ignorable
But _id is the very first field in the document:
Document{{_id=6216a7f64cedfd00011c35a5,
So I tried something else rather using the first document but then I don't know how to get the next document:
while(cursor.hasNext()) {
// System.out.println(cursor.next().toJson());
Client client = new Client();
try {
JsonParser jsonParser = new JsonFactory().createParser(cursor.next().toJson());
ObjectMapper mapper = new ObjectMapper();
ObjectNode rootNode = mapper.createObjectNode();
String customerInfo = fi.first().toJson();
JsonNode jobj = mapper.readTree(customerInfo);
// this gives the error// client = mapper.readValue(jsonParser,Client.class);
client.setId(jobj.path("_id").path("$oid").asText());
Please advise.
In order to:
retrieves all documents for clientid = 567, but I only want to show the address field
You would execute the following:
collection
.find(Filters.eq("clientId", 567))
.projection(Projections.fields(
Projections.include("address"),
Projections.excludeId())
).first()
Breaking it down:
.find(Filters.eq("clientId", 567)): apply the predicate 'where clientId = 567'
.projection(Projections.fields(Projections.include("address"), Projections.excludeId())): let the response include the address field and exclude the _id field

MongoDB: Simple query issue

currently I'm trying to learn dealing with MongoDB in Java. I created the collection "plots" and inserted a document:
final Document plotObj = new Document();
plotObj.put(DataKey.PLOT_UUID.getKey(), plot.getUniqueId());
plotObj.put(DataKey.REGION_ID.getKey(), plot.getRegionId());
plotObj.put(DataKey.REGION_WORLD.getKey(), plot.getRegionWorld());
plotObj.put(DataKey.REGION_OWNER.getKey(), plot.getPlotOwner().isPresent() ? plot.getPlotOwner() : null);
plotObj.put(DataKey.PLOT_TRUSTED.getKey(), new BasicDBList().addAll(plot.getTrusted()));
this.collection.insertOne(plotObj);
"DataKey.PLOT_UUID.getKey()" represents a String. "plot.getUniqueId()" represents a java.util.UUID. After inserting this Document, I want to query it:
public boolean hasPlot(UUID plotId){
final BasicDBObject query = new BasicDBObject(DataKey.PLOT_UUID.getKey(), new BasicDBObject("$eq", plotId));
return this.collection.find(query).iterator().hasNext();
}
However this methods always returns false event though the Document was successfully inserted.
Maybe this problem can be fixed with ease but nevertheless: thanks in advance! :)
According to the documentation you don't need the $eq
just write
new BasicDBObject(DataKey.PLOT_UUID.getKey(), plotId));

Java MongoDB getting value for sub document

I am trying to get the value of a key from a sub-document and I can't seem to figure out how to use the BasicDBObject.get() function since the key is embedded two levels deep. Here is the structure of the document
File {
name: file_1
report: {
name: report_1,
group: RnD
}
}
Basically a file has multiple reports and I need to retrieve the names of all reports in a given file. I am able to do BasicDBObject.get("name") and I can get the value "file_1", but how do I do something like this BasicDBObject.get("report.name")? I tried that but it did not work.
You should first get the "report" object and then access its contents.You can see the sample code in the below.
DBCursor cur = coll.find();
for (DBObject doc : cur) {
String fileName = (String) doc.get("name");
System.out.println(fileName);
DBObject report = (BasicDBObject) doc.get("report");
String reportName = (String) report.get("name");
System.out.println(reportName);
}
I found a second way of doing it, on another post (didnt save the link otherwise I would have included that).
(BasicDBObject)(query.get("report")).getString("name")
where query = (BasicDBObject) cursor.next()
You can also use queries, as in the case of MongoTemplate and so on...
Query query = new Query(Criteria.where("report.name").is("some value"));
You can try this, this worked for me
BasicDBObject query = new BasicDBObject("report.name", "some value");

How to update value of specific embedded document, inside an array, of a specific document in MongoDB?

I have the following structure in my document:
{
_id : ObjectId("43jh4j343j4j"),
array : [
{
_arrayId : ObjectId("dsd87dsa9d87s9d7"),
someField : "something",
someField2 : "something2"
},
{
_arrayId : ObjectId("sds9a0d9da0d9sa0"),
someField : "somethingElse",
someField2 : "somethingElse2"
}
]
}
I want to update someField and someField2 but only for one of the items in the array, the one that matches _arrayId (e.g. _arrayId : ObjectId("dsd87dsa9d87s9d7"); and only for this document (e.g. _id : ObjectId("43jh4j343j4j") ) and no other.
The arrayIds are not unique to the document that's why I need it to be for a specific document. I could use the $ positional operator if I wanted to update that value within the array for every document it exists in, but that's not what I want.
I am trying to accomplish this in java but a command line solution would work as well.
Here is RameshVel's solution translated to java:
DB db = conn.getDB( "yourDB" );
DBCollection coll = db.getCollection( "yourCollection" );
ObjectId _id = new ObjectId("4e71b07ff391f2b283be2f95");
ObjectId arrayId = new ObjectId("4e639a918dca838d4575979c");
BasicDBObject query = new BasicDBObject();
query.put("_id", _id);
query.put("array._arrayId", arrayId);
BasicDBObject data = new BasicDBObject();
data.put("array.$.someField", "updated");
BasicDBObject command = new BasicDBObject();
command.put("$set", data);
coll.update(query, command);
You could still use $ positional operator to accomplish this. But you need to specify the objectid of the parent doc along with the _arrayid filter. The below command line query works fine
db.so.update({_id:ObjectId("4e719eb07f1d878c5cf7333c"),
"array._arrayId":ObjectId("dsd87dsa9d87s9d7")},
{$set:{"array.$.someField":"updated"}})
...and this is how to do it with mongo-driver version >= 3.1 (mine is 3.2.2):
final MongoClient mongoClient = new MongoClient(new MongoClientURI(mongoURIString));
final MongoDatabase blogDatabase = mongoClient.getDatabase("yourDB");
MongoCollection<Document> postsCollection = blogDatabase.getCollection("yourCollection");
ObjectId _id = new ObjectId("4e71b07ff391f2b283be2f95");
ObjectId arrayId = new ObjectId("4e639a918dca838d4575979c");
Bson filter = Filters.and(Filters.eq( "_id", id ), Filters.eq("array._arrayId", arrayId));
Bson setUpdate = Updates.set("array.$.someField", "updated");
postsCollection.updateOne(postFilter, setUpdate);
Seeing as none of the answers actually explain how to do this a) in Java and b) for multiple fields in a nested array item, here is the solution for mongo-java-driver 3.12.3.
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Updates;
import org.bson.Document;
import org.bson.types.ObjectId;
MongoClient mongoClient = MongoClients.create(...);
MongoDatabase db = mongoClient.getDatabase("testDb");
MongoCollection<Document> collection = db.getCollection("testCollection");
collection.updateOne(
Filters.and(
Filters.eq("_id", new ObjectId("43jh4j343j4j")),
Filters.eq("array._arrayId", new ObjectId("dsd87dsa9d87s9d7"))
),
Updates.combine(
Updates.set("array.$.someField", "new value 1"),
Updates.set("array.$.someField2", "new value 2")
)
);
This thread has helped me towards the right solution, but I had to do more research for the full solution, so hoping that someone else will benefit from my answer too.

Categories