PMM-685: explain

This commit is contained in:
Kamil Dziedzic
2017-08-31 13:52:37 +02:00
parent 04f95917ae
commit 5393a2b001
17 changed files with 751 additions and 26 deletions

View File

@@ -5,7 +5,21 @@ language: go
go: go:
- 1.8.x - 1.8.x
- tip
services:
- docker
env:
matrix:
- MONGODB_IMAGE=mongo:3.2
- MONGODB_IMAGE=mongo:3.4
- MONGODB_IMAGE=percona/percona-server-mongodb:3.2
- MONGODB_IMAGE=percona/percona-server-mongodb:3.4
matrix:
include:
- go: tip
env:
install: install:
- go get -u github.com/Masterminds/glide - go get -u github.com/Masterminds/glide
@@ -16,6 +30,12 @@ install:
before_script: before_script:
# check if vendor dir is correct # check if vendor dir is correct
- git diff --exit-code - git diff --exit-code
# run docker containers
- docker-compose up -d
# log versions
- docker --version
- docker-compose --version
- docker-compose exec mongo mongo --version
script: script:
- go test -timeout 1m $(glide nv) - go test -timeout 1m $(glide nv)

View File

@@ -5,9 +5,8 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"strings"
"regexp" "regexp"
"strings"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
) )

View File

@@ -0,0 +1,32 @@
package explain
import (
"github.com/percona/percona-toolkit/src/go/mongolib/proto"
"github.com/percona/pmgo"
"gopkg.in/mgo.v2/bson"
)
type explain struct {
session pmgo.SessionManager
}
func New(session pmgo.SessionManager) *explain {
return &explain{
session: session,
}
}
func (e *explain) Explain(eq proto.ExampleQuery) ([]byte, error) {
var result proto.BsonD
err := e.session.DB(eq.Db()).Run(eq.ExplainCmd(), &result)
if err != nil {
return nil, err
}
resultJson, err := bson.MarshalJSON(result)
if err != nil {
return nil, err
}
return resultJson, nil
}

View File

