MongoDB multiple document updation passing multiple parameters - mongodb

We have a single document updation,
public void Update<T>(string collectionName, T value,string feildName,int id)
{
try
{
mongoServer.Connect();
MongoCollection<T> mongoCollection = mongoDatabase.GetCollection<T>(collectionName);
IMongoQuery Marker = Query.EQ(feildName, id);
BsonDocument doc = value.ToBsonDocument();
mongoCollection.update(doc);
}
catch
{
throw;
}
finally
{
mongoServer.Disconnect();
}
}

Related

Update Query with annotation using Spring and MongoRepository

I am using the latest version of Spring Boot and Spring Data MongoRepository. I have written a custom repository interface
public interface CompanyRepository extends MongoRepository<Company, String>{
#Query(value = "{ 'employer.userId' : ?0 }")
Company findByCompanyUserUserId(String userId);
}
In the same way i want to use #Query annotation for updating a particular field. can someone suggest me?
Create an annotation like this:
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
public #interface MongoUpdate {
String find() default "{}";
String update() default "{}";
String collection();
boolean multi() default false;
}
And an aspect like this:
#Aspect
#Component
#SuppressWarnings("unchecked")
public class MongoUpdateAspect {
private static final Logger logger = LoggerFactory.getLogger(MongoUpdateAspect.class);
#Autowired
private MongoTemplate mongoTemplate;
#Pointcut("#annotation(com.ofb.commons.aop.common.MongoUpdate)")
public void pointCut() {
}
#Around("com.ofb.commons.aspect.MongoUpdateAspect.pointCut() && #annotation(mongoUpdate)")
public Object applyQueryUpdate(ProceedingJoinPoint joinPoint, MongoUpdate mongoUpdate) throws Throwable {
Object[] args = joinPoint.getArgs();
String findQuery = mongoUpdate.find();
String updateQuery = mongoUpdate.update();
String collection = mongoUpdate.collection();
boolean multiUpdate = mongoUpdate.multi();
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Collection) {
Collection collection1 = (Collection) args[i];
String replaceStr = (String) collection1.stream().map(object -> {
if (object instanceof Number) {
return object.toString();
} else {
return String.format("\"%s\"", object.toString());
}
}).collect(Collectors.joining(","));
findQuery = findQuery.replace(String.format("?%s", i), replaceStr);
updateQuery = updateQuery.replace(String.format("?%s", i), replaceStr);
} else if (args[i] instanceof Object[]) {
Object[] objects = (Object[]) args[i];
String replaceStr = Arrays.stream(objects).map(object -> {
if (object instanceof Number) {
return object.toString();
} else {
return String.format("\"%s\"", object.toString());
}
}).collect(Collectors.joining(","));
findQuery = findQuery.replace(String.format("?%s", i), replaceStr);
updateQuery = updateQuery.replace(String.format("?%s", i), replaceStr);
} else {
if (args[i] instanceof Number) {
findQuery = findQuery.replace(String.format("?%s", i), args[i].toString());
updateQuery.replace(String.format("?%s", i), args[i].toString());
} else {
findQuery = findQuery.replace(String.format("?%s", i), String.format("\"%s\"", args[i].toString()));
updateQuery =
updateQuery.replace(String.format("?%s", i), String.format("\"%s\"", args[i].toString()));
}
}
}
Query query = new BasicQuery(findQuery);
Update update = new BasicUpdate(updateQuery);
if (multiUpdate) {
mongoTemplate.updateMulti(query, update, collection);
} else {
mongoTemplate.updateFirst(query, update, collection);
}
return null;
}
}
This will not work in MongoRepository implemented interfaces but you can create an empty bodied method in your service layer
#MongoUpdate(find = {}, update = "{$push : {'offFeatures' : ?0}}", collection = "userPreference", multi = true)
public void offFeatures(String feature) {
}
It's a reasonable question. Assuming that you're using the org.springframework.data.mongodb.repository.MongoRepository class, can you not simply use the insert(..) or save(..) methods for what you need?
API docs

Update for MongoCollection<Documnet>

