I'm totally new to MongoDB. I'm using Morphia to access it from Java.
I need to do something along the lines of this:
public boolean isUnique(short s){
//OPERATION 1: Check in mongo if object with field "id" = s exists
//if so, return false
//else,
//OPERATION 2: create this object in the database
//and return true
}
The problem for me to grasp is not the actual syntax, but the problem with atomicity. How do I assure that only one thread/process can have access to the document so that OP1 and OP2 are atomic?
I think this has to be managed on a database level since the Java server is in a clustered environment.
Regards,
Anders
You could replace both operations with one upsert of the {id: s} document that becomes a no-op if the document already exists, but an insert if it doesn't. Calling the getLastError command (the driver can do this for you) will tell you whether a new document was created or not.
I'm using Java and Morphia so here is what the actual code looks like, if anyone should be wanting to achieve the same thing in the future. It's the true in the call to updateFirst that tell Mongo that it's an upsert:
public boolean checkIfExistsAndInsertOtherwise(short id){
Datastore datastore = getDatastore();
Query<OrganizationId> updateQuery = datastore.createQuery(OrganizationId.class).field("identificationNumber").equal(id);
//Bogus operation, just set id to itself
UpdateOperations<OrganizationId> ops = datastore.createUpdateOperations(OrganizationId.class).set("identificationNumber", id);
UpdateResults<OrganizationId> result = datastore.updateFirst(updateQuery, ops, true,WriteConcern.SAFE);
return result.getInsertedCount() == 0;
}
Related
//Below code is Not Working
//Here, my query just returns one object, So I am trying to use findOne() //method.
Query<Topic> query = Ebean.find(Topic.class);
Topic topic = new Topic();
Topic topic=Topic.find.where().eq("columnName", "nameToMatch").findOne();
//Below part is working if I use findList(). But I have to do get(0) to //fetch the topic which is not good practice I think.
List<Topic> topicList = Ebean.find(Topic.class).where().eq("columnName", "NametoMatch").findList();
topicList.get(0)
Can anyone provide ideas how to return just One Object instead of list ?
I don't know if findOne exists in Ebean, but when I need to retrieve only one object I use findUnique()
If you're sure the object you want to find is unique, you can get it via findUnique(): Topic.find.where().eq("columnName", "nameToMatch").findUnique();
Otherwise you can use findList() with setMaxRows(), because you don't want to load in memory whole result set:
Topic.find.where().eq("columnName", "nameToMatch").setMaxRows(1).findList();
I am creating a REST API using play framework. I want to use lazy loading ( finder.ref(id) or Ebean.getReference(id) ) to see if an enity with a specific id exists in database. If it doesn't exist, I will return a 404.
If I try to delete using an id that doesn't exist, an OptimisticLockException is thrown. But that doesn't seem like a valid basis to see if an entity exists.
Is it possible to check if an entity exists by an id using lazy loading? I can always do finder.byId(id) and that can get me what I want. But I want to do this efficiently.
Thanks
You can just count items with specified id, while your id is unique, it will return 1 if item exists and 0 if it doesn't, so you can easily make a condition:
boolean itemExists
= (YourModel.find.where().eq("id", id).findRowCount() == 1) ? true : false;
Logger.info("Item " + ((itemExists) ? "exists" : "not found!"));
On the other hand if your intension is returning existing entity for an example in Json, you don't need to make separate checking, just check if it's not null...
YourModel entity = YourModel.find.byId(id);
if (entity == null) return notFound("No such record");
// .. rest of code for preparing API...
Edit
About costs: find.byId(id) tries to fetch whole entity, while find.ref(id) gets only reference. Unfortunately you can't determine if object exists by ref(id) as it's always not null, therefore IMHO counting elements by id is cheaper than selecting even single field to check if Db returns the entity.
Actually find.byId(id) is most expensive option as it loads whole entity, for well optimized APIs it's usually better to write dedicated methods using Ebean's select() and fetch(), like:
YourModel e = YourModel.find.select("id, name, age").where().eq("id", id).findUnique();
or
List<YourModel> le = YourModel.find.select("id, name, age").where().eq("id", id).findList();
I am newbie to mongodb. May I know how to avoid duplicate entries. In relational tables, we use primary key to avoid it. May I know how to specify it in Mongodb using java?
Use an index with the {unique:true} option.
// everyone's username must be unique:
db.users.createIndex({email:1},{unique:true});
You can also do this across multiple fields. See this section in the docs for more details and examples.
A unique index ensures that the indexed fields do not store duplicate values; i.e. enforces uniqueness for the indexed fields. By default, MongoDB creates a unique index on the _id field during the creation of a collection.
If you wish for null values to be ignored from the unique key, then you have to also make the index sparse (see here), by also adding the sparse option:
// everyone's username must be unique,
//but there can be multiple users with no email field or a null email:
db.users.createIndex({email:1},{unique:true, sparse:true});
If you want to create the index using the MongoDB Java Driver. Try:
Document keys = new Document("email", 1);
collection.createIndex(keys, new IndexOptions().unique(true));
This can be done using "_id" field although this use is discouraged.
suppose you want the names to be unique, then you can put the names in "_id" column and as you might know "_id" column is unique for each entry.
BasicDBObject bdbo = new BasicDBObject("_id","amit");
Now , no other entry can have name as "amit" in the collection.This can be one of the way you are asking for.
As of Mongo's v3.0 Java driver, the code to create the index looks like:
public void createUniqueIndex() {
Document index = new Document("fieldName", 1);
MongoCollection<Document> collection = client.getDatabase("dbName").getCollection("CollectionName");
collection.createIndex(index, new IndexOptions().unique(true));
}
// And test to verify it works as expected
#Test
public void testIndex() {
MongoCollection<Document> collection = client.getDatabase("dbName").getCollection("CollectionName");
Document newDoc = new Document("fieldName", "duplicateValue");
collection.insertOne(newDoc);
// this will throw a MongoWriteException
try {
collection.insertOne(newDoc);
fail("Should have thrown a mongo write exception due to duplicate key");
} catch (MongoWriteException e) {
assertTrue(e.getMessage().contains("duplicate key"));
}
}
Theon solution didn't work for me, but this one did:
BasicDBObject query = new BasicDBObject(<fieldname>, 1);
collection.ensureIndex(query, <index_name>, true);
I am not a Java programmer however you can probably convert this over.
MongoDB by default does have a primary key known as the _id you can use upsert() or save() on this key to prevent the document from being written twice like so:
var doc = {'name': 'sam'};
db.users.insert(doc); // doc will get an _id assigned to it
db.users.insert(doc); // Will fail since it already exists
This will stop immediately duplicates. As to multithread safe inserts under certain conditions: well, we would need to know more about your condition in that case.
I should add however that the _id index is unqiue by default.
using pymongo it looks like:
mycol.create_index("id", unique=True)
where myCol is the collection in the DB
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pymongo
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
mydb = myclient["mydatabase"]
mycol = mydb["customers"]
mycol.create_index("id", unique=True)
mydict = {"name": "xoce", "address": "Highway to hell 666", "id": 1}
x = mycol.insert_one(mydict)
Prevent mongoDB to save duplicate email
UserSchema.path('email').validate(async(email)=>{
const emailcount = await mongoose.models.User.countDocuments({email})
return !emailcount
}, 'Email already exits')
May this help ur question...
worked for me..
use in user model.
refer for explaination
THANKS...
What is the best method to get the Mongo generated ID of a document inserted via Java.
The Java process inserting the documents is multi-thread, meaning that we need some atomic way to insert and return the ID of the object.
Also, if we setup a unique index, in the event that the object is a duplicate, will an ID be returned?
Thanks!
Generate the ObjectId early, use it in the insert, and there will no need to have the database return it to you.
ObjectId doesn't use a shared sequence number to be unique, so it doesn't matter if you generate one before inserting or retrieve it after.
public ObjectId createThing() {
ObjectId result = new ObjectId();
BasicDBObject thingToInsert = new BasicDbObject();
thingToInsert.put('_id', result);
//set other fields here
collection.insert(thingToInsert);
return result;
}
native ObjectId's which are generated by Mongo are globally unique and can be safely used from the multi-threaded application.
generated ObjectId can be obtained from the DbObject under _id key.
If inserted document violates a unique index constraint - java driver may throw an exception, depending on a value of WriteConcern:
http://api.mongodb.org/java/current/com/mongodb/WriteConcern.html
If it's value is higher then NORMAL- exception will be thrown.
WriteConcern can be specified for every individual insert (or update) method, or globally by using DBCollection.setWriteConcern
I retrieve the document with _id but when I get the data into my java class eg mobile, _id attribute which is of type ObjectID me I change it set the value of the document in mongodb.
Using the GeoTools WFS-T plugin, I have created a new row, and after a commit, I have a FeatureId whos .getId() returns an ugly string that looks something like this:
newmy_database:my_table.9223372036854775807
Aside from the fact that the word "new" at the beginning of "my_database" is a surprise, the number in no way reflects the primary key of the new row (which in this case is "23"). Fair enough, I thought this may be some internal numbering system. However, now I want a foreign key in another table to get the primary key of the new row in this one, and I'm not sure how to get the value from this FID. Some places suggest that you can use an FID in a query like this:
Filter filter = filterFactory.id(Collections.singleton(fid));
Query query = new Query(tableName, filter);
SimpleFeatureCollection features = simpleFeatureSource.getFeatures(query);
But this fails at parsing the FID, at the underscore of all places! That underscore was there when the row was created (I had to pass "my_database:my_table" as the table to add the row to).
I'm sure that either there is something wrong with the id, or I'm using it incorrectly somehow. Can anyone shed any light?
It appears as if a couple things are going wrong - and perhaps a bug report is needed.
The FeatureId with "new" at the beginning is a temporary id; that should be replaced with the real result once commit has been called.
There are a number of way to be aware of this:
1) You can listen for a BatchFeatureEvent; this offers the information on "temp id" -> "wfs id"
2) Internally this information is parsed from the Transaction Result returned from your WFS. The result is saved in the WFSTransactionState for you to access. This was before BatchFeatureEvent was invented.
Transaction transaction = new transaction("insert");
try {
SimpleFeatureStore featureStore =
(SimpleFeatureStore) wfs.getFeatureSource( typeName );
featureStore.setTransaction( transaction );
featureStore.addFeatures( DataUtilities.collection( feature ) );
transaction.commit();
// get the final feature id
WFSTransactionState wfsts = (WFSTransactionState) transaction.getState(wfs);
// In this example there is only one fid. Get it.
String result = wfsts.getFids( typeName )[0];
}
finally {
transaction.close();
}
I have updated the documentation with the above example:
http://docs.geotools.org/latest/userguide/library/data/wfs.html