@@ -0,0 +1,159 @@
package explain
import (
"io/ioutil"
"log"
"os"
"testing"
"fmt"
"github.com/percona/percona-toolkit/src/go/lib/tutil"
"github.com/percona/percona-toolkit/src/go/mongolib/proto"
"github.com/percona/pmgo"
"github.com/stretchr/testify/require"
"gopkg.in/mgo.v2/bson"
)
const (
samples = "/src/go/tests/"
)
type testVars struct {
RootPath string
}
var vars testVars
func TestMain(m *testing.M) {
var err error
if vars.RootPath, err = tutil.RootPath(); err != nil {
log.Printf("cannot get root path: %s", err.Error())
os.Exit(1)
}
os.Exit(m.Run())
}
func TestExplain(t *testing.T) {
t.Parallel()
dir := vars.RootPath + samples + "/doc/out/"
files, err := ioutil.ReadDir(dir)
if err != nil {
t.Fatalf("cannot list samples: %s", err)
}
expectError := map[string]string{
"aggregate_2.6.12": "Cannot explain cmd: aggregate",
"aggregate_3.0.15": "Cannot explain cmd: aggregate",
"aggregate_3.2.16": "Cannot explain cmd: aggregate",
"aggregate_3.4.7": "Cannot explain cmd: aggregate",
"aggregate_3.5.11": "Cannot explain cmd: aggregate",
"count_2.6.12": "<nil>",
"count_3.0.15": "<nil>",
"count_3.2.16": "<nil>",
"count_3.4.7": "<nil>",
"count_3.5.11": "<nil>",
"count_with_query_2.6.12": "<nil>",
"count_with_query_3.0.15": "<nil>",
"count_with_query_3.2.16": "<nil>",
"count_with_query_3.4.7": "<nil>",
"count_with_query_3.5.11": "<nil>",
"delete_2.6.12": "<nil>",
"delete_3.0.15": "<nil>",
"delete_3.2.16": "<nil>",
"delete_3.4.7": "<nil>",
"delete_3.5.11": "<nil>",
"delete_all_2.6.12": "<nil>",
"delete_all_3.0.15": "<nil>",
"delete_all_3.2.16": "<nil>",
"delete_all_3.4.7": "<nil>",
"delete_all_3.5.11": "<nil>",
"distinct_2.6.12": "<nil>",
"distinct_3.0.15": "<nil>",
"distinct_3.2.16": "<nil>",
"distinct_3.4.7": "<nil>",
"distinct_3.5.11": "<nil>",
"find_empty_2.6.12": "<nil>",
"find_empty_3.0.15": "<nil>",
"find_empty_3.2.16": "<nil>",
"find_empty_3.4.7": "<nil>",
"find_empty_3.5.11": "<nil>",
"find_2.6.12": "<nil>",
"find_3.0.15": "<nil>",
"find_3.2.16": "<nil>",
"find_3.4.7": "<nil>",
"find_3.5.11": "<nil>",
"find_andrii_2.6.12": "<nil>",
"find_andrii_3.0.15": "<nil>",
"find_andrii_3.2.16": "<nil>",
"find_andrii_3.4.7": "<nil>",
"find_andrii_3.5.11": "<nil>",
"findandmodify_2.6.12": "<nil>",
"findandmodify_3.0.15": "<nil>",
"findandmodify_3.2.16": "<nil>",
"findandmodify_3.4.7": "<nil>",
"findandmodify_3.5.11": "<nil>",
"geonear_2.6.12": "Cannot explain cmd: geoNear",
"geonear_3.0.15": "Cannot explain cmd: geoNear",
"geonear_3.2.16": "Cannot explain cmd: geoNear",
"geonear_3.4.7": "Cannot explain cmd: geoNear",
"geonear_3.5.11": "Cannot explain cmd: geoNear",
"group_2.6.12": "<nil>",
"group_3.0.15": "<nil>",
"group_3.2.16": "<nil>",
"group_3.4.7": "<nil>",
"group_3.5.11": "<nil>",
"insert_2.6.12": "Cannot explain cmd: insert",
"insert_3.0.15": "Cannot explain cmd: insert",
"insert_3.2.16": "Cannot explain cmd: insert",
"insert_3.4.7": "Cannot explain cmd: insert",
"insert_3.5.11": "Cannot explain cmd: insert",
"mapreduce_2.6.12": "Cannot explain cmd: mapReduce",
"mapreduce_3.0.15": "Cannot explain cmd: mapReduce",
"mapreduce_3.2.16": "Cannot explain cmd: mapReduce",
"mapreduce_3.4.7": "Cannot explain cmd: mapReduce",
"mapreduce_3.5.11": "Cannot explain cmd: mapReduce",
"update_2.6.12": "<nil>",
"update_3.0.15": "<nil>",
"update_3.2.16": "<nil>",
"update_3.4.7": "<nil>",
"update_3.5.11": "<nil>",
}
dialer := pmgo.NewDialer()
dialInfo, err := pmgo.ParseURL("127.0.0.1:27017")
require.NoError(t, err)
session, err := dialer.DialWithInfo(dialInfo)
require.NoError(t, err)
defer session.Close()
ex := New(session)
for _, file := range files {
t.Run(file.Name(), func(t *testing.T) {
eq := proto.ExampleQuery{}
err := tutil.LoadBson(dir+file.Name(), &eq)
if err != nil {
t.Fatalf("cannot load sample %s: %s", dir+file.Name(), err)
}
got, err := ex.Explain(eq)
expectErrMsg := expectError[file.Name()]
gotErrMsg := fmt.Sprintf("%v", err)
if gotErrMsg != expectErrMsg {
t.Fatalf("explain error should be '%s' but was '%s'", expectErrMsg, gotErrMsg)
}
if err != nil {
return
}
result := proto.BsonD{}
err = bson.UnmarshalJSON(got, &result)
if err != nil {
t.Fatalf("cannot unmarshal json explain result: %s", err)
}
})
}
}