I am trying to update the document but I am not able to do it.
I tried using update() as well as updateOne() methods..None of it worked.
public boolean updateDocument(String docId, String JSONString) {
try{
MongoCollection<Document> collection = getCollection("abc");
DBObject queryObject = (DBObject)(collection.find(eq("_id","docId")).first());
if(queryObject==null)
return false;
else {
DBObject updateObject = (DBObject)JSON.parse(JSONString);
collection.update(queryObject, updateObject);
return true;
}
}catch (Exception e) {
System.out.println(e.getMessage());
return false;
}
}
The Error is coming like this : The method update(dbObject,dbObject) is undefined for the type MongoCollection
I can use updateOne is applicable for Bson arguments but I need to use for DbObjects..
Can anyone can suggest any solution?
I tried this and worked..I needed to use MongoCollection type beacuse in the whole class I have specifed with this only.
public boolean updateDocument(String docId, String JSONString) {
try{
MongoCollection<Document> collection = getCollection("123");
Document queryObject = collection.find(eq("_id",docId)).first(); //finding the document then converting to DBObject type
if(queryObject==null)
return false;
else {
Document updatedoc = new Document();
updatedoc = Document.parse(JSONString);
collection.updateOne(queryObject,updatedoc );
return true;
}
}catch (Exception e) {
System.out.println(e.getMessage());
return false;
}
}

How to select shard for search query?

I'm recently implemented ShardIdentifierProvider. It is working fine. But how to ensure it is using only one shared for query?
public class SiteIdAsShardIdProvider extends ShardIdentifierProviderTemplate {
#Override
protected Set<String> loadInitialShardNames(Properties properties, BuildContext buildContext) {
ServiceManager serviceManager = buildContext.getServiceManager();
SessionFactory sessionFactory = serviceManager.requestService(HibernateSessionFactoryServiceProvider.class, buildContext);
Session session = sessionFactory.openSession();
try {
#SuppressWarnings("unchecked")
List<String> ids = session.createSQLQuery("select cast(id as CHAR(3)) from website").list();
return new HashSet<>(ids);
} finally {
session.close();
}
}
#Override
public String getShardIdentifier(Class<?> entityType, Serializable id, String idAsString, Document document) {
return document.getFieldable("siteId").stringValue();
}
}
Creating your own custom filter and overriding getShardIdentifiersForQuery should do the trick. Here is something that does approximately the same as what's in the documentation, but with a ShardIdentifierProviderTemplate:
#Override
public Set<String> getShardIdentifiersForQuery(FullTextFilterImplementor[] filters) {
FullTextFilter filter = getFilterByName( filters, "customer" );
if ( filter == null ) {
return getAllShardIdentifiers();
}
else {
Set<String> result = new HashSet<>();
result.add( filter.getParameter( "customerID" ) );
return result;
}
}
private FullTextFilter getFilterByName(FullTextFilterImplementor[] filters, String name) {
for ( FullTextFilterImplementor filter: filters ) {
if ( filter.getName().equals( name ) ) {
return filter;
}
}
return null;
}
I created a ticket to update the documentation: https://hibernate.atlassian.net/browse/HSEARCH-2513
The shard selection at query time is controlled by using a custom Filter.
See "5.3.1. Using filters in a sharded environment" for details and examples.

Dynamic models Entity Framework

