Query n to m SQlite database - database

I have n to m database (movie and cast). I need to get a list of cast associated with a movie.
#Entity(tableName = "movie")
data class MovieDbModel(
#PrimaryKey(autoGenerate = false)
val id: Int,
val poster_path: String,
val overview: String,
val title: String)
#Entity(tableName = "cast")
#TypeConverters(CastConverter::class)
data class CastDbModel(
#PrimaryKey(autoGenerate = false)
val id : Int,
val cast: Cast //Arraylist of casts
)
data class Cast(
#Embedded
var name: String,
var profile_path: String?,
var character: String
)
Crossreferenced class:
#Entity(
tableName = "movie_cast",
primaryKeys = ["movieIdMap","castIdMap"],
foreignKeys = [
ForeignKey(
entity = MovieDbModel::class,
parentColumns = ["id"],
childColumns = ["movieIdMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
ForeignKey(
entity = CastDbModel::class,
parentColumns = ["id"],
childColumns = ["castIdMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class MovieCastCrossRef(
var movieIdMap: Int,
#ColumnInfo(index = true)
var castIdMap: Int
)
Relation:
data class MovieWithListOfCast(
#Embedded /* The parent */
var movie: MovieDbModel,
#Relation(
entity = CastDbModel::class,
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(
value = MovieCastCrossRef::class,
parentColumn = "castIdMap",
entityColumn = "movieIdMap"
)
)
var castList: List<CastDbModel>
)
The Query looks like this:
#Transaction
#Query("select * FROM `cast` WHERE id = :id")
fun getAllCastAssociatedWithMovie(id: Int): List<MovieWithListOfCast>
But I get the following warning:
The query returns some columns [cast] which are not used by MovieWithListOfCast. You can use #ColumnInfo annotation on the fields to specify the mapping. You can annotate the method with #RewriteQueriesToDropUnusedColumns to direct Room to rewrite your query to avoid fetching unused columns.
MovieWithListOfCast has some fields [poster_path, overview, title] which are not returned by the query. If they are not supposed to be read from the result, you can mark them with #Ignore annotation. You can suppress this warning by annotating the method with #SuppressWarnings(RoomWarnings.CURSOR_MISMATCH). Columns returned by the query: id, cast.
How do I query to get a list of Cast that contains name, profile_path, and character?

I believe that you have concepts reversed.
First,
the #Embedded is the parent thus the parentColumn should specify a column in the #Embedded, you have parentColumn = "castIdMap" when it should be parentColumn = "movieIdMap" along with changing to use entityColumn = "castIdMap".
This would not necessarily be apparent at first but would likely lead to confusion (all depends upon the reversed id's)
Second,
the MovieWithListOfCast will get the list of cast for a movie, therefore it needs to get the movie from the movie table not the cast table.
So MovieWithListOfClass could be:-
data class MovieWithListOfCast(
#Embedded /* The parent */
var movie: MovieDbModel,
#Relation(
entity = CastDbModel::class,
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(
value = MovieCastCrossRef::class,
parentColumn = "movieIdMap",
entityColumn = "castIdMap"
)
)
var castList: List<CastDbModel>
)
Along with:-
#Transaction
#Query("select * FROM `movie` WHERE id = :id")
fun getAllCastAssociatedWithMovie(id: Int): List<MovieWithListOfCast>
You could also have (just in case that is what you are thinking):-
data class CastWithListOfMovies(
#Embedded
var castList: CastDbModel,
#Relation(
entity = MovieDbModel::class,
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(
value = MovieCastCrossRef::class,
parentColumn = "castIdMap",
entityColumn = "movieIdMap"
)
)
var movieList: List<MovieDbModel>
)
Along with:-
#Transaction
#Query("select * FROM `cast` WHERE id = :id")
fun getAllMoviesAssociatedWithCast(id: Int): List<CastWithListOfMovies>
Putting both into action using your code amended as above and with a suitable #Database annotated class then using:-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getAllDao()
val m1id = dao.insert(MovieDbModel(100,"posetr001","Western","The Good, the Bad and the Ugly"))
val m2id = dao.insert(MovieDbModel(200,"poster002","Sci-Fi","Star Wars"))
val m3id = dao.insert(MovieDbModel(300,"poster003","Soppy","Gone with the Wind"))
val c1id = dao.insert(CastDbModel(1,Cast("Actor1","acts","an actor")))
val c2id = dao.insert(CastDbModel(2,Cast("Actor2","acts","an actor")))
val c3id = dao.insert(CastDbModel(3,Cast("Actor3","acts","an actor")))
val c4id = dao.insert(CastDbModel(4,Cast("Actor4","acts","an actor")))
val c5id = dao.insert(CastDbModel(5,Cast("Actor5","acts","an actor")))
val c6id = dao.insert(CastDbModel(6,Cast("Actor6","acts","an actor")))
val c7id = dao.insert(CastDbModel(7,Cast("Actor7","acts","an actor")))
dao.insert(MovieCastCrossRef(m1id.toInt(),c1id.toInt()))
dao.insert(MovieCastCrossRef(m1id.toInt(),c3id.toInt()))
dao.insert(MovieCastCrossRef(m1id.toInt(),c5id.toInt()))
dao.insert(MovieCastCrossRef(m1id.toInt(),c7id.toInt()))
dao.insert(MovieCastCrossRef(m2id.toInt(),c2id.toInt()))
dao.insert(MovieCastCrossRef(m2id.toInt(),c4id.toInt()))
dao.insert(MovieCastCrossRef(m2id.toInt(),c6id.toInt()))
dao.insert(MovieCastCrossRef(m3id.toInt(),c1id.toInt()))
dao.insert(MovieCastCrossRef(m3id.toInt(),c2id.toInt()))
dao.insert(MovieCastCrossRef(m3id.toInt(),c3id.toInt()))
dao.insert(MovieCastCrossRef(m3id.toInt(),c4id.toInt()))
dao.insert(MovieCastCrossRef(m3id.toInt(),c5id.toInt()))
dao.insert(MovieCastCrossRef(m3id.toInt(),c6id.toInt()))
dao.insert(MovieCastCrossRef(m3id.toInt(),c7id.toInt()))
logCastWithMovie(c1id.toInt(),"C1")
logMovieWithCast(m1id.toInt(),"M1")
logCastWithMovie(c2id.toInt(),"C2")
logMovieWithCast(m2id.toInt(),"M2")
logCastWithMovie(c3id.toInt(),"C3")
logMovieWithCast(m3id.toInt(),"M3")
}
fun logMovieWithCast(movieId: Int, tagSuffix: String) {
var sb = StringBuilder()
for (cawm in dao.getAllCastAssociatedWithMovie(movieId)) {
for (c in cawm.castList) {
sb.append("\n\t Name is ${c.cast.name} Profile is ${c.cast.profile_path} Character is ${c.cast.character}")
}
Log.d("DBINFO_CAWM_$tagSuffix","Movie is ${cawm.movie.title} cast are:$sb")
}
}
fun logCastWithMovie(castId: Int, tagSuffix: String) {
var sb = StringBuilder()
for (mawc in dao.getAllMoviesAssociatedWithCast(castId)) {
for (m in mawc.movieList) {
sb.append("\n\tTitle is ${m.title} Overview is ${m.overview} Poster is ${m.poster_path}")
}
Log.d("DBINFO_MAWC_$tagSuffix","Cast is ${mawc.castList.cast.name} Movies are $sb")
}
}
}
Results in the log including:-
2022-09-03 08:15:25.084 D/DBINFO_MAWC_C1: Cast is Actor1 Movies are
Title is The Good, the Bad and the Ugly Overview is Western Poster is posetr001
Title is Gone with the Wind Overview is Soppy Poster is poster003
2022-09-03 08:15:25.095 D/DBINFO_CAWM_M1: Movie is The Good, the Bad and the Ugly cast are:
Name is Actor1 Profile is acts Character is an actor
Name is Actor3 Profile is acts Character is an actor
Name is Actor5 Profile is acts Character is an actor
Name is Actor7 Profile is acts Character is an actor
2022-09-03 08:15:25.102 D/DBINFO_MAWC_C2: Cast is Actor2 Movies are
Title is Star Wars Overview is Sci-Fi Poster is poster002
Title is Gone with the Wind Overview is Soppy Poster is poster003
2022-09-03 08:15:25.109 D/DBINFO_CAWM_M2: Movie is Star Wars cast are:
Name is Actor2 Profile is acts Character is an actor
Name is Actor4 Profile is acts Character is an actor
Name is Actor6 Profile is acts Character is an actor
2022-09-03 08:15:25.111 D/DBINFO_MAWC_C3: Cast is Actor3 Movies are
Title is The Good, the Bad and the Ugly Overview is Western Poster is posetr001
Title is Gone with the Wind Overview is Soppy Poster is poster003
2022-09-03 08:15:25.117 D/DBINFO_CAWM_M3: Movie is Gone with the Wind cast are:
Name is Actor1 Profile is acts Character is an actor
Name is Actor2 Profile is acts Character is an actor
Name is Actor3 Profile is acts Character is an actor
Name is Actor4 Profile is acts Character is an actor
Name is Actor5 Profile is acts Character is an actor
Name is Actor6 Profile is acts Character is an actor
Name is Actor7 Profile is acts Character is an actor

