Skip to content

Commit 9e17a01

Browse files
authored
Merge pull request prisma#4457 from prisma/MongoCursorBug
Mongo Aggregation Fixes
2 parents 509b1fd + d0d622d commit 9e17a01

File tree

4 files changed

+121
-27
lines changed

4 files changed

+121
-27
lines changed

server/connectors/api-connector-mongo/src/main/scala/com/prisma/api/connector/mongo/database/AggregationQueryBuilder.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ trait AggregationQueryBuilder extends FilterConditionBuilder with ProjectionBuil
2626
selectedFields: SelectedFields,
2727
rowValueOpt: Option[GCValue]): Future[Seq[Document]] = {
2828
aggregationQueryForId(database, model, queryArguments, rowValueOpt).flatMap { ids =>
29-
val inFilter = in("_id", ids.map(GCToBson(_)): _*)
30-
database.getCollection(model.dbName).find(inFilter).projection(projectSelected(selectedFields)).toFuture
29+
val bsonIds = ids.distinct.map(GCToBson(_))
30+
database.getCollection(model.dbName).find(in("_id", bsonIds: _*)).projection(projectSelected(selectedFields)).toFuture.map { seq =>
31+
bsonIds.map(id => seq.find(doc => doc.get("_id").get == id).get) //sort according to ids ordering
32+
}
3133
}
3234
}
3335

@@ -58,7 +60,7 @@ trait AggregationQueryBuilder extends FilterConditionBuilder with ProjectionBuil
5860
//-------------------------------- Match on Cursor Condition ----------------------------------------
5961
val pipeline = rowValueOpt match {
6062
case None => common
61-
case Some(rowValue) => CursorConditionBuilder.buildCursorCondition(model, queryArguments, rowValue) +: common
63+
case Some(rowValue) => `match`(CursorConditionBuilder.buildCursorCondition(model, queryArguments, rowValue)) +: common
6264
}
6365

6466
database.getCollection(model.dbName).aggregate(pipeline).toFuture.map(_.map(readsId))

server/connectors/api-connector-mongo/src/main/scala/com/prisma/api/connector/mongo/database/NodeManyQueries.scala

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -116,29 +116,16 @@ trait NodeManyQueries extends FilterConditionBuilder with AggregationQueryBuilde
116116

117117
//Fixme this does not use all queryarguments
118118
def countFromModel(model: Model, queryArguments: QueryArguments) = SimpleMongoAction { database =>
119-
// val queryArgFilter = queryArguments match {
120-
// case Some(arg) => arg.filter
121-
// case None => None
122-
// }
123-
//
124-
// val skipAndLimit = LimitClauseHelper.skipAndLimitValues(queryArguments)
125-
//
126-
// val cursorCondition = CursorConditionBuilder.buildCursorCondition(queryArguments)
127-
// We could try passing the other args into countoptions, but not sure about order
128-
// val baseQuery2 = collection.countDocuments(Filters.and(buildConditionForFilter(queryArgFilter), cursorCondition)).toFuture()
129-
//
130-
// val baseQuery: FindObservable[Document] = collection(Filters.and(buildConditionForFilter(queryArgFilter), cursorCondition))
131-
// val queryWithOrder: FindObservable[Document] = OrderByClauseBuilder.queryWithOrder(baseQuery, queryArguments)
132-
// val queryWithSkip: FindObservable[Document] = queryWithOrder.skip(skipAndLimit.skip)
133-
//
134-
// val queryWithLimit = skipAndLimit.limit match {
135-
// case Some(limit) => queryWithSkip.limit(limit)
136-
// case None => queryWithSkip
137-
// }
138-
//
139-
// queryWithLimit.collect().toFuture
140-
141-
database.getCollection(model.dbName).countDocuments(buildConditionForFilter(queryArguments.filter)).toFuture.map(_.toInt)
119+
if (needsAggregation(queryArguments.filter)) {
120+
aggregationQueryForId(database, model, queryArguments).map { x =>
121+
x.length match {
122+
case 0 => 0
123+
case x => x - 1 // we always fetch one more item for the page info we need to subtract that
124+
}
125+
}
126+
} else {
127+
database.getCollection(model.dbName).countDocuments(buildConditionForFilter(queryArguments.filter)).toFuture.map(_.toInt)
128+
}
142129
}
143130

