Grails MongoDB Lists do not update, am I doing something wrong? - mongodb

I am currently using Grails 2.5.4, with the MongoDB plugin (org.grails.plugins:mongodb:6.0.0.RC1) and whenever I try to update a List of any domain class, it doesn't work, example:
Votation class:
class Votation {
String question
int minVotes
List <VoteOption> options
User owner
Chat chat
static belongsTo = [chat: Chat]
static embedded = ['options']
static constraints = {
owner nullable: false
chat nullable: false
//question nullable: false
}
VoteOption class:
class VoteOption {
String option
String url
List <User> voters
static belongsTo = [chat: Chat]
}
When I try to update the list:
//some more code...
Votation votation = Votation.findById(votationId as Long)
VoteOption option = votation.options.find { it.option == votationOption }
User user = User.findOrCreateNew(params.user)
if (option.voters) {
option.voters.add(user) // THIS DOESN'T WORK!
}
else {
option.voters = [user] //This DOES work
}
This is just an example, I have 2 more domain classes that also have Lists, and they don't work either.
Restarting Grails does not fix this, and this also happens on the other developer's computer, so it's not my enviroment. Everything else is saved correctly

Try this
//some more code...
Votation votation = Votation.findById(votationId as Long)
VoteOption option = votation.options.find { it.option == votationOption }
User user = User.findOrCreateNew(params.user)
if (user) {
option.addToVoters(user) // <----
}
option.save(flush:true, failOnError:true)
Ref: http://docs.grails.org/2.1.0/ref/Domain%20Classes/addTo.html

Related

Migration not starting with mongock 4.3.8

I am trying to create indexes on a mongodb collection using mongock version 4.3.8. The mongockLock and mongockChangeLog collections are eventually created, but the migration itself does not run. mongockChangeLog table is empty. Query db.BOOKS.getIndexes(); returns a single string: with an index for id (the value of name in it is equal to _id_), which is most likely generated automatically. There are no obvious errors in the logs either.
Everything else seems to be set up correctly, I don't understand why my migration is not running.
build.gradle.kts:
implementation("com.github.cloudyrock.mongock:mongodb-springdata-v3-driver:4.3.8")
implementation("com.github.cloudyrock.mongock:mongock-spring-v5:4.3.8")
implementation(platform("com.github.cloudyrock.mongock:mongock-bom:4.3.8"))
application.yml:
mongock:
change-logs-scan-package:
- com.dreamland.configuration.migration
config file:
#EnableMongock
#Configuration
#EnableMongoAuditing
class MongoConfig
Migration file:
#ChangeLog(order = "1668471203")
class IndexChangeLog {
companion object : KLogging()
#ChangeSet(order = "1668471204", id = "1668471204_create_indexes", author = "Irish")
fun createIndexes(mongockTemplate: MongockTemplate) {
val indexOps = mongockTemplate.indexOps(BookObjectDocument::class.java)
Index().on("createdAt", Sort.Direction.DESC).let { indexOps.ensureIndex(it) }.also { log(it) }
Index().on("createdBy", Sort.Direction.ASC).let { indexOps.ensureIndex(it) }.also { log(it) }
Index().on("type", Sort.Direction.DESC).let { indexOps.ensureIndex(it) }.also { log(it) }
}
private fun log(indexName: String) {
logger.info { "Index '$indexName' was created successfully" }
}
}
Document class:
#Document(collection = "BOOKS_V2")
data class BookObjectDocument(
val type: BookObjectType?,
val description: String?
) {
#Id
lateinit var id: String
#CreatedDate
lateinit var createdAt: Instant
#CreatedBy
var createdBy: String? = null
}

Dart: parse api response and dynamically create child T from parent

Looking for a clean way to parse out data from an API.
The API returns data for creating “Boat” and “Car” models wrapped up in metadata.
{
“timestamp” “0000”,
“data”: {
“count” “1”,
“results” [
{
“name”: “boatyMcBoatFace”
}
]
}
}
I want to create 2 classes for the metadata and also the boat/car data using custom fromJson() methods.
Current implementation is something like this
Class Boat {
String name;
Boat.fromJson(json) {
this.name = json[‘name’]
}
static List<Boat> listFromJson(List<dynamic> json) {
return json.map((c) => Boat.fromJson(c)).toList();
}
}
ResponseModel<T> {
String timestamp
DataModel data
ResponseModel.fromJson(json) {
this.timestamp = json[‘timestamp’]
this.data = DataModel<T>.fromJson(json[‘data’])
}
}
DataModel<T> {
String count
List<T> results
DataModel.fromJson(json) {
this.count = json[‘count’]
this.results = json[‘results’] // change this, see below
}
}
Currently I'm creating a ResponseModel, which in turn creates a DataModel.
But then I'm manually creating and setting Boats using:
// yes
final res = methodThatMakesHttpRequest();
final apiResponse = ResponseModel<Boat>.fromJson(res);
// no
final boats = Boat.listFromJson(apiResponse.data.results);
apiResponse.data.results = boats;
Ideally I would remove those last two lines, and instead have Boats get created dynamically within DataModel.fromJson with something like
DataModel.fromJson(json) {
this.count = json[‘count’]
T.listFromJson(json[‘results’])
}
But this of course does not work as T.listFromJson does not exist.

Using Dynamic LINQ with EF.Functions.Like

On the Dynamic LINQ website there's an example using the Like function.
I am unable to get it to work with ef core 3.1
[Test]
public void DynamicQuery()
{
using var context = new SamDBContext(Builder.Options);
var config = new ParsingConfig { ResolveTypesBySimpleName = true };
var lst = context.Contacts.Where(config, "DynamicFunctions.Like(FirstName, \"%Ann%\")".ToList();
lst.Should().HaveCountGreaterThan(1);
}
Example from the Dynamic LINQ website
var example1 = Cars.Where(c => EF.Functions.Like(c.Brand, "%t%"));
example1.Dump();
var config = new ParsingConfig { ResolveTypesBySimpleName = true };
var example2 = Cars.Where(config, "DynamicFunctions.Like(Brand, \"%t%\")");
example2.Dump();
Looks like my code. But I am getting the following error
System.Linq.Dynamic.Core.Exceptions.ParseException : No property or field 'DynamicFunctions' exists in type 'Contact'
you don't need the ResolveTypesBySimpleName, implement your wont type provider.
The piece below people to use PostgreSQL ILike with unnaccent
public class LinqCustomProvider : DefaultDynamicLinqCustomTypeProvider
{
public override HashSet<Type> GetCustomTypes()
{
var result = base.GetCustomTypes();
result.Add(typeof(NpgsqlFullTextSearchDbFunctionsExtensions));
result.Add(typeof(NpgsqlDbFunctionsExtensions));
result.Add(typeof(DbFunctionsExtensions));
result.Add(typeof(DbFunctions));
result.Add(typeof(EF));
return result;
}
}
// ....
var expressionString = $"EF.Functions.ILike(EF.Functions.Unaccent(People.Name), \"%{value}%\")";
var config = new ParsingConfig()
{
DateTimeIsParsedAsUTC = true,
CustomTypeProvider = new LinqCustomProvider()
};
return query.Where(config, expressionString);
Hope this helps people, took me some time to get this sorted.

Unable to cast object of type Entity to Type ActivityParty

Im working with a custom plugin for CRM online 2015 and every time I try to access the activityparty from the field "Email.To" I get
"base {System.SystemException} = {"Unable to cast object of type 'Microsoft.Xrm.Sdk.Entity' to type ...ActivityParty'."}"
Here is how my code looks like:
public class PreCreate : Plugin
{
public PreCreate()
: base(typeof(PreCreate))
{
base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Create", "email", new Action<LocalPluginContext>(ExecutePreEntityCreate)));
}
public void ExecutePreEntityCreate(LocalPluginContext localContext)
{
var target = (Entity)localContext.PluginExecutionContext.InputParameters["Target"];
using (var context = new XrmServiceContext(localContext.OrganizationService))
{
var email = target.ToEntity<Email>(); //The entity has the right values
var activityPartyList=email.To // here I see the exception
//If I use the following code:
var activityParty = email.GetAttributeValue<EntityCollection>("to");
//I get an empty ActivityParty(empty Id)
}
}
}
Do I have to do some initialization for activityparty types?
There is no issue with the code, the field Email.To will return a EntityCollection and to obtain that you need to use:
var entityCollection = email.GetAttributeValue<EntityCollection>("to");
This will give you a collection of entities that need to be converted to ActivityParty(entityCollection.Entities).
To convert the Entities you need to:
foreach (var entityItem in entityCollection.Entities)
{
var ap = entityItem.ToEntity<ActivityParty>();
//Here you will get the LogicalName in this case Lead
// the Id and the name
var leadId = ap.PartyId.Id;
//To get the Lead
var lead=context.LeadSet.FirstOrDefault(l => l.Id == leadId);
}

Update Mongo document field from Grails app

I have a requirement where I have to check in the database if the value of a boolean variable(crawled) is false. If so I have to set it to true. I am finding the record based on the value of a string variable(website). I referenced this link but it didn't help. Please tell me what I am doing wrong.
I tried this:
def p = Website.findByWebsite(website);
if(p['crawled'] == true) {
println "already crawled"
} else {
mongo.website.update( p, new BasicDBObject( '$set', new BasicDBObject( 'crawled', 'false' ) ) )
println "updated object"
}
It gives me an error No such property: mongo for class: cmsprofiler.ResourceController
My domain class is as follows:
class Website{
String website
User user
Boolean crawled
static belongsTo = [user: User]
static constraints = {
website( url:true, unique: ['user'])
}
static hasMany = [resource: Resource]
static mapping = {resource cascade:"all-delete-orphan" }
}
you should be using
Website.mongo.update(...)
or let the framework inject it:
class ResourceController {
def mongo
def list(){
mongo.website.update(...)
}
}
This worked for me. Posting it here in case anyone else has similar requirement.
#Grab(group='com.gmongo', module='gmongo', version='0.9.3')
import com.gmongo.GMongo
#Transactional(readOnly = true)
class ResourceController {
def mongo = new GMongo()
def db = mongo.getDB("resources")
def p = Website.findByWebsite(website)
if(p['crawled']==false){
db.website.update([crawled:false],[$set:[crawled:true]])
}