Related

How do I make a mutableArrayValue that I can access through a relationship?

I've added a to-many relationship called listItems between my entities Person and ListItem in my data model, and then successfully added objects to a given Person using
let personSelectedListItems = person.mutableSetValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
Where selectedListItems is an array of objects of type ListItem. The print statement gives:
personSelectedListItems after addObjects contains Relationship 'listItems' on managed object (0x6080000d2050) <PersonMO: 0x6080000d2050> (entity: Person; id: 0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1> ; data: {
age = Ky;
firstName = Uykyu;
image = <89504e47 0d0a1a0a 0000000d 49484452 000002cc 00000333 08020000 003d00d3 35000000 01735247 4200aece 1ce90000 001c>;
isVisited = 0;
lastName = Kuyuy;
listItems = (
"0xd000000005140000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p325>",
"0xd000000005680000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p346>",
"0xd0000000023c0000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p143>"
);
locationCity = Uyk;
locationState = You;
notes = "";
phoneNumber = "";
score = nil;
}) with objects {(
<ListItemMO: 0x6000000b6c20> (entity: ListItem; id: 0xd000000005140000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p325> ; data: {
category = Meta;
isSelected = 1;
listItem = "Similar values";
listItemStatus = 1;
listItemWeight = 1;
people = (
"0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1>"
);
}),
<ListItemMO: 0x6000000af900> (entity: ListItem; id: 0xd000000000080000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p2> ; data: {
category = Appearance;
isSelected = 1;
listItem = Attractive;
listItemStatus = 3;
listItemWeight = 1;
people = (
"0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1>"
);
}),
<ListItemMO: 0x6000000b27e0> (entity: ListItem; id: 0xd0000000023c0000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p143> ; data: {
category = Behavior;
isSelected = 1;
listItem = "Good grammar";
listItemStatus = 1;
listItemWeight = 1;
people = (
"0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1>"
);
})
)}
This is good, because I want to have these objects created for each Person, but it's bad because it's an unordered set that I'm seemingly not able to access. I'd like for it to be a mutable array so I can access each object at an index, but when I do
let personSelectedListItems = person.mutableArrayValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
I get this error message:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'NSManagedObjects of entity 'Person' do not support -mutableArrayValueForKey: for the property 'listItems''
Which is puzzling, since I don't know why mutableSetValueForKey: was was supported but the array version wasn't. I'd really like to be able to access the objects through the relationship, ideally through dot notation. What is the best way to go about this?
Core Data to-many relationships are always represented by sets. Part of the support for this is that NSManagedObject implements mutableSetValue(forKey:), which is one of the documented ways to access the relationship. Since to-many relationships are sets, NSManagedObject does not support mutableArrayValue(forKey:), as that error message indicates.
If you want an ordered relationship, mark it as ordered in the data model editor, and then you can use NSOrderedSet. It's still not an array but it does have ordering.
Both #Wain and #TomHarrington pointed me in the right direction on this, but here's what the answer to my question was (after some searching and tinkering):
First and foremost, I needed to mark the to-many relationship as 'ordered' in the Data Model Inspector of my data model file. The second thing was to change
let personSelectedListItems = person.mutableSetValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
to
let personSelectedListItems = person.mutableOrderedSetValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
so that mutableOrderedSetValue would reflect the ordered nature of the relationship. After that, I had a returned set that was in order. Hurray! But now how do I access and change those individual objects through the relationship? That brings me to the third thing - I had to downcast the set to my class (called ListItemMO). To grab the first object in the ordered set and then change a property through the relationship, it looks like this:
if let listItemSet = personVar.listItems?.object(at: 0) as? ListItemMO {
listItemSet.listItemStatus = 0
}

