Skip to content

Commit

Permalink
Ordering with a relation id
Browse files Browse the repository at this point in the history
  • Loading branch information
Julius de Bruijn committed Mar 16, 2020
1 parent 186979d commit 89330c5
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 81 deletions.
4 changes: 4 additions & 0 deletions libs/prisma-models/src/field.rs
Expand Up @@ -58,6 +58,10 @@ impl DataSourceField {
pub fn model_field(&self) -> Field {
self.model_field.upgrade()
}

pub fn name(&self) -> &str {
self.backing_field.name.as_str()
}
}

impl Deref for DataSourceField {
Expand Down
10 changes: 6 additions & 4 deletions libs/prisma-models/src/field/relation.rs
Expand Up @@ -173,9 +173,9 @@ impl RelationField {
}

pub fn model(&self) -> ModelRef {
self.model
.upgrade()
.expect("Model does not exist anymore. Parent model got deleted without deleting the child.")
self.model.upgrade().expect(
"Model does not exist anymore. Parent model got deleted without deleting the child.",
)
}

pub fn relation(&self) -> RelationRef {
Expand Down Expand Up @@ -250,6 +250,8 @@ impl RelationField {
}

pub fn db_names(&self) -> impl Iterator<Item = &str> {
self.data_source_fields().into_iter().map(|dsf| dsf.name.as_str())
self.data_source_fields()
.into_iter()
.map(|dsf| dsf.name.as_str())
}
}
92 changes: 79 additions & 13 deletions libs/prisma-models/src/record.rs
@@ -1,4 +1,7 @@
use crate::{DataSourceFieldRef, DomainError, ModelProjection, PrismaValue, RecordProjection};
use crate::{
DataSourceFieldRef, DomainError, Field, ModelProjection, OrderBy, PrismaValue, RecordProjection,
};
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub struct SingleRecord {
Expand All @@ -17,7 +20,10 @@ impl Into<ManyRecords> for SingleRecord {

impl SingleRecord {
pub fn new(record: Record, field_names: Vec<String>) -> Self {
Self { record, field_names }
Self {
record,
field_names,
}
}

pub fn projection(&self, projection: &ModelProjection) -> crate::Result<RecordProjection> {
Expand All @@ -36,7 +42,59 @@ pub struct ManyRecords {
}

impl ManyRecords {
pub fn projections(&self, model_projection: &ModelProjection) -> crate::Result<Vec<RecordProjection>> {
pub fn new(field_names: Vec<String>) -> Self {
Self {
records: Vec::new(),
field_names,
}
}

pub fn order_by(&mut self, order_by: &OrderBy) {
let field_indices: HashMap<&str, usize> = self
.field_names
.iter()
.enumerate()
.map(|(i, name)| (name.as_str(), i))
.collect();

self.records.sort_by(|a, b| match order_by.field {
Field::Scalar(ref sf) => {
let index = field_indices[sf.db_name()];

if order_by.sort_order.is_ascending() {
a.values[index].cmp(&b.values[index])
} else {
b.values[index].cmp(&a.values[index])
}
}
Field::Relation(ref rf) => {
let ds_fields = rf.data_source_fields();
let mut a_vals = Vec::with_capacity(ds_fields.len());
let mut b_vals = Vec::with_capacity(ds_fields.len());

for dsf in ds_fields {
let index = field_indices[dsf.name()];
a_vals.push(&a.values[index]);
b_vals.push(&b.values[index]);
}

if order_by.sort_order.is_ascending() {
a_vals.cmp(&b_vals)
} else {
b_vals.cmp(&a_vals)
}
}
})
}

pub fn push(&mut self, record: Record) {
self.records.push(record);
}

pub fn projections(
&self,
model_projection: &ModelProjection,
) -> crate::Result<Vec<RecordProjection>> {
self.records
.iter()
.map(|record| {
Expand Down Expand Up @@ -123,16 +181,24 @@ impl Record {
Ok(x)
}

pub fn get_field_value(&self, field_names: &[String], field: &str) -> crate::Result<&PrismaValue> {
let index = field_names.iter().position(|r| r == field).map(Ok).unwrap_or_else(|| {
Err(DomainError::FieldNotFound {
name: field.to_string(),
model: format!(
"Field not found in record {:?}. Field names are: {:?}, looking for: {:?}",
&self, &field_names, field
),
})
})?;
pub fn get_field_value(
&self,
field_names: &[String],
field: &str,
) -> crate::Result<&PrismaValue> {
let index = field_names
.iter()
.position(|r| r == field)
.map(Ok)
.unwrap_or_else(|| {
Err(DomainError::FieldNotFound {
name: field.to_string(),
model: format!(
"Field not found in record {:?}. Field names are: {:?}, looking for: {:?}",
&self, &field_names, field
),
})
})?;

Ok(&self.values[index])
}
Expand Down
Expand Up @@ -5,19 +5,18 @@ import util.{ApiSpecBase, ProjectDsl}

class InSelectionBatching extends FlatSpec with Matchers with ApiSpecBase {
val project = ProjectDsl.fromString {
"""model Artist {
| id String @id @default(cuid())
| ArtistId Int @unique
| Name String
| Albums Album[]
"""model A {
| id Int @id
| b B
| c C
|}
|
|model Album {
| id String @id @default(cuid())
| AlbumId Int @unique
| Title String
| Artist Artist @relation(references: [id])
| @@index([Artist])
|model B {
| id Int @id
| as A[]
|}
|model C {
| id Int @id
| as A[]
|}
|"""
}
Expand All @@ -27,50 +26,55 @@ class InSelectionBatching extends FlatSpec with Matchers with ApiSpecBase {
database.setup(project)

server.query(
"""mutation artistWithoutAlbums {createArtist(data:{
| Name: "ArtistWithoutAlbums"
| ArtistId: 1
|}){Name}}""",
"""mutation a {createA(data:{
| id: 1
| b: { create: { id: 1 } }
| c: { create: { id: 1 } }
|}){id}}""",
project = project
)

server.query(
"""mutation artistWithAlbumButWithoutTracks {createArtist(data:{
| Name: "ArtistWithOneAlbumWithoutTracks"
| ArtistId: 2,
|}){Name}}""",
"""mutation a {createA(data:{
| id: 2
| b: { connect: { id: 1 } }
| c: { create: { id: 2 } }
|}){id}}""",
project = project
)

server.query(
"""mutation artistWithAlbumButWithoutTracks {createArtist(data:{
| Name: "Three"
| ArtistId: 3,
|}){Name}}""",
"""mutation a {createA(data:{
| id: 3
| b: { create: { id: 3 } }
| c: { create: { id: 3 } }
|}){id}}""",
project = project
)

server.query(
"""mutation artistWithAlbumButWithoutTracks {createArtist(data:{
| Name: "Four"
| ArtistId: 4,
|}){Name}}""",
"""mutation a {createA(data:{
| id: 4
| b: { create: { id: 4 } }
| c: { create: { id: 4 } }
|}){id}}""",
project = project
)

server.query(
"""mutation artistWithAlbumButWithoutTracks {createArtist(data:{
| Name: "Five"
| ArtistId: 5,
|}){Name}}""",
"""mutation a {createA(data:{
| id: 5
| b: { create: { id: 5 } }
| c: { create: { id: 5 } }
|}){id}}""",
project = project
)
}

"batching of IN queries" should "work when having more than the specified amount of items" in {
val res = server.query(
"""query idInTest {
| findManyArtist(where: { ArtistId_in: [5,4,3,2,1,1,1,2,3,4,5,6,7,6,5,4,3,2,1,2,3,4,5,6] }) { ArtistId }
| findManyA(where: { id_in: [5,4,3,2,1,1,1,2,3,4,5,6,7,6,5,4,3,2,1,2,3,4,5,6] }) { id }
|}
|""".stripMargin,
project = project,
Expand All @@ -79,14 +83,14 @@ class InSelectionBatching extends FlatSpec with Matchers with ApiSpecBase {
)

res.toString should be(
"""{"data":{"findManyArtist":[{"ArtistId":1},{"ArtistId":2},{"ArtistId":3},{"ArtistId":4},{"ArtistId":5}]}}""".stripMargin
"""{"data":{"findManyA":[{"id":1},{"id":2},{"id":3},{"id":4},{"id":5}]}}""".stripMargin
)
}

"ascending ordering of batched IN queries" should "work when having more than the specified amount of items" in {
val res = server.query(
"""query idInTest {
| findManyArtist(where: { ArtistId_in: [5,4,3,2,1,2,1,1,3,4,5,6,7,6,5,4,3,2,1,2,3,4,5,6] }, orderBy: ArtistId_ASC) { ArtistId }
| findManyA(where: { id_in: [5,4,3,2,1,2,1,1,3,4,5,6,7,6,5,4,3,2,1,2,3,4,5,6] }, orderBy: id_ASC) { id }
|}
|""".stripMargin,
project = project,
Expand All @@ -95,14 +99,56 @@ class InSelectionBatching extends FlatSpec with Matchers with ApiSpecBase {
)

res.toString should be(
"""{"data":{"findManyArtist":[{"ArtistId":1},{"ArtistId":2},{"ArtistId":3},{"ArtistId":4},{"ArtistId":5}]}}""".stripMargin
"""{"data":{"findManyA":[{"id":1},{"id":2},{"id":3},{"id":4},{"id":5}]}}""".stripMargin
)
}

"descending ordering of batched IN queries" should "work when having more than the specified amount of items" in {
val res = server.query(
"""query idInTest {
| findManyArtist(where: { ArtistId_in: [5,4,3,2,1,1,1,2,3,4,5,6,7,6,5,4,3,2,1,2,3,4,5,6] }, orderBy: ArtistId_DESC) { ArtistId }
| findManyA(where: {id_in: [5,4,3,2,1,1,1,2,3,4,5,6,7,6,5,4,3,2,1,2,3,4,5,6] }, orderBy: id_DESC) { id }
|}
|""".stripMargin,
project = project,
legacy = false,
batchSize = 2,
)

res.toString should be(
"""{"data":{"findManyA":[{"id":5},{"id":4},{"id":3},{"id":2},{"id":1}]}}""".stripMargin
)
}

"ascending ordering of batched IN with relation field" should "work" in {
val res = server.query(
"""
|query {
| findManyB {
| as(orderBy: c_ASC) {
| c { id }
| }
| }
|}
|""".stripMargin,
project = project,
legacy = false,
batchSize = 2,
)

res.toString should be(
"""{"data":{"findManyB":[{"as":[{"c":{"id":1}},{"c":{"id":2}}]},{"as":[{"c":{"id":3}}]},{"as":[{"c":{"id":4}}]},{"as":[{"c":{"id":5}}]}]}}""".stripMargin
)
}

"descending ordering of batched IN with relation field" should "work" in {
val res = server.query(
"""
|query {
| findManyB {
| as(orderBy: c_DESC) {
| c { id }
| }
| }
|}
|""".stripMargin,
project = project,
Expand All @@ -111,7 +157,7 @@ class InSelectionBatching extends FlatSpec with Matchers with ApiSpecBase {
)

res.toString should be(
"""{"data":{"findManyArtist":[{"ArtistId":5},{"ArtistId":4},{"ArtistId":3},{"ArtistId":2},{"ArtistId":1}]}}""".stripMargin
"""{"data":{"findManyB":[{"as":[{"c":{"id":2}},{"c":{"id":1}}]},{"as":[{"c":{"id":3}}]},{"as":[{"c":{"id":4}}]},{"as":[{"c":{"id":5}}]}]}}""".stripMargin
)
}
}

0 comments on commit 89330c5

Please sign in to comment.