View File

@@ -103,6 +103,11 @@ func TestFingerprints(t *testing.T) {
"delete_3.2.16": "REMOVE coll a,b", "delete_3.2.16": "REMOVE coll a,b",
"delete_3.4.7": "REMOVE coll a,b", "delete_3.4.7": "REMOVE coll a,b",
"delete_3.5.11": "REMOVE coll a,b", "delete_3.5.11": "REMOVE coll a,b",
"delete_all_2.6.12": "REMOVE coll a,b",
"delete_all_3.0.15": "REMOVE coll a,b",
"delete_all_3.2.16": "REMOVE coll a,b",
"delete_all_3.4.7": "REMOVE coll a,b",
"delete_all_3.5.11": "REMOVE coll a,b",
"distinct_2.6.12": "DISTINCT coll a,b", "distinct_2.6.12": "DISTINCT coll a,b",
"distinct_3.0.15": "DISTINCT coll a,b", "distinct_3.0.15": "DISTINCT coll a,b",
"distinct_3.2.16": "DISTINCT coll a,b", "distinct_3.2.16": "DISTINCT coll a,b",

View File

@@ -73,7 +73,7 @@ func TestRegularIterator(t *testing.T) {
ID: "16196765fb4c14edb91efdbe4f5c5973", ID: "16196765fb4c14edb91efdbe4f5c5973",
Namespace: "samples.col1", Namespace: "samples.col1",
Operation: "query", Operation: "query",
Query: "{\n \"find\": \"col1\",\n \"shardVersion\": [\n 0,\n \"000000000000000000000000\"\n ]\n}", Query: "{\"ns\":\"samples.col1\",\"op\":\"query\",\"query\":{\"find\":\"col1\",\"shardVersion\":[0,\"000000000000000000000000\"]}}\n",
Fingerprint: "FIND col1 find", Fingerprint: "FIND col1 find",
FirstSeen: firstSeen, FirstSeen: firstSeen,
LastSeen: lastSeen, LastSeen: lastSeen,
@@ -130,7 +130,7 @@ func TestIteratorTimeout(t *testing.T) {
ID: "16196765fb4c14edb91efdbe4f5c5973", ID: "16196765fb4c14edb91efdbe4f5c5973",
Namespace: "samples.col1", Namespace: "samples.col1",
Operation: "query", Operation: "query",
Query: "{\n \"find\": \"col1\",\n \"shardVersion\": [\n 0,\n \"000000000000000000000000\"\n ]\n}", Query: "{\"ns\":\"samples.col1\",\"op\":\"query\",\"query\":{\"find\":\"col1\",\"shardVersion\":[0,\"000000000000000000000000\"]}}\n",
Fingerprint: "FIND col1 find", Fingerprint: "FIND col1 find",
FirstSeen: firstSeen, FirstSeen: firstSeen,
LastSeen: lastSeen, LastSeen: lastSeen,
@@ -211,7 +211,7 @@ func TestTailIterator(t *testing.T) {
ID: "16196765fb4c14edb91efdbe4f5c5973", ID: "16196765fb4c14edb91efdbe4f5c5973",
Namespace: "samples.col1", Namespace: "samples.col1",
Operation: "query", Operation: "query",
Query: "{\n \"find\": \"col1\",\n \"shardVersion\": [\n 0,\n \"000000000000000000000000\"\n ]\n}", Query: "{\"ns\":\"samples.col1\",\"op\":\"query\",\"query\":{\"find\":\"col1\",\"shardVersion\":[0,\"000000000000000000000000\"]}}\n",
Fingerprint: "FIND col1 find", Fingerprint: "FIND col1 find",
FirstSeen: parseDate("2017-04-01T23:01:20.214+00:00"), FirstSeen: parseDate("2017-04-01T23:01:20.214+00:00"),
LastSeen: parseDate("2017-04-01T23:01:20.214+00:00"), LastSeen: parseDate("2017-04-01T23:01:20.214+00:00"),
@@ -226,7 +226,7 @@ func TestTailIterator(t *testing.T) {
ID: "16196765fb4c14edb91efdbe4f5c5973", ID: "16196765fb4c14edb91efdbe4f5c5973",
Namespace: "samples.col1", Namespace: "samples.col1",
Operation: "query", Operation: "query",
Query: "{\n \"find\": \"col1\",\n \"shardVersion\": [\n 0,\n \"000000000000000000000000\"\n ]\n}", Query: "{\"ns\":\"samples.col1\",\"op\":\"query\",\"query\":{\"find\":\"col1\",\"shardVersion\":[0,\"000000000000000000000000\"]}}\n",
Fingerprint: "FIND col1 find", Fingerprint: "FIND col1 find",
FirstSeen: parseDate("2017-04-01T23:01:19.914+00:00"), FirstSeen: parseDate("2017-04-01T23:01:19.914+00:00"),
LastSeen: parseDate("2017-04-01T23:01:19.914+00:00"), LastSeen: parseDate("2017-04-01T23:01:19.914+00:00"),

View File

@@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
) )
@@ -97,17 +98,31 @@ func (d BsonD) MarshalJSON() ([]byte, error) {
} }
// marshal key // marshal key
key, err := json.Marshal(v.Name) key, err := bson.MarshalJSON(v.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
b.Write(key) b.Write(key)
b.WriteByte(':') b.WriteByte(':')
// marshal value var val []byte
val, err := json.Marshal(v.Value) if value, ok := v.Value.(float64); ok && math.IsInf(value, 0) {
if err != nil { if math.IsInf(value, 1) {
return nil, err val = []byte("Infinity")
} else {
val = []byte("-Infinity")
}
// below is wrong, but I'm later unable to Unmarshal Infinity,
// so we turn it into string for now
val = append([]byte(`"`), val...)
val = append(val, '"')
} else {
// marshal value
val, err = bson.MarshalJSON(v.Value)
if err != nil {
return nil, err
}
} }
b.Write(val) b.Write(val)
} }

View File

@@ -1,7 +1,10 @@
package proto package proto
import ( import (
"strings"
"time" "time"
"gopkg.in/mgo.v2/bson"
) )
type SystemProfile struct { type SystemProfile struct {
@@ -74,9 +77,148 @@ type SystemProfile struct {
Op string `bson:"op"` Op string `bson:"op"`
Protocol string `bson:"protocol"` Protocol string `bson:"protocol"`
Query BsonD `bson:"query"` Query BsonD `bson:"query"`
UpdateObj BsonD `bson:"updateobj"`
Command BsonD `bson:"command"` Command BsonD `bson:"command"`
ResponseLength int `bson:"responseLength"` ResponseLength int `bson:"responseLength"`
Ts time.Time `bson:"ts"` Ts time.Time `bson:"ts"`
User string `bson:"user"` User string `bson:"user"`
WriteConflicts int `bson:"writeConflicts"` WriteConflicts int `bson:"writeConflicts"`
} }
func NewExampleQuery(doc SystemProfile) ExampleQuery {
return ExampleQuery{
Ns: doc.Ns,
Op: doc.Op,
Query: doc.Query,
Command: doc.Command,
UpdateObj: doc.UpdateObj,
}
}
// ExampleQuery is a subset of SystemProfile
type ExampleQuery struct {
Ns string `bson:"ns" json:"ns"`
Op string `bson:"op" json:"op"`
Query BsonD `bson:"query,omitempty" json:"query,omitempty"`
Command BsonD `bson:"command,omitempty" json:"command,omitempty"`
UpdateObj BsonD `bson:"updateobj,omitempty" json:"updateobj,omitempty"`
}
func (self ExampleQuery) Db() string {
ns := strings.SplitN(self.Ns, ".", 2)
if len(ns) > 0 {
return ns[0]
}
return ""
}
func (self ExampleQuery) ExplainCmd() bson.D {
cmd := self.Command
switch self.Op {
case "query":
if cmd.Len() == 0 {
cmd = self.Query
}
if cmd.Len() == 0 || cmd[0].Name != "find" {
var filter interface{}
if cmd.Len() > 0 && cmd[0].Name == "query" {
filter = cmd[0].Value
} else {
filter = cmd
}
coll := ""
s := strings.SplitN(self.Ns, ".", 2)
if len(s) == 2 {
coll = s[1]
}
cmd = BsonD{
{"find", coll},
{"filter", filter},
}
}
case "update":
s := strings.SplitN(self.Ns, ".", 2)
coll := ""
if len(s) == 2 {
coll = s[1]
}
if cmd.Len() == 0 {
cmd = BsonD{
{Name: "q", Value: self.Query},
{Name: "u", Value: self.UpdateObj},
}
}
cmd = BsonD{
{Name: "update", Value: coll},
{Name: "updates", Value: []interface{}{cmd}},
}
case "remove":
s := strings.SplitN(self.Ns, ".", 2)
coll := ""
if len(s) == 2 {
coll = s[1]
}
if cmd.Len() == 0 {
cmd = BsonD{
{Name: "q", Value: self.Query},
// we can't determine if limit was 1 or 0 so we assume 0
{Name: "limit", Value: 0},
}
}
cmd = BsonD{
{Name: "delete", Value: coll},
{Name: "deletes", Value: []interface{}{cmd}},
}
case "insert":
if cmd.Len() == 0 {
cmd = self.Query
}
if cmd.Len() == 0 || cmd[0].Name != "insert" {
coll := ""
s := strings.SplitN(self.Ns, ".", 2)
if len(s) == 2 {
coll = s[1]
}
cmd = BsonD{
{"insert", coll},
}
}
case "command":
if cmd.Len() == 0 || cmd[0].Name != "group" {
break
}
if v, ok := cmd[0].Value.(BsonD); ok {
for i := range v {
// for MongoDB <= 3.2
// "$reduce" : function () {}
// It is then Unmarshaled as empty value, so in essence not working
//
// for MongoDB >= 3.4
// "$reduce" : {
// "code" : "function () {}"
// }
// It is then properly Unmarshaled but then explain fails with "not code"
//
// The $reduce function shouldn't affect explain execution plan (e.g. what indexes are picked)
// so we ignore it for now until we find better way to handle this issue
if v[i].Name == "$reduce" {
v[i].Value = ""
cmd[0].Value = v
break
}
}
}
}
return bson.D{
{
Name: "explain",
Value: cmd,
},
}
}

View File

@@ -6,11 +6,11 @@ import (
"sort" "sort"
"sync" "sync"
"time" "time"
"encoding/json"
"github.com/montanaflynn/stats" "github.com/montanaflynn/stats"
"github.com/percona/percona-toolkit/src/go/mongolib/fingerprinter" "github.com/percona/percona-toolkit/src/go/mongolib/fingerprinter"
"github.com/percona/percona-toolkit/src/go/mongolib/proto" "github.com/percona/percona-toolkit/src/go/mongolib/proto"
"gopkg.in/mgo.v2/bson"
) )
type StatsError struct { type StatsError struct {
@@ -30,7 +30,6 @@ func (e *StatsError) Parent() error {
} }
type StatsFingerprintError StatsError type StatsFingerprintError StatsError
type StatsGetQueryFieldError StatsError
// New creates new instance of stats with given fingerprinter // New creates new instance of stats with given fingerprinter
func New(fingerprinter fingerprinter.Fingerprinter) *Stats { func New(fingerprinter fingerprinter.Fingerprinter) *Stats {
@@ -75,14 +74,8 @@ func (s *Stats) Add(doc proto.SystemProfile) error {
Namespace: doc.Ns, Namespace: doc.Ns,
} }
if qiac, ok = s.getQueryInfoAndCounters(key); !ok { if qiac, ok = s.getQueryInfoAndCounters(key); !ok {
query := doc.Query query := proto.NewExampleQuery(doc)
if doc.Command.Len() > 0 { queryBson, err := bson.MarshalJSON(query)
query = doc.Command
}
if err != nil {
return &StatsGetQueryFieldError{err}
}
queryBson, err := json.MarshalIndent(query, "", " ")
if err != nil { if err != nil {
return err return err
} }

View File

@@ -142,7 +142,7 @@ func TestStats(t *testing.T) {
ID: "4e4774ad26f934a193757002a991ebb8", ID: "4e4774ad26f934a193757002a991ebb8",
Namespace: "samples.col1", Namespace: "samples.col1",
Operation: "query", Operation: "query",
Query: "{\n \"find\": \"col1\",\n \"filter\": {\n \"s2\": {\n \"$gte\": \"41991\",\n \"$lt\": \"33754\"\n }\n },\n \"shardVersion\": [\n 0,\n \"000000000000000000000000\"\n ]\n}", Query: "{\"ns\":\"samples.col1\",\"op\":\"query\",\"query\":{\"find\":\"col1\",\"filter\":{\"s2\":{\"$gte\":\"41991\",\"$lt\":\"33754\"}},\"shardVersion\":[0,\"000000000000000000000000\"]}}\n",
Fingerprint: "FIND col1 s2", Fingerprint: "FIND col1 s2",
FirstSeen: parseDate("2017-04-10T13:15:53.532-03:00"), FirstSeen: parseDate("2017-04-10T13:15:53.532-03:00"),
LastSeen: parseDate("2017-04-10T13:15:53.532-03:00"), LastSeen: parseDate("2017-04-10T13:15:53.532-03:00"),

View File

@@ -0,0 +1,33 @@
{
"op" : "remove",
"ns" : "test.coll",
"query" : {
"a" : {
"$gte" : 2
},
"b" : {
"$gte" : 2
}
},
"ndeleted" : 15,
"keyUpdates" : 0,
"numYield" : 0,
"lockStats" : {
"timeLockedMicros" : {
"r" : NumberLong(0),
"w" : NumberLong(238)
},
"timeAcquiringMicros" : {
"r" : NumberLong(0),
"w" : NumberLong(4)
}
},
"millis" : 0,
"execStats" : {
},
"ts" : ISODate("2017-08-30T10:54:45.348Z"),
"client" : "127.0.0.1",
"allUsers" : [ ],
"user" : ""
}

View File

@@ -0,0 +1,47 @@
{
"op" : "remove",
"ns" : "test.coll",
"query" : {
"a" : {
"$gte" : 2
},
"b" : {
"$gte" : 2
}
},
"ndeleted" : 15,
"keyUpdates" : 0,
"writeConflicts" : 0,
"numYield" : 0,
"locks" : {
"Global" : {
"acquireCount" : {
"r" : NumberLong(1),
"w" : NumberLong(1)
}
},
"MMAPV1Journal" : {
"acquireCount" : {
"w" : NumberLong(16)
}
},
"Database" : {
"acquireCount" : {
"w" : NumberLong(1)
}
},
"Collection" : {
"acquireCount" : {
"W" : NumberLong(1)
}
}
},
"millis" : 1,
"execStats" : {
},
"ts" : ISODate("2017-08-30T10:54:52.821Z"),
"client" : "127.0.0.1",
"allUsers" : [ ],
"user" : ""
}

View File

@@ -0,0 +1,42 @@
{
"op" : "remove",
"ns" : "test.coll",
"query" : {
"a" : {
"$gte" : 2
},
"b" : {
"$gte" : 2
}
},
"ndeleted" : 15,
"keyUpdates" : 0,
"writeConflicts" : 0,
"numYield" : 0,
"locks" : {
"Global" : {
"acquireCount" : {
"r" : NumberLong(1),
"w" : NumberLong(1)
}
},
"Database" : {
"acquireCount" : {
"w" : NumberLong(1)
}
},
"Collection" : {
"acquireCount" : {
"w" : NumberLong(1)
}
}
},
"millis" : 0,
"execStats" : {
},
"ts" : ISODate("2017-08-30T10:55:02.238Z"),
"client" : "127.0.0.1",
"allUsers" : [ ],
"user" : ""
}

View File

@@ -0,0 +1,113 @@
{
"op" : "remove",
"ns" : "test.coll",
"query" : {
"a" : {
"$gte" : 2
},
"b" : {
"$gte" : 2
}
},
"keysExamined" : 39,
"docsExamined" : 39,
"ndeleted" : 15,
"keysDeleted" : 30,
"numYield" : 0,
"locks" : {
"Global" : {
"acquireCount" : {
"r" : NumberLong(1),
"w" : NumberLong(1)
}
},
"Database" : {
"acquireCount" : {
"w" : NumberLong(1)
}
},
"Collection" : {
"acquireCount" : {
"w" : NumberLong(1)
}
}
},
"millis" : 0,
"planSummary" : "IXSCAN { a: 1 }",
"execStats" : {
"stage" : "DELETE",
"nReturned" : 0,
"executionTimeMillisEstimate" : 0,
"works" : 40,
"advanced" : 0,
"needTime" : 39,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"nWouldDelete" : 15,
"nInvalidateSkips" : 0,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"b" : {
"$gte" : 2
}
},
"nReturned" : 15,
"executionTimeMillisEstimate" : 0,
"works" : 40,
"advanced" : 15,
"needTime" : 24,
"needYield" : 0,
"saveState" : 15,
"restoreState" : 15,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 39,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 39,
"executionTimeMillisEstimate" : 0,
"works" : 40,
"advanced" : 39,
"needTime" : 0,
"needYield" : 0,
"saveState" : 15,
"restoreState" : 15,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"a" : 1
},
"indexName" : "a_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"a" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"a" : [
"[2.0, inf.0]"
]
},
"keysExamined" : 39,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"ts" : ISODate("2017-08-30T10:55:09.833Z"),
"client" : "127.0.0.1",
"appName" : "MongoDB Shell",
"allUsers" : [ ],
"user" : ""
}

View File

@@ -0,0 +1,116 @@
{
"op" : "remove",
"ns" : "test.coll",
"command" : {
"q" : {
"a" : {
"$gte" : 2
},
"b" : {
"$gte" : 2
}
},
"limit" : 0
},
"keysExamined" : 39,
"docsExamined" : 39,
"ndeleted" : 15,
"keysDeleted" : 30,
"numYield" : 0,
"locks" : {
"Global" : {
"acquireCount" : {
"r" : NumberLong(1),
"w" : NumberLong(1)
}
},
"Database" : {
"acquireCount" : {
"w" : NumberLong(1)
}
},
"Collection" : {
"acquireCount" : {
"w" : NumberLong(1)
}
}
},
"millis" : 0,
"planSummary" : "IXSCAN { a: 1 }",
"execStats" : {
"stage" : "DELETE",
"nReturned" : 0,
"executionTimeMillisEstimate" : 0,
"works" : 40,
"advanced" : 0,
"needTime" : 39,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"nWouldDelete" : 15,
"nInvalidateSkips" : 0,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"b" : {
"$gte" : 2
}
},
"nReturned" : 15,
"executionTimeMillisEstimate" : 0,
"works" : 40,
"advanced" : 15,
"needTime" : 24,
"needYield" : 0,
"saveState" : 15,
"restoreState" : 15,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 39,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 39,
"executionTimeMillisEstimate" : 0,
"works" : 40,
"advanced" : 39,
"needTime" : 0,
"needYield" : 0,
"saveState" : 15,
"restoreState" : 15,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"a" : 1
},
"indexName" : "a_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"a" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"a" : [
"[2.0, inf.0]"
]
},
"keysExamined" : 39,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"ts" : ISODate("2017-08-30T10:55:19.142Z"),
"client" : "127.0.0.1",
"appName" : "MongoDB Shell",
"allUsers" : [ ],
"user" : ""
}

