Only update not null fields to Mongodb from Pojo - mongodb

I want to update only those fields in mongo document that have values in the POJO. How do I do this?
I have a User collection with the following document:
{
_id: ObjectId("someId"),
name: "Foo",
age: 20,
gender: "male"
}
Now I want to update only the age to 22 so, I have a User pojo with age = 22 and _id = ObjectId("someId").
I don't want to use Updates.set("age",22) because then I'll have to handle every field. The field to be populated may be something other than age.
So, I used reflection to make a generic method to get the list of Update.sets for the pojo
classMembers = User::class.memberProperties
// get hashmap from pojo
val fieldsMap: HashMap<String, Any> =
mapper.convertValue(pojo, object : TypeReference<HashMap<String, Any>>() {}) ?:
throw CustomException( HttpStatus.BAD_REQUEST, "Could not parse request" )
// get hashmap from default/empty pojo
val defaultMap: HashMap<String, Any> =
mapper.convertValue(emptyPojo, object : TypeReference<HashMap<String, Any>>() {}) ?:
throw CustomException( HttpStatus.BAD_REQUEST, "Could not parse request" )
for (member in classMembers) {
val name = member.name
val value = fieldsMap[name]
//add to list only if the current value is not null or default and current value is different
if (value != null && defaultMap[member.name] != value && member.getter.annotations.isNotEmpty()) {
val field = (member.getter.annotations[0] as BsonProperty).value
setList.add(Updates.set(field, value))
}
}
This works. But I wanted to know if theres a better way to do it? A default way to do this via mongoClient or mongoTemplate or mongoRepository?

Related

How to avoid a nested query?

In Sangria I'm getting a list of users by IDs
lazy val UsersType: ObjectType[GraphQLContext, Seq[User]] = ObjectType("users", () =>
fields[GraphQLContext, Seq[User]](
Field("users", ListType(UserDetailType), resolve =
_.value),
))
The Query works, but I have to write the name of the Query and the name of the Field "users" twice, they are the same.
The Query now is:
users(id: 123) {
users {
id
name
}
}
How to avoid the need to type the query name and the field name? How to turn the query to:
users(id: 123) {
id
name
}
Just use ListType(UsersType) in schema

Entity Core Insert Adding Duplicate Rows

I'm trying to inserts an object and it's coming from a JSON file. The object has relationships which I've configured in EF Core and show in the database properly.
Unfortunately some of the data has duplicate entries in it but the Save action only happens at the end - so instead of getting a single child I get two child object when only one should exist.
JArray obj2 = (JArray)result.SelectToken("resistances");
List<CardResistance> pCardResistances = new List<CardResistance>();
foreach (var result2 in obj2)
{
//Search for Resistance Value object and create if not exists
Resistance ResistanceObj = ctx.Resistances.SingleOrDefault(m => m.ResistanceValue.Equals((string)result2["value"]))
?? new Resistance
{
ResistanceValue = (string)result2["value"],
LastUpdateDate = DateTime.Now
};
pCardResistances.Add(
new PokemonCardResistance
{
PokemonCard = PokemonCardObj,
Resistance = ResistanceObj
}
);
}
CardObj.CardResistances = pCardResistances;
...
ctx.AddOrUpdate and ctx.SaveChanges occur later in the code.
If you have JSON with the:
"resistances": [
{
"type": "Value1",
"value": "-30"
},
{
"type": "Value1",
"value": "-30"
}
]
In it what's happening is both rows get inserted for the entity (caused as the Save happens right at the end of all of the object data being populated. How can I save just the child object after each loop so it won't insert this data twice?
Your problem is to do with how you are checking for existence:
List<CardResistance> pCardResistances = new List<CardResistance>();
foreach (var result2 in obj2)
{
//Search for Resistance Value object and create if not exists.
//This will do a DB hit each time - if it doesn't exist and you have two
//items of the same value to add, then you will get that it does not exist
//each time.
Resistance ResistanceObj = ctx.Resistances.SingleOrDefault(m =>
m.ResistanceValue.Equals((string)result2["value"]))
?? new Resistance
{
ResistanceValue = (string)result2["value"],
LastUpdateDate = DateTime.Now
};
//One way to fix this would be to check that there is not already an item in this
//list that has this value set. And only either add/overwrite if that is the case.
pCardResistances.Add(
new PokemonCardResistance
{
PokemonCard = PokemonCardObj,
Resistance = ResistanceObj
}
);
}
CardObj.CardResistances = pCardResistances;
The other thing to note is more of a question as to what this should do exactly. Getting two updates in the same JSON for the same entity like this is a bit of an odd approach - is this valid?
If it is valid, then which one should 'win' (first or last in the json) and you may then be better placed altering the collection that you loop around (obj2 in your code - less said about your naming conventions the better...).

