How to update a inner/embedded document in a mongodb using mongotemplate - mongodb

Can some one help me to write a code to update "coordinates". I was able to update the address but not the coordinates.
{
"_id": "2c9080e54b4ee7ac014b4ee8e5100000",
"_class": "com.myparking.dataservice.mongodb.documents.ParkingSiteDocument",
"address": {
"streetAddress": "bellandur",
"locality": "ORR",
"region": "bangalore",
"country": "india",
"postalCode": "560102"
},
"geoLocation": {
"coordinates": [ 12.934292, 77.680215 ],
"type": "Point"
}
}
My code goes like this: When I update the address it is working but I am not able to update the coordinates.
public ParkingSiteDocument updateParkingSite(final ParkingSpaceDTO pSpace) {
ParkingSiteDocument parkingSpace = null;
try{
// If the collection doesn't exist return.
if (!mongoTemplate.collectionExists(ParkingSiteDocument.class)) {
// return.
return null;
}
Query query = new Query();
// query to fetch the parking site based on the id.
query.addCriteria(Criteria.where("pSiteId").is(pSpace.getpSpaceId()));
parkingSpace = mongoTemplate.findOne(query, ParkingSiteDocument.class);
// If the parking space is not available return;
if(parkingSpace == null) {
return null;
}
// Update address and coordinates
Update update = new Update();
// Updating the address.
if(pSpace.getAddress() != null) {
Address newAddress = new Address();
newAddress.setCountry(pSpace.getAddress().getCountry());
newAddress.setLocality(pSpace.getAddress().getLocality());
newAddress.setPostalCode(pSpace.getAddress().getPostalCode());
newAddress.setRegion(pSpace.getAddress().getRegion());
newAddress.setStreetAddress(pSpace.getAddress().getStreetAddress());
// converting it into mongo document.
MongoConverter converter = mongoTemplate.getConverter();
DBObject newRec = (DBObject) converter.convertToMongoType(newAddress);
update.set("address", newRec);
}
// Update the geolocation coordinates
if(pSpace.getGeoCoordinates() != null) {
// creating new coordinates from the input DTO.
Double[] coordinates = new Double[]{pSpace.getGeoCoordinates().getLongitude(),
pSpace.getGeoCoordinates().getLatitude()};
MongoConverter converter = mongoTemplate.getConverter();
DBObject newRec = (DBObject) converter.convertToMongoType(coordinates);
update.set("geoLocation.coordinates", newRec);
}
// update query.
mongoTemplate.updateFirst(query, update, ParkingSiteDocument.class);
} catch(Exception e) {
logger.error(this.getClass().getSimpleName(), "updateParkingSite | Exception" + e.getMessage());
}
return parkingSpace;
}

if (!mongoTemplate.collectionExists(ParkingSiteDocument.class))//or document name
mongoTemplate.createCollection(ParkingSiteDocument.class);//or document name
DBCollection db=mongoTemplate.getCollection(ParkingSiteDocument.class);//document name
BasicDBObject updateDocument = new BasicDBObject();
DBObject update = new BasicDBObject("$set",
new BasicDBObject("geoLocation",
new BasicDBObject("coordinates", "12.934292,77.680215")));
BasicDBObject searchQuery= new BasicDBObject().append("_id", new ObjectId("54d1d939e4b044860afcdf6d"));
db.update(searchQuery, update);

Related

Mongodb C# driver Insert new child of object to parents

I want to insert new child on the record below and i use this code on .net core
var builder = Builders<parent>.Filter;
var filter = builder.Eq("_id", "123");
var update = Builders<parent>.Update.AddToSet<IEnumerable<Child>>
("Child", Child);
await _expensesContext.collection.UpdateOneAsync(filter, update);
parent
{
"_Id": "123"
"Child": [
{"Id": "1234", "name": "jhon"}
]
}
but i enconter this error Unable to cast object of type 'System.Collections.Generic.List`1[child]' to type 'Child'.'
I fixed this using addtosetEach instead of addtoset:
var builder = Builders<parent>.Filter;
var filter = builder.Eq("_id", "123");
var update = Builders<parent>.Update.AddToSetEach<IEnumerable<Child>>
("Child", Child);
await _Context.collection.UpdateOneAsync(filter, update);

Transaction not working with Web API .net core 2.2

