{"action":"created","comment":{"url":"https://api.github.com/repos/apache/spark/pulls/comments/43113163","id":43113163,"diff_hunk":"@@ -0,0 +1,141 @@\n+/*\n+ * Licensed to the Apache Software Foundation (ASF) under one or more\n+ * contributor license agreements.  See the NOTICE file distributed with\n+ * this work for additional information regarding copyright ownership.\n+ * The ASF licenses this file to You under the Apache License, Version 2.0\n+ * (the \"License\"); you may not use this file except in compliance with\n+ * the License.  You may obtain a copy of the License at\n+ *\n+ *    http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+package org.apache.spark.sql.execution\n+\n+import org.apache.spark.sql.catalyst.InternalRow\n+import org.apache.spark.sql.catalyst.expressions.codegen.{GenerateUnsafeProjection, GenerateOrdering}\n+import org.apache.spark.sql.catalyst.expressions.{Attribute, SortOrder, Ascending, Expression}\n+\n+object GroupedIterator {\n+  def apply(\n+      input: Iterator[InternalRow],\n+      keyExpressions: Seq[Expression],\n+      inputSchema: Seq[Attribute]): Iterator[(InternalRow, Iterator[InternalRow])] = {\n+    if (input.hasNext) {\n+      new GroupedIterator(input, keyExpressions, inputSchema)\n+    } else {\n+      Iterator.empty\n+    }\n+  }\n+}\n+\n+/**\n+ * Iterates over a presorted set of rows, chunking it up by the grouping expression.  Each call to\n+ * next will return a pair containing the current group and an iterator that will return all the\n+ * elements of that group.  Iterators for each group are lazily constructed by extracting rows\n+ * from the input iterator.  As such, full groups are never materialized by this class.\n+ *\n+ * Example input:\n+ * {{{\n+ *   Input: [a, 1], [b, 2], [b, 3]\n+ *   Grouping: x#1\n+ *   InputSchema: x#1, y#2\n+ * }}}\n+ *\n+ * Result:\n+ * {{{\n+ *   First call to next():  ([a], Iterator([a, 1])\n+ *   Second call to next(): ([b], Iterator([b, 2], [b, 3])\n+ * }}}\n+ *\n+ * Note, the class does not handle the case of an empty input for simplicity of implementation.\n+ * Use the factory to construct a new instance.\n+ *\n+ * @param input An iterator of rows.  This iterator must be ordered by the groupingExpressions or\n+ *              it is possible for the same group to appear more than once.\n+ * @param groupingExpressions The set of expressions used to do grouping.  The result of evaluating\n+ *                            these expressions will be returned as the first part of each call\n+ *                            to `next()`.\n+ * @param inputSchema The schema of the rows in the `input` iterator.\n+ */\n+class GroupedIterator private(\n+    input: Iterator[InternalRow],\n+    groupingExpressions: Seq[Expression],\n+    inputSchema: Seq[Attribute])\n+  extends Iterator[(InternalRow, Iterator[InternalRow])] {\n+\n+  /** Compares two input rows and returns 0 if they are in the same group. */\n+  val sortOrder = groupingExpressions.map(SortOrder(_, Ascending))\n+  val keyOrdering = GenerateOrdering.generate(sortOrder, inputSchema)\n+\n+  /** Creates a row containing only the key for a given input row. */\n+  val keyProjection = GenerateUnsafeProjection.generate(groupingExpressions, inputSchema)\n+\n+  /**\n+   * Holds null or the row that will be returned on next call to `next()` in the inner iterator.\n+   */\n+  var currentRow = input.next()\n+\n+  /** Holds a copy of an input row that is in the current group. */\n+  var currentGroup = currentRow.copy()\n+  var currentIterator: Iterator[InternalRow] = null\n+  assert(keyOrdering.compare(currentGroup, currentRow) == 0)\n+\n+  // Return true if we already have the next iterator or fetching a new iterator is successful.\n+  def hasNext: Boolean = currentIterator != null || fetchNextGroupIterator\n+\n+  def next(): (InternalRow, Iterator[InternalRow]) = {\n+    assert(hasNext) // Ensure we have fetched the next iterator.\n+    val ret = (keyProjection(currentGroup), currentIterator)\n+    currentIterator = null\n+    ret\n+  }\n+\n+  def fetchNextGroupIterator(): Boolean = {\n+    if (currentRow != null || input.hasNext) {\n+      val inputIterator = new Iterator[InternalRow] {","path":"sql/core/src/main/scala/org/apache/spark/sql/execution/GroupedIterator.scala","position":101,"original_position":101,"commit_id":"e251f87a830df9f40d523f00bbe14c174c91b2ae","original_commit_id":"e251f87a830df9f40d523f00bbe14c174c91b2ae","user":{"login":"marmbrus","id":527,"avatar_url":"https://avatars.githubusercontent.com/u/527?v=3","gravatar_id":"","url":"https://api.github.com/users/marmbrus","html_url":"https://github.com/marmbrus","followers_url":"https://api.github.com/users/marmbrus/followers","following_url":"https://api.github.com/users/marmbrus/following{/other_user}","gists_url":"https://api.github.com/users/marmbrus/gists{/gist_id}","starred_url":"https://api.github.com/users/marmbrus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marmbrus/subscriptions","organizations_url":"https://api.github.com/users/marmbrus/orgs","repos_url":"https://api.github.com/users/marmbrus/repos","events_url":"https://api.github.com/users/marmbrus/events{/privacy}","received_events_url":"https://api.github.com/users/marmbrus/received_events","type":"User","site_admin":false},"body":"Thats a good catch.  We can just advance the internal iterator all the way to the next group if they call `next` on the outer iterator before exhausting the inner iterator (and add documentation that you can't use two inner iterators concurrently).\r\n\r\nCould you open a PR to unit test this class and fix the problem?","created_at":"2015-10-27T12:11:43Z","updated_at":"2015-10-27T12:11:43Z","html_url":"https://github.com/apache/spark/pull/9190#discussion_r43113163","pull_request_url":"https://api.github.com/repos/apache/spark/pulls/9190","_links":{"self":{"href":"https://api.github.com/repos/apache/spark/pulls/comments/43113163"},"html":{"href":"https://github.com/apache/spark/pull/9190#discussion_r43113163"},"pull_request":{"href":"https://api.github.com/repos/apache/spark/pulls/9190"}}},"pull_request":{"url":"https://api.github.com/repos/apache/spark/pulls/9190","id":48278018,"html_url":"https://github.com/apache/spark/pull/9190","diff_url":"https://github.com/apache/spark/pull/9190.diff","patch_url":"https://github.com/apache/spark/pull/9190.patch","issue_url":"https://api.github.com/repos/apache/spark/issues/9190","number":9190,"state":"closed","locked":false,"title":"[SPARK-11116] [SQL] First Draft of Dataset API","user":{"login":"marmbrus","id":527,"avatar_url":"https://avatars.githubusercontent.com/u/527?v=3","gravatar_id":"","url":"https://api.github.com/users/marmbrus","html_url":"https://github.com/marmbrus","followers_url":"https://api.github.com/users/marmbrus/followers","following_url":"https://api.github.com/users/marmbrus/following{/other_user}","gists_url":"https://api.github.com/users/marmbrus/gists{/gist_id}","starred_url":"https://api.github.com/users/marmbrus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marmbrus/subscriptions","organizations_url":"https://api.github.com/users/marmbrus/orgs","repos_url":"https://api.github.com/users/marmbrus/repos","events_url":"https://api.github.com/users/marmbrus/events{/privacy}","received_events_url":"https://api.github.com/users/marmbrus/received_events","type":"User","site_admin":false},"body":"*This PR adds a new experimental API to Spark, tentitively named Datasets.*\r\n\r\nA `Dataset` is a strongly-typed collection of objects that can be transformed in parallel using functional or relational operations.  Example usage is as follows:\r\n\r\n### Functional\r\n```scala\r\n> val ds: Dataset[Int] = Seq(1, 2, 3).toDS()\r\n> ds.filter(_ % 1 == 0).collect()\r\nres1: Array[Int] = Array(1, 2, 3)\r\n```\r\n\r\n### Relational\r\n```scala\r\nscala> ds.toDF().show()\r\n+-----+\r\n|value|\r\n+-----+\r\n|    1|\r\n|    2|\r\n|    3|\r\n+-----+\r\n\r\n> ds.select(expr(\"value + 1\").as[Int]).collect()\r\nres11: Array[Int] = Array(2, 3, 4)\r\n```\r\n\r\n## Comparison to RDDs\r\n A `Dataset` differs from an `RDD` in the following ways:\r\n  - The creation of a `Dataset` requires the presence of an explicit `Encoder` that can be\r\n    used to serialize the object into a binary format.  Encoders are also capable of mapping the\r\n    schema of a given object to the Spark SQL type system.  In contrast, RDDs rely on runtime\r\n    reflection based serialization.\r\n  - Internally, a `Dataset` is represented by a Catalyst logical plan and the data is stored\r\n    in the encoded form.  This representation allows for additional logical operations and\r\n    enables many operations (sorting, shuffling, etc.) to be performed without deserializing to\r\n    an object.\r\n\r\nA `Dataset` can be converted to an `RDD` by calling the `.rdd` method.\r\n\r\n## Comparison to DataFrames\r\n\r\nA `Dataset` can be thought of as a specialized DataFrame, where the elements map to a specific\r\nJVM object type, instead of to a generic `Row` container. A DataFrame can be transformed into\r\nspecific Dataset by calling `df.as[ElementType]`.  Similarly you can transform a strongly-typed\r\n`Dataset` to a generic DataFrame by calling `ds.toDF()`.\r\n\r\n## Implementation Status and TODOs\r\n\r\nThis is a rough cut at the least controversial parts of the API.  The primary purpose here is to get something committed so that we can better parallelize further work and get early feedback on the API.  The following is being deferred to future PRs:\r\n - Joins and Aggregations (prototype here https://github.com/apache/spark/commit/f11f91e6f08c8cf389b8388b626cd29eec32d937)\r\n - Support for Java\r\n\r\nAdditionally, the responsibility for binding an encoder to a given schema is currently done in a fairly ad-hoc fashion.  This is an internal detail, and what we are doing today works for the cases we care about.  However, as we add more APIs we'll probably need to do this in a more principled way (i.e. separate resolution from binding as we do in DataFrames).\r\n\r\n## COMPATIBILITY NOTE\r\nLong term we plan to make `DataFrame` extend `Dataset[Row]`.  However,\r\nmaking this change to che class hierarchy would break the function signatures for the existing\r\nfunction operations (map, flatMap, etc).  As such, this class should be considered a preview\r\nof the final API.  Changes will be made to the interface after Spark 1.6.","created_at":"2015-10-21T01:14:48Z","updated_at":"2015-10-27T12:11:43Z","closed_at":"2015-10-22T22:21:37Z","merged_at":null,"merge_commit_sha":"f6ae967ae6c0cf3199e16a67095a063113e0394d","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/apache/spark/pulls/9190/commits","review_comments_url":"https://api.github.com/repos/apache/spark/pulls/9190/comments","review_comment_url":"https://api.github.com/repos/apache/spark/pulls/comments{/number}","comments_url":"https://api.github.com/repos/apache/spark/issues/9190/comments","statuses_url":"https://api.github.com/repos/apache/spark/statuses/e251f87a830df9f40d523f00bbe14c174c91b2ae","head":{"label":"marmbrus:dataset-infra","ref":"dataset-infra","sha":"e251f87a830df9f40d523f00bbe14c174c91b2ae","user":{"login":"marmbrus","id":527,"avatar_url":"https://avatars.githubusercontent.com/u/527?v=3","gravatar_id":"","url":"https://api.github.com/users/marmbrus","html_url":"https://github.com/marmbrus","followers_url":"https://api.github.com/users/marmbrus/followers","following_url":"https://api.github.com/users/marmbrus/following{/other_user}","gists_url":"https://api.github.com/users/marmbrus/gists{/gist_id}","starred_url":"https://api.github.com/users/marmbrus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marmbrus/subscriptions","organizations_url":"https://api.github.com/users/marmbrus/orgs","repos_url":"https://api.github.com/users/marmbrus/repos","events_url":"https://api.github.com/users/marmbrus/events{/privacy}","received_events_url":"https://api.github.com/users/marmbrus/received_events","type":"User","site_admin":false},"repo":{"id":17230869,"name":"spark","full_name":"marmbrus/spark","owner":{"login":"marmbrus","id":527,"avatar_url":"https://avatars.githubusercontent.com/u/527?v=3","gravatar_id":"","url":"https://api.github.com/users/marmbrus","html_url":"https://github.com/marmbrus","followers_url":"https://api.github.com/users/marmbrus/followers","following_url":"https://api.github.com/users/marmbrus/following{/other_user}","gists_url":"https://api.github.com/users/marmbrus/gists{/gist_id}","starred_url":"https://api.github.com/users/marmbrus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marmbrus/subscriptions","organizations_url":"https://api.github.com/users/marmbrus/orgs","repos_url":"https://api.github.com/users/marmbrus/repos","events_url":"https://api.github.com/users/marmbrus/events{/privacy}","received_events_url":"https://api.github.com/users/marmbrus/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/marmbrus/spark","description":"Mirror of Apache Spark","fork":true,"url":"https://api.github.com/repos/marmbrus/spark","forks_url":"https://api.github.com/repos/marmbrus/spark/forks","keys_url":"https://api.github.com/repos/marmbrus/spark/keys{/key_id}","collaborators_url":"https://api.github.com/repos/marmbrus/spark/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/marmbrus/spark/teams","hooks_url":"https://api.github.com/repos/marmbrus/spark/hooks","issue_events_url":"https://api.github.com/repos/marmbrus/spark/issues/events{/number}","events_url":"https://api.github.com/repos/marmbrus/spark/events","assignees_url":"https://api.github.com/repos/marmbrus/spark/assignees{/user}","branches_url":"https://api.github.com/repos/marmbrus/spark/branches{/branch}","tags_url":"https://api.github.com/repos/marmbrus/spark/tags","blobs_url":"https://api.github.com/repos/marmbrus/spark/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/marmbrus/spark/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/marmbrus/spark/git/refs{/sha}","trees_url":"https://api.github.com/repos/marmbrus/spark/git/trees{/sha}","statuses_url":"https://api.github.com/repos/marmbrus/spark/statuses/{sha}","languages_url":"https://api.github.com/repos/marmbrus/spark/languages","stargazers_url":"https://api.github.com/repos/marmbrus/spark/stargazers","contributors_url":"https://api.github.com/repos/marmbrus/spark/contributors","subscribers_url":"https://api.github.com/repos/marmbrus/spark/subscribers","subscription_url":"https://api.github.com/repos/marmbrus/spark/subscription","commits_url":"https://api.github.com/repos/marmbrus/spark/commits{/sha}","git_commits_url":"https://api.github.com/repos/marmbrus/spark/git/commits{/sha}","comments_url":"https://api.github.com/repos/marmbrus/spark/comments{/number}","issue_comment_url":"https://api.github.com/repos/marmbrus/spark/issues/comments{/number}","contents_url":"https://api.github.com/repos/marmbrus/spark/contents/{+path}","compare_url":"https://api.github.com/repos/marmbrus/spark/compare/{base}...{head}","merges_url":"https://api.github.com/repos/marmbrus/spark/merges","archive_url":"https://api.github.com/repos/marmbrus/spark/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/marmbrus/spark/downloads","issues_url":"https://api.github.com/repos/marmbrus/spark/issues{/number}","pulls_url":"https://api.github.com/repos/marmbrus/spark/pulls{/number}","milestones_url":"https://api.github.com/repos/marmbrus/spark/milestones{/number}","notifications_url":"https://api.github.com/repos/marmbrus/spark/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/marmbrus/spark/labels{/name}","releases_url":"https://api.github.com/repos/marmbrus/spark/releases{/id}","created_at":"2014-02-27T00:22:54Z","updated_at":"2014-11-27T06:21:59Z","pushed_at":"2015-10-25T14:52:29Z","git_url":"git://github.com/marmbrus/spark.git","ssh_url":"git@github.com:marmbrus/spark.git","clone_url":"https://github.com/marmbrus/spark.git","svn_url":"https://github.com/marmbrus/spark","homepage":null,"size":170319,"stargazers_count":2,"watchers_count":2,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":2,"forks":2,"open_issues":2,"watchers":2,"default_branch":"catalyst"}},"base":{"label":"apache:master","ref":"master","sha":"ccf536f903ef1f81fb3e1b6ce781d5e40d0ae3e0","user":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"repo":{"id":17165658,"name":"spark","full_name":"apache/spark","owner":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/apache/spark","description":"Mirror of Apache Spark","fork":false,"url":"https://api.github.com/repos/apache/spark","forks_url":"https://api.github.com/repos/apache/spark/forks","keys_url":"https://api.github.com/repos/apache/spark/keys{/key_id}","collaborators_url":"https://api.github.com/repos/apache/spark/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/apache/spark/teams","hooks_url":"https://api.github.com/repos/apache/spark/hooks","issue_events_url":"https://api.github.com/repos/apache/spark/issues/events{/number}","events_url":"https://api.github.com/repos/apache/spark/events","assignees_url":"https://api.github.com/repos/apache/spark/assignees{/user}","branches_url":"https://api.github.com/repos/apache/spark/branches{/branch}","tags_url":"https://api.github.com/repos/apache/spark/tags","blobs_url":"https://api.github.com/repos/apache/spark/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/apache/spark/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/apache/spark/git/refs{/sha}","trees_url":"https://api.github.com/repos/apache/spark/git/trees{/sha}","statuses_url":"https://api.github.com/repos/apache/spark/statuses/{sha}","languages_url":"https://api.github.com/repos/apache/spark/languages","stargazers_url":"https://api.github.com/repos/apache/spark/stargazers","contributors_url":"https://api.github.com/repos/apache/spark/contributors","subscribers_url":"https://api.github.com/repos/apache/spark/subscribers","subscription_url":"https://api.github.com/repos/apache/spark/subscription","commits_url":"https://api.github.com/repos/apache/spark/commits{/sha}","git_commits_url":"https://api.github.com/repos/apache/spark/git/commits{/sha}","comments_url":"https://api.github.com/repos/apache/spark/comments{/number}","issue_comment_url":"https://api.github.com/repos/apache/spark/issues/comments{/number}","contents_url":"https://api.github.com/repos/apache/spark/contents/{+path}","compare_url":"https://api.github.com/repos/apache/spark/compare/{base}...{head}","merges_url":"https://api.github.com/repos/apache/spark/merges","archive_url":"https://api.github.com/repos/apache/spark/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/apache/spark/downloads","issues_url":"https://api.github.com/repos/apache/spark/issues{/number}","pulls_url":"https://api.github.com/repos/apache/spark/pulls{/number}","milestones_url":"https://api.github.com/repos/apache/spark/milestones{/number}","notifications_url":"https://api.github.com/repos/apache/spark/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/apache/spark/labels{/name}","releases_url":"https://api.github.com/repos/apache/spark/releases{/id}","created_at":"2014-02-25T08:00:08Z","updated_at":"2015-10-27T12:01:54Z","pushed_at":"2015-10-27T11:36:23Z","git_url":"git://github.com/apache/spark.git","ssh_url":"git@github.com:apache/spark.git","clone_url":"https://github.com/apache/spark.git","svn_url":"https://github.com/apache/spark","homepage":null,"size":1644153,"stargazers_count":5902,"watchers_count":5902,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":5510,"mirror_url":"git://git.apache.org/spark.git","open_issues_count":340,"forks":5510,"open_issues":340,"watchers":5902,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/apache/spark/pulls/9190"},"html":{"href":"https://github.com/apache/spark/pull/9190"},"issue":{"href":"https://api.github.com/repos/apache/spark/issues/9190"},"comments":{"href":"https://api.github.com/repos/apache/spark/issues/9190/comments"},"review_comments":{"href":"https://api.github.com/repos/apache/spark/pulls/9190/comments"},"review_comment":{"href":"https://api.github.com/repos/apache/spark/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/apache/spark/pulls/9190/commits"},"statuses":{"href":"https://api.github.com/repos/apache/spark/statuses/e251f87a830df9f40d523f00bbe14c174c91b2ae"}}}}