Google App Engine - Get from repeated StructuredProperty

I have the following structures:
class UserOther(ndb.Model):
other_type = ndb.StringProperty(indexed = True)
other_data = ndb.StringProperty(indexed = False)
class User(ndb.Model):
name = ndb.StringProperty(default = "NULL", indexed = False)
email = ndb.StringProperty(default = "NULL", indexed = False)
active = ndb.BooleanProperty(default = True)
others = ndb.StructuredProperty(UserOther, repeated = True)
updated_at = ndb.DateTimeProperty(auto_now = True)
How can I use an User key id and a string for other_type(like "job") to get and be able to edit that information. I tried using the ancestor parameter, but perhaps I didn't do that correctly.
user_key = ndb.Key("User", user_id)
user = user_key.get()
other = UserOther.query(UserOther.other_type == "job", ancestor = user_key).get()
So if i print my user looks like this :
1425436064.0User(key=Key('User', 5171003185430528), active=True, email=u'NULL', name=u'NULL', others=[UserOther(other_data=u'0', other_type=u'job'), UserOther(other_data=u'0', other_type=u'times_worked'), UserOther(other_data=u'0', other_type=u'times_opened')], updated_at=datetime.datetime(2015, 3, 6, 10, 35, 24, 838078))
But if I print the job variable it is
1425436759.0None
You've misunderstood how to query for structured properties. The UserOther entity doesn't live on its own, it's part of the relevant User entity, so that's what you need to query.
The documentation explains exactly how to do this, but in summary you would do:
job = User.query(User.others.other_type == "job").get()
What I would do is get the user (by id) and then filter the 'others' in code:
user = User.get_by_id(user_key_id)
for other in user.others:
if other.other_type == 'job':
print other.other_data # do edits