I have a async web API that has services and a connection to the db using an EF context.
In one of the services, where I inject the context using DI I want to make a simple transactional service, like:
public async Task<int> ServiceMethod()
{
using (var transaction = context.Database.BeginTransaction())
{
await context.Table1.AddAsync(new Table1());
await context.SaveChangeAsync();
CallOtherservice(context);
await context.SaveChangeAsync();
transaction.Commit();
}
}
CallOtherService(Context context)
{
await context.Table2.AddAsync();
}
I have now mocked CallOtherService() to throw an exception and I expect nothing to be commited, but it looks like changes to Table1 persist, amid the transaction trhat should stop them. I have tried calling Rollback() in a try catch statement and using a TransactionScope() instead, but both were useless. Also, I have noticed that after calling .BeginTransaction(), context.Database.CurrentTransaction is null, which I consider a bit weird.
EDIT:
the method that I test:
public async Task<int> AddMatchAsync(Models.Database.Match match)
{
//If match is null, nothing can be done
if (match == null)
return 0;
else
{
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
try
{
//If the match already exists, return its id
var dbId = await ExistsMatchAsync(match.HomeTeam.Name, match.AwayTeam.Name, match.Time);
if (dbId > 0)
{
log.Info($"Found match with id {dbId}");
return dbId;
}
//add computable data
match = await AttachMatchName(match);
match.Season = CommonServices.toSeason(match);
// Prepare the relational entities accordingly
match = PrepareTeamsForAddMatch(match).Result;
match = PrepareLeagueForAddMatch(match).Result;
//add the match and save it to the DB
await context.Matches.AddAsync(match);
await context.SaveChangesAsync();
log.Info($"Successfully added a new match! id: {match.Id}");
// Create new TeamLeagues entities if needed
leaguesService.CreateTeamLeaguesForNewMatchAsync(context, match);
await context.SaveChangesAsync();
scope.Complete();
return match.Id;
}
catch (Exception ex)
{
log.Error($"Error adding match - Rolling back: {ex}");
throw;
}
}
}
}
and the test:
[Test]
public void AddMatchSaveChangesTransaction()
{
//Arrange
var exceptiontext = "This exception should prevent the match from being saved";
var leagueServiceMock = new Mock<ILeaguesService>();
leagueServiceMock.Setup(p => p.CreateTeamLeaguesForNewMatchAsync(It.IsAny<InFormCoreContext>(),It.IsAny<Match>()))
.Throws(new Exception(exceptiontext));
leagueServiceMock.Setup(p => p.GetLeagueAsync(It.IsAny<string>()))
.ReturnsAsync((string name) => new League{Id = 1, Name = name });
var match = new Match
{
HomeTeam = new Team { Name = "Liverpool" },
AwayTeam = new Team { Name = "Everton" },
League = new League { Name = "PL", IsSummer = true },
Time = DateTime.UtcNow,
HomeGoals = 3,
AwayGoals = 0
};
var mcount = context.Matches.Count();
//Act
var matchService = new MatchesService(context, leagueServiceMock.Object, new LogNLog(), TeamsService);
//Assert
var ex = Assert.ThrowsAsync<Exception>(async () => await matchService.AddMatchAsync(match));
Assert.AreEqual(ex.Message, exceptiontext);
Assert.AreEqual(mcount, context.Matches.Count(), "The match has been added - the transaction does not work");
}`
```

Specify multiple criteria's in spring mongo db query

I am iterating over a list of key/value pairs and executing find for each key/value. Can I create a single query document to be kind of union in sql, So that there will be only one database call.
List<User> userList = new ArrayList<User>();
for (Map accounts:attributes) {
Query query = new Query();
List<Criteria> andCriteriaList = new ArrayList<Criteria>();
accounts.forEach((key, value) -> {
Criteria criteria = Criteria.where((String) key).is(value);
andCriteriaList.add(criteria);
});
query.addCriteria(new Criteria().andOperator(andCriteriaList.toArray(new Criteria[andCriteriaList.size()])));
if (mongoTemplate.exists(query, User.class)) {
userList.add((User)mongoTemplate.find(query, User.class));
//System.out.println(mongoTemplate.find(query, User.class));
}
Thanks,
You can refactor your code to create $or expressions. No explicit $and operator needed.
Something like
Query orQuery = new Query();
Criteria orCriteria = new Criteria();
List<Criteria> orExpression = new ArrayList<>();
for (Map<String, Object> accounts : attributes) {
Criteria expression = new Criteria();
accounts.forEach((key, value) -> expression.and(key).is(value));
orExpression.add(expression);
}
orQuery.addCriteria(orCriteria.orOperator(orExpression.toArray(new Criteria[orExpression.size()])));
List<User> userList = mongoOperations.find(orQuery, User.class);
This should output query like
{ "$or" : [{ "key1" : "value1", "key2" : "value2" }, { "key3" : "value3", "key4" : "value4" }] }
if you want to query multiple fields(field1, field2, ....) with values below is the solution
Query query = new Query();
List<Criteria> criteria = new ArrayList<>();
criteria.add(Criteria.where(field1).is(field1val));
criteria.add(Criteria.where(field2).is(field2val));
// you can add all your fields here as above
query.addCriteria(new Criteria().andOperator(criteria.toArray(new Criteria[criteria.size()])));
List<JSONObject> filteredVals = mongoOperations.find(query, JSONObject.class);
the above return filteredVals is JSONObject

how to calculate average of a particular field in mongodb spring?

how to calculate average of a field in mongoDB and spring. we have $avg() function for terminal use but how to execute it with mongotemplate.
for example in
db.sales.aggregate(
[
{
$group:
{
_id: "$item",
avgAmount: { $avg: { $multiply: [ "$price", "$quantity" ] } },
avgQuantity: { $avg: "$quantity" }
}
}
]
)
we are calculating average here so how can we execute it with mongotemplate.
Now I am using a function to get average rating
i am using function like this..
public List getrating() {
TypedAggregation<RatingReviewModel> agg = newAggregation(RatingReviewModel.class,
group("hospitalid")
.avg("rating").as("avgrating")
);
AggregationResults<DBObject> result = operations.aggregate(agg, DBObject.class);
List<DBObject> resultList = result.getMappedResults();
return resultList;
}
but at the time of debugging resultList is Empty so it is returning nothing.
Suppose your Sale object is defined as:
class Sale {
String id;
String item;
double price;
int quantity;
}
Using the mongotemplate you would need a $project stage in the pipeline before hand to get the calculated fields, which can be a bit counter-intuitive because with the native MongoDB aggregation all is done in one $group operation pipeline rather than splitting the aggregation into two stages, thus:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
TypedAggregation<Sale> agg = newAggregation(Sale.class,
project("quantity")
.andExpression("price * quantity").as("totalAmount"),
group("item")
.avg("totalAmount").as("avgAmount")
.avg("quantity").as("avgQuantity")
);
AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, DBObject.class);
List<DBObject> resultList = result.getMappedResults();
The above can also be achieved using the native Java Driver implementation:
ApplicationContext context = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
MongoOperations operation = (MongoOperations) context.getBean("mongoTemplate");
BasicDBList pipeline = new BasicDBList();
String[] multiplier = { "$price", "$quantity" };
pipeline.add(
new BasicDBObject("$group",
new BasicDBObject("_id", "$item")
.append("avgAmount", new BasicDBObject(
"$avg", new BasicDBObject(
"$multiply", multiplier
)
))
.append("avgQuantity", new BasicDBObject("$avg", "$quantity"))
)
);
BasicDBObject aggregation = new BasicDBObject("aggregate", "sales")
.append("pipeline", pipeline);
System.out.println(aggregation);
CommandResult commandResult = operation.executeCommand(aggregation);