I'm currently working on a generic function for inserting data tables via entity framework. However, with my current solution I ended up with a ton of very repetitive code with only a few minor differences. I would like to simplify what I have and remove the need for large case statements based on my table names (I only included two cases in this example to save space).
Here is what I currently have:
public static void InsertByTable(IEnumerable<DataTable> chunkedTable, string tableName)
{
switch (tableName)
{
#region Parcel
case TaxDataConstant.Parcel:
Parallel.ForEach(
chunkedTable,
new ParallelOptions
{
MaxDegreeOfParallelism = Convert.ToInt32(ConfigurationManager.AppSettings["MaxThreads"])
},
chunk =>
{
Realty_Records_ProdEntities entities = null;
try
{
entities = new Realty_Records_ProdEntities();
entities.Configuration.AutoDetectChangesEnabled = false;
foreach (DataRow dr in chunk.Rows)
{
var parcelToInsert = new Parcel();
foreach (DataColumn c in dr.Table.Columns)
{
SetProperty(parcelToInsert, c.ColumnName, dr[c.ColumnName]);
}
entities.Parcels.Add(parcelToInsert);
}
entities.SaveChanges();
}
catch (Exception ex)
{
TaxDataError.AddTaxApplicationLog(
TaxDataConstant.CategoryError,
ex.Source,
ex.Message,
ex.StackTrace);
throw;
}
finally
{
entities?.Dispose();
}
});
break;
#endregion
#region Asmt
case TaxDataConstant.Asmt:
Parallel.ForEach(
chunkedTable,
new ParallelOptions
{
MaxDegreeOfParallelism = Convert.ToInt32(ConfigurationManager.AppSettings["MaxThreads"])
},
chunk =>
{
Realty_Records_ProdEntities entities = null;
try
{
entities = new Realty_Records_ProdEntities();
entities.Configuration.AutoDetectChangesEnabled = false;
foreach (DataRow dr in chunk.Rows)
{
var asmtToInsert = new Asmt();
foreach (DataColumn c in dr.Table.Columns)
{
SetProperty(asmtToInsert, c.ColumnName, dr[c.ColumnName]);
}
entities.Asmts.Add(asmtToInsert);
}
entities.SaveChanges();
}
catch (Exception ex)
{
TaxDataError.AddTaxApplicationLog(
TaxDataConstant.CategoryError,
ex.Source,
ex.Message,
ex.StackTrace);
throw;
}
finally
{
entities?.Dispose();
}
});
break;
#endregion
}
}
Is there any way I can make this table agnostic?
You can probably move the logic in the case statement to a helper extension method which is generic. something like this (untested) pseudo code:
public static class EntityAdder
{
public static void AddEntities<T>(this IEnumerable<DataTable> chunkedEntities, Action<Realty_Records_ProdEntities, T entity> addingFunction)
{
Parallel.ForEach(
chunkedTable,
new ParallelOptions
{
MaxDegreeOfParallelism = Convert.ToInt32(ConfigurationManager.AppSettings["MaxThreads"])
},
chunk =>
{
Realty_Records_ProdEntities entities = null;
try
{
entities = new Realty_Records_ProdEntities();
entities.Configuration.AutoDetectChangesEnabled = false;
foreach (DataRow dr in chunk.Rows)
{
var toInsert = new T();
foreach (DataColumn c in dr.Table.Columns)
{
SetProperty(parcelToInsert, c.ColumnName, dr[c.ColumnName]);
}
addingFunction(entities,toInsert);
}
entities.SaveChanges();
}
catch (Exception ex)
{
TaxDataError.AddTaxApplicationLog(
TaxDataConstant.CategoryError,
ex.Source,
ex.Message,
ex.StackTrace);
throw;
}
finally
{
entities?.Dispose();
}
});
}
}
this could then be used something like this:
public static void InsertByTable(IEnumerable<DataTable> chunkedTable, string tableName)
{
switch (tableName)
{
#region Parcel
case TaxDataConstant.Parcel:
chunkedTable.AddEntities((entities,newEntity)=> entities.Parcels.Add(newEntity))
break;
#endregion
#region Asmt
case TaxDataConstant.Asmt:
chunkedTable.AddEntities((entities,newEntity)=> entities.Asmts.Add(newEntity))
break;
#endregion
}
}
you might be able to automate the adding bit by finding the property on the class which is a list of the things you want to add and avoid passing the action in if you wanted.
Based on Sams response and some further research I was able to refactor out the case statement.
Here's my solution:
public static void InsertByTable(IEnumerable<DataTable> chunkedTable, Type type)
{
Parallel.ForEach(
chunkedTable,
new ParallelOptions
{
MaxDegreeOfParallelism = Convert.ToInt32(ConfigurationManager.AppSettings["MaxThreads"])
},
chunk =>
{
Realty_Records_ProdEntities entities = null;
try
{
entities = new Realty_Records_ProdEntities();
entities.Configuration.AutoDetectChangesEnabled = false;
var set = entities.Set(type);
foreach (DataRow dr in chunk.Rows)
{
var objectToInsert = Activator.CreateInstance(type);
foreach (DataColumn c in dr.Table.Columns)
{
SetProperty(objectToInsert, c.ColumnName, dr[c.ColumnName]);
}
set.Add(objectToInsert);
}
entities.SaveChanges();
}
catch (Exception ex)
{
TaxDataError.AddTaxApplicationLog(
TaxDataConstant.CategoryError,
ex.Source,
ex.Message,
ex.StackTrace);
throw;
}
finally
{
entities?.Dispose();
}
});
}