View File

@@ -0,0 +1,9 @@
var coll = db.coll
for (i = 0; i < 10; ++i) {
coll.insert({a: i, b: i});
}
coll.createIndex({a: 1});
coll.remove({a: {$gte: 2}, b: {$gte: 2}})

View File

@@ -3,7 +3,7 @@
"ID": "16196765fb4c14edb91efdbe4f5c5973", "ID": "16196765fb4c14edb91efdbe4f5c5973",
"Namespace": "samples.col1", "Namespace": "samples.col1",
"Operation": "query", "Operation": "query",
"Query": "{\n \"find\": \"col1\",\n \"shardVersion\": [\n 0,\n \"000000000000000000000000\"\n ]\n}", "Query": "{\"ns\":\"samples.col1\",\"op\":\"query\",\"query\":{\"find\":\"col1\",\"shardVersion\":[0,\"000000000000000000000000\"]}}\n",
"Fingerprint": "FIND col1 find", "Fingerprint": "FIND col1 find",
"FirstSeen": "2017-04-10T13:16:23.29-03:00", "FirstSeen": "2017-04-10T13:16:23.29-03:00",
"LastSeen": "2017-04-10T13:16:23.29-03:00", "LastSeen": "2017-04-10T13:16:23.29-03:00",
@@ -56,7 +56,7 @@
"ID": "4e4774ad26f934a193757002a991ebb8", "ID": "4e4774ad26f934a193757002a991ebb8",
"Namespace": "samples.col1", "Namespace": "samples.col1",
"Operation": "query", "Operation": "query",
"Query": "{\n \"find\": \"col1\",\n \"filter\": {\n \"s2\": {\n \"$gte\": \"41991\",\n \"$lt\": \"33754\"\n }\n },\n \"shardVersion\": [\n 0,\n \"000000000000000000000000\"\n ]\n}", "Query": "{\"ns\":\"samples.col1\",\"op\":\"query\",\"query\":{\"find\":\"col1\",\"filter\":{\"s2\":{\"$gte\":\"41991\",\"$lt\":\"33754\"}},\"shardVersion\":[0,\"000000000000000000000000\"]}}\n",
"Fingerprint": "FIND col1 s2", "Fingerprint": "FIND col1 s2",
"FirstSeen": "2017-04-10T13:15:53.532-03:00", "FirstSeen": "2017-04-10T13:15:53.532-03:00",
"LastSeen": "2017-04-10T13:15:53.532-03:00", "LastSeen": "2017-04-10T13:15:53.532-03:00",
@@ -109,7 +109,7 @@
"ID": "8cb8666ace7e54767b4d3c4f61860cf9", "ID": "8cb8666ace7e54767b4d3c4f61860cf9",
"Namespace": "samples.col1", "Namespace": "samples.col1",
"Operation": "query", "Operation": "query",
"Query": "{\n \"find\": \"col1\",\n \"filter\": {\n \"user_id\": {\n \"$gte\": 3384024924,\n \"$lt\": 195092007\n }\n },\n \"projection\": {\n \"$sortKey\": {\n \"$meta\": \"sortKey\"\n }\n },\n \"shardVersion\": [\n 0,\n \"000000000000000000000000\"\n ],\n \"sort\": {\n \"user_id\": 1\n }\n}", "Query": "{\"ns\":\"samples.col1\",\"op\":\"query\",\"query\":{\"find\":\"col1\",\"filter\":{\"user_id\":{\"$gte\":3.384024924e+09,\"$lt\":1.95092007e+08}},\"projection\":{\"$sortKey\":{\"$meta\":\"sortKey\"}},\"shardVersion\":[0,\"000000000000000000000000\"],\"sort\":{\"user_id\":1}}}\n",
"Fingerprint": "FIND col1 user_id", "Fingerprint": "FIND col1 user_id",
"FirstSeen": "2017-04-10T13:15:53.524-03:00", "FirstSeen": "2017-04-10T13:15:53.524-03:00",
"LastSeen": "2017-04-10T13:15:53.524-03:00", "LastSeen": "2017-04-10T13:15:53.524-03:00",