IPP Create Customers Never Appears in QBD

I am trying to add a customer and an invoice to QuickBooks, but neither appear. QuickBooks responds with this XML:
http://pastebin.com/PLsFbA6N
My code for adding customers and invoices appears to work and I see no errors:
public Customer BuildCustomerAddRq(JMAOrder _Order)
{
// Construct subordinate required records
//BuildStandardTermsAddRq("Web Order");
// build the main customer record
Customer QBCustomerAdd = new Customer();
var Customer = _Order.BillingAddress;
var Billing = _Order.BillingAddress;
PhysicalAddress phy = new PhysicalAddress();
// if the setting is that all orders go under the same customer ID, then push
// the address lines down one and store the customer name on address line 1.
if (_qboSettings.CustomerID == "SingleName")
{
QBCustomerAdd.DBAName = "Web Store";
QBCustomerAdd.Email = new EmailAddress[] { new EmailAddress() { Address = "info#webstore.com", Tag = new string[] { "Business" } } };
QBCustomerAdd.GivenName = "Web";
QBCustomerAdd.Active = true;
QBCustomerAdd.FamilyName = "Store";
phy.Line1 = "Web Store";
phy.Line2 = "";
phy.Tag = new string[] { "Billing" };
}
else
{
//QBCustomerAdd.DBAName = GetCustId(_Order);
QBCustomerAdd.Email = new EmailAddress[] { new EmailAddress() { Address = Customer.Email, Tag = new string[] { "Business" } } };
QBCustomerAdd.GivenName = Customer.FirstName;
QBCustomerAdd.Active = true;
QBCustomerAdd.FamilyName = Customer.LastName;
if (!String.IsNullOrEmpty(Customer.PhoneNumber))
{
QBCustomerAdd.Phone = new TelephoneNumber[] { new TelephoneNumber() { FreeFormNumber = Customer.PhoneNumber, Tag = new string[] { "Business" } } };
}
phy.Line1 = Billing.Address1;
if (!String.IsNullOrEmpty(Billing.Address2))
{
phy.Line2 = Billing.Address2;
}
phy.City = Billing.City;
if (Billing.RegionName != null)
{
phy.CountrySubDivisionCode = Billing.RegionName;
}
phy.PostalCode = Billing.PostalCode;
phy.Country = Billing.CountryName;
phy.Tag = new string[] { "Billing" };
}
// build add request and exit
QBCustomerAdd.Address = new PhysicalAddress[] { phy };
try
{
Customer cu = dataServices.Add(QBCustomerAdd);
return cu;
}
catch (Exception ex)
{
ErrorMessageDataSource.Insert(new ErrorMessage(MessageSeverity.Error, "QBO", String.Format("Error adding customer : {0}", ex.ToString())));
Customer ct = new Customer();
return ct;
}
When I run Intuit Sync Manager, I see no new customer or invoice. Is it possible to add new customers to QuickBooks?
It appears that the customer entered QuickBooks in error state. I needed to add the QBCustomerAdd.Name field.
CustomerQuery cq = new CustomerQuery();
cq.ErroredObjectsOnly = true;
var bList = cq.ExecuteQuery<Customer>(dataServices.ServiceContext);