144131
def countFromTable(table: String, filter: Option[Filter]) = SimpleMongoAction { database =>

server/servers/api/src/test/scala/com/prisma/api/queries/MultiItemConnectionQuerySpec.scala

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.prisma.api.queries
22

3-
import com.prisma.api.ApiSpecBase
3+
import com.prisma.api.{ApiSpecBase, TestDataModels}
44
import com.prisma.shared.schema_dsl.SchemaDsl
55
import org.scalatest.{FlatSpec, Matchers}
6+
import com.prisma.IgnoreSQLite
67

78
class MultiItemConnectionQuerySpec extends FlatSpec with Matchers with ApiSpecBase {
9+
810
val project = SchemaDsl.fromStringV11() {
911
"""type Todo {
1012
| id: ID! @id
@@ -108,4 +110,56 @@ class MultiItemConnectionQuerySpec extends FlatSpec with Matchers with ApiSpecBa
108110
)
109111
.toString should equal("""{"data":{"todoesConnection":{"edges":[{"node":{"title":"Hello World!"}}]}}}""")
110112
}
113+
114+
"the connection query" should "work when using cursors when not on Mongo" taggedAs (IgnoreSQLite) in {
115+
val datamodels = {
116+
val dm1 =
117+
"""type User {
118+
id: ID! @id
119+
name: String
120+
following: [User!]! @relation(name: "UserToFollow", link: INLINE)
121+
followers: [User!]! @relation(name: "UserToFollow")
122+
}"""
123+
124+
val dm2 =
125+
"""type User {
126+
| id: ID! @id
127+
| name: String
128+
| following: [User!]! @relation(name: "UserToFollow", link: TABLE)
129+
| followers: [User!]! @relation(name: "UserToFollow")
130+
|}"""
131+
132+
TestDataModels(mongo = Vector(dm1), sql = Vector(dm2))
133+
}
134+
datamodels.testV11 { project =>
135+
val a = server.query(s"""mutation{createUser(data:{name: "a", followers:{create:[{name:"b"}, {name:"c"}, {name:"x"}]}}){id}}""", project)
136+
val d = server.query(s"""mutation{createUser(data:{name: "d", followers:{create:[{name:"e"}, {name:"f"}, {name:"x"}]}}){id}}""", project)
137+
val g = server.query(s"""mutation{createUser(data:{name: "g", followers:{create:[{name:"h"}, {name:"i"}, {name:"x"}]}}){id}}""", project)
138+
val k = server.query(s"""mutation{createUser(data:{name: "k", followers:{create:[{name:"l"}, {name:"m"}, {name:"x"}]}}){id}}""", project)
139+
140+
val result = server.query(
141+
s"""{
142+
| usersConnection(where: {
143+
| followers_some: {
144+
| name: "x"
145+
| },
146+
| }, first: 2, after: "${a.pathAsString("data.createUser.id")}") {
147+
| aggregate {
148+
| count
149+
| }
150+
| edges {
151+
| node {
152+
| name
153+
| }
154+
| }
155+
| }
156+
|}""".stripMargin,
157+
project
158+
)
159+
160+
result.toString should be("""{"data":{"usersConnection":{"aggregate":{"count":2},"edges":[{"node":{"name":"d"}},{"node":{"name":"g"}}]}}}""")
161+
162+
}
163+
}
164+
111165
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.prisma.api.queries
2+
3+
import com.prisma.api.{ApiSpecBase, TestDataModels}
4+
import org.scalatest.{FlatSpec, Matchers}
5+
6+
class RelationFilterOrderingSpec extends FlatSpec with Matchers with ApiSpecBase {
7+
8+
val datamodels = {
9+
val dm1 = """type Blog {
10+
id: ID! @id
11+
title: String!
12+
score: Int!
13+
labels: [Label!]! @relation(name: "BlogLabels", link: INLINE)
14+
}
15+
type Label {
16+
id: ID! @id
17+
text: String! @unique
18+
}"""
19+
20+
val dm2 = """type Blog {
21+
id: ID! @id
22+
title: String!
23+
score: Int!
24+
labels: [Label!]! @relation(name: "BlogLabels", link: TABLE)
25+
}
26+
type Label {
27+
id: ID! @id
28+
text: String! @unique
29+
}"""
30+
31+
TestDataModels(mongo = Vector(dm1), sql = Vector(dm2))
32+
}
33+
34+
"Using relational filters" should "return items in the specified order" in {
35+
datamodels.testV11 { project =>
36+
server.query(s"""mutation {createLabel(data: {text: "x"}) {text }}""", project)
37+
38+
server.query(s"""mutation {createBlog(data: {title: "blog_1", score: 10,labels: {connect: {text: "x"}}}) {title}}""", project)
39+
server.query(s"""mutation {createBlog(data: {title: "blog_1", score: 20,labels: {connect: {text: "x"}}}) {title}}""", project)
40+
server.query(s"""mutation {createBlog(data: {title: "blog_1", score: 30,labels: {connect: {text: "x"}}}) {title}}""", project)
41+
42+
val res1 = server.query("""query {blogs(first: 2, orderBy: score_DESC) {title, score}}""", project)
43+
44+
res1.toString should be("""{"data":{"blogs":[{"title":"blog_1","score":30},{"title":"blog_1","score":20}]}}""")
45+
46+
val res2 = server.query("""query {blogs (first: 2, orderBy: score_DESC, where:{labels_some: {text: "x"}}) {title, score}}""", project)
47+
res2.toString should be("""{"data":{"blogs":[{"title":"blog_1","score":30},{"title":"blog_1","score":20}]}}""")
48+
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)