ReactiveMongoRepository / MongoRepository does not return _id field

I think this issue probably has to do with my Mongo Document Koltin Data class, but for our business case we need to allow the user to add on any JSON fields to describe their RF data set.
Extending the BasicDBObject was the best way I have found.
The mono being returned when I save a SigMfMetaDocument does not contain the _id field.
I cannot figure out why the save method does not return a Mono wrapping a SigMfDocument with and _id
If there is a better way to create a Type for ReactiveMongoRepository that can dynamically accept any fields I am all ears.
#Document(collection = "sigmfmeta")
class SigMfMetaDocument : BasicDBObject {
#Id
#JsonProperty("id")
val id: String? = UUID.randomUUID().toString()
constructor(map: Map<String, Any>) : super(map)
constructor() : super()
constructor(key: String, value: Object): super()
}
#Repository
interface SigMfMetaRepository : ReactiveMongoRepository<SigMfMetaDocument, String>
So I found a way to solve this for my use case. I was originally assuming the description in the documentation for the save method would apply
(Saves a given entity. Use the returned instance for further operations as the save operation might have changed the entity instance completely).
My thought Mongo auto inserting the _id value would apply to this description.
I changed my model to:
#Document(collection = "sigmfmeta")
class SigMfMetaDocument : BasicBSONObject {
constructor(map: Map<String, Any>) : super(map) {
val id = ObjectId()
this.put("_id", id)
}
constructor() : super()
}
This way I have the _id value after saving for some business logic. Again I defined my Model this way because the metadata file we are accepting needs to allow a client to add any fields they wish to describe a binary file of RF measurement data.

using findAndModify in mongodb using grails gorm

I need to use findAndModify in my application with grails and mongoDB.
I used this code :
public static String getNextId(DB db, String seq_name) {
String sequence_collection = "seq"; // the name of the sequence collection
String sequence_field = "seq"; // the name of the field which holds the sequence
DBCollection seq = db.getCollection(sequence_collection); // get the collection (this will create it if needed)
// this object represents your "query", its analogous to a WHERE clause in SQL
DBObject query = new BasicDBObject();
query.put("_id", seq_name); // where _id = the input sequence name
// this object represents the "update" or the SET blah=blah in SQL
DBObject change = new BasicDBObject(sequence_field, 1);
DBObject update = new BasicDBObject("$inc", change); // the $inc here is a mongodb command for increment
// Atomically updates the sequence field and returns the value for you
DBObject res = seq.findAndModify(query, new BasicDBObject(), new BasicDBObject(), false, update, true, true);
return res.get(sequence_field).toString();
}
and it work successful. But now I want use findAndModify without native mongodb object, and with using GORM.
Is there any solution for this work?
There is not way to accomplish this without native API, you can however write your code a bit more compact like this:
def collection = Seq.collection
collection.findAndModify([_id: seq_name ], [ "\$inc": [seq:1] ])
Config your DataSource.groovy with db configurations.
Then define a Domain class:
Class Seq{
int seq
}
And use dynamic finder in a sevice:
Class SeqService {
String findAndModify(String seq_name) {
def seqInstance = Seq.get(seq_name)
if(seqInstance){
seqInstance.seq ++
seqInstance.save()
return seqInstance.seq.toString()
}
return '' //instance not found
}
}
Then make a call when you need that action:
def seqService
def id
.......
def result = seqService.findAndModify(id)
....

MongoDB C# Remove doesn't work

i have this code for removing an item froma a mongofb collation
private MongoCollection<T> GetCollection()
{
connectionString = "mongodb://localhost/?safe=true";
server = MongoServer.Create(connectionString);
database = server.GetDatabase("CSCatalog");
return database.GetCollection<T>("myCollectionName");
}
public bool Delete(T entity)
{
var id = typeof(T).GetProperty("Id").GetValue(entity,null).ToString();
var query = Query.EQ("_id",id);
var finded = GetCollection().Find(query); // return null
var result= GetCollection().Remove(query, MongoDB.Driver.RemoveFlags.Single); // no errors, but don't remove
return esito.Ok; //return true but donn't remove.
}
the GetCollection() method retrive the right collection, i have tested it width debug.
In the collection there is the item that i want remove, it have the same id that i have retrived in first line.
the entity have some fields and a Objectid filed called "Id"
the type of _id you created is ObjectId class and you are trying to equate with string so its not able to remove. use
var queryId = new ObjectId(id);
Your finded variable should not be null if the .find() has returned something from your database. That it is null means that you have not found anything, and therefore nothing is to be removed.
What it looks like is happening here is that you are querying on _id for the ObjectId, while you are storing that ObjectId in the database as Id.