how to retrieve the value of a string column with slick?

I can't find a string result contained in a column...
here is the table:
object Equivalences extends Table[(Option[Int], String, String)]("EQUIVALENCES") {
def id = column[Int]("EQV_ID", O.PrimaryKey, O.AutoInc)
def racine = column[String]("RAC")
def definition = column[String]("DEF")
def * = id.? ~ racine ~ definition
}
and here is the wrong code:
def ajoute_si_racine_absente(racine_ajoutée: String, definition_ajoutée: String) = {
val cul = Query(Equivalences).filter(
equ => {
println(equ.racine)
equ.racine.toString.contains(racine_ajoutée)
})
if (cul.list().length == 0) {
Equivalences.insert(None, racine_ajoutée, definition_ajoutée)
}
}
the wrong code aims to insert a value if it does not exists, but the "println" within displays this result: "(EQUIVALENCES #409303125).RAC" and it does not match the column's content.
Maybe should I use the "getResult" method but I did not found any example on the net.
thanks.
Karol S is right. This does what you want:
def ajoute_si_racine_absente(racine_ajoutée: String, definition_ajoutée: String) = {
val cul = Query(Equivalences).list().filter(
equ => {
println(equ.racine)
equ.racine.toString.contains(racine_ajoutée)
})
if (cul.length == 0) {
Equivalences.insert(None, racine_ajoutée, definition_ajoutée)
}
}
But it may not be efficient, because you fetch the complete table. Slick is a query builder with a collection like API. Everything you write just resembles and builds up a query description until you finally call .listor .run. Only then the query is executed. Everything before are just placeholder objects representing tables, queries and columns. And the placeholder object for column racine prints as "(EQUIVALENCES #409303125).RAC".