wicket download output stream

I want to download csv file , i take the response content and write to it , apprently wicket write after me and the content iam getting is the page html where it should be my csv
I have seen in the example the usage of throw new AbortException();
I am using version 6.7 , do you know if my version wicket has somthing instead of it ?
or rather I am doing somthing wrong ....
can you please help me ...
add(new Link<Void>("export") {
#Override
public void onClick() {
WebResponse response = (WebResponse) getResponse();
response.setAttachmentHeader("export.csv");
response.setContentType("text/csv");
OutputStream out = getResponse().getOutputStream();
try {
c.exportData(dataSource.getListForExport(), columns, out);
} catch (Exception ex) {
System.err.println(ex);
}
}
});
public <T> void exportData(List<T> list, List<IGridColumn<IDataSource<T>, T, String>> columns, OutputStream outputStream)
throws IOException {
long startTime = System.nanoTime();
PrintWriter out = new PrintWriter(new OutputStreamWriter(outputStream, Charset.forName(characterSet)));
try {
if (isExportHeadersEnabled()) {
boolean first = true;
for (IGridColumn<IDataSource<T>, T, String> col : columns) {
if (first) {
first = false;
} else {
out.print(delimiter);
System.out.println(delimiter);
}
if (col.getId().equals("checkBox")) {
continue;
}
out.print(quoteValue(col.getId()));
System.out.println(col.getId());
}
out.print("\r\n");
System.out.println("\r\n");
}
Iterator<? extends T> rowIterator = list.iterator();
while (rowIterator.hasNext()) {
T row = rowIterator.next();
boolean first = true;
for (IGridColumn<IDataSource<T>, T, String> col : columns) {
if (first) {
first = false;
} else {
out.print(delimiter);
System.out.println(delimiter);
}
if (col.getId().equals("checkBox")) {
continue;
}
Object o = (new PropertyModel<>(row, col.getId())).getObject();// ((AbstractColumn<T,
if (o != null) {
Class<?> c = o.getClass();
String s;
IConverter converter = Application.get().getConverterLocator().getConverter(c);
if (converter == null) {
s = o.toString();
} else {
s = converter.convertToString(o, Session.get().getLocale());
}
out.print(quoteValue(s));
System.out.println(quoteValue(s));
}
}
out.print("\r\n");
System.out.println("\r\n");
}
} catch (Exception ex) {
System.out.println(ex);
} finally {
System.out.println(out);
out.close();
// measure
System.out.print(System.nanoTime() - startTime);
}
}
The best way to do this is using dynamic resources. I'll suggest you to read chapter "Resource managment with Wicket" of this magnific free Wicket guide: https://code.google.com/p/wicket-guide/.
Here you have a similar example given in this guide in the section "Custom resources".
public class RSSProducerResource extends AbstractResource {
#Override
protected ResourceResponse newResourceResponse(Attributes attributes) {
ResourceResponse resourceResponse = new ResourceResponse();
resourceResponse.setContentType("text/xml");
resourceResponse.setTextEncoding("utf-8");
resourceResponse.setWriteCallback(new WriteCallback()
{
#Override
public void writeData(Attributes attributes) throws IOException
{
OutputStream outputStream = attributes.getResponse().getOutputStream();
Writer writer = new OutputStreamWriter(outputStream);
SyndFeedOutput output = new SyndFeedOutput();
try {
output.output(getFeed(), writer);
} catch (FeedException e) {
throw new WicketRuntimeException("Problems writing feed to response...");
}
}
});
return resourceResponse;
}
// method getFeed()...
}
And then you need to add the link in the desired page or component:
add(new ResourceLink("rssLink", new RSSProducerResource()));