Slick MSSQL inserting object with auto increment

I've recently had to move a project over from MySQL to MSSQL. I'm using IDENTITY(1,1) on my id columns for my tables to match MySQL's auto-increment feature.
When I try to insert an object though, I'm getting this error:
[SQLServerException: Cannot insert explicit value for identity column in table 'categories' when IDENTITY_INSERT is set to OFF.]
Now after some research I found out that it's because I'm trying to insert a value for my id(0) on my tables. So for example I have an object Category
case class Category(
id: Long = 0L,
name: String
)
object Category extends Table[Category]("categories"){
def name = column[String]("name", O.NotNull)
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def * = id ~ name <> (Category.apply _, Category.unapply _)
def add(model:Category) = withSession{ implicit session =>
Category.insert(model)
}
def remove(id:Long) = withSession{implicit session =>
try{Some(Query(Category).filter(_.id === id).delete)}
catch{case _ => None}
}
}
Is there a way to insert my object into the database and ignoring the 0L without MSSQL throwing an SQLException? MySQL would just ignore the id's value and do the increment like it didn't receive an id.
I'd really rather not create a new case class with everything but the id.
Try redefining your add method like this and see if it works for you:
def add(model:Category) = withSession{ implicit session =>
Category.name.insert(model.name)
}
If you had more columns then you could have added a forInsert projection to your Category table class that specified all fields except id, but since you don't, this should work instead.
EDIT
Now if you do have more than 2 fields on your table objects, then you can do something like this, which is described in the Lifted Embedding documentation here:
case class Category(
id: Long = 0L,
name: String,
foo:String
)
object Category extends Table[Category]("categories"){
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name", O.NotNull)
def foo = column[String]("foo", O.NotNull)
def * = id ~ name ~ foo <> (Category.apply _, Category.unapply _)
def forInsert = name ~ foo <> (t => Category(0L, t._1, t._2), {(c:Category) => Some(c.name, c.foo)})
def add(model:Category) = withSession{ implicit session =>
Category.forInsert insert model
}
def remove(id:Long) = withSession{implicit session =>
try{Some(Query(Category).filter(_.id === id).delete)}
catch{case _ => None}
}
def withSession(f: Session => Unit){
}
}

Google App Engine - Datastore - GQL Query

class ProjectCategory(db.Model):
name = db.StringProperty("Category name", required = True)
def __str__(self):
return str(self.name)
class Project(db.Model):
name = db.StringProperty("Name", required = True)
category = db.ReferenceProperty(ProjectCategory)
description = db.TextProperty("Description", required = True)
#file_name = db.StringProperty("File name", required = True)
file = db.BlobProperty("Image")
whenstarted = db.DateTimeProperty("Start time")
whenended = db.DateTimeProperty("End Time")
def __str__(self):
return str(self.title)
How to get all Projects where category is CatName
hmm
db.GqlQuery("SELECT * FROM Project WHERE category = :1", "CatName")
don't work ?
The query does not work because you are passing a "CatName" string instead of a ProjectCategory instance's key.
Retrieve your desired ProjectCategory entity from Datastore first with:
pjc = GqlQuery("SELECT * FROM ProjectCategory WHERE name = :1", "CatName").get()
then use it as parameter in the query like this:
db.GqlQuery("SELECT * FROM Project WHERE category = :1", pjc.key())
A second approach is to use the implicit modelname_set Property of the ProjectCategory instance:
pjc = GqlQuery("SELECT * FROM ProjectCategory WHERE name = :1", "CatName").get()
pjc.project_set.fetch(10) #should contains some CatName projects

Resources