├── src ├── main │ └── java │ │ ├── com │ │ ├── github │ │ │ └── fakemongo │ │ │ │ ├── impl │ │ │ │ ├── Filter.java │ │ │ │ ├── ValueFilter.java │ │ │ │ ├── aggregation │ │ │ │ │ ├── Skip.java │ │ │ │ │ ├── Limit.java │ │ │ │ │ ├── Sort.java │ │ │ │ │ ├── Out.java │ │ │ │ │ ├── Match.java │ │ │ │ │ ├── Sample.java │ │ │ │ │ ├── PipelineKeyword.java │ │ │ │ │ ├── ReplaceRoot.java │ │ │ │ │ ├── Lookup.java │ │ │ │ │ └── Unwind.java │ │ │ │ ├── Tuple2.java │ │ │ │ ├── index │ │ │ │ │ ├── HashedIndex.java │ │ │ │ │ ├── IndexFactory.java │ │ │ │ │ ├── IndexedList.java │ │ │ │ │ ├── Index.java │ │ │ │ │ └── GeoIndex.java │ │ │ │ └── Aggregator.java │ │ │ │ ├── FongoException.java │ │ │ │ ├── AwaitResultSingleResultCallback.java │ │ │ │ ├── FongoConnectionSource.java │ │ │ │ ├── async │ │ │ │ └── FongoAsyncConnectionSource.java │ │ │ │ ├── junit │ │ │ │ └── FongoAsyncRule.java │ │ │ │ └── Fongo.java │ │ └── mongodb │ │ │ ├── operation │ │ │ └── FongoBsonArrayWrapper.java │ │ │ ├── InsertManyWriteConcernException.java │ │ │ ├── async │ │ │ └── client │ │ │ │ ├── FongoAsyncMongoDatabase.java │ │ │ │ └── MockAsyncMongoClient.java │ │ │ ├── FongoMapReduceOutput.java │ │ │ ├── FongoMongoDatabase.java │ │ │ ├── FongoBulkWriteCombiner.java │ │ │ ├── FongoMongoCollection.java │ │ │ ├── util │ │ │ ├── FongoJSONCallback.java │ │ │ └── JSONCallback.java │ │ │ └── MockMongoClient.java │ │ └── org │ │ └── bson │ │ └── json │ │ └── JsonBuffer.java └── test │ ├── java │ └── com │ │ ├── github │ │ └── fakemongo │ │ │ ├── integration │ │ │ ├── SpringModelMapRepository.java │ │ │ ├── TestRepository.java │ │ │ ├── jongo │ │ │ │ ├── Gender.java │ │ │ │ ├── Coordinate.java │ │ │ │ ├── Friend.java │ │ │ │ ├── JongoItem.java │ │ │ │ └── FongoJongoTest.java │ │ │ ├── SpringModelMap.java │ │ │ ├── FongoScalaTest.scala │ │ │ ├── FongoAbstractTest.scala │ │ │ ├── Item.java │ │ │ ├── SpringMongoOperationTest.java │ │ │ └── SpringQueryTest.java │ │ │ ├── FongoV3ServerVersion0Test.java │ │ │ ├── FongoV3ServerVersion3_0Test.java │ │ │ ├── FongoV3ServerVersion3_2Test.java │ │ │ ├── FongoV3ServerVersion3_3Test.java │ │ │ ├── test │ │ │ └── beans │ │ │ │ ├── AbstractTestBean.java │ │ │ │ ├── TestChildBean.java │ │ │ │ └── TestParentBean.java │ │ │ ├── FongoGridFSTest.java │ │ │ ├── ExpectedMongoException.java │ │ │ ├── FongoCodecRegistryTest.java │ │ │ ├── impl │ │ │ ├── geo │ │ │ │ └── GeoUtilTest.java │ │ │ ├── index │ │ │ │ ├── IndexedListTest.java │ │ │ │ └── IndexTest.java │ │ │ └── aggregation │ │ │ │ └── ReplaceRootTest.java │ │ │ ├── PerfTest.java │ │ │ ├── FongoFindTest.java │ │ │ ├── FongoTextSearchTest.java │ │ │ ├── FongoAggregateLookupTest.java │ │ │ ├── FongoAggregateOutTest.java │ │ │ └── FongoAggregateZipTest.java │ │ └── mongodb │ │ ├── TestResultObject.java │ │ ├── FongoCastTest.java │ │ ├── FongoMongoTest.java │ │ ├── FongoMongoDatabaseTest.java │ │ ├── CollectionNameTest.java │ │ └── FongoDBTest.java │ ├── resources │ └── logback-test.xml │ └── scala │ └── com │ └── github │ └── fakemongo │ └── impl │ ├── FongoCasbahSuite.scala │ └── UtilScalaTest.scala ├── .gitignore ├── .travis.yml ├── Jenkinsfile └── CHANGELOG /src/main/java/com/github/fakemongo/impl/Filter.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl; 2 | 3 | import com.mongodb.DBObject; 4 | 5 | public interface Filter { 6 | boolean apply(DBObject o); 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | target 3 | .idea 4 | *.tmproj 5 | *.iml 6 | *.ipr 7 | *.iws 8 | *.swp 9 | .project 10 | .classpath 11 | .cache 12 | .settings 13 | test-output 14 | bin 15 | *.sublime-project 16 | *.sublime-workspace 17 | *.log 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: java 4 | 5 | cache: 6 | directories: 7 | - $HOME/.m2/repository 8 | 9 | jdk: 10 | - oraclejdk7 11 | - openjdk7 12 | - openjdk6 13 | - oraclejdk8 14 | - oraclejdk10 15 | 16 | install: travis_wait mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -B -V 17 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/SpringModelMapRepository.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.integration; 2 | 3 | import org.springframework.data.mongodb.repository.MongoRepository; 4 | 5 | /** 6 | * @author Tom Dearman 7 | */ 8 | public interface SpringModelMapRepository extends MongoRepository { 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/FongoV3ServerVersion0Test.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.mongodb.connection.ServerVersion; 4 | 5 | public class FongoV3ServerVersion0Test extends AbstractFongoV3Test { 6 | 7 | @Override 8 | public ServerVersion serverVersion() { 9 | return Fongo.OLD_SERVER_VERSION; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/FongoV3ServerVersion3_0Test.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.mongodb.connection.ServerVersion; 4 | 5 | public class FongoV3ServerVersion3_0Test extends AbstractFongoV3Test { 6 | 7 | @Override 8 | public ServerVersion serverVersion() { 9 | return Fongo.V3_SERVER_VERSION; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/ValueFilter.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl; 2 | 3 | /** 4 | * Filter for any value, not just DBObject as {@link Filter} 5 | * Needed by $pull command that can filter out both sub-documents and primitive values like string, etc 6 | */ 7 | public interface ValueFilter { 8 | boolean apply(Object object); 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/TestRepository.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.integration; 2 | 3 | import org.springframework.data.mongodb.repository.MongoRepository; 4 | 5 | /** 6 | * User: william 7 | * Date: 28/08/13 8 | */ 9 | public interface TestRepository extends MongoRepository { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/mongodb/operation/FongoBsonArrayWrapper.java: -------------------------------------------------------------------------------- 1 | package com.mongodb.operation; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 7 | */ 8 | public final class FongoBsonArrayWrapper { 9 | 10 | private FongoBsonArrayWrapper() { 11 | } 12 | 13 | public static BsonArrayWrapper bsonArrayWrapper(List array) { 14 | return new BsonArrayWrapper(array); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/FongoException.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | /** 4 | * Internal fongo exception 5 | * 6 | * @author jon 7 | */ 8 | public class FongoException extends RuntimeException { 9 | private static final long serialVersionUID = 1L; 10 | 11 | private final Integer code; 12 | 13 | public FongoException(String message) { 14 | super(message); 15 | code = null; 16 | } 17 | 18 | public FongoException(Integer code, String message) { 19 | super(message); 20 | this.code = code; 21 | } 22 | 23 | public Integer getCode() { 24 | return code; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d [%thread] %-5level %logger{36} - %X{addData} %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/test/java/com/mongodb/TestResultObject.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | 3 | /** 4 | * Test result object which extends a {@link BasicDBObject}. 5 | * 6 | * @author Nils Meder 7 | */ 8 | public class TestResultObject extends BasicDBObject { 9 | 10 | private static final long serialVersionUID = -7428775686006312017L; 11 | 12 | /** 13 | * Constructs a new {@link TestResultObject}. 14 | */ 15 | public TestResultObject() { 16 | super(); 17 | } 18 | 19 | /** 20 | * Returns the string of the {@code _id} field. 21 | * @return the string of the {@code _id} field. 22 | */ 23 | public String getEntityId() { 24 | return this.getString("_id"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/mongodb/InsertManyWriteConcernException.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | 3 | import java.util.List; 4 | import static java.util.Collections.emptyList; 5 | 6 | public class InsertManyWriteConcernException extends RuntimeException { 7 | 8 | private BulkWriteResult result; 9 | private List errors = emptyList(); 10 | 11 | public InsertManyWriteConcernException(BulkWriteResult result, List errors) { 12 | this.result = result; 13 | this.errors = errors; 14 | } 15 | 16 | public BulkWriteResult getResult() { 17 | return result; 18 | } 19 | 20 | public List getErrors() { 21 | return errors; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | node { 2 | // Mark the code checkout 'stage'.... 3 | stage 'Checkout' 4 | 5 | // Get some code from a GitHub repository 6 | git url: 'https://github.com/fakemongo/fongo.git' 7 | 8 | // Get the maven tool. 9 | // ** NOTE: This 'M3' maven tool must be configured 10 | // ** in the global configuration. 11 | def mvnHome = tool 'M3' 12 | 13 | // Mark the code build 'stage'.... 14 | stage 'Build' 15 | // Run the maven build 16 | sh "${mvnHome}/bin/mvn clean install" 17 | 18 | stage 'Notify' 19 | mail body: 'Please go to ${env.BUILD_URL}', charset: 'UTF-8', mimeType: 'text/plain', subject: 'Job \'${env.JOB_NAME}\' (${env.BUILD_NUMBER}) is ${currentBuild.result}', to: 'william.delanoue@gmail.com' 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/com/mongodb/async/client/FongoAsyncMongoDatabase.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2016 Deveryware S.A. All Rights Reserved. 3 | */ 4 | package com.mongodb.async.client; 5 | 6 | import com.github.fakemongo.async.FongoAsync; 7 | import com.mongodb.ReadConcern; 8 | import com.mongodb.ReadPreference; 9 | import com.mongodb.WriteConcern; 10 | import org.bson.codecs.configuration.CodecRegistry; 11 | 12 | /** 13 | * 14 | */ 15 | public class FongoAsyncMongoDatabase extends MongoDatabaseImpl { 16 | 17 | public FongoAsyncMongoDatabase(String name, CodecRegistry codecRegistry, ReadPreference readPreference, WriteConcern writeConcern, ReadConcern readConcern, FongoAsync fongoAsync) { 18 | super(name, codecRegistry, readPreference, writeConcern, readConcern, fongoAsync); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/mongodb/FongoCastTest.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | import com.github.fakemongo.Fongo; 3 | import org.jongo.Jongo; 4 | import org.jongo.MongoCollection; 5 | import org.junit.Test; 6 | 7 | public class FongoCastTest { 8 | 9 | @Test(expected=DuplicateKeyException.class) 10 | public void checkFongoSupportsMajority() { 11 | Jongo jongo = new Jongo(new Fongo("fake-mongo-server").getDB("fake-database")); 12 | MongoCollection turnips = jongo.getCollection("turnip").withWriteConcern(WriteConcern.MAJORITY); 13 | turnips.insert(new Turnip("turnip")); 14 | turnips.insert(new Turnip("turnip")); 15 | } 16 | 17 | private class Turnip { 18 | private String _id; 19 | 20 | public Turnip() { 21 | } 22 | 23 | public Turnip(String _id) { 24 | this._id = _id; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/aggregation/Skip.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.aggregation; 2 | 3 | import com.mongodb.DB; 4 | import com.mongodb.DBCollection; 5 | import com.mongodb.DBObject; 6 | import com.mongodb.annotations.ThreadSafe; 7 | import java.util.List; 8 | 9 | /** 10 | * User: william 11 | * Date: 24/07/13 12 | */ 13 | @ThreadSafe 14 | public class Skip extends PipelineKeyword { 15 | public static final Skip INSTANCE = new Skip(); 16 | 17 | private Skip() { 18 | } 19 | 20 | /** 21 | */ 22 | @Override 23 | public DBCollection apply(DB originalDB, DBCollection coll, DBObject object) { 24 | List objects = coll.find().skip(((Number) object.get(getKeyword())).intValue()).toArray(); 25 | return dropAndInsert(coll, objects); 26 | } 27 | 28 | @Override 29 | public String getKeyword() { 30 | return "$skip"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/aggregation/Limit.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.aggregation; 2 | 3 | import com.mongodb.DB; 4 | import com.mongodb.DBCollection; 5 | import com.mongodb.DBObject; 6 | import com.mongodb.annotations.ThreadSafe; 7 | import java.util.List; 8 | 9 | /** 10 | * User: william 11 | * Date: 24/07/13 12 | */ 13 | @ThreadSafe 14 | public class Limit extends PipelineKeyword { 15 | public static final Limit INSTANCE = new Limit(); 16 | 17 | private Limit() { 18 | } 19 | 20 | /** 21 | */ 22 | @Override 23 | public DBCollection apply(DB originalDB, DBCollection coll, DBObject object) { 24 | List objects = coll.find().limit(((Number) object.get(getKeyword())).intValue()).toArray(); 25 | return dropAndInsert(coll, objects); 26 | } 27 | 28 | @Override 29 | public String getKeyword() { 30 | return "$limit"; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/jongo/Gender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Benoit GUEROUT and Yves AMSELLEM 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fakemongo.integration.jongo; 18 | 19 | public enum Gender { 20 | FEMALE 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/aggregation/Sort.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.aggregation; 2 | 3 | import com.github.fakemongo.impl.ExpressionParser; 4 | import com.mongodb.DB; 5 | import com.mongodb.DBCollection; 6 | import com.mongodb.DBObject; 7 | import com.mongodb.annotations.ThreadSafe; 8 | import java.util.List; 9 | 10 | /** 11 | * 12 | */ 13 | @ThreadSafe 14 | public class Sort extends PipelineKeyword { 15 | public static final Sort INSTANCE = new Sort(); 16 | 17 | private Sort() { 18 | } 19 | 20 | /** 21 | */ 22 | @Override 23 | public DBCollection apply(DB originalDB, DBCollection coll, DBObject object) { 24 | final List objects = coll.find().sort(ExpressionParser.toDbObject(object.get(getKeyword()))).toArray(); 25 | return dropAndInsert(coll, objects); 26 | } 27 | 28 | @Override 29 | public String getKeyword() { 30 | return "$sort"; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/SpringModelMap.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.integration; 2 | 3 | import org.springframework.data.mongodb.core.index.CompoundIndex; 4 | import org.springframework.data.mongodb.core.index.CompoundIndexes; 5 | import org.springframework.data.mongodb.core.mapping.Document; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * @author Tom Dearman 11 | */ 12 | @Document 13 | @CompoundIndexes({ 14 | @CompoundIndex(name = "serial_date_idx", def = "{'barcode.serial': 1, barcode.time: 1}") 15 | }) 16 | public class SpringModelMap 17 | { 18 | private Map barcode; 19 | 20 | SpringModelMap(Map barcode) { 21 | this.barcode = barcode; 22 | } 23 | 24 | public Map getBarcode() { 25 | return barcode; 26 | } 27 | 28 | public void setBarcode( Map barcode) { 29 | this.barcode = barcode; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/aggregation/Out.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.aggregation; 2 | 3 | import com.mongodb.BasicDBObject; 4 | import com.mongodb.DB; 5 | import com.mongodb.DBCollection; 6 | import com.mongodb.DBObject; 7 | import java.util.List; 8 | 9 | /** 10 | * User: gdepourtales 11 | * 2015/06/15 12 | */ 13 | public class Out extends PipelineKeyword { 14 | 15 | public static final Out INSTANCE = new Out(); 16 | 17 | @Override 18 | public DBCollection apply(DB originalDB, DBCollection coll, DBObject object) { 19 | final List objects = coll.find().toArray(); 20 | DBCollection newCollection = originalDB.getCollection(object.get(getKeyword()).toString()); 21 | // By default, remove all in the collection without dropping indexes. 22 | newCollection.remove(new BasicDBObject()); 23 | newCollection.insert(objects); 24 | return coll; 25 | } 26 | 27 | @Override 28 | public String getKeyword() { 29 | return "$out"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/FongoScalaTest.scala: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.integration 2 | 3 | import com.mongodb.BasicDBObject 4 | import org.scalatest._ 5 | import org.scalatest.junit.JUnitRunner 6 | import org.junit.runner.RunWith 7 | import com.github.fakemongo.Fongo 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class FongoScalaTest extends FunSuite with BeforeAndAfter { 11 | 12 | var fongo: Fongo = _ 13 | 14 | before { 15 | fongo = new Fongo("InMemoryMongo") 16 | } 17 | 18 | test("Fongo should not throw npe") { 19 | val db = fongo.getDB("myDB") 20 | val col = db.createCollection("myCollection", new BasicDBObject()) 21 | val result = col.findOne() 22 | assert(result == null) 23 | } 24 | 25 | test("Insert should work") { 26 | val collection = fongo.getDB("myDB").createCollection("myCollection", new BasicDBObject()) 27 | 28 | collection.insert(new BasicDBObject("basic", "basic")) 29 | 30 | assert(1 === collection.count()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/FongoAbstractTest.scala: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.integration 2 | 3 | import _root_.com.mongodb.{MongoClient, DBCollection} 4 | import org.scalatest._ 5 | import java.util.UUID 6 | import com.github.fakemongo.Fongo 7 | 8 | trait FongoAbstractTest extends FunSuite with BeforeAndAfter { 9 | // If you want to test against real world (a real mongodb client). 10 | def realWorld: Boolean 11 | 12 | def init(): Unit 13 | 14 | var collection: DBCollection = _ 15 | 16 | before { 17 | if (realWorld) { 18 | val mongo = new MongoClient("localhost") 19 | collection = mongo.getDB(UUID.randomUUID().toString).createCollection("myCollection", null) 20 | } else { 21 | val fongo = new Fongo("InMemoryMongo") 22 | collection = fongo.getDB("myDB").createCollection("myCollection", null) 23 | } 24 | 25 | init() 26 | } 27 | 28 | after { 29 | if (realWorld) { 30 | collection.getDB().dropDatabase(); 31 | } else { 32 | collection.drop(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/AwaitResultSingleResultCallback.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.mongodb.async.SingleResultCallback; 4 | import java.util.concurrent.CountDownLatch; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public class AwaitResultSingleResultCallback implements SingleResultCallback { 8 | 9 | private T result; 10 | private Throwable throwable; 11 | private CountDownLatch countDownLatch = new CountDownLatch(1); 12 | 13 | @Override 14 | public void onResult(T result, Throwable t) { 15 | this.result = result; 16 | throwable = t; 17 | countDownLatch.countDown(); 18 | } 19 | 20 | public T awaitResult() throws Throwable { 21 | return awaitResult(1, TimeUnit.MINUTES); 22 | } 23 | 24 | public T awaitResult(long time, TimeUnit timeUnit) throws Throwable { 25 | if (!countDownLatch.await(time, timeUnit)) { 26 | throw new RuntimeException("take too much time..."); 27 | } 28 | if (throwable != null) { 29 | throw throwable; 30 | } 31 | return result; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/FongoV3ServerVersion3_2Test.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.mongodb.MongoCommandException; 4 | import com.mongodb.client.MongoCollection; 5 | import com.mongodb.client.model.IndexOptions; 6 | import com.mongodb.connection.ServerVersion; 7 | import org.bson.Document; 8 | import org.junit.Test; 9 | 10 | public class FongoV3ServerVersion3_2Test extends AbstractFongoV3Test { 11 | 12 | @Override 13 | public ServerVersion serverVersion() { 14 | return Fongo.V3_2_SERVER_VERSION; 15 | } 16 | 17 | @Test 18 | public void insertOneWithDuplicateValueForUniqueColumn_throwsMongoCommandException() { 19 | // Given 20 | MongoCollection collection = newCollection(); 21 | collection.createIndex(new Document("a", 1), new IndexOptions().name("a").unique(true)); 22 | collection.insertOne(new Document("_id", 1).append("a", 1)); 23 | collection.insertOne(new Document("_id", 2).append("a", 2)); 24 | 25 | // When 26 | exception.expect(MongoCommandException.class); 27 | collection.findOneAndUpdate(docId(2), new Document("$set", new Document("a", 1))); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/FongoV3ServerVersion3_3Test.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.mongodb.DuplicateKeyException; 4 | import com.mongodb.client.MongoCollection; 5 | import com.mongodb.client.model.IndexOptions; 6 | import com.mongodb.connection.ServerVersion; 7 | import org.bson.Document; 8 | import org.junit.Test; 9 | 10 | public class FongoV3ServerVersion3_3Test extends AbstractFongoV3Test { 11 | 12 | @Override 13 | public ServerVersion serverVersion() { 14 | return Fongo.V3_3_SERVER_VERSION; 15 | } 16 | 17 | @Test 18 | public void insertOneWithDuplicateValueForUniqueColumn_throwsDuplicateKeyException() { 19 | // Given 20 | MongoCollection collection = newCollection(); 21 | collection.createIndex(new Document("a", 1), new IndexOptions().name("a").unique(true)); 22 | collection.insertOne(new Document("_id", 1).append("a", 1)); 23 | collection.insertOne(new Document("_id", 2).append("a", 2)); 24 | 25 | // When 26 | exception.expect(DuplicateKeyException.class); 27 | collection.findOneAndUpdate(docId(2), new Document("$set", new Document("a", 1))); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/Item.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.integration; 2 | 3 | import java.util.Date; 4 | import java.util.UUID; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.mongodb.core.index.Indexed; 7 | import org.springframework.data.mongodb.core.mapping.Document; 8 | import org.springframework.data.mongodb.core.mapping.Field; 9 | 10 | /** 11 | * User: william 12 | * Date: 15/03/14 13 | */ 14 | @Document(collection = Item.COLLECTION_NAME) 15 | public class Item { 16 | 17 | public static final String COLLECTION_NAME = "items"; 18 | 19 | @Id 20 | private final UUID id; 21 | 22 | @Indexed 23 | @Field 24 | private final String name; 25 | 26 | @Field 27 | private final Date creationDateTime; 28 | 29 | public Item(UUID id, String name, Date creationDateTime) { 30 | this.id = id; 31 | this.name = name; 32 | this.creationDateTime = creationDateTime; 33 | } 34 | 35 | public UUID getId() { 36 | return id; 37 | } 38 | 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | public Date getCreationDateTime() { 44 | return creationDateTime; 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/Tuple2.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl; 2 | 3 | 4 | public class Tuple2 { 5 | 6 | public final A _1; 7 | public final B _2; 8 | 9 | public Tuple2(A _1, B _2) { 10 | this._1 = _1; 11 | this._2 = _2; 12 | } 13 | 14 | @Override 15 | public int hashCode() { 16 | final int prime = 31; 17 | int result = 1; 18 | result = prime * result + ((_1 == null) ? 0 : _1.hashCode()); 19 | result = prime * result + ((_2 == null) ? 0 : _2.hashCode()); 20 | return result; 21 | } 22 | 23 | @Override 24 | public boolean equals(Object obj) { 25 | if (this == obj) 26 | return true; 27 | if (obj == null) 28 | return false; 29 | if (getClass() != obj.getClass()) 30 | return false; 31 | Tuple2 other = (Tuple2) obj; 32 | if (_1 == null) { 33 | if (other._1 != null) 34 | return false; 35 | } else if (!_1.equals(other._1)) 36 | return false; 37 | if (_2 == null) { 38 | if (other._2 != null) 39 | return false; 40 | } else if (!_2.equals(other._2)) 41 | return false; 42 | return true; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "Tuple2(" + _1 + ", " + _2 + ")"; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/FongoConnectionSource.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.mongodb.binding.ConnectionSource; 4 | import com.mongodb.connection.Connection; 5 | import com.mongodb.connection.ServerConnectionState; 6 | import com.mongodb.connection.ServerDescription; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * 12 | */ 13 | class FongoConnectionSource implements ConnectionSource { 14 | private final static Logger LOG = LoggerFactory.getLogger(FongoConnectionSource.class); 15 | 16 | private final Fongo fongo; 17 | 18 | public FongoConnectionSource(Fongo fongo) { 19 | this.fongo = fongo; 20 | } 21 | 22 | @Override 23 | public ServerDescription getServerDescription() { 24 | return ServerDescription.builder().address(fongo.getServerAddress()).state(ServerConnectionState.CONNECTED).version(fongo.getServerVersion()).build(); 25 | } 26 | 27 | @Override 28 | public Connection getConnection() { 29 | return new FongoConnection(this.fongo); 30 | } 31 | 32 | @Override 33 | public ConnectionSource retain() { 34 | return this; 35 | } 36 | 37 | @Override 38 | public int getCount() { 39 | return 0; 40 | } 41 | 42 | @Override 43 | public void release() { 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/aggregation/Match.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.aggregation; 2 | 3 | import com.github.fakemongo.impl.ExpressionParser; 4 | import com.mongodb.DB; 5 | import com.mongodb.DBCollection; 6 | import com.mongodb.DBObject; 7 | import com.mongodb.annotations.ThreadSafe; 8 | import java.util.List; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * User: william 14 | * Date: 24/07/13 15 | */ 16 | @ThreadSafe 17 | public class Match extends PipelineKeyword { 18 | private static final Logger LOG = LoggerFactory.getLogger(Match.class); 19 | 20 | public static final Match INSTANCE = new Match(); 21 | 22 | private Match() { 23 | } 24 | 25 | /** 26 | * {@see http://docs.mongodb.org/manual/reference/aggregation/match/#pipe._S_match} 27 | */ 28 | @Override 29 | public DBCollection apply(DB originalDB, DBCollection coll, DBObject object) { 30 | LOG.debug("computeResult() match : {}", object); 31 | 32 | List objects = coll.find(ExpressionParser.toDbObject(object.get(getKeyword()))).toArray(); 33 | coll = dropAndInsert(coll, objects); 34 | LOG.debug("computeResult() match : {}, result : {}", object, objects); 35 | return coll; 36 | } 37 | 38 | @Override 39 | public String getKeyword() { 40 | return "$match"; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/index/HashedIndex.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.index; 2 | 3 | import com.mongodb.DBObject; 4 | import com.mongodb.MongoException; 5 | import java.util.LinkedHashMap; 6 | import java.util.List; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * An index for the MongoDB. 12 | */ 13 | public class HashedIndex extends IndexAbstract { 14 | private static final Logger LOG = LoggerFactory.getLogger(HashedIndex.class); 15 | 16 | HashedIndex(String name, DBObject keys, boolean unique, String hashed, boolean sparse) { 17 | super(name, keys, unique, new LinkedHashMap>(), hashed, sparse); 18 | } 19 | 20 | /** 21 | * Create the key for the hashmap. 22 | * 23 | * @param object 24 | * @return 25 | */ 26 | @Override 27 | protected DBObject getKeyFor(DBObject object) { 28 | return object; 29 | } 30 | 31 | @Override 32 | public DBObject embedded(DBObject object) { 33 | return object; 34 | } 35 | 36 | @Override 37 | public List> addOrUpdate(DBObject object, DBObject oldObject) { 38 | if (object.get(this.geoIndex) instanceof List) { 39 | throw new MongoException(16766, "Error: hashed indexes do not currently support array values"); 40 | } 41 | return super.addOrUpdate(object, oldObject); 42 | } 43 | } -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/test/beans/AbstractTestBean.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.test.beans; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.google.common.base.Objects; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.annotation.Version; 7 | 8 | /** 9 | * @author Kollivakkam Raghavan 10 | * @created 4/23/2016 11 | */ 12 | public abstract class AbstractTestBean { 13 | 14 | @Id 15 | @JsonProperty("_id") 16 | private String id; 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object o) { 28 | if (this == o) { 29 | return true; 30 | } 31 | if (o == null || getClass() != o.getClass()) { 32 | return false; 33 | } 34 | AbstractTestBean that = (AbstractTestBean) o; 35 | return Objects.equal(id, that.id); 36 | } 37 | 38 | @Override 39 | public int hashCode() { 40 | return Objects.hashCode(id); 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | final StringBuilder sb = new StringBuilder("AbstractTestBean{"); 46 | sb.append("id='").append(id).append('\''); 47 | sb.append('}'); 48 | return sb.toString(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/FongoGridFSTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.github.fakemongo.junit.FongoRule; 4 | import com.mongodb.gridfs.GridFS; 5 | import com.mongodb.gridfs.GridFSDBFile; 6 | import com.mongodb.gridfs.GridFSInputFile; 7 | import org.bson.types.ObjectId; 8 | import org.junit.Before; 9 | import org.junit.Rule; 10 | import org.junit.Test; 11 | 12 | import java.io.ByteArrayOutputStream; 13 | 14 | import static org.junit.Assert.assertArrayEquals; 15 | import static org.junit.Assert.assertEquals; 16 | 17 | public class FongoGridFSTest { 18 | 19 | @Rule 20 | public final FongoRule fongoRule = new FongoRule(false); 21 | 22 | private GridFS fs; 23 | 24 | @Before 25 | public void setUp() throws Exception { 26 | fs = new GridFS(fongoRule.getDB()); 27 | } 28 | 29 | @Test 30 | public void shouldStoreFileInMultipleChunks() throws Exception { 31 | final byte[] data = new byte[]{1, 2, 3, 4, 5}; 32 | 33 | final GridFSInputFile file = fs.createFile(data); 34 | file.setChunkSize(3); //chunk size is less than data size in order to get more than one chunk 35 | file.save(); 36 | 37 | final GridFSDBFile result = fs.findOne((ObjectId) file.getId()); 38 | 39 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 40 | assertEquals(data.length, result.writeTo(out)); 41 | 42 | assertArrayEquals(data, out.toByteArray()); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/async/FongoAsyncConnectionSource.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.async; 2 | 3 | import com.mongodb.async.SingleResultCallback; 4 | import com.mongodb.binding.AsyncConnectionSource; 5 | import com.mongodb.connection.AsyncConnection; 6 | import com.mongodb.connection.ServerConnectionState; 7 | import com.mongodb.connection.ServerDescription; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * 13 | */ 14 | class FongoAsyncConnectionSource implements AsyncConnectionSource { 15 | private final static Logger LOG = LoggerFactory.getLogger(FongoAsyncConnectionSource.class); 16 | 17 | private final FongoAsync fongoAsync; 18 | 19 | public FongoAsyncConnectionSource(FongoAsync fongoAsync) { 20 | this.fongoAsync = fongoAsync; 21 | } 22 | 23 | @Override 24 | public ServerDescription getServerDescription() { 25 | return ServerDescription.builder().address(fongoAsync.getServerAddress()).state(ServerConnectionState.CONNECTED).version(fongoAsync.getServerVersion()).build(); 26 | } 27 | 28 | @Override 29 | public void getConnection(SingleResultCallback callback) { 30 | callback.onResult(new FongoAsyncConnection(this.fongoAsync), null); 31 | } 32 | 33 | @Override 34 | public AsyncConnectionSource retain() { 35 | return this; 36 | } 37 | 38 | @Override 39 | public int getCount() { 40 | return 0; 41 | } 42 | 43 | @Override 44 | public void release() { 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/com/mongodb/FongoMongoTest.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | 3 | import com.github.fakemongo.Fongo; 4 | import java.net.InetSocketAddress; 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertNotNull; 8 | import org.junit.Test; 9 | 10 | public class FongoMongoTest { 11 | 12 | @Test 13 | public void mongoClientHasOptions() { 14 | MongoClient mongoClient = new Fongo("test").getMongo(); 15 | assertNotNull(mongoClient.getMongoClientOptions()); 16 | assertNotNull(mongoClient.getMongoOptions()); 17 | } 18 | 19 | @Test 20 | public void mongoHasWriteConcern() { 21 | Fongo fongo = new Fongo("test"); 22 | assertEquals(WriteConcern.ACKNOWLEDGED, fongo.getMongo().getWriteConcern()); 23 | assertEquals(WriteConcern.ACKNOWLEDGED, fongo.getWriteConcern()); 24 | } 25 | 26 | @Test 27 | public void mongoAllAddressOverride() { 28 | MongoClient mongoClient = new Fongo("test").getMongo(); 29 | 30 | assertThat(mongoClient.getAllAddress()).containsOnly(new ServerAddress(new InetSocketAddress(ServerAddress.defaultHost(), ServerAddress.defaultPort()))); 31 | } 32 | 33 | @Test 34 | public void mongoGetAddressOverride() { 35 | MongoClient mongoClient = new Fongo("test").getMongo(); 36 | 37 | assertThat(mongoClient.getAddress()).isEqualTo(new ServerAddress(new InetSocketAddress(ServerAddress.defaultHost(), ServerAddress.defaultPort()))); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/test/beans/TestChildBean.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.test.beans; 2 | 3 | import com.google.common.base.Objects; 4 | 5 | /** 6 | * @author Kollivakkam Raghavan 7 | * @created 4/23/2016 8 | */ 9 | public class TestChildBean extends TestParentBean { 10 | 11 | private String parentId; 12 | 13 | public String getParentId() { 14 | return parentId; 15 | } 16 | 17 | public void setParentId(String parentId) { 18 | this.parentId = parentId; 19 | } 20 | 21 | @Override 22 | public boolean equals(Object o) { 23 | if (this == o) { 24 | return true; 25 | } 26 | if (o == null || getClass() != o.getClass()) { 27 | return false; 28 | } 29 | if (!super.equals(o)) { 30 | return false; 31 | } 32 | TestChildBean that = (TestChildBean) o; 33 | return Objects.equal(parentId, that.parentId); 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return Objects.hashCode(super.hashCode(), parentId); 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | final StringBuilder sb = new StringBuilder("InheritedAttributes {"); 44 | final String parentStr = super.toString(); 45 | sb.append(parentStr).append("}, ChildAttributes:{"); 46 | sb.append("parentId='").append(parentId).append('\''); 47 | sb.append('}'); 48 | return sb.toString(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/jongo/Coordinate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Benoit GUEROUT and Yves AMSELLEM 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fakemongo.integration.jongo; 18 | 19 | 20 | public class Coordinate { 21 | 22 | public int lat, lng; 23 | 24 | Coordinate() { 25 | } 26 | 27 | public Coordinate(int lat, int lng) { 28 | this.lat = lat; 29 | this.lng = lng; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (!(o instanceof Coordinate)) return false; 36 | 37 | Coordinate that = (Coordinate) o; 38 | 39 | if (lat != that.lat) return false; 40 | if (lng != that.lng) return false; 41 | 42 | return true; 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | int result = lat; 48 | result = 31 * result + lng; 49 | return result; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/bson/json/JsonBuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2014 MongoDB, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.bson.json; 18 | 19 | public class JsonBuffer { 20 | 21 | private final String buffer; 22 | private int position; 23 | 24 | public JsonBuffer(final String buffer) { 25 | this.buffer = buffer; 26 | } 27 | 28 | public int getPosition() { 29 | return position; 30 | } 31 | 32 | public void setPosition(final int position) { 33 | this.position = position; 34 | } 35 | 36 | public int read() { 37 | return (position >= buffer.length()) ? -1 : buffer.charAt(position++); 38 | } 39 | 40 | public void unread(final int c) { 41 | if (c != -1 && buffer.charAt(position - 1) == c) { 42 | position--; 43 | } 44 | } 45 | 46 | public String substring(final int beginIndex) { 47 | return buffer.substring(beginIndex); 48 | } 49 | 50 | public String substring(final int beginIndex, final int endIndex) { 51 | return buffer.substring(beginIndex, endIndex); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/com/mongodb/FongoMongoDatabaseTest.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | 3 | import com.github.fakemongo.Fongo; 4 | import static com.mongodb.WriteConcern.REPLICA_ACKNOWLEDGED; 5 | import com.mongodb.client.MongoDatabase; 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | import org.bson.codecs.configuration.CodecRegistry; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | public class FongoMongoDatabaseTest { 12 | 13 | private FongoMongoDatabase instance; 14 | 15 | @Before 16 | public void before() { 17 | instance = new FongoMongoDatabase("test", new Fongo("test2")); 18 | } 19 | 20 | @Test 21 | public void withCodecRegistry_change_codec_and_good_instance() throws Exception { 22 | final CodecRegistry codecRegistry = MongoClient.getDefaultCodecRegistry(); 23 | final MongoDatabase mongoDatabase = instance.withCodecRegistry(codecRegistry); 24 | 25 | assertThat(mongoDatabase).hasSameClassAs(instance); 26 | assertThat(mongoDatabase.getCodecRegistry()).isEqualTo(codecRegistry); 27 | } 28 | 29 | @Test 30 | public void withReadPreference_change_readPreference_and_good_instance() throws Exception { 31 | final MongoDatabase mongoDatabase = instance.withReadPreference(ReadPreference.nearest()); 32 | 33 | assertThat(mongoDatabase).hasSameClassAs(instance); 34 | assertThat(mongoDatabase.getReadPreference()).isEqualTo(ReadPreference.nearest()); 35 | } 36 | 37 | @Test 38 | public void withWriteConcern_change_writeConcern_and_good_instance() throws Exception { 39 | final MongoDatabase mongoDatabase = instance.withWriteConcern(REPLICA_ACKNOWLEDGED); 40 | 41 | assertThat(mongoDatabase).hasSameClassAs(instance); 42 | assertThat(mongoDatabase.getWriteConcern()).isEqualTo(REPLICA_ACKNOWLEDGED); 43 | } 44 | } -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/test/beans/TestParentBean.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.test.beans; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 4 | import com.google.common.base.Objects; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author Kollivakkam Raghavan 10 | * @created 4/23/2016 11 | */ 12 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 13 | public class TestParentBean extends AbstractTestBean { 14 | 15 | private String attr; 16 | 17 | private List secondaryCollItems; 18 | 19 | public String getAttr() { 20 | return attr; 21 | } 22 | 23 | public void setAttr(String attr) { 24 | this.attr = attr; 25 | } 26 | 27 | public List getSecondaryCollItems() { 28 | return secondaryCollItems; 29 | } 30 | 31 | public void setSecondaryCollItems(List secondaryCollItems) { 32 | this.secondaryCollItems = secondaryCollItems; 33 | } 34 | 35 | @Override 36 | public boolean equals(Object o) { 37 | if (this == o) { 38 | return true; 39 | } 40 | if (o == null || getClass() != o.getClass()) { 41 | return false; 42 | } 43 | if (!super.equals(o)) { 44 | return false; 45 | } 46 | TestParentBean that = (TestParentBean) o; 47 | return Objects.equal(attr, that.attr); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return Objects.hashCode(super.hashCode(), attr); 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | final StringBuilder sb = new StringBuilder("InheritedAttributes {"); 58 | final String parentStr = super.toString(); 59 | sb.append(parentStr).append("}, ChildAttributes:{"); 60 | sb.append("attr='").append(attr).append('\''); 61 | sb.append('}'); 62 | return sb.toString(); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/mongodb/FongoMapReduceOutput.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | 3 | import com.mongodb.operation.MapReduceBatchCursor; 4 | import com.mongodb.operation.MapReduceStatistics; 5 | import static java.util.Collections.singleton; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | 9 | /** 10 | * 11 | */ 12 | public class FongoMapReduceOutput extends MapReduceOutput { 13 | 14 | public FongoMapReduceOutput(final DBObject command, final DBCollection collection, final MapReduceStatistics mapReduceStatistics) { 15 | super(command, collection.find(), mapReduceStatistics, collection); 16 | 17 | } 18 | 19 | public FongoMapReduceOutput(DBObject command, final List objects) { 20 | // TODO : verify minimal data. 21 | super(command, new MapReduceBatchCursor() { 22 | 23 | final Iterator> iterator = singleton(objects).iterator(); 24 | 25 | @Override 26 | public MapReduceStatistics getStatistics() { 27 | return null; 28 | } 29 | 30 | @Override 31 | public void close() { 32 | } 33 | 34 | @Override 35 | public boolean hasNext() { 36 | return iterator.hasNext(); 37 | } 38 | 39 | @Override 40 | public List next() { 41 | return iterator.next(); 42 | } 43 | 44 | @Override 45 | public void setBatchSize(int batchSize) { 46 | 47 | } 48 | 49 | @Override 50 | public int getBatchSize() { 51 | return 0; 52 | } 53 | 54 | @Override 55 | public List tryNext() { 56 | return next(); 57 | } 58 | 59 | @Override 60 | public ServerCursor getServerCursor() { 61 | return null; 62 | } 63 | 64 | @Override 65 | public ServerAddress getServerAddress() { 66 | return null; 67 | } 68 | 69 | @Override 70 | public void remove() { 71 | throw new IllegalStateException("cannot remove"); 72 | } 73 | }); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/aggregation/Sample.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.aggregation; 2 | 3 | import com.mongodb.DB; 4 | import com.mongodb.DBCollection; 5 | import com.mongodb.DBObject; 6 | import com.mongodb.annotations.ThreadSafe; 7 | 8 | import java.util.HashSet; 9 | import java.util.List; 10 | import java.util.ArrayList; 11 | import java.util.Random; 12 | import java.util.Set; 13 | 14 | /** 15 | */ 16 | @ThreadSafe 17 | public class Sample extends PipelineKeyword { 18 | public static final Sample INSTANCE = new Sample(); 19 | 20 | private static final Random rnd = new Random(); 21 | 22 | // Based on Floyd's random sample algorithm, taken from here: http://stackoverflow.com/a/3724708/736741 23 | private static Set randomSample(int max, int n) { 24 | HashSet res = new HashSet(n); 25 | int count = max + 1; 26 | for (int i = count - n; i < count; i++) { 27 | Integer item = rnd.nextInt(i + 1); 28 | if (res.contains(item)) 29 | res.add(i); 30 | else 31 | res.add(item); 32 | } 33 | return res; 34 | } 35 | 36 | /** 37 | */ 38 | @Override 39 | public DBCollection apply(DB originalDB, DBCollection coll, DBObject object) { 40 | DBObject dbObject = (DBObject) object.get(getKeyword()); 41 | int size = ((Number) dbObject.get("size")).intValue(); 42 | 43 | List objects = new ArrayList(size); 44 | int count = (int) coll.count(); 45 | if (count <= size) { // no need to sample, collection has less elements than we want to sample 46 | return coll; 47 | } 48 | 49 | if (count != 0) { 50 | Set samples = randomSample(count - 1, size); 51 | List collAsArray = coll.find().toArray(); 52 | for (Integer sample : samples) { 53 | objects.add(collAsArray.get(sample)); 54 | } 55 | } 56 | 57 | return dropAndInsert(coll, objects); 58 | } 59 | 60 | @Override 61 | public String getKeyword() { 62 | return "$sample"; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/scala/com/github/fakemongo/impl/FongoCasbahSuite.scala: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl 2 | 3 | import java.util.Date 4 | 5 | import com.github.fakemongo.Fongo 6 | import com.mongodb.casbah.Imports._ 7 | import org.scalatest.{FunSuite, ShouldMatchers} 8 | 9 | class FongoCasbahSuite extends FunSuite with ShouldMatchers { 10 | 11 | test("Fongo bug with $in") { 12 | // val fongo = MongoClient("localhost") //"in-memory MongoDB") 13 | val fongo = new Fongo("in-memory MongoDB") 14 | val collection = fongo.getDB("test").asScala.getCollection("items").asScala 15 | 16 | collection.insert(DBObject("name" -> "ABC"), DBObject("name" -> "DEF")) 17 | 18 | val query = "name" $in Seq("ABC", "bunch of monkeys") 19 | collection.find(query).hasNext shouldBe true 20 | } 21 | 22 | test("Fongo with $or") { 23 | // val fongo = MongoClient("localhost") 24 | val fongo = new Fongo("in-memory MongoDB") 25 | val collection = fongo.getDB("test").getCollection("items") 26 | 27 | val now = new Date() 28 | collection.insert(DBObject("published" -> true, "expiration" -> now)) 29 | 30 | val query: DBObject = $and( 31 | "published" $ne false, 32 | $or("startDate" $exists false, "startDate" $lt now), 33 | $or("expiration" $exists false, "expiration" $gte now) 34 | ) 35 | collection.find(query).hasNext shouldBe true 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/index/IndexFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.index; 2 | 3 | import com.github.fakemongo.impl.Util; 4 | import com.mongodb.DBObject; 5 | import com.mongodb.MongoException; 6 | import java.util.Map; 7 | 8 | /** 9 | * A factory for index. 10 | */ 11 | public final class IndexFactory { 12 | private IndexFactory() { 13 | } 14 | 15 | public static IndexAbstract create(String name, DBObject keys, boolean unique, boolean sparse) throws MongoException { 16 | String geoIndex = getGeoKey(keys); 17 | if (geoIndex != null) { 18 | return new GeoIndex(name, keys, unique, geoIndex, sparse); 19 | } else { 20 | String hashed = getHashedKey(keys); 21 | if (hashed != null) { 22 | return new HashedIndex(name, keys, unique, hashed, sparse); 23 | } 24 | return new Index(name, keys, unique, sparse); 25 | } 26 | } 27 | 28 | private static String getHashedKey(DBObject keys) { 29 | String hashed = null; 30 | for (Map.Entry entry : Util.entrySet(keys)) { 31 | Object value = entry.getValue(); 32 | if (value instanceof String) { 33 | boolean localHashed = "hashed".equals(value); 34 | if (localHashed) { 35 | hashed = entry.getKey(); 36 | } 37 | } 38 | } 39 | return hashed; 40 | } 41 | 42 | private static String getGeoKey(DBObject keys) { 43 | boolean first = true; 44 | String geo = null; 45 | for (Map.Entry entry : Util.entrySet(keys)) { 46 | Object value = entry.getValue(); 47 | if (value instanceof String) { 48 | boolean localGeo = "2d".equals(value) || "2dsphere".equals(value); 49 | if (localGeo) { 50 | if (!first && "2d".equals(value)) { 51 | // "err" : "2d has to be first in index", "code" : 13023, "n" : 0, "connectionId" : 206, "ok" : 1 52 | throw new MongoException(13023, "2d has to be first in index"); 53 | } 54 | geo = entry.getKey(); 55 | } 56 | } 57 | first = false; 58 | } 59 | return geo; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/ExpectedMongoException.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | //import com.mongodb.CommandFailureException; 4 | 5 | import com.mongodb.MongoCommandException; 6 | import com.mongodb.MongoException; 7 | import com.mongodb.WriteConcernException; 8 | import org.hamcrest.Description; 9 | import org.hamcrest.Matcher; 10 | import org.hamcrest.TypeSafeMatcher; 11 | import org.junit.rules.ExpectedException; 12 | 13 | public final class ExpectedMongoException { 14 | 15 | private ExpectedMongoException() { 16 | } 17 | 18 | public static ExpectedException expectMongoCommandException(ExpectedException expectedException, int code) { 19 | return expectCode(expectedException, code, MongoCommandException.class); 20 | } 21 | 22 | public static ExpectedException expectWriteConcernException(ExpectedException expectedException, int code) { 23 | return expectCode(expectedException, code, WriteConcernException.class); 24 | } 25 | 26 | public static ExpectedException expect(ExpectedException expectedException, Class exception) { 27 | expectedException.expect(exception); 28 | return expectedException; 29 | } 30 | 31 | public static ExpectedException expectCode(ExpectedException expectedException, int code) { 32 | expectedException.expect(equalCode(code)); 33 | return expectedException; 34 | } 35 | 36 | public static ExpectedException expectCode(ExpectedException expectedException, int code, Class exception) { 37 | expectedException.expect(exception); 38 | expectedException.expect(equalCode(code)); 39 | return expectedException; 40 | } 41 | 42 | private static Matcher equalCode(final int code) { 43 | return new TypeSafeMatcher() { 44 | public void describeTo(Description description) { 45 | description.appendText("exception must have code " + code); 46 | } 47 | 48 | @Override 49 | public boolean matchesSafely(Throwable item) { 50 | MongoException mongoException = (MongoException) item; 51 | return mongoException.getCode() == code; 52 | } 53 | }; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/aggregation/PipelineKeyword.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.aggregation; 2 | 3 | import com.github.fakemongo.Fongo; 4 | import com.mongodb.DB; 5 | import com.mongodb.DBCollection; 6 | import com.mongodb.DBObject; 7 | import com.mongodb.FongoDB; 8 | import java.util.List; 9 | import java.util.UUID; 10 | 11 | /** 12 | * User: william Date: 24/07/13 13 | */ 14 | public abstract class PipelineKeyword { 15 | 16 | protected static final FongoDB fongo = new Fongo("aggregation_pipeline").getDB("pipeline"); 17 | 18 | 19 | /** 20 | * Apply the keyword on the collection 21 | * 22 | * @param originalDB original DB from collection. 23 | * @param coll collection to be processed (will be destroyed). 24 | * @param object parameters for keyword. 25 | * @return a new collection in result. 26 | */ 27 | public abstract DBCollection apply(DB originalDB, DBCollection coll, DBObject object); 28 | 29 | /** 30 | * Return the keyword in the pipeline (like $sort, $group...). 31 | */ 32 | public abstract String getKeyword(); 33 | 34 | /** 35 | * Drop collection and create new one with objects. 36 | * 37 | * @param coll 38 | * @param objects 39 | * @return the new collection. 40 | */ 41 | protected DBCollection dropAndInsert(DBCollection coll, List objects) { 42 | coll.drop(); 43 | return createAndInsert(objects); 44 | } 45 | 46 | static void errorResult(DBCollection coll, int code, String err) { 47 | ((FongoDB) coll.getDB()).notOkErrorResult(code, err).throwOnError(); 48 | } 49 | 50 | protected DBCollection createAndInsert(List objects) { 51 | DBCollection coll = fongo.doGetCollection(UUID.randomUUID().toString(), true, false); 52 | coll.insert(objects); 53 | return coll; 54 | } 55 | 56 | public boolean canApply(DBObject object) { 57 | return object.containsField(getKeyword()); 58 | } 59 | 60 | static void validateNull(T param, String msg) { 61 | if(param == null) { 62 | fongo.errorResult(15955, msg).throwOnError(); 63 | } 64 | } 65 | 66 | static void validateTrue(boolean expr, String msg) { 67 | if(!expr) { 68 | fongo.errorResult(15955, msg).throwOnError(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/mongodb/FongoMongoDatabase.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | 3 | import com.github.fakemongo.Fongo; 4 | import com.mongodb.client.MongoCollection; 5 | import com.mongodb.client.MongoDatabase; 6 | import org.bson.codecs.configuration.CodecRegistry; 7 | 8 | /** 9 | * 10 | */ 11 | public class FongoMongoDatabase extends MongoDatabaseImpl { 12 | 13 | private final Fongo fongo; 14 | 15 | public FongoMongoDatabase(final String databaseName, final Fongo fongo) { 16 | this(databaseName, fongo, fongo.getCodecRegistry(), ReadPreference.primary(), WriteConcern.ACKNOWLEDGED, ReadConcern.DEFAULT); 17 | } 18 | 19 | private FongoMongoDatabase(final String databaseName, final Fongo fongo, final CodecRegistry codecRegistry, final ReadPreference readPreference, final WriteConcern writeConcern, final ReadConcern readConcern) { 20 | super(databaseName, codecRegistry, readPreference, writeConcern, readConcern, fongo); 21 | this.fongo = fongo; 22 | } 23 | 24 | @Override 25 | public MongoDatabase withCodecRegistry(CodecRegistry codecRegistry) { 26 | return new FongoMongoDatabase(super.getName(), this.fongo, codecRegistry, super.getReadPreference(), super.getWriteConcern(), super.getReadConcern()); 27 | } 28 | 29 | @Override 30 | public MongoDatabase withReadPreference(ReadPreference readPreference) { 31 | return new FongoMongoDatabase(super.getName(), this.fongo, super.getCodecRegistry(), readPreference, super.getWriteConcern(), super.getReadConcern()); 32 | } 33 | 34 | @Override 35 | public MongoDatabase withWriteConcern(WriteConcern writeConcern) { 36 | return new FongoMongoDatabase(super.getName(), this.fongo, super.getCodecRegistry(), super.getReadPreference(), writeConcern, super.getReadConcern()); 37 | } 38 | 39 | @Override 40 | public MongoDatabase withReadConcern(final ReadConcern readConcern) { 41 | return new FongoMongoDatabase(super.getName(), this.fongo, super.getCodecRegistry(), super.getReadPreference(), super.getWriteConcern(), readConcern); 42 | } 43 | 44 | @Override 45 | public MongoCollection getCollection(final String collectionName, final Class documentClass) { 46 | return new FongoMongoCollection(this.fongo, new MongoNamespace(super.getName(), collectionName), documentClass, super.getCodecRegistry(), super.getReadPreference(), 47 | super.getWriteConcern(), super.getReadConcern()); 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/index/IndexedList.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.index; 2 | 3 | import java.util.*; 4 | 5 | 6 | public class IndexedList<Е> { 7 | private Map<Е, List> indexes; 8 | 9 | private final List<Е> elements; 10 | 11 | private boolean isSingle = true; 12 | 13 | public IndexedList(List<Е> elements) { 14 | this.elements = elements; 15 | 16 | if (elements.size() > 1) { 17 | initIndex(elements); 18 | } 19 | } 20 | 21 | private void initIndex(List<Е> newElements) { 22 | indexes = new HashMap<Е, List>(); 23 | 24 | isSingle = false; 25 | 26 | int count = 0; 27 | for (Е el : newElements) { 28 | addIndex(el, count); 29 | count += 1; 30 | } 31 | } 32 | 33 | public List<Е> getElements() { 34 | return elements; 35 | } 36 | 37 | public int size() { 38 | return elements.size(); 39 | } 40 | 41 | public boolean contains(Е element) { 42 | if (isSingle) 43 | return elements.contains(element); 44 | 45 | return indexes.containsKey(element); 46 | } 47 | 48 | public void add(Е element) { 49 | if (elements.size() == 1) { 50 | initIndex(elements); 51 | } 52 | 53 | elements.add(element); 54 | 55 | if (elements.size() > 1) 56 | addIndex(element, elements.size()-1); 57 | } 58 | 59 | private void addIndex(Е element, int position) { 60 | final List list; 61 | if (!indexes.containsKey(element)) { 62 | list = new ArrayList(); 63 | indexes.put(element, list); 64 | } 65 | else 66 | list = indexes.get(element); 67 | 68 | list.add(position); 69 | } 70 | 71 | public void remove(Е element) { 72 | if (isSingle) { 73 | elements.remove(element); 74 | return; 75 | } 76 | 77 | List index = indexes.get(element); 78 | 79 | if (index == null) 80 | return; 81 | 82 | if (index.size() != 0) { 83 | int pos = index.get(0); 84 | elements.remove(pos); 85 | index.remove(0); 86 | 87 | for(int i=pos; i index) { 99 | for (int i = 0; i < index.size(); i++) { 100 | index.set(i, index.get(i) - 1); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/test/scala/com/github/fakemongo/impl/UtilScalaTest.scala: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl 2 | 3 | import org.scalatest._ 4 | import com.mongodb._ 5 | import org.junit.runner.RunWith 6 | import org.scalatest.junit.JUnitRunner 7 | 8 | @RunWith(classOf[JUnitRunner]) 9 | class UtilScalaTest extends FunSuite { 10 | 11 | test("extractField must handle simple case") { 12 | val obj = new BasicDBObject("field", "value") 13 | 14 | assert("value" === Util.extractField(obj, "field").asInstanceOf[String]) 15 | } 16 | 17 | test("extractField must handle one step case") { 18 | val obj = new BasicDBObject("field", new BasicDBObject("field2", "value")) 19 | 20 | assert("value" === Util.extractField(obj, "field.field2").asInstanceOf[String]) 21 | } 22 | 23 | test("extractField must handle tree step case") { 24 | val obj = new BasicDBObject("field", new BasicDBObject("field1", "badvalue").append("field2", new BasicDBObject("field1", "value"))) 25 | 26 | assert("value" === Util.extractField(obj, "field.field2.field1").asInstanceOf[String]) 27 | } 28 | 29 | test("contains Field must return true for simple case") { 30 | val obj = new BasicDBObject("field", "value") 31 | 32 | assert(true === Util.containsField(obj, "field")) 33 | } 34 | 35 | test("containsField must handle one step case") { 36 | val obj = new BasicDBObject("field", new BasicDBObject("field2", "value")) 37 | 38 | assert(true === Util.containsField(obj, "field.field2")) 39 | } 40 | 41 | test("containsField must handle tree step case") { 42 | val obj = new BasicDBObject("field", new BasicDBObject("field1", "badvalue").append("field2", new BasicDBObject("field1", "value"))) 43 | 44 | assert(true === Util.containsField(obj, "field.field2.field1")) 45 | } 46 | 47 | test("containsField must return false when not present") { 48 | val obj = new BasicDBObject("field", new BasicDBObject("field1", "badvalue").append("field2", new BasicDBObject("field1", "value").append("nullfield", null))) 49 | 50 | assert(false === Util.containsField(obj, "field2")) 51 | assert(false === Util.containsField(obj, "field2.field2")) 52 | assert(false === Util.containsField(obj, "field.field3")) 53 | assert(false === Util.containsField(obj, "field.field3")) 54 | } 55 | 56 | test("containsField must return true when present but null") { 57 | val obj = new BasicDBObject("field", new BasicDBObject("field1", "badvalue").append("field2", new BasicDBObject("field1", "value").append("nullfield", null))) 58 | 59 | assert(true === Util.containsField(obj, "field.field2.nullfield")) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/Aggregator.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl; 2 | 3 | import com.github.fakemongo.impl.aggregation.*; 4 | import com.mongodb.BasicDBObject; 5 | import com.mongodb.DBCollection; 6 | import com.mongodb.DBObject; 7 | import com.mongodb.FongoDB; 8 | import com.mongodb.FongoDBCollection; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.UUID; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | /** 16 | * Handle the aggregation of a collection. 17 | */ 18 | public class Aggregator { 19 | private static final Logger LOG = LoggerFactory.getLogger(Aggregator.class); 20 | 21 | private final FongoDB fongoDB; 22 | private final FongoDBCollection fongoDBCollection; 23 | private final List pipeline; 24 | private static final List keywords = Arrays.asList(Match.INSTANCE, Project.INSTANCE, Group.INSTANCE, 25 | Sort.INSTANCE, Sample.INSTANCE, Limit.INSTANCE, 26 | Skip.INSTANCE, Unwind.INSTANCE, Out.INSTANCE, 27 | Lookup.INSTANCE, Bucket.INSTANCE, 28 | ReplaceRoot.INSTANCE, AddFields.INSTANCE); 29 | 30 | public Aggregator(FongoDB fongoDB, FongoDBCollection coll, List pipeline) { 31 | this.fongoDB = fongoDB; 32 | this.fongoDBCollection = coll; 33 | this.pipeline = pipeline; 34 | } 35 | 36 | /** 37 | * @return null if error. 38 | */ 39 | public List computeResult() { 40 | DBCollection coll = fongoDB.createCollection(UUID.randomUUID().toString(), new BasicDBObject()); 41 | try { 42 | coll.insert(this.fongoDBCollection.find().toArray()); 43 | 44 | for (DBObject object : pipeline) { 45 | boolean found = false; 46 | for (PipelineKeyword keyword : keywords) { 47 | if (keyword.canApply(object)) { 48 | coll = keyword.apply(this.fongoDBCollection.getDB(), coll, object); 49 | found = true; 50 | break; 51 | } 52 | } 53 | if (!found) { 54 | fongoDB.notOkErrorResult(16436, "exception: Unrecognized pipeline stage name: '" + object.keySet() + "'").throwOnError(); 55 | } 56 | // Not found : com.mongodb.CommandFailureException: { "serverUsed" : "localhost/127.0.0.1:27017" , "errmsg" : "exception: Unrecognized pipeline stage name: '_id'" , "code" : 16436 , "ok" : 0.0} 57 | } 58 | 59 | List result = coll.find().toArray(); 60 | LOG.debug("computeResult() : {}", result); 61 | return result; 62 | } finally { 63 | coll.drop(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/FongoCodecRegistryTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.mongodb.MongoClient; 4 | import com.mongodb.client.MongoCollection; 5 | import org.bson.BsonReader; 6 | import org.bson.BsonWriter; 7 | import org.bson.codecs.Codec; 8 | import org.bson.codecs.DecoderContext; 9 | import org.bson.codecs.EncoderContext; 10 | import org.bson.codecs.configuration.CodecConfigurationException; 11 | import org.bson.codecs.configuration.CodecRegistries; 12 | import org.bson.codecs.configuration.CodecRegistry; 13 | import org.junit.Test; 14 | 15 | import static org.junit.Assert.assertEquals; 16 | 17 | /** 18 | * 19 | */ 20 | public class FongoCodecRegistryTest { 21 | 22 | public class Pojo { 23 | public String _id; 24 | public String value; 25 | 26 | public Pojo() { 27 | } 28 | 29 | public Pojo(String _id, String value) { 30 | this._id = _id; 31 | this.value = value; 32 | } 33 | } 34 | 35 | public class PojoCodec implements Codec { 36 | @Override 37 | public Pojo decode(BsonReader reader, DecoderContext decoderContext) { 38 | Pojo pojo = new Pojo(); 39 | reader.readStartDocument(); 40 | pojo._id = reader.readString("_id"); 41 | pojo.value = reader.readString("value"); 42 | reader.readEndDocument(); 43 | return pojo; 44 | } 45 | 46 | @Override 47 | public void encode(BsonWriter writer, Pojo value, EncoderContext encoderContext) { 48 | writer.writeStartDocument(); 49 | writer.writeString("_id", value._id); 50 | writer.writeString("value", value.value); 51 | writer.writeEndDocument(); 52 | } 53 | 54 | @Override 55 | public Class getEncoderClass() { 56 | return Pojo.class; 57 | } 58 | } 59 | 60 | @Test 61 | public void testSerializeAndDeserializeWithDedicatedCodecShouldWork() { 62 | CodecRegistry registry = CodecRegistries.fromRegistries(MongoClient.getDefaultCodecRegistry(), CodecRegistries.fromCodecs(new PojoCodec())); 63 | Fongo fongo = new Fongo("test", Fongo.DEFAULT_SERVER_VERSION, registry); 64 | Pojo pojo = new Pojo("1", "ok codec"); 65 | MongoCollection col = fongo.getDatabase("test").getCollection("test").withDocumentClass(Pojo.class); 66 | col.insertOne(pojo); 67 | Pojo loadedPojo = col.find().first(); 68 | assertEquals(pojo._id, loadedPojo._id); 69 | assertEquals(pojo.value, loadedPojo.value); 70 | } 71 | 72 | @Test(expected= CodecConfigurationException.class) 73 | public void testSerializeAndDeserializeWithoutDedicatedCodecShouldFail() { 74 | Fongo fongo = new Fongo("test", Fongo.DEFAULT_SERVER_VERSION); 75 | Pojo pojo = new Pojo("1", "ok codec"); 76 | MongoCollection col = fongo.getDatabase("test").getCollection("test").withDocumentClass(Pojo.class); 77 | col.insertOne(pojo); 78 | } 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/aggregation/ReplaceRoot.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.aggregation; 2 | 3 | import com.github.fakemongo.impl.ExpressionParser; 4 | import com.mongodb.*; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by rkolliva 14 | * 1/28/17. 15 | */ 16 | 17 | 18 | public class ReplaceRoot extends PipelineKeyword { 19 | 20 | private static final Logger LOGGER = LoggerFactory.getLogger(ReplaceRoot.class); 21 | 22 | public static ReplaceRoot INSTANCE = new ReplaceRoot(); 23 | 24 | 25 | @Override 26 | public DBCollection apply(DB originalDB, DBCollection parentColl, DBObject replaceRootQuery) { 27 | LOGGER.trace(">>>> applying $replaceRoot pipeline operation"); 28 | 29 | DBObject newRoot = ExpressionParser.toDbObject(replaceRootQuery.get(getKeyword())); 30 | LOGGER.trace("<<<< applying $replaceRoot pipeline operation"); 31 | List dbObjects = replaceRootFromDocument(parentColl, newRoot); 32 | return dropAndInsert(parentColl, dbObjects); 33 | } 34 | 35 | private List replaceRootFromDocument(DBCollection parentColl, DBObject replaceRootExpr) { 36 | Object replaceRootExprValue = replaceRootExpr.get("newRoot"); 37 | validateNull(replaceRootExprValue, "newRoot expression cannot be null"); 38 | List retval = new ArrayList(); 39 | if(replaceRootExprValue instanceof String) { 40 | String newRootValue = (String)replaceRootExprValue; 41 | int index = 0; 42 | validateTrue(newRootValue.charAt(0) == '$', "Field path expression for newRoot must start with $"); 43 | newRootValue = newRootValue.substring(1); 44 | DBCursor cursor = parentColl.find(); 45 | while(cursor.hasNext()) { 46 | DBObject object = cursor.next(); 47 | Object embeddedDoc = object.get(newRootValue); 48 | validateNull(embeddedDoc, newRootValue + " is missing in collection at index " + index); 49 | validateTrue(DBObject.class.isAssignableFrom(embeddedDoc.getClass()), 50 | "Embedded value must evaluate to document at " + index); 51 | index++; 52 | retval.add((DBObject) embeddedDoc); 53 | } 54 | return retval; 55 | } 56 | else { 57 | DBObject newRootExpr = ExpressionParser.toDbObject(replaceRootExprValue); 58 | String projectionExpr = "{$project :".concat(newRootExpr.toString()).concat("}"); 59 | DBObject projection = ExpressionParser.toDbObject(BasicDBObject.parse(projectionExpr)); 60 | AggregationOutput aggregationOutput = parentColl.aggregate(Arrays.asList(projection)); 61 | Iterable results = aggregationOutput.results(); 62 | validateNull(results, "Expression for new root resulted in a null object"); 63 | for (DBObject result : results) { 64 | retval.add(result); 65 | } 66 | } 67 | 68 | return retval; 69 | } 70 | 71 | @Override 72 | public String getKeyword() { 73 | return "$replaceRoot"; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/index/Index.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.index; 2 | 3 | import com.github.fakemongo.impl.ExpressionParser; 4 | import com.github.fakemongo.impl.Util; 5 | import com.mongodb.BasicDBObject; 6 | import com.mongodb.DBObject; 7 | import com.mongodb.FongoDBCollection; 8 | import java.util.ArrayList; 9 | import java.util.LinkedHashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.TreeMap; 13 | 14 | /** 15 | * An index for the MongoDB. 16 | */ 17 | public class Index extends IndexAbstract { 18 | 19 | Index(String name, DBObject keys, boolean unique, boolean sparse) { 20 | super(name, keys, unique, createMap(keys, unique), null, sparse); 21 | } 22 | 23 | private static Map> createMap(DBObject keys, boolean unique) { 24 | // Preserve order only for id. 25 | if (keys.containsField(FongoDBCollection.ID_FIELD_NAME) && keys.toMap().size() == 1) { 26 | return new LinkedHashMap>(); 27 | } else { 28 | //noinspection unchecked 29 | return new TreeMap>(new ExpressionParser().buildObjectComparator(isAsc(keys))); 30 | } 31 | } 32 | 33 | @Override 34 | public DBObject embedded(DBObject object) { 35 | return expandObject(object); // Important : do not clone, indexes share objects between them. 36 | } 37 | 38 | /** 39 | * Expand all flattened {@link DBObject}s to match the current MongoDB behaviour. 40 | * 41 | * @param object The {@link DBObject} to insert. 42 | * @return The expanded {@link DBObject}. 43 | */ 44 | private DBObject expandObject(final DBObject object) { 45 | final List keysToRemove = new ArrayList(); 46 | final List objectsToPut = new ArrayList(); 47 | 48 | for (final String key : object.keySet()) { 49 | if (key.contains(".")) { 50 | final Object actualValue = object.get(key); 51 | 52 | DBObject expandedObject = null; 53 | 54 | final List splittedKeys = Util.split(key); 55 | 56 | for (int i = splittedKeys.size() - 1; i >= 0; i--) { 57 | if (expandedObject == null) { 58 | expandedObject = new BasicDBObject(splittedKeys.get(i), actualValue); 59 | } else { 60 | final DBObject partialObject = expandedObject; 61 | expandedObject = new BasicDBObject(splittedKeys.get(i), partialObject); 62 | } 63 | } 64 | 65 | keysToRemove.add(key); 66 | objectsToPut.add(expandedObject); 67 | } 68 | } 69 | 70 | for (final String keyToRemove : keysToRemove) { 71 | object.removeField(keyToRemove); 72 | } 73 | 74 | for (final DBObject objectToPut : objectsToPut) { 75 | final String rootElement = objectToPut.keySet().iterator().next(); 76 | if (object.containsField(rootElement)) { 77 | DBObject objectToAdd = ExpressionParser.toDbObject(objectToPut.get(rootElement)); 78 | ExpressionParser.toDbObject(object.get(rootElement)).putAll(objectToAdd); 79 | } else { 80 | object.putAll(objectToPut); 81 | } 82 | } 83 | 84 | return object; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/mongodb/async/client/MockAsyncMongoClient.java: -------------------------------------------------------------------------------- 1 | package com.mongodb.async.client; 2 | 3 | import com.github.fakemongo.async.FongoAsync; 4 | import com.mongodb.async.SingleResultCallback; 5 | import com.mongodb.connection.Cluster; 6 | import com.mongodb.connection.ClusterDescription; 7 | import com.mongodb.connection.ClusterSettings; 8 | import com.mongodb.connection.Server; 9 | import com.mongodb.operation.AsyncOperationExecutor; 10 | import com.mongodb.selector.ServerSelector; 11 | import org.bson.Document; 12 | 13 | public class MockAsyncMongoClient extends MongoClientImpl { 14 | 15 | private final FongoAsync fongoAsync; 16 | 17 | public static MockAsyncMongoClient create(FongoAsync fongoAsync) { 18 | // using objenesis here to prevent default constructor from spinning up background threads. 19 | // MockAsyncMongoClient client = new ObjenesisStd().getInstantiatorOf(MockAsyncMongoClient.class).newInstance(); 20 | MongoClientSettings settings = MongoClientSettings.builder().codecRegistry(fongoAsync.getFongo().getCodecRegistry()).build(); 21 | MockAsyncMongoClient client = new MockAsyncMongoClient(fongoAsync, settings, new Cluster() { 22 | @Override 23 | public ClusterSettings getSettings() { 24 | return null; 25 | } 26 | 27 | @Override 28 | public ClusterDescription getDescription() { 29 | return null; 30 | } 31 | 32 | @Override 33 | public Server selectServer(ServerSelector serverSelector) { 34 | return null; 35 | } 36 | 37 | @Override 38 | public void selectServerAsync(ServerSelector serverSelector, SingleResultCallback callback) { 39 | 40 | } 41 | 42 | @Override 43 | public void close() { 44 | 45 | } 46 | 47 | @Override 48 | public boolean isClosed() { 49 | return false; 50 | } 51 | }, fongoAsync); 52 | return client; 53 | } 54 | 55 | public MockAsyncMongoClient(final FongoAsync fongoAsync, final MongoClientSettings settings, final Cluster cluster, final AsyncOperationExecutor executor) { 56 | super(settings, cluster, executor); 57 | this.fongoAsync = fongoAsync; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return fongoAsync.toString(); 63 | } 64 | 65 | 66 | /** 67 | * Gets the database with the given name. 68 | * 69 | * @param name the name of the database 70 | * @return the database 71 | */ 72 | @Override 73 | public MongoDatabase getDatabase(String name) { 74 | return fongoAsync.getDatabase(name); 75 | } 76 | 77 | @Override 78 | public void close() { 79 | } 80 | 81 | /** 82 | * Gets the list of databases 83 | * 84 | * @return the list databases iterable interface 85 | */ 86 | @Override 87 | public ListDatabasesIterable listDatabases() { 88 | return null; 89 | } 90 | 91 | /** 92 | * Gets the list of databases 93 | * 94 | * @param tResultClass the class to cast the database documents to 95 | * @return the list databases iterable interface 96 | */ 97 | @Override 98 | public ListDatabasesIterable listDatabases(Class tResultClass) { 99 | return null; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/test/java/com/mongodb/CollectionNameTest.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | 3 | 4 | import static org.hamcrest.CoreMatchers.is; 5 | import static org.junit.Assert.assertThat; 6 | 7 | import java.util.UUID; 8 | 9 | import org.bson.Document; 10 | import org.junit.Before; 11 | import org.junit.BeforeClass; 12 | import org.junit.Test; 13 | 14 | import com.github.fakemongo.Fongo; 15 | import com.mongodb.client.MongoCollection; 16 | import com.mongodb.client.MongoDatabase; 17 | 18 | 19 | /** 20 | * These tests check if a collection name is valid before being created by an insert command. 21 | * If it isn't valid an IllegalArgumentException will be thrown. 22 | * 23 | * Reference: The _id field not returned for collections with name starting with "system" #242 24 | * 25 | * @author Nicola Viola 26 | * 27 | */ 28 | public class CollectionNameTest { 29 | 30 | private static Fongo fongo; 31 | private UUID id; 32 | private Document document; 33 | 34 | @BeforeClass 35 | public static void setUpBeforeClass() throws Exception { 36 | fongo = new Fongo("test"); 37 | } 38 | 39 | @Before 40 | public void setUp() throws Exception { 41 | id = UUID.randomUUID(); 42 | document = new Document("_id", id); 43 | } 44 | 45 | @Test 46 | public void collectionnameIsSystemUser_thenTheDocumentIsFound() throws Exception { 47 | 48 | String collectionName = "systemUser"; 49 | UUID result = findADocumentInACollection(collectionName, document, fongo.getMongo()); 50 | assertThat(result, is(id)); 51 | } 52 | 53 | @Test(expected=IllegalArgumentException.class) 54 | public void collectionnameIsSystemDotUser_throwIllegalArgumentException() throws Exception { 55 | String collectionName = "system.User"; 56 | findADocumentInACollection(collectionName, document, fongo.getMongo()); 57 | } 58 | 59 | @Test(expected=IllegalArgumentException.class) 60 | public void collectionnameIsSystemDot_throwIllegalArgumentException() throws Exception { 61 | String collectionName = "system."; 62 | 63 | findADocumentInACollection(collectionName, document, fongo.getMongo()); 64 | } 65 | 66 | @Test 67 | public void collectionnameIsSystem_thenTheDocumentIsFound() throws Exception { 68 | String collectionName = "system"; 69 | 70 | UUID result = findADocumentInACollection(collectionName, document, fongo.getMongo()); 71 | assertThat(result, is(id)); 72 | } 73 | 74 | 75 | @Test(expected=IllegalArgumentException.class) 76 | public void collectionnameIs$t_thenTheDocumentIsFound() throws Exception { 77 | String collectionName = "$t"; 78 | 79 | findADocumentInACollection(collectionName, document, fongo.getMongo()); 80 | } 81 | 82 | 83 | private UUID findADocumentInACollection(String collectionName, Document document, MongoClient fongo){ 84 | 85 | MongoDatabase database = fongo.getDatabase("test-db"); 86 | 87 | MongoCollection systemUsers = database.getCollection(collectionName); 88 | systemUsers.insertOne(document); 89 | final Document retrievedDocument = systemUsers.find().iterator().next(); 90 | 91 | return retrievedDocument.get("_id", UUID.class); 92 | } 93 | 94 | 95 | 96 | 97 | 98 | } -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/impl/geo/GeoUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.geo; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.mongodb.DBObject; 6 | import com.mongodb.util.JSON; 7 | import com.vividsolutions.jts.geom.Coordinate; 8 | import org.geojson.*; 9 | import org.junit.Test; 10 | 11 | import java.util.List; 12 | 13 | import static java.util.Arrays.asList; 14 | import static org.assertj.core.util.Arrays.array; 15 | import static org.hamcrest.CoreMatchers.instanceOf; 16 | import static org.junit.Assert.assertArrayEquals; 17 | import static org.junit.Assert.assertThat; 18 | 19 | public class GeoUtilTest { 20 | 21 | @Test 22 | public void testGeoJsonPointsCanBeConvertedToJTSGeometry() throws Exception { 23 | final Point geoJson = new Point(pos(-1, -1)); 24 | final DBObject dbObject = toDBObject(geoJson); 25 | 26 | final com.vividsolutions.jts.geom.Geometry geometry = GeoUtil.toGeometry(dbObject); 27 | 28 | assertThat(geometry, instanceOf(com.vividsolutions.jts.geom.Point.class)); 29 | assertArrayEquals(array(coord(-1, -1)), geometry.getCoordinates()); 30 | } 31 | 32 | @Test 33 | public void testGeoJsonMultiPointsCanBeConvertedToJTSGeometry() throws Exception { 34 | final MultiPoint geoJson = new MultiPoint(pos(-1, -1), pos(1, 1)); 35 | final DBObject dbObject = toDBObject(geoJson); 36 | 37 | final com.vividsolutions.jts.geom.Geometry geometry = GeoUtil.toGeometry(dbObject); 38 | 39 | assertThat(geometry, instanceOf(com.vividsolutions.jts.geom.MultiPoint.class)); 40 | assertArrayEquals(array(coord(-1, -1), coord(1, 1)), geometry.getCoordinates()); 41 | } 42 | 43 | @Test 44 | public void testGeoJsonPolygonsCanBeConvertedToJTSGeometry() throws Exception { 45 | final Polygon geoJson = new Polygon(createRing(pos(-1, -1), pos(1, -1), pos(0, 1), pos(-1, -1))); 46 | final DBObject dbObject = toDBObject(geoJson); 47 | 48 | final com.vividsolutions.jts.geom.Geometry geometry = GeoUtil.toGeometry(dbObject); 49 | 50 | assertThat(geometry, instanceOf(com.vividsolutions.jts.geom.Polygon.class)); 51 | assertArrayEquals(array(coord(-1, -1), coord(-1, 1), coord(1, 0), coord(-1, -1)), geometry.getCoordinates()); 52 | } 53 | 54 | @Test 55 | public void testGeoJsonMultiPolygonsCanBeConvertedToJTSGeometry() throws Exception { 56 | final MultiPolygon geoJson = new MultiPolygon(new Polygon(createRing(pos(-1, -1), pos(1, -1), pos(0, 1), pos(-1, -1)))); 57 | final DBObject dbObject = toDBObject(geoJson); 58 | 59 | final com.vividsolutions.jts.geom.Geometry geometry = GeoUtil.toGeometry(dbObject); 60 | 61 | assertThat(geometry, instanceOf(com.vividsolutions.jts.geom.MultiPolygon.class)); 62 | assertArrayEquals(array(coord(-1, -1), coord(-1, 1), coord(1, 0), coord(-1, -1)), geometry.getCoordinates()); 63 | } 64 | 65 | private Coordinate coord(int x, int y) { 66 | return new Coordinate(x, y); 67 | } 68 | 69 | private LngLatAlt pos(int longitude, int latitude) { 70 | return new LngLatAlt(longitude, latitude); 71 | } 72 | 73 | private List createRing(LngLatAlt... coordinates) { 74 | return asList(coordinates); 75 | } 76 | 77 | private DBObject toDBObject(GeoJsonObject geoJsonGeometry) throws JsonProcessingException { 78 | return (DBObject) JSON.parse(new ObjectMapper().writeValueAsString(geoJsonGeometry)); 79 | } 80 | } -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/SpringMongoOperationTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.integration; 2 | 3 | import com.github.fakemongo.junit.FongoRule; 4 | import com.google.common.collect.Sets; 5 | import com.mongodb.DBCollection; 6 | import com.mongodb.Mongo; 7 | import com.mongodb.WriteResult; 8 | import java.util.Collections; 9 | import java.util.Date; 10 | import java.util.Set; 11 | import java.util.UUID; 12 | import org.assertj.core.api.Assertions; 13 | import static org.junit.Assert.assertEquals; 14 | import org.junit.Before; 15 | import org.junit.Rule; 16 | import org.junit.Test; 17 | import org.springframework.data.mongodb.core.IndexOperations; 18 | import org.springframework.data.mongodb.core.MongoOperations; 19 | import org.springframework.data.mongodb.core.MongoTemplate; 20 | import org.springframework.data.mongodb.core.SimpleMongoDbFactory; 21 | import org.springframework.data.mongodb.core.index.IndexInfo; 22 | import static org.springframework.data.mongodb.core.query.Criteria.where; 23 | import static org.springframework.data.mongodb.core.query.Query.query; 24 | import org.springframework.data.mongodb.core.query.Update; 25 | 26 | /** 27 | * User: william 28 | * Date: 15/03/14 29 | */ 30 | public class SpringMongoOperationTest { 31 | 32 | @Rule 33 | public FongoRule fongoRule = new FongoRule(false); 34 | 35 | private MongoOperations mongoOperations; 36 | 37 | @Before 38 | public void before() throws Exception { 39 | Mongo mongo = fongoRule.getMongo(); 40 | //Mongo mongo = new MongoClient(); 41 | mongoOperations = new MongoTemplate(new SimpleMongoDbFactory(mongo, UUID.randomUUID().toString())); 42 | } 43 | 44 | @Test 45 | public void insertAndIndexesTest() { 46 | Item item = new Item(UUID.randomUUID(), "name", new Date()); 47 | mongoOperations.insert(item); 48 | 49 | DBCollection collection = mongoOperations.getCollection(Item.COLLECTION_NAME); 50 | assertEquals(1, collection.count()); 51 | 52 | IndexOperations indexOperations = mongoOperations.indexOps(Item.COLLECTION_NAME); 53 | System.out.println(indexOperations.getIndexInfo()); 54 | boolean indexedId = false; 55 | boolean indexedName = false; 56 | for (IndexInfo indexInfo : indexOperations.getIndexInfo()) { 57 | if (indexInfo.isIndexForFields(Collections.singletonList("_id"))) { 58 | indexedId = true; 59 | } 60 | if (indexInfo.isIndexForFields(Collections.singletonList("name"))) { 61 | indexedName = true; 62 | } 63 | } 64 | Assertions.assertThat(indexedId).as("_id field is not indexedId").isTrue(); 65 | Assertions.assertThat(indexedName).as("name field is not indexedId").isTrue(); 66 | } 67 | 68 | 69 | static class A { 70 | public A(String id, Set bs) { 71 | this.id = id; 72 | this.bs = bs; 73 | } 74 | 75 | String id; 76 | Set bs; 77 | } 78 | 79 | static class B { 80 | public B(String reference, Set ids) { 81 | this.reference = reference; 82 | this.ids = ids; 83 | } 84 | 85 | String reference; 86 | Set ids; 87 | } 88 | 89 | @Test 90 | public void test_ids() { 91 | // Given 92 | B b = new B("ref", Sets.newHashSet("1", "2")); 93 | B b2 = new B("ref2", null); 94 | A a = new A("id", Sets.newHashSet(b, b2)); 95 | mongoOperations.insert(a); 96 | String reference = "ref"; 97 | String idToPull = null; 98 | 99 | // When 100 | final WriteResult writeResult = mongoOperations.updateFirst(query(where("_id").is(a.id).and("bs").elemMatch(where("reference").is(reference))), new Update().pull("bs.$.ids", idToPull), A.class); 101 | 102 | // Then 103 | Assertions.assertThat(writeResult.getN()).isEqualTo(1); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/jongo/Friend.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Benoit GUEROUT and Yves AMSELLEM 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fakemongo.integration.jongo; 18 | 19 | import org.bson.types.ObjectId; 20 | import org.jongo.marshall.jackson.oid.Id; 21 | import org.jongo.marshall.jackson.oid.MongoId; 22 | 23 | public class Friend { 24 | 25 | @Id 26 | @MongoId //see NewAnnotationsCompatibilitySuiteTest for more informations 27 | private ObjectId id; 28 | private String name; 29 | private String address; 30 | private Coordinate coordinate; 31 | private Gender gender; 32 | 33 | public Friend(String name) { 34 | this.name = name; 35 | } 36 | 37 | public Friend(ObjectId id, String name) { 38 | this.id = id; 39 | this.name = name; 40 | } 41 | 42 | public Friend(String name, String address) { 43 | this.name = name; 44 | this.address = address; 45 | } 46 | 47 | public Friend(String name, Coordinate coordinate) { 48 | this.name = name; 49 | this.coordinate = coordinate; 50 | } 51 | 52 | public Friend() { 53 | } 54 | 55 | public Friend(String name, String address, Coordinate coordinate) { 56 | this.name = name; 57 | this.address = address; 58 | this.coordinate = coordinate; 59 | } 60 | 61 | public ObjectId getId() { 62 | return id; 63 | } 64 | 65 | public String getName() { 66 | return name; 67 | } 68 | 69 | public String getAddress() { 70 | return address; 71 | } 72 | 73 | public void setAddress(String address) { 74 | this.address = address; 75 | } 76 | 77 | public Coordinate getCoordinate() { 78 | return coordinate; 79 | } 80 | 81 | public void setCoordinate(Coordinate coordinate) { 82 | this.coordinate = coordinate; 83 | } 84 | 85 | @Override 86 | public boolean equals(Object o) { 87 | if (this == o) return true; 88 | if (!(o instanceof Friend)) return false; 89 | 90 | Friend friend = (Friend) o; 91 | 92 | if (address != null ? !address.equals(friend.address) : friend.address != null) return false; 93 | if (coordinate != null ? !coordinate.equals(friend.coordinate) : friend.coordinate != null) return false; 94 | if (id != null ? !id.equals(friend.id) : friend.id != null) return false; 95 | if (name != null ? !name.equals(friend.name) : friend.name != null) return false; 96 | 97 | return true; 98 | } 99 | 100 | @Override 101 | public int hashCode() { 102 | int result = id != null ? id.hashCode() : 0; 103 | result = 31 * result + (name != null ? name.hashCode() : 0); 104 | result = 31 * result + (address != null ? address.hashCode() : 0); 105 | result = 31 * result + (coordinate != null ? coordinate.hashCode() : 0); 106 | return result; 107 | } 108 | 109 | public void setGender(Gender gender) { 110 | this.gender = gender; 111 | } 112 | 113 | public Gender getGender() { 114 | return gender; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/jongo/JongoItem.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.integration.jongo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import org.jongo.marshall.jackson.oid.Id; 5 | 6 | public class JongoItem { 7 | 8 | @Id 9 | private JongoItemId id; 10 | 11 | private String field; 12 | 13 | public JongoItem() { 14 | } 15 | 16 | public JongoItemId getId() { 17 | return this.id; 18 | } 19 | 20 | public String getField() { 21 | return this.field; 22 | } 23 | 24 | public void setId(JongoItemId id) { 25 | this.id = id; 26 | } 27 | 28 | public void setField(String field) { 29 | this.field = field; 30 | } 31 | 32 | public boolean equals(Object o) { 33 | if (o == this) return true; 34 | if (!(o instanceof JongoItem)) return false; 35 | final JongoItem other = (JongoItem) o; 36 | if (!other.canEqual((Object) this)) return false; 37 | final Object this$id = this.id; 38 | final Object other$id = other.id; 39 | if (this$id == null ? other$id != null : !this$id.equals(other$id)) return false; 40 | final Object this$field = this.field; 41 | final Object other$field = other.field; 42 | if (this$field == null ? other$field != null : !this$field.equals(other$field)) return false; 43 | return true; 44 | } 45 | 46 | public int hashCode() { 47 | final int PRIME = 59; 48 | int result = 1; 49 | final Object $id = this.id; 50 | result = result * PRIME + ($id == null ? 0 : $id.hashCode()); 51 | final Object $field = this.field; 52 | result = result * PRIME + ($field == null ? 0 : $field.hashCode()); 53 | return result; 54 | } 55 | 56 | protected boolean canEqual(Object other) { 57 | return other instanceof JongoItem; 58 | } 59 | 60 | public String toString() { 61 | return "com.github.fakemongo.integration.jongo.JongoItem(id=" + this.id + ", field=" + this.field + ")"; 62 | } 63 | 64 | public static class JongoItemId { 65 | private String name, id; 66 | 67 | @java.beans.ConstructorProperties({"name", "id"}) 68 | public JongoItemId(@JsonProperty("name") String name, @JsonProperty("id") String id) { 69 | this.name = name; 70 | this.id = id; 71 | } 72 | 73 | public String getName() { 74 | return this.name; 75 | } 76 | 77 | public String getId() { 78 | return this.id; 79 | } 80 | 81 | public void setName(String name) { 82 | this.name = name; 83 | } 84 | 85 | public void setId(String id) { 86 | this.id = id; 87 | } 88 | 89 | public boolean equals(Object o) { 90 | if (o == this) return true; 91 | if (!(o instanceof JongoItemId)) return false; 92 | final JongoItemId other = (JongoItemId) o; 93 | if (!other.canEqual((Object) this)) return false; 94 | final Object this$name = this.name; 95 | final Object other$name = other.name; 96 | if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false; 97 | final Object this$id = this.id; 98 | final Object other$id = other.id; 99 | if (this$id == null ? other$id != null : !this$id.equals(other$id)) return false; 100 | return true; 101 | } 102 | 103 | public int hashCode() { 104 | final int PRIME = 59; 105 | int result = 1; 106 | final Object $name = this.name; 107 | result = result * PRIME + ($name == null ? 0 : $name.hashCode()); 108 | final Object $id = this.id; 109 | result = result * PRIME + ($id == null ? 0 : $id.hashCode()); 110 | return result; 111 | } 112 | 113 | protected boolean canEqual(Object other) { 114 | return other instanceof JongoItemId; 115 | } 116 | 117 | public String toString() { 118 | return "com.github.fakemongo.integration.jongo.JongoItem.JongoItemId(name=" + this.name + ", id=" + this.id + ")"; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 1.4.7 2 | ----- 3 | release : 03 may 2014 4 | 5 | - mongo-java-driver 2.12.0 6 | 7 | 1.3.3 8 | ----- 9 | release : 17 December 2013 10 | 11 | - fix Spring @GeoSpatialIndexed for double[] by @drei01 12 | - better compatibility with jongo, handle 'Binary' 13 | 14 | 1.3.2 15 | ----- 16 | release : 13 November 2013 17 | 18 | - fix NPE in log 19 | 20 | 1.3.0 21 | ----- 22 | release: 02 November 2013 23 | 24 | Apologees, it's been a long time since a release. I'm no longer using fongo at Foursquare, so I'll be looking to hand 25 | it over to an enthusiastic maintainer in the near future. 26 | 27 | All changes below by @twillouer: 28 | 29 | - full real indexes!! 30 | - better object comparisons 31 | - aggregation framework! 32 | - map reduce! 33 | - geospatial indexes! 34 | - findAndModify as a command 35 | - $elemMatch support in finds 36 | 37 | 38 | Other fixes: 39 | - fix for multiple projections with the same prefix (@jcodagnone) 40 | - fix for findAndRemove where nothing is found (@hoffrocket) 41 | 42 | 1.2.0 43 | ----- 44 | release: 26 July 2013 45 | 46 | Bumped minor version due to potentially breaking changes 47 | 48 | - use overriden MongoClient instance instead of Mockito (@hoffrocket) 49 | - default write concern is now WriteConcern.ACKNOWLEDGED 50 | - fix for skip on count (@twillouer) 51 | - skip respects filter (@twillouer) 52 | - fix for Positional update of inner DBObject field (Anton Bobukh) 53 | 54 | 1.1.1 55 | ----- 56 | release: 12 July 2013 57 | 58 | - fix for regex options parsing (@hoffrocket) 59 | - BSON encoding hooks (@serj-de-sudden) 60 | - Result projections on find queries (@phjardas) 61 | - defensive copy of find result DBObjects (@hoffrocket) 62 | 63 | 1.1.0 64 | ----- 65 | release: 10 June 2013 66 | 67 | - setting WriteConcern on mongo instance propagates to collection (@hoffrocket) 68 | - upgraded mongo-java-driver to 2.11.1 (@serj-de-sudden) 69 | - fix for findAndModify to return original object before update with new: false (@phjardas) 70 | 71 | 1.0.9 72 | ----- 73 | release: 24 May 2013 74 | 75 | - fix for DBRef field.$id queries (@hoffrocket) 76 | 77 | 1.0.8 78 | ----- 79 | release: 15 May 2013 80 | 81 | - fix for inserted oid's marked as new (@hoffrocket) 82 | - fix for handling of {field: null} filters (@bgranvea) 83 | 84 | 1.0.7 85 | ----- 86 | release: 17 April 2013 87 | 88 | - fix for CME while iterating getCollectionNames and dropping each collection (@hoffrocket) 89 | - support for "count" via command interface (@hoffrocket) 90 | - fix for unidentified command result (@hoffrocket) 91 | 92 | 1.0.6 93 | ----- 94 | release: 25 March 2013 95 | 96 | - fix for ConcurrentModificationException in dropDatabase (@rlindsjo) 97 | - support for top level $and queries (@dm3) 98 | - support for Arrays with $in queries (@grahamar) 99 | - support for regex in $all queries (@dm3) 100 | 101 | 1.0.5 102 | ----- 103 | release: 6 March 2013 104 | 105 | - fixed override for getCount (@philnate) 106 | - included updateExisting in update WriteResult 107 | 108 | 1.0.4 109 | ----- 110 | release: 30 January 2013 111 | 112 | - bumped mongo-java-driver to 2.10.1 113 | - proper distinct handling (@tobyclemson) 114 | - fix update _id handling (@tobyclemson) 115 | - allow Map in query object (@tobyclemson) 116 | - fix for createCollection (@dagi) 117 | - fix for $or operator bug 118 | - fix for remove query with List value bug 119 | 120 | 1.0.3 121 | ----- 122 | release: 29 November 2012 123 | 124 | - properly pass updated count in WriteResult (@palmerabollo) 125 | - replace null _id in DBObject (@aakhmerov) 126 | - drop commands clear out children (@efranceschi) 127 | 128 | 1.0.2 129 | ----- 130 | release: 09 November 2012 131 | 132 | - bumped mongo-java-driver to 2.9.3 133 | 134 | 1.0.0 135 | ----- 136 | release: 13 June 2012 137 | 138 | - Initial Release. All basic query and update syntax supported 139 | - mongo-java-driver 2.7.2 140 | -------------------------------------------------------------------------------- /src/main/java/com/mongodb/FongoBulkWriteCombiner.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.List; 6 | import java.util.Set; 7 | import java.util.TreeSet; 8 | import org.bson.BsonDocument; 9 | 10 | public class FongoBulkWriteCombiner { 11 | 12 | private final WriteConcern writeConcern; 13 | 14 | private int insertedCount = 0; 15 | private int matchedCount = 0; 16 | private int removedCount = 0; 17 | private int modifiedCount = 0; 18 | 19 | private final Set upserts = new TreeSet(new Comparator() { 20 | @Override 21 | public int compare(final com.mongodb.BulkWriteUpsert o1, final com.mongodb.BulkWriteUpsert o2) { 22 | return (o1.getIndex() < o2.getIndex()) ? -1 : ((o1.getIndex() == o2.getIndex()) ? 0 : 1); 23 | } 24 | }); 25 | private final TreeSet errors = new TreeSet(new Comparator() { 26 | @Override 27 | public int compare(final WriteError o1, final WriteError o2) { 28 | return (o1.getIndex() < o2.getIndex()) ? -1 : ((o1.getIndex() == o2.getIndex()) ? 0 : 1); 29 | } 30 | }); 31 | 32 | public FongoBulkWriteCombiner(WriteConcern writeConcern) { 33 | this.writeConcern = writeConcern; 34 | } 35 | 36 | public void addReplaceResult(int idx, WriteResult wr) { 37 | // Same logic applies 38 | addUpdateResult(idx, wr); 39 | } 40 | 41 | public void addUpdateResult(int idx, WriteResult wr) { 42 | if (wr.isUpdateOfExisting()) { 43 | matchedCount += wr.getN(); 44 | modifiedCount += wr.getN(); 45 | } else { 46 | if (wr.getUpsertedId() != null) { 47 | upserts.add(new BulkWriteUpsert(idx, wr.getUpsertedId())); 48 | // insertedCount++; 49 | } 50 | } 51 | } 52 | 53 | public void addRemoveResult(WriteResult wr) { 54 | matchedCount += wr.getN(); 55 | removedCount += wr.getN(); 56 | } 57 | 58 | public void addInsertResult(WriteResult wr) { 59 | insertedCount += wr.getN(); 60 | } 61 | 62 | public void addInsertError(int idx, WriteConcernException exception) { 63 | errors.add(new WriteError(idx, exception)); 64 | } 65 | 66 | public BulkWriteResult getBulkWriteResult(WriteConcern writeConcern) { 67 | if (!writeConcern.isAcknowledged()) { 68 | return new UnacknowledgedBulkWriteResult(); 69 | } 70 | return new AcknowledgedBulkWriteResult(insertedCount, matchedCount, removedCount, modifiedCount, new ArrayList(upserts)); 71 | } 72 | 73 | public void throwOnError(ServerAddress address) { 74 | if (!errors.isEmpty()) { 75 | List bwErrors = new ArrayList(); 76 | for (WriteError e: errors) { 77 | bwErrors.add(new BulkWriteError(e.code, e.message, FongoDBCollection.dbObject(e.details), e.index)); 78 | } 79 | BulkWriteResult bulkWriteResult = getBulkWriteResult(writeConcern); 80 | throw new BulkWriteException(bulkWriteResult, bwErrors, null, address); 81 | } 82 | } 83 | 84 | public static class WriteError { 85 | 86 | private final int index; 87 | private final int code; 88 | private final String message; 89 | private final BsonDocument details; 90 | private final WriteConcernException exception; 91 | 92 | public WriteError(int index, WriteConcernException exception) { 93 | this.index = index; 94 | this.exception = exception; 95 | this.code = exception.getErrorCode(); 96 | this.message = exception.getErrorMessage(); 97 | this.details = exception.getResponse(); 98 | } 99 | 100 | public int getIndex() { 101 | return index; 102 | } 103 | 104 | public int getCode() { 105 | return code; 106 | } 107 | 108 | public String getMessage() { 109 | return message; 110 | } 111 | 112 | public BsonDocument getDetails() { 113 | return details; 114 | } 115 | 116 | public WriteConcernException getException() { 117 | return exception; 118 | } 119 | 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/mongodb/FongoMongoCollection.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | 3 | import com.github.fakemongo.Fongo; 4 | import com.mongodb.client.MongoCollection; 5 | import com.mongodb.client.model.CountOptions; 6 | import com.mongodb.client.model.IndexModel; 7 | import com.mongodb.util.FongoJSON; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import org.bson.Document; 11 | import org.bson.codecs.configuration.CodecRegistry; 12 | import org.bson.conversions.Bson; 13 | 14 | /** 15 | * 16 | */ 17 | public class FongoMongoCollection extends MongoCollectionImpl { 18 | 19 | private final Fongo fongo; 20 | 21 | private final DBCollection dbCollection; 22 | 23 | FongoMongoCollection(Fongo fongo, MongoNamespace namespace, Class tDocumentClass, CodecRegistry codecRegistry, ReadPreference readPreference, WriteConcern writeConcern, ReadConcern readConcern) { 24 | super(namespace, tDocumentClass, codecRegistry, readPreference, writeConcern, readConcern, fongo); 25 | this.fongo = fongo; 26 | this.dbCollection = fongo.getDB(namespace.getDatabaseName()).getCollection(namespace.getCollectionName()); 27 | } 28 | 29 | @Override 30 | public MongoCollection withDocumentClass(Class clazz) { 31 | return new FongoMongoCollection(this.fongo, super.getNamespace(), clazz, super.getCodecRegistry(), super.getReadPreference(), super.getWriteConcern(), super.getReadConcern()); 32 | } 33 | 34 | @Override 35 | public MongoCollection withCodecRegistry(CodecRegistry codecRegistry) { 36 | return new FongoMongoCollection(this.fongo, super.getNamespace(), super.getDocumentClass(), codecRegistry, super.getReadPreference(), super.getWriteConcern(), super.getReadConcern()); 37 | } 38 | 39 | @Override 40 | public MongoCollection withReadPreference(ReadPreference readPreference) { 41 | return new FongoMongoCollection(this.fongo, super.getNamespace(), super.getDocumentClass(), super.getCodecRegistry(), readPreference, super.getWriteConcern(), super.getReadConcern()); 42 | } 43 | 44 | @Override 45 | public MongoCollection withWriteConcern(WriteConcern writeConcern) { 46 | return new FongoMongoCollection(this.fongo, super.getNamespace(), super.getDocumentClass(), super.getCodecRegistry(), super.getReadPreference(), writeConcern, super.getReadConcern()); 47 | } 48 | 49 | @Override 50 | public MongoCollection withReadConcern(ReadConcern readConcern) { 51 | return new FongoMongoCollection(this.fongo, super.getNamespace(), super.getDocumentClass(), super.getCodecRegistry(), super.getReadPreference(), super.getWriteConcern(), readConcern); 52 | } 53 | 54 | @Override 55 | public long count(Bson filter, CountOptions options) { 56 | final DBObject query = dbObject(filter); 57 | final int limit = options.getLimit(); 58 | final int skip = options.getSkip(); 59 | 60 | return dbCollection.getCount(query, null, limit, skip); 61 | } 62 | 63 | // 64 | // @Override 65 | // public DistinctIterable distinct(String fieldName, Class tResultClass) { 66 | // return new DistinctIterableImpl(getNamespace(), getDocumentClass(), tResultClass, getCodecRegistry(), getReadPreference(), fongo, fieldName){ 67 | // @Override 68 | // public MongoCursor iterator() { 69 | // return super.iterator(); 70 | // } 71 | // }; 72 | // } 73 | 74 | 75 | @Override 76 | public List createIndexes(List indexes) { 77 | ArrayList names = new ArrayList(indexes.size()); 78 | for (IndexModel indexModel : indexes) { 79 | this.dbCollection.createIndex(dbObject(indexModel.getKeys()), indexModel.getOptions().getName(), indexModel.getOptions().isUnique()); 80 | names.add(indexModel.getOptions().getName()); 81 | } 82 | // return super.createIndexes(indexes); 83 | return names; 84 | } 85 | 86 | private DBObject dbObject(Bson bson) { 87 | if (bson == null) { 88 | return null; 89 | } 90 | // TODO Performance killer 91 | return (DBObject) FongoJSON.parse(bson.toBsonDocument(Document.class, super.getCodecRegistry()).toString()); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/index/GeoIndex.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.index; 2 | 3 | import com.github.fakemongo.impl.Filter; 4 | import com.github.fakemongo.impl.Util; 5 | import com.github.fakemongo.impl.geo.GeoUtil; 6 | import com.mongodb.BasicDBObject; 7 | import com.mongodb.DBObject; 8 | import com.mongodb.FongoDBCollection; 9 | import com.vividsolutions.jts.geom.Geometry; 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.Collections; 13 | import java.util.Comparator; 14 | import java.util.LinkedHashMap; 15 | import java.util.LinkedHashSet; 16 | import java.util.List; 17 | import java.util.Map; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | /** 22 | * An index for the MongoDB. 23 | *

24 | * TODO : more $geometry. 25 | */ 26 | public class GeoIndex extends IndexAbstract { 27 | private static final Logger LOG = LoggerFactory.getLogger(GeoIndex.class); 28 | 29 | GeoIndex(String name, DBObject keys, boolean unique, String geoIndex, boolean sparse) { 30 | super(name, keys, unique, new LinkedHashMap>(), geoIndex, sparse); 31 | //TreeMap>(new GeoUtil.GeoComparator(geoIndex)), geoIndex); 32 | } 33 | 34 | /** 35 | * Create the key for the hashmap. 36 | */ 37 | @Override 38 | protected GeoUtil.GeoDBObject getKeyFor(DBObject object) { 39 | return new GeoUtil.GeoDBObject(super.getKeyFor(object), geoIndex); 40 | } 41 | 42 | @Override 43 | public GeoUtil.GeoDBObject embedded(DBObject object) { 44 | return new GeoUtil.GeoDBObject(object, geoIndex); // Important : do not clone, indexes share objects between them. 45 | } 46 | 47 | public List geoNear(DBObject query, Geometry geometry, int limit, boolean spherical) { 48 | lookupCount++; 49 | 50 | LOG.info("geoNear() query:{}, geometry:{}, limit:{}, spherical:{} (mapValues size:{})", query, geometry, limit, spherical, mapValues.size()); 51 | // Filter values 52 | Filter filterValue = expressionParser.buildFilter(query); 53 | 54 | // Preserve order and remove duplicates. 55 | LinkedHashSet resultSet = new LinkedHashSet(); 56 | geoNearCoverAll(mapValues, filterValue, geometry, spherical, resultSet); 57 | 58 | return sortAndLimit(resultSet, limit); 59 | } 60 | 61 | /** 62 | * Try all the map, without trying to filter by geohash. 63 | */ 64 | private void geoNearCoverAll(Map> values, Filter filterValue, Geometry near, boolean spherical, LinkedHashSet resultSet) { 65 | for (Map.Entry> entry : values.entrySet()) { 66 | geoNearResults(entry.getValue().getElements(), filterValue, near, resultSet, spherical); 67 | } 68 | } 69 | 70 | /** 71 | * Sort the results and limit them. 72 | */ 73 | private List sortAndLimit(Collection resultSet, int limit) { 74 | List result = new ArrayList(resultSet); 75 | // Sort values by distance. 76 | Collections.sort(result, new Comparator() { 77 | @Override 78 | public int compare(DBObject o1, DBObject o2) { 79 | return ((Double) o1.get("dis")).compareTo((Double) o2.get("dis")); 80 | } 81 | }); 82 | // Applying limit 83 | return result.subList(0, Math.min(result.size(), limit)); 84 | } 85 | 86 | 87 | // Now transform to {dis:, obj:} 88 | private void geoNearResults(List values, Filter filterValue, Geometry near, Collection result, boolean spherical) { 89 | for (GeoUtil.GeoDBObject geoDBObject : values) { 90 | // Test against the query filter. 91 | if (geoDBObject.getGeometry() != null && filterValue.apply(geoDBObject)) { 92 | double radians = GeoUtil.distanceInRadians(geoDBObject.getGeometry(), near, spherical); 93 | geoDBObject.removeField(FongoDBCollection.FONGO_SPECIAL_ORDER_BY); 94 | result.add(new BasicDBObject("dis", radians).append("obj", Util.clone(geoDBObject))); 95 | } 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/com/mongodb/FongoDBTest.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | 3 | import com.github.fakemongo.Fongo; 4 | import com.github.fakemongo.junit.FongoRule; 5 | import com.mongodb.connection.ServerVersion; 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | import org.junit.Assert; 8 | import org.junit.Before; 9 | import org.junit.Ignore; 10 | import org.junit.Rule; 11 | import org.junit.Test; 12 | import org.springframework.util.StringUtils; 13 | 14 | /** 15 | * @author Anton Bobukh 16 | */ 17 | public class FongoDBTest { 18 | 19 | private final ReadPreference preference = ReadPreference.nearest(); 20 | 21 | @Rule 22 | public FongoRule fongoRule = new FongoRule(!true, Fongo.V3_SERVER_VERSION); 23 | private DB db; 24 | 25 | @Before 26 | public void setUp() { 27 | db = fongoRule.getDB(); 28 | } 29 | 30 | @Test 31 | public void commandGetLastErrorAliases() { 32 | BasicDBObject command; 33 | 34 | command = new BasicDBObject("getlasterror", 1); 35 | Assert.assertTrue(db.command(command, preference).containsField("ok")); 36 | 37 | command = new BasicDBObject("getLastError", 1); 38 | Assert.assertTrue(db.command(command, preference).containsField("ok")); 39 | } 40 | 41 | @Test 42 | public void commandFindAndModifyAliases() { 43 | BasicDBObject command = new BasicDBObject("findandmodify", "test").append("remove", true); 44 | CommandResult commandResult = db.command(command, preference); 45 | assertThat(commandResult.toMap()).containsEntry("ok", 1.0).containsKey("value"); 46 | 47 | command = new BasicDBObject("findAndModify", "test").append("remove", true); 48 | commandResult = db.command(command, preference); 49 | assertThat(commandResult.toMap()).containsEntry("ok", 1.0).containsKey("value"); 50 | } 51 | 52 | @Test 53 | public void commandFindAndModifyNeedRemoveOrUpdate() { 54 | BasicDBObject command = new BasicDBObject("findandmodify", "test"); 55 | CommandResult commandResult = db.command(command, preference); 56 | assertThat(commandResult.toMap()).containsEntry("ok", .0).containsEntry("errmsg", "need remove or update"); 57 | } 58 | 59 | @Test 60 | public void commandBuildInfoAliases() { 61 | BasicDBObject command; 62 | 63 | command = new BasicDBObject("buildinfo", 1); 64 | Assert.assertTrue(db.command(command, preference).containsField("version")); 65 | 66 | command = new BasicDBObject("buildInfo", 1); 67 | Assert.assertTrue(db.command(command, preference).containsField("version")); 68 | } 69 | 70 | @Test 71 | public void commandBuildInfoPullsDefaultMongoVersionFromFongo() throws Exception { 72 | CommandResult commandResult = db.command("buildInfo"); 73 | String expectedVersionAsString = StringUtils.collectionToDelimitedString(fongoRule.getServerVersion().getVersionList(), "."); 74 | assertThat(commandResult.getString("version")).isEqualTo(expectedVersionAsString); 75 | } 76 | 77 | @Test 78 | public void commandBuildInfoPullsChangedMongoVersionFromFongo() throws Exception { 79 | db = new Fongo("testfongo", new ServerVersion(3, 1)).getDB("testdb"); 80 | CommandResult commandResult = db.command("buildInfo"); 81 | assertThat(commandResult.getString("version")).isEqualTo("3.1.0"); 82 | } 83 | 84 | @Test 85 | @Ignore("not sure to understant the test") 86 | public void commandMapReduceAliases() { 87 | BasicDBObject command; 88 | 89 | command = new BasicDBObject("mapreduce", "test").append("out", new BasicDBObject("inline", 1)); 90 | CommandResult commandResult = db.command(command, preference); 91 | assertThat(commandResult.toMap()).containsKey("results"); 92 | assertThat(db.command(command, preference).toMap()).containsKey("results"); 93 | 94 | command = new BasicDBObject("mapReduce", "test").append("out", new BasicDBObject("inline", 1)); 95 | commandResult = db.command(command, preference); 96 | assertThat(commandResult.toMap()).containsKey("results"); 97 | } 98 | 99 | @Test 100 | public void commandStats() throws Exception { 101 | db = new Fongo("testfongo", new ServerVersion(3, 1)).getDB("testdb"); 102 | CommandResult commandResult = db.getStats(); 103 | assertThat(commandResult.isEmpty()).isTrue(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/aggregation/Lookup.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.aggregation; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | import org.junit.Assert; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import com.github.fakemongo.impl.ExpressionParser; 14 | import com.mongodb.DB; 15 | import com.mongodb.DBCollection; 16 | import com.mongodb.DBCursor; 17 | import com.mongodb.DBObject; 18 | 19 | /** 20 | * @author Kollivakkam Raghavan 21 | * @created 4/22/2016 22 | */ 23 | public class Lookup extends PipelineKeyword { 24 | 25 | private static final Logger LOG = LoggerFactory.getLogger(Lookup.class); 26 | 27 | public static final Lookup INSTANCE = new Lookup(); 28 | public static final String ID = "_id"; 29 | 30 | @Override 31 | public DBCollection apply(DB originalDB, DBCollection parentColl, DBObject object) { 32 | DBObject lookup = ExpressionParser.toDbObject(object.get(getKeyword())); 33 | List parentItems = performLookup(originalDB, parentColl, lookup); 34 | return dropAndInsert(parentColl, parentItems); 35 | } 36 | 37 | @Override 38 | public String getKeyword() { 39 | return "$lookup"; 40 | } 41 | 42 | private List performLookup(DB originalDB, DBCollection parentColl, DBObject lookup) { 43 | String from = (String) lookup.get("from"); 44 | String localField = (String) lookup.get("localField"); 45 | String foreignField = (String) lookup.get("foreignField"); 46 | String as = (String) lookup.get("as"); 47 | LOG.debug("Value {} will be returned from {} in parent collection {}. Local field {} will be joined with {}", 48 | as, from, parentColl.getName(), localField, foreignField); 49 | 50 | DBCursor parentItems = parentColl.find(); 51 | // can't use lambdas 52 | Iterator iterator = parentItems.iterator(); 53 | Map> parentMap = new HashMap>(); 54 | List parentsWithLocalField = new ArrayList(); 55 | List parentsWithMissingLocalField = new ArrayList(); 56 | // go through all parent items - put a list of DBObjects for the children 57 | while (iterator.hasNext()) { 58 | DBObject parentItem = iterator.next(); 59 | Object localFieldValue = parentItem.get(localField); 60 | if (localFieldValue == null || "".equals(localField.toString().trim())) { 61 | parentItem.put(as, new ArrayList()); 62 | parentsWithMissingLocalField.add(parentItem); 63 | } 64 | else { 65 | List childItems = (List) parentItem.get(as); 66 | if (childItems == null) { 67 | childItems = new ArrayList(); 68 | parentItem.put(as, childItems); 69 | } 70 | if(!parentMap.containsKey(localFieldValue)) { 71 | parentMap.put(localFieldValue, new ArrayList()); 72 | } 73 | parentMap.get(localFieldValue).add(parentItem); 74 | parentsWithLocalField.add(parentItem); 75 | } 76 | } 77 | // now loop through the children item and add them to the parent 78 | DBCollection childColl = originalDB.getCollection(from); 79 | 80 | DBCursor childItems = childColl.find(); 81 | Iterator childIterator = childItems.iterator(); 82 | while (childIterator.hasNext()) { 83 | DBObject childItem = childIterator.next(); 84 | Object parentOid = childItem.get(foreignField); 85 | if (parentOid == null) { 86 | LOG.warn("Ignoring null parent id"); 87 | } 88 | else { 89 | if(parentMap.containsKey(parentOid)) { 90 | for(DBObject parent: parentMap.get(parentOid)) { 91 | LOG.debug("Adding child with id {} to parent wth id {}", childItem.get(ID), parentOid); 92 | List childObjects = (List) parent.get(as); 93 | Assert.assertNotNull("Unexpected null value", childObjects); 94 | childObjects.add(childItem); 95 | } 96 | } else { 97 | LOG.warn("Ignoring missing parent with id {}", parentOid); 98 | } 99 | } 100 | } 101 | List retval = new ArrayList(parentsWithLocalField.size() + parentsWithMissingLocalField.size()); 102 | retval.addAll(parentsWithLocalField); 103 | retval.addAll(parentsWithMissingLocalField); 104 | return retval; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/PerfTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import ch.qos.logback.classic.Level; 4 | import com.github.fakemongo.impl.ExpressionParser; 5 | import com.mongodb.BasicDBObject; 6 | import com.mongodb.DB; 7 | import com.mongodb.DBCollection; 8 | import com.mongodb.FongoDBCollection; 9 | import com.mongodb.client.MongoCollection; 10 | import com.mongodb.client.MongoDatabase; 11 | import org.bson.Document; 12 | import org.openjdk.jmh.annotations.*; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import org.openjdk.jmh.runner.Runner; 16 | import org.openjdk.jmh.runner.RunnerException; 17 | import org.openjdk.jmh.runner.options.Options; 18 | import org.openjdk.jmh.runner.options.OptionsBuilder; 19 | 20 | @State(Scope.Benchmark) 21 | public class PerfTest { 22 | public int size = 1000; 23 | 24 | private Fongo fongo; 25 | 26 | @Setup 27 | public void prepare() { 28 | fongo = new Fongo("fongo"); 29 | } 30 | 31 | private DB createDB() { 32 | return fongo.getDB("db"); 33 | } 34 | 35 | @Benchmark 36 | public void doit() { 37 | final DB db = createDB(); 38 | final DBCollection collection = db.getCollection("coll"); 39 | 40 | for (int k = 0; k < size; k++) { 41 | collection.insert(new BasicDBObject("_id", k).append("n", new BasicDBObject("a", 1))); 42 | collection.findOne(new BasicDBObject("_id", k)); 43 | } 44 | 45 | db.dropDatabase(); 46 | } 47 | 48 | @Benchmark 49 | public void doitFindN() { 50 | final DB db = createDB(); 51 | final DBCollection collection = db.getCollection("coll"); 52 | 53 | for (int k = 0; k < size; k++) { 54 | collection.insert(new BasicDBObject("_id", k).append("n", new BasicDBObject("a", k))); 55 | collection.findOne(new BasicDBObject("a", k)); 56 | } 57 | 58 | db.dropDatabase(); 59 | } 60 | 61 | @Benchmark 62 | public void doitFindUniqueIndex() { 63 | final DB db = createDB(); 64 | final DBCollection collection = db.getCollection("coll"); 65 | 66 | collection.createIndex(new BasicDBObject("n", 1)); 67 | for (int k = 0; k < size; k++) { 68 | collection.insert(new BasicDBObject("_id", k).append("n", new BasicDBObject("a", 1))); 69 | collection.findOne(new BasicDBObject("_id", k)); 70 | } 71 | 72 | db.dropDatabase(); 73 | } 74 | 75 | @Benchmark 76 | public void doitFindNWithIndex() { 77 | final DB db = createDB(); 78 | final DBCollection collection = db.getCollection("coll"); 79 | 80 | collection.createIndex(new BasicDBObject("n", 1)); 81 | for (int k = 0; k < size; k++) { 82 | collection.insert(new BasicDBObject("_id", k).append("n", k % 100)); 83 | collection.findOne(new BasicDBObject("n.a", k % 100)); 84 | } 85 | 86 | db.dropDatabase(); 87 | } 88 | 89 | @Benchmark 90 | public void doitRemoveWithIndex() { 91 | DB db = fongo.getDB("db"); 92 | DBCollection collection = db.getCollection("coll"); 93 | collection.createIndex(new BasicDBObject("n", 1)); 94 | 95 | for (int k = 0; k < size; k++) { 96 | collection.insert(new BasicDBObject("_id", k).append("n", k % 100)); 97 | } 98 | 99 | for (int k = 0; k < size; k++) { 100 | collection.remove(new BasicDBObject("n.a", k % 100)); 101 | } 102 | 103 | db.dropDatabase(); 104 | } 105 | 106 | @Benchmark 107 | public void doitRemoveWithIndexNew() { 108 | MongoDatabase db = fongo.getDatabase("db"); 109 | MongoCollection collection = db.getCollection("coll"); 110 | 111 | collection.createIndex(new Document("n", 1)); 112 | for (int k = 0; k < size; k++) { 113 | collection.insertOne(new Document("_id", k).append("n", k % 100)); 114 | } 115 | 116 | for (int k = 0; k < size; k++) { 117 | collection.deleteOne(new Document("n.a", k % 100)); 118 | } 119 | db.drop(); 120 | } 121 | 122 | public static void main(String[] args) throws RunnerException { 123 | // Desactivate logback 124 | ch.qos.logback.classic.Logger log = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(FongoDBCollection.class); 125 | log.setLevel(Level.ERROR); 126 | log = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ExpressionParser.class); 127 | log.setLevel(Level.ERROR); 128 | 129 | Options opt = new OptionsBuilder() 130 | .include(PerfTest.class.getSimpleName()) 131 | .forks(1) 132 | .warmupIterations(10) 133 | .measurementIterations(20) 134 | .build(); 135 | 136 | new Runner(opt).run(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/FongoFindTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.github.fakemongo.impl.Util; 4 | import com.github.fakemongo.junit.FongoRule; 5 | import static com.google.common.collect.Lists.newArrayList; 6 | import com.mongodb.BasicDBList; 7 | import com.mongodb.BasicDBObject; 8 | import com.mongodb.DBCollection; 9 | import com.mongodb.DBCursor; 10 | import com.mongodb.DBObject; 11 | import com.mongodb.DBRef; 12 | import java.util.HashSet; 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | import org.junit.Rule; 15 | import org.junit.Test; 16 | 17 | 18 | public class FongoFindTest { 19 | 20 | @Rule 21 | public FongoRule fongoRule = new FongoRule(!true); 22 | 23 | @Test 24 | public void testFindByNotExactType() { 25 | // Given 26 | DBCollection collection = fongoRule.newCollection(); 27 | collection.insert(new BasicDBObject("_id", 1).append("field", 12L)); 28 | 29 | // When 30 | DBObject result = collection.findOne(new BasicDBObject("field", 12)); 31 | 32 | // Then 33 | assertThat(result).isEqualTo(new BasicDBObject("_id", 1).append("field", 12L)); 34 | } 35 | 36 | 37 | /** 38 | * Checks that specified fields in find()'s projection from a list are actually returned (and are the only returned). 39 | */ 40 | @Test 41 | public void testListFieldsProjection() { 42 | // Given 43 | DBCollection collection = fongoRule.newCollection(); 44 | BasicDBList list = new BasicDBList(); 45 | list.add(new BasicDBObject("a", "1").append("b", "2").append("c", "3")); 46 | list.add(new BasicDBObject("a", "4").append("b", "5").append("d", "6")); 47 | collection.insert(new BasicDBObject("_id", 1).append("lst", list)); 48 | 49 | // When 50 | DBCursor cursor = collection.find(new BasicDBObject(), new BasicDBObject("lst.a", 1).append("lst.b", 1)); 51 | 52 | // Then 53 | BasicDBList lst = (BasicDBList) cursor.next().get("lst"); 54 | for (Object o : lst) { 55 | DBObject item = (DBObject) o; 56 | assertThat(item.get("a")).as("'a' is expected from projection").isNotNull(); 57 | assertThat(item.get("b")).as("'b' is expected from projection").isNotNull(); 58 | assertThat(item.get("c")).as("'c' is not expected since it is not in projection").isNull(); 59 | } 60 | } 61 | 62 | @Test 63 | public void should_handle_mixed_type_in_$in() { 64 | // Given 65 | DBCollection collection = fongoRule.newCollection(); 66 | collection.insert(new BasicDBObject("_id", 1).append("other", 12L)); 67 | collection.insert(new BasicDBObject("_id", 2).append("other", 13L)); 68 | collection.insert(new BasicDBObject("_id", 3).append("other", 14L)); 69 | 70 | // When 71 | DBObject inFind = new BasicDBObject("other", new BasicDBObject("$in", Util.list(12, 13))); 72 | DBCursor cursor = collection.find(inFind); 73 | 74 | // Then 75 | assertThat(cursor.size()).isEqualTo(2); 76 | } 77 | 78 | @Test 79 | public void should_handle_$in_with_dbref() { 80 | // Given 81 | DBCollection collection = fongoRule.newCollection(); 82 | collection.insert(new BasicDBObject("_id", 1).append("lang", "de").append("platform", new DBRef("platforms", "demo"))); 83 | collection.insert(new BasicDBObject("_id", 2).append("lang", "de").append("platform", new DBRef("platforms", "demo2"))); 84 | collection.insert(new BasicDBObject("lang", "de").append("platform", new DBRef("platforms", "demo3"))); 85 | 86 | BasicDBList platforms = new BasicDBList(); 87 | platforms.add(new DBRef("platforms", "demo2")); 88 | DBObject query = new BasicDBObject("lang", "de").append("platform", new BasicDBObject("$in", platforms)); 89 | 90 | // When 91 | DBCursor cursor = collection.find(query); 92 | 93 | // Then 94 | assertThat(cursor.toArray()).isEqualTo(newArrayList(new BasicDBObject("_id", 2).append("lang", "de").append("platform", new DBRef("platforms", "demo2")))); 95 | } 96 | 97 | @Test 98 | public void should_handle_collection_in_query() { 99 | DBCollection collection = fongoRule.newCollection(); 100 | collection.insert(new BasicDBObject("_id", 1).append("lang", "de")); 101 | assertThat(collection.findOne( 102 | new BasicDBObject("lang", new BasicDBObject("$in", new HashSet(newArrayList("de"))))) 103 | ).isNotNull(); 104 | } 105 | 106 | @Test 107 | public void testCursorLength() { 108 | DBCollection collection = fongoRule.newCollection(); 109 | collection.insert(new BasicDBObject("_id", 1).append("lang", "de")); 110 | assertThat(collection.find().length()) 111 | .isEqualTo(1); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/mongodb/util/FongoJSONCallback.java: -------------------------------------------------------------------------------- 1 | package com.mongodb.util; 2 | 3 | import com.mongodb.BasicDBList; 4 | import com.mongodb.BasicDBObject; 5 | import com.mongodb.DBObject; 6 | import com.mongodb.DBRef; 7 | import java.text.ParsePosition; 8 | import java.text.SimpleDateFormat; 9 | import java.util.Date; 10 | import java.util.GregorianCalendar; 11 | import java.util.SimpleTimeZone; 12 | import java.util.UUID; 13 | import java.util.regex.Pattern; 14 | import javax.xml.bind.DatatypeConverter; 15 | import org.bson.BSON; 16 | import org.bson.BSONObject; 17 | import org.bson.BasicBSONCallback; 18 | import org.bson.BsonUndefined; 19 | import org.bson.types.BSONTimestamp; 20 | import org.bson.types.Binary; 21 | import org.bson.types.Code; 22 | import org.bson.types.CodeWScope; 23 | import org.bson.types.MaxKey; 24 | import org.bson.types.MinKey; 25 | import org.bson.types.ObjectId; 26 | 27 | /** 28 | * 29 | */ 30 | public class FongoJSONCallback extends BasicBSONCallback { 31 | 32 | @Override 33 | public BSONObject create() { 34 | return new BasicDBObject(); 35 | } 36 | 37 | @Override 38 | protected BSONObject createList() { 39 | return new BasicDBList(); 40 | } 41 | 42 | @Override 43 | public void arrayStart(final String name) { 44 | _lastArray = true; 45 | super.arrayStart(name); 46 | } 47 | 48 | @Override 49 | public void objectStart(final String name) { 50 | _lastArray = false; 51 | super.objectStart(name); 52 | } 53 | 54 | @Override 55 | public Object objectDone() { 56 | String name = curName(); 57 | Object o = super.objectDone(); 58 | if (_lastArray) { 59 | return o; 60 | } 61 | BSONObject b = (BSONObject) o; 62 | 63 | // override the object if it's a special type 64 | if (b.containsField("$oid")) { 65 | o = new ObjectId((String) b.get("$oid")); 66 | } else if (b.containsField("$date")) { 67 | if (b.get("$date") instanceof Number) { 68 | o = new Date(((Number) b.get("$date")).longValue()); 69 | } else { 70 | SimpleDateFormat format = new SimpleDateFormat(_msDateFormat); 71 | format.setCalendar(new GregorianCalendar(new SimpleTimeZone(0, "GMT"))); 72 | o = format.parse(b.get("$date").toString(), new ParsePosition(0)); 73 | 74 | if (o == null) { 75 | // try older format with no ms 76 | format = new SimpleDateFormat(_secDateFormat); 77 | format.setCalendar(new GregorianCalendar(new SimpleTimeZone(0, "GMT"))); 78 | o = format.parse(b.get("$date").toString(), new ParsePosition(0)); 79 | } 80 | } 81 | } else if (b.containsField("$regex")) { 82 | o = Pattern.compile((String) b.get("$regex"), 83 | BSON.regexFlags((String) b.get("$options"))); 84 | } else if (b.containsField("$ts")) { //Legacy timestamp format 85 | Integer ts = ((Number) b.get("$ts")).intValue(); 86 | Integer inc = ((Number) b.get("$inc")).intValue(); 87 | o = new BSONTimestamp(ts, inc); 88 | } else if (b.containsField("$timestamp")) { 89 | BSONObject tsObject = (BSONObject) b.get("$timestamp"); 90 | Integer ts = ((Number) tsObject.get("t")).intValue(); 91 | Integer inc = ((Number) tsObject.get("i")).intValue(); 92 | o = new BSONTimestamp(ts, inc); 93 | } else if (b.containsField("$code")) { 94 | if (b.containsField("$scope")) { 95 | o = new CodeWScope((String) b.get("$code"), (DBObject) b.get("$scope")); 96 | } else { 97 | o = new Code((String) b.get("$code")); 98 | } 99 | } else if (b.containsField("$ref")) { 100 | o = new DBRef((String) b.get("$ref"), b.get("$id")); 101 | } else if (b.containsField("$minKey")) { 102 | o = new MinKey(); 103 | } else if (b.containsField("$maxKey")) { 104 | o = new MaxKey(); 105 | } else if (b.containsField("$uuid")) { 106 | o = UUID.fromString((String) b.get("$uuid")); 107 | } else if (b.containsField("$binary")) { 108 | Object extracted = b.get("$type"); 109 | int type; 110 | if (extracted instanceof Integer) { 111 | type = (Integer) extracted; 112 | } else { 113 | type = Integer.valueOf(extracted.toString()); 114 | } 115 | byte[] bytes = DatatypeConverter.parseBase64Binary((String) b.get("$binary")); 116 | o = new Binary((byte) type, bytes); 117 | } else if (b.containsField("$undefined") && b.get("$undefined").equals(true)) { 118 | o = new BsonUndefined(); 119 | } else if (b.containsField("$numberLong")) { 120 | o = Long.valueOf((String) b.get("$numberLong")); 121 | } 122 | 123 | if (!isStackEmpty()) { 124 | _put(name, o); 125 | } else { 126 | o = !BSON.hasDecodeHooks() ? o : BSON.applyDecodingHooks(o); 127 | setRoot(o); 128 | } 129 | return o; 130 | } 131 | 132 | private boolean _lastArray = false; 133 | 134 | public static final String _msDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; 135 | public static final String _secDateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"; 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/FongoTextSearchTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Alexander Arutuniants . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.fakemongo; 17 | 18 | import com.github.fakemongo.impl.text.TextSearch; 19 | import com.github.fakemongo.junit.FongoRule; 20 | import com.mongodb.BasicDBList; 21 | import com.mongodb.BasicDBObject; 22 | import com.mongodb.DBCollection; 23 | import com.mongodb.DBObject; 24 | import com.mongodb.util.FongoJSON; 25 | import java.util.List; 26 | import org.assertj.core.api.Assertions; 27 | import static org.junit.Assert.assertEquals; 28 | import org.junit.Before; 29 | import org.junit.Rule; 30 | import org.junit.Test; 31 | 32 | /** 33 | * @author Alexander Arutuniants 34 | */ 35 | public class FongoTextSearchTest { 36 | 37 | private DBCollection collection; 38 | private TextSearch ts; 39 | 40 | @Rule 41 | public FongoRule fongoRule = new FongoRule(!true); 42 | 43 | @Before 44 | public void setUp() { 45 | collection = fongoRule.newCollection(); 46 | collection.insert((DBObject) FongoJSON.parse("{ _id:1, textField: \"aaa bbb\", otherField: \"text1 aaa\" }")); 47 | collection.insert((DBObject) FongoJSON.parse("{ _id:2, textField: \"ccc ddd\", otherField: \"text2 aaa\" }")); 48 | collection.insert((DBObject) FongoJSON.parse("{ _id:3, textField: \"eee fff\", otherField: \"text3 aaa\" }")); 49 | collection.insert((DBObject) FongoJSON.parse("{ _id:4, textField: \"aaa eee\", otherField: \"text4 aaa\" }")); 50 | 51 | collection.createIndex(new BasicDBObject("textField", "text")); 52 | 53 | ts = new TextSearch(collection); 54 | } 55 | 56 | @Test 57 | public void testFindByTextSearch_String() { 58 | String searchString = "aaa -eee -bbb"; 59 | 60 | DBObject result = ts.findByTextSearch(searchString); 61 | 62 | Assertions.assertThat(((List) result.get("results"))).hasSize(0); 63 | 64 | DBObject expected = new BasicDBObject("language", "english"); 65 | expected.put("results", new BasicDBList()); 66 | expected.put("stats", 67 | new BasicDBObject("nscannedObjects", 5L) 68 | .append("nscanned", 2L) 69 | .append("n", 0L) 70 | .append("timeMicros", 1) 71 | ); 72 | expected.put("ok", 1); 73 | Assertions.assertThat(result).isEqualTo(expected); 74 | } 75 | 76 | @Test 77 | public void testFindByTextSearch_String_DBObject() { 78 | String searchString = "aaa -eee"; 79 | DBObject project = new BasicDBObject("textField", 1); 80 | 81 | DBObject result = ts.findByTextSearch(searchString, project); 82 | 83 | DBObject expected = new BasicDBObject("language", "english"); 84 | expected.put("results", FongoJSON.parse("[ { " 85 | + "\"score\" : 0.75 , " 86 | + "\"obj\" : { \"_id\" : 1 , \"textField\" : \"aaa bbb\"}}]")); 87 | expected.put("stats", 88 | new BasicDBObject("nscannedObjects", 4L) 89 | .append("nscanned", 2L) 90 | .append("n", 1L) 91 | .append("timeMicros", 1) 92 | ); 93 | expected.put("ok", 1); 94 | Assertions.assertThat(result).isEqualTo(expected); 95 | assertEquals("aaa bbb", 96 | ((DBObject) ((DBObject) ((List) result.get("results")).get(0)).get("obj")).get("textField")); 97 | 98 | } 99 | 100 | @Test 101 | public void testFindByTextSearch_3args() { 102 | String searchString = "aaa bbb ccc ddd eee"; 103 | DBObject project = new BasicDBObject("textField", 1).append("otherField", 1); 104 | 105 | DBObject result = ts.findByTextSearch(searchString, project, 2); 106 | 107 | DBObject expected = new BasicDBObject("language", "english"); 108 | expected.put("results", FongoJSON.parse("[ " 109 | + "{ \"score\" : 1.5 , " 110 | + "\"obj\" : { \"_id\" : 1 , \"textField\" : \"aaa bbb\" , \"otherField\" : \"text1 aaa\"}} , " 111 | + "{ \"score\" : 1.5 , " 112 | + "\"obj\" : { \"_id\" : 2 , \"textField\" : \"ccc ddd\" , \"otherField\" : \"text2 aaa\"}}]")); 113 | expected.put("stats", 114 | new BasicDBObject("nscannedObjects", 6L) 115 | .append("nscanned", 6L) 116 | .append("n", 2L) 117 | .append("timeMicros", 1) 118 | ); 119 | expected.put("ok", 1); 120 | Assertions.assertThat(result).isEqualTo(expected); 121 | assertEquals("ccc ddd", 122 | ((DBObject) ((DBObject) ((List) result.get("results")).get(1)).get("obj")).get("textField")); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/impl/aggregation/Unwind.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.aggregation; 2 | 3 | import com.github.fakemongo.impl.Util; 4 | import com.mongodb.BasicDBList; 5 | import com.mongodb.BasicDBObject; 6 | import com.mongodb.DB; 7 | import com.mongodb.DBCollection; 8 | import com.mongodb.DBObject; 9 | import com.mongodb.MongoException; 10 | import com.mongodb.annotations.ThreadSafe; 11 | import org.bson.BSONObject; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * 18 | */ 19 | @ThreadSafe 20 | public class Unwind extends PipelineKeyword { 21 | public static final Unwind INSTANCE = new Unwind(); 22 | 23 | private Unwind() { 24 | } 25 | 26 | /** 27 | * {@see http://docs.mongodb.org/manual/reference/aggregation/unwind/#pipe._S_unwind} 28 | *

29 | * Note $unwind has the following behaviors: 30 | *

 31 |    * $unwind is most useful in combination with $group.
 32 |    * You may undo the effects of unwind operation with the $group pipeline operator.
 33 |    * If you specify a target field for $unwind that does not exist in an input document, the pipeline ignores the input document, and will generate no result documents, unless the option preserveNullAndEmptyArrays is set to true.
 34 |    * If the operand does not resolve to an array but is not missing, null, or an empty array, $unwind treats the operand as a single element array.
 35 |    * If you specify a target field for $unwind that holds an empty array ([]) in an input document, the pipeline ignores the input document, and will generates no result documents, unless the option preserveNullAndEmptyArrays is set to true.
 36 |    * 
37 | * 38 | * @param coll 39 | * @param object 40 | * @return 41 | */ 42 | @Override 43 | public DBCollection apply(DB originalDB, DBCollection coll, DBObject object) { 44 | String fieldName = null; 45 | boolean preserveNullAndEmptyArrays = false; 46 | 47 | Object unwindObject = object.get(getKeyword()); 48 | if (unwindObject instanceof String) { 49 | fieldName = unwindObject.toString(); 50 | } else if (unwindObject instanceof BasicDBObject) { 51 | BasicDBObject unwindDBObject = (BasicDBObject) unwindObject; 52 | if (unwindDBObject.containsField("path")) { 53 | fieldName = unwindDBObject.getString("path"); 54 | preserveNullAndEmptyArrays = getPreserveNullAndEmptyArrays(unwindDBObject); 55 | } 56 | } 57 | 58 | if (fieldName == null || fieldName.trim().isEmpty()) { 59 | throw new MongoException(28812, "no path specified to $unwind stage"); 60 | } 61 | if (!fieldName.startsWith("$")) { 62 | throw new MongoException(28818, String.format("path option to $unwind stage should be prefixed with a '$': %s", fieldName)); 63 | } 64 | fieldName = fieldName.substring(1); 65 | 66 | List result = new ArrayList(); 67 | for (DBObject dbObject : coll.find().toArray()) { 68 | if (Util.containsField(dbObject, fieldName)) { 69 | Object oValue = Util.extractField(dbObject, fieldName); 70 | if (!(oValue instanceof BasicDBList)) { 71 | // throw fongoDB..errorResult(15978, "$unwind: value at end of field path must be an array").getException; 72 | // throw new MongoException(15978, "exception: $unwind: value at end of field path must be an array"); 73 | DBObject newValue = Util.clone(dbObject); 74 | result.add(newValue); 75 | } else { 76 | BasicDBList list = (BasicDBList) oValue; 77 | for (Object sublist : list) { 78 | DBObject newValue = Util.clone(dbObject); 79 | Util.putValue(newValue, fieldName, sublist); 80 | // newValue.removeField("_id"); // TODO _id must be the same (but Fongo doesn't handle) 81 | result.add(newValue); 82 | } 83 | if (preserveNullAndEmptyArrays && list.isEmpty()) { 84 | DBObject newValue = Util.clone(dbObject); 85 | Util.removeField(newValue, fieldName); 86 | result.add(newValue); 87 | } 88 | } 89 | } else if (preserveNullAndEmptyArrays) { 90 | result.add(Util.clone(dbObject)); 91 | } 92 | } 93 | return dropAndInsert(coll, result); 94 | } 95 | 96 | private boolean getPreserveNullAndEmptyArrays(BasicDBObject unwindDBObject) { 97 | if (unwindDBObject.containsField("preserveNullAndEmptyArrays")) { 98 | String option = unwindDBObject.getString("preserveNullAndEmptyArrays"); 99 | if (!"true".equalsIgnoreCase(option) && !"false".equalsIgnoreCase(option)) { 100 | throw new MongoException(28810, String.format("expected a boolean for the preserveNullAndEmptyArrays option to $unwind stage, got %s", option)); 101 | } 102 | return Boolean.parseBoolean(option); 103 | } 104 | return false; 105 | } 106 | 107 | @Override 108 | public String getKeyword() { 109 | return "$unwind"; 110 | } 111 | 112 | //exception: $unwind: value at end of field path must be an array" , "code" : 15978 113 | } 114 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/SpringQueryTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.integration; 2 | 3 | import com.github.fakemongo.Fongo; 4 | import com.mongodb.Mongo; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | import static org.junit.Assert.assertEquals; 10 | import org.junit.Test; 11 | import org.springframework.context.ConfigurableApplicationContext; 12 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.data.annotation.Id; 15 | import org.springframework.data.mongodb.config.AbstractMongoConfiguration; 16 | import org.springframework.data.mongodb.core.mapping.Document; 17 | import org.springframework.data.mongodb.repository.MongoRepository; 18 | import org.springframework.data.mongodb.repository.Query; 19 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; 20 | import org.springframework.stereotype.Repository; 21 | 22 | public class SpringQueryTest { 23 | 24 | // Issue #92 : mixed source of date (Integer vs Long) problem. 25 | @Test 26 | public void should_mixed_data_works_in_spring() throws Exception { 27 | //Given 28 | ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(FongoConfig.class); 29 | QueryRepository repository = ctx.getBean(QueryRepository.class); 30 | 31 | //When 32 | DomainObject object = new DomainObject(); 33 | ArrayList longList = new ArrayList(); 34 | longList.add(25L); 35 | longList.add(10L); 36 | object.setLongList(longList); 37 | 38 | repository.save(object); 39 | 40 | //Then 41 | 42 | //Queries generated from method names always work 43 | assertThat(repository.findByLongList(longList)).isNotNull(); 44 | assertThat(repository.findByLongList(10L)).isNotNull(); 45 | assertThat(repository.findByLongList(longList)).hasSize(1); 46 | assertThat(repository.findByLongList(10L)).hasSize(1); 47 | 48 | //Hand written queries do not work on Fongo 49 | assertThat(repository.findLongListWithQuery(longList)).isNotNull(); 50 | assertThat(repository.findLongListWithQuery(10L)).isNotNull(); 51 | assertThat(repository.findLongListWithQuery(longList)).hasSize(1); 52 | assertThat(repository.findLongListWithQuery(10L)).hasSize(1); 53 | ctx.close(); 54 | } 55 | 56 | @Test 57 | public void should_IN_query_work_in_spring() throws Exception { 58 | //Given 59 | ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(FongoConfig.class); 60 | QueryRepository repository = ctx.getBean(QueryRepository.class); 61 | 62 | DomainObject object = new DomainObject(); 63 | object.setOther(1L); 64 | repository.save(object); 65 | 66 | DomainObject object2 = new DomainObject(); 67 | object2.setOther(2L); 68 | repository.save(object2); 69 | 70 | DomainObject object3 = new DomainObject(); 71 | object3.setOther(3L); 72 | repository.save(object3); 73 | 74 | // When 75 | final List withInQuery = repository.findOtherWithInQuery(Arrays.asList(1L, 2L)); 76 | 77 | // Then 78 | assertEquals(2, withInQuery.size()); 79 | 80 | ctx.close(); 81 | } 82 | 83 | @Configuration 84 | @EnableMongoRepositories 85 | public static class FongoConfig extends AbstractMongoConfiguration { 86 | 87 | @Override 88 | protected String getDatabaseName() { 89 | return "FongoDB"; 90 | } 91 | 92 | @Override 93 | public Mongo mongo() throws Exception { 94 | return new Fongo(getDatabaseName()).getMongo(); 95 | } 96 | 97 | } 98 | 99 | @Document 100 | public static class DomainObject { 101 | 102 | @Id 103 | private String _id; 104 | private List longList; 105 | 106 | private Long other; 107 | 108 | public List getLongList() { 109 | return longList; 110 | } 111 | 112 | public void setLongList(List longList) { 113 | this.longList = longList; 114 | } 115 | 116 | public String get_id() { 117 | return _id; 118 | } 119 | 120 | public void set_id(String _id) { 121 | this._id = _id; 122 | } 123 | 124 | public Long getOther() { 125 | return other; 126 | } 127 | 128 | public void setOther(Long other) { 129 | this.other = other; 130 | } 131 | } 132 | 133 | } 134 | 135 | @Repository 136 | interface QueryRepository extends MongoRepository { 137 | 138 | @Query("{'longList' : ?0}") 139 | List findLongListWithQuery(Long foo); 140 | 141 | @Query("{'longList' : ?0}") 142 | List findLongListWithQuery(List foo); 143 | 144 | @Query("{'other' : {$in: ?0 }}") 145 | List findOtherWithInQuery(List foo); 146 | 147 | List findByLongList(Long foo); 148 | 149 | List findByLongList(List foo); 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/junit/FongoAsyncRule.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.junit; 2 | 3 | import static com.github.fakemongo.Fongo.DEFAULT_SERVER_VERSION; 4 | import com.github.fakemongo.async.FongoAsync; 5 | import static com.github.fakemongo.junit.FongoRule.randomName; 6 | import com.mongodb.async.SingleResultCallback; 7 | import com.mongodb.async.client.MongoClient; 8 | import com.mongodb.async.client.MongoClients; 9 | import com.mongodb.async.client.MongoCollection; 10 | import com.mongodb.async.client.MongoDatabase; 11 | import com.mongodb.connection.ServerVersion; 12 | import java.net.UnknownHostException; 13 | import java.util.concurrent.CountDownLatch; 14 | import java.util.concurrent.TimeUnit; 15 | import org.bson.Document; 16 | import org.junit.rules.ExternalResource; 17 | 18 | /** 19 | * Create a Junit Rule to use with annotation 20 | *

21 | * @Rule 22 | * public FongoAsyncRule rule = new FongoAsyncRule(). 23 | *

24 | *

25 | * Note than you can switch to a real mongodb on your localhost (for now). 26 | *

27 | *

28 | * WARNING : database is dropped after the test !! 29 | *

30 | */ 31 | public class FongoAsyncRule extends ExternalResource { 32 | 33 | /** 34 | * Will be true if we use the real MongoDB to test things against real world. 35 | */ 36 | private final boolean realMongo; 37 | private final String dbName; 38 | private final FongoAsync fongo; 39 | private MongoClient mongo; 40 | private MongoDatabase mongoDatabase; 41 | 42 | /** 43 | * Setup a rule with a real MongoDB. 44 | * 45 | * @param dbName the dbName to use. 46 | * @param serverVersion version of the server to use for fongo. 47 | * @param realMongo set to true if you want to use a real mongoDB. 48 | * @param mongoClientIfReal real client to use if realMongo si true. 49 | */ 50 | public FongoAsyncRule(final String dbName, final ServerVersion serverVersion, final boolean realMongo, final MongoClient mongoClientIfReal) { 51 | this.dbName = dbName; 52 | this.realMongo = realMongo || "true".equals(System.getProperty("fongo.force.realMongo")); 53 | this.fongo = realMongo ? null : newFongo(serverVersion); 54 | this.mongo = mongoClientIfReal; 55 | } 56 | 57 | public FongoAsyncRule() { 58 | this(DEFAULT_SERVER_VERSION); 59 | } 60 | 61 | public FongoAsyncRule(final ServerVersion serverVersion) { 62 | this(randomName(), serverVersion, false, null); 63 | } 64 | 65 | public FongoAsyncRule(boolean realMongo) { 66 | this(realMongo, DEFAULT_SERVER_VERSION); 67 | } 68 | 69 | public FongoAsyncRule(boolean realMongo, ServerVersion serverVersion) { 70 | this(randomName(), serverVersion, realMongo, null); 71 | } 72 | 73 | public FongoAsyncRule(boolean realMongo, MongoClient mongoClientIfReal) { 74 | this(randomName(), DEFAULT_SERVER_VERSION, realMongo, mongoClientIfReal); 75 | } 76 | 77 | public FongoAsyncRule(String dbName, boolean realMongo) { 78 | this(dbName, DEFAULT_SERVER_VERSION, realMongo, null); 79 | } 80 | 81 | public FongoAsyncRule(String dbName) { 82 | this(dbName, DEFAULT_SERVER_VERSION, false, null); 83 | } 84 | 85 | public boolean isRealMongo() { 86 | return this.realMongo; 87 | } 88 | 89 | @Override 90 | protected void before() throws UnknownHostException { 91 | if (realMongo) { 92 | if (mongo == null) { 93 | mongo = MongoClients.create(); 94 | } 95 | } else { 96 | mongo = this.fongo.getMongo(); 97 | } 98 | mongoDatabase = mongo.getDatabase(dbName); 99 | } 100 | 101 | @Override 102 | protected void after() { 103 | final CountDownLatch countDownLatch = new CountDownLatch(1); 104 | mongoDatabase.drop(new SingleResultCallback() { 105 | @Override 106 | public void onResult(Void result, Throwable t) { 107 | countDownLatch.countDown(); 108 | } 109 | }); 110 | try { 111 | countDownLatch.await(1, TimeUnit.SECONDS); 112 | } catch (InterruptedException e) { 113 | throw new RuntimeException(e); 114 | } 115 | } 116 | 117 | public MongoCollection newMongoCollection() { 118 | return newMongoCollection(randomName()); 119 | } 120 | 121 | public MongoCollection newMongoCollection(final String collectionName) { 122 | return mongoDatabase.getCollection(collectionName); 123 | } 124 | 125 | public MongoCollection newMongoCollection(final Class documentClass) { 126 | return newMongoCollection(randomName(), documentClass); 127 | } 128 | 129 | public MongoCollection newMongoCollection(final String collectionName, final Class documentClass) { 130 | return mongoDatabase.getCollection(collectionName, documentClass); 131 | } 132 | 133 | protected FongoAsync newFongo(ServerVersion serverVersion) { 134 | return new FongoAsync("test", serverVersion); 135 | } 136 | 137 | public FongoAsync getFongo() { 138 | return this.fongo; 139 | } 140 | 141 | public MongoDatabase getDatabase(String name) { 142 | return this.mongo.getDatabase(name); 143 | } 144 | 145 | public MongoDatabase getDatabase() { 146 | return this.mongoDatabase; 147 | } 148 | 149 | public MongoClient getMongoClient() { 150 | return this.mongo; 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/com/mongodb/util/JSONCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2014 MongoDB, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // JSONCallback.java 18 | 19 | package com.mongodb.util; 20 | 21 | import com.mongodb.BasicDBList; 22 | import com.mongodb.BasicDBObject; 23 | import com.mongodb.DBObject; 24 | import com.mongodb.DBRef; 25 | import java.text.ParsePosition; 26 | import java.text.SimpleDateFormat; 27 | import java.util.Date; 28 | import java.util.GregorianCalendar; 29 | import java.util.SimpleTimeZone; 30 | import java.util.UUID; 31 | import java.util.regex.Pattern; 32 | import javax.xml.bind.DatatypeConverter; 33 | import org.bson.BSON; 34 | import org.bson.BSONObject; 35 | import org.bson.BasicBSONCallback; 36 | import org.bson.BsonUndefined; 37 | import org.bson.types.BSONTimestamp; 38 | import org.bson.types.Binary; 39 | import org.bson.types.Code; 40 | import org.bson.types.CodeWScope; 41 | import org.bson.types.MaxKey; 42 | import org.bson.types.MinKey; 43 | import org.bson.types.ObjectId; 44 | 45 | /** 46 | * Converts JSON to DBObjects and vice versa. 47 | */ 48 | public class JSONCallback extends BasicBSONCallback { 49 | 50 | @Override 51 | public BSONObject create() { 52 | return new BasicDBObject(); 53 | } 54 | 55 | @Override 56 | protected BSONObject createList() { 57 | return new BasicDBList(); 58 | } 59 | 60 | @Override 61 | public void arrayStart(final String name) { 62 | _lastArray = true; 63 | super.arrayStart(name); 64 | } 65 | 66 | @Override 67 | public void objectStart(final String name) { 68 | _lastArray = false; 69 | super.objectStart(name); 70 | } 71 | 72 | @Override 73 | public Object objectDone() { 74 | String name = curName(); 75 | Object o = super.objectDone(); 76 | if (_lastArray) { 77 | return o; 78 | } 79 | BSONObject b = (BSONObject) o; 80 | 81 | // override the object if it's a special type 82 | if (b.containsField("$oid")) { 83 | o = new ObjectId((String) b.get("$oid")); 84 | } else if (b.containsField("$date")) { 85 | if (b.get("$date") instanceof Number) { 86 | o = new Date(((Number) b.get("$date")).longValue()); 87 | } else { 88 | SimpleDateFormat format = new SimpleDateFormat(_msDateFormat); 89 | format.setCalendar(new GregorianCalendar(new SimpleTimeZone(0, "GMT"))); 90 | o = format.parse(b.get("$date").toString(), new ParsePosition(0)); 91 | 92 | if (o == null) { 93 | // try older format with no ms 94 | format = new SimpleDateFormat(_secDateFormat); 95 | format.setCalendar(new GregorianCalendar(new SimpleTimeZone(0, "GMT"))); 96 | o = format.parse(b.get("$date").toString(), new ParsePosition(0)); 97 | } 98 | } 99 | } else if (b.containsField("$regex")) { 100 | o = Pattern.compile((String) b.get("$regex"), 101 | BSON.regexFlags((String) b.get("$options"))); 102 | } else if (b.containsField("$ts")) { //Legacy timestamp format 103 | Integer ts = ((Number) b.get("$ts")).intValue(); 104 | Integer inc = ((Number) b.get("$inc")).intValue(); 105 | o = new BSONTimestamp(ts, inc); 106 | } else if (b.containsField("$timestamp")) { 107 | BSONObject tsObject = (BSONObject) b.get("$timestamp"); 108 | Integer ts = ((Number) tsObject.get("t")).intValue(); 109 | Integer inc = ((Number) tsObject.get("i")).intValue(); 110 | o = new BSONTimestamp(ts, inc); 111 | } else if (b.containsField("$code")) { 112 | if (b.containsField("$scope")) { 113 | o = new CodeWScope((String) b.get("$code"), (DBObject) b.get("$scope")); 114 | } else { 115 | o = new Code((String) b.get("$code")); 116 | } 117 | } else if (b.containsField("$ref")) { 118 | o = new DBRef((String) b.get("$ref"), b.get("$id")); 119 | } else if (b.containsField("$minKey")) { 120 | o = new MinKey(); 121 | } else if (b.containsField("$maxKey")) { 122 | o = new MaxKey(); 123 | } else if (b.containsField("$uuid")) { 124 | o = UUID.fromString((String) b.get("$uuid")); 125 | } else if (b.containsField("$binary")) { 126 | Object extracted = b.get("$type"); 127 | int type; 128 | if (extracted instanceof Integer) { 129 | type = (Integer) extracted; 130 | } else { 131 | type = Integer.valueOf(extracted.toString()); 132 | } 133 | byte[] bytes = DatatypeConverter.parseBase64Binary((String) b.get("$binary")); 134 | o = new Binary((byte) type, bytes); 135 | } else if (b.containsField("$undefined") && b.get("$undefined").equals(true)) { 136 | o = new BsonUndefined(); 137 | } else if (b.containsField("$numberLong")) { 138 | o = Long.valueOf((String) b.get("$numberLong")); 139 | } 140 | 141 | if (!isStackEmpty()) { 142 | _put(name, o); 143 | } else { 144 | o = !BSON.hasDecodeHooks() ? o : BSON.applyDecodingHooks(o); 145 | setRoot(o); 146 | } 147 | return o; 148 | } 149 | 150 | private boolean _lastArray = false; 151 | 152 | public static final String _msDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; 153 | public static final String _secDateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"; 154 | } 155 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/FongoAggregateLookupTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.fail; 5 | 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | import org.assertj.core.api.Assertions; 11 | import org.assertj.core.util.Lists; 12 | import org.bson.types.ObjectId; 13 | import org.junit.Before; 14 | import org.junit.Rule; 15 | import org.junit.Test; 16 | import org.junit.rules.ExpectedException; 17 | import org.junit.rules.RuleChain; 18 | import org.junit.rules.TestRule; 19 | import org.springframework.data.mongodb.util.DBObjectUtils; 20 | 21 | import com.github.fakemongo.junit.FongoRule; 22 | import com.mongodb.AggregationOutput; 23 | import com.mongodb.BasicDBObject; 24 | import com.mongodb.DBCollection; 25 | import com.mongodb.DBObject; 26 | 27 | public class FongoAggregateLookupTest { 28 | private static final String PARENT = "orders"; 29 | private static final String CHILD = "orderItems"; 30 | public final FongoRule fongoRule = new FongoRule(false); 31 | 32 | public final ExpectedException exception = ExpectedException.none(); 33 | 34 | @Rule 35 | public TestRule rules = RuleChain.outerRule(exception).around(fongoRule); 36 | 37 | private ObjectId id1 = new ObjectId(); 38 | private ObjectId id2 = new ObjectId(); 39 | private ObjectId id3 = new ObjectId(); 40 | private DBCollection parentCollection; 41 | private DBCollection childCollection; 42 | 43 | @Before 44 | public void setUp() { 45 | parentCollection = fongoRule.newCollection(PARENT); 46 | childCollection = fongoRule.newCollection(CHILD); 47 | 48 | childCollection.insert(new BasicDBObject("_id", id1).append("name", "Tom")); 49 | childCollection.insert(new BasicDBObject("_id", id2).append("name", "Jerry")); 50 | childCollection.insert(new BasicDBObject("_id", id3).append("name", "Itchy")); 51 | } 52 | 53 | @Test 54 | public void shouldJoinOnNonStringFields() { 55 | parentCollection.insert(new BasicDBObject("_id", "firstOrder").append("item", id1)); 56 | parentCollection.insert(new BasicDBObject("_id", "newOrder").append("item", id2)); 57 | parentCollection.insert(new BasicDBObject("_id", "disOrder").append("item", null)); 58 | parentCollection.insert(new BasicDBObject("_id", "order66")); 59 | 60 | List result = lookupResults(createLookup("item", "_id", "lookedUpItem")); 61 | 62 | Assertions.assertThat(result).hasSize(4); 63 | assertContains(result, "firstOrder", 1, "Tom"); 64 | assertContains(result, "newOrder", 1, "Jerry"); 65 | assertContains(result, "disOrder", 0); 66 | assertContains(result, "order66", 0); 67 | } 68 | 69 | @Test 70 | public void shouldJoinOnCollectionFields() { 71 | parentCollection.insert(new BasicDBObject("_id", "firstOrder").append("item", DBObjectUtils.dbList(id1, id2))); 72 | parentCollection.insert(new BasicDBObject("_id", "camcOrder").append("item", id2)); 73 | parentCollection.insert(new BasicDBObject("_id", "newOrder").append("item", id3)); 74 | parentCollection.insert(new BasicDBObject("_id", "disOrder").append("item", null)); 75 | parentCollection.insert(new BasicDBObject("_id", "order66")); 76 | 77 | List result = lookupResults(Arrays.asList( 78 | bo("$unwind", "$item"), 79 | createLookup("item", "_id", "lookedUpItem").get(0), 80 | bo("$unwind", "$lookedUpItem"), 81 | bo("$group", bo("_id", "$_id") 82 | .append("item", bo("$push", "$item")) 83 | .append("lookedUpItem", bo("$push", "$lookedUpItem"))))); 84 | 85 | Assertions.assertThat(result).hasSize(3); 86 | assertContains(result, "firstOrder", 2, "Tom", "Jerry"); 87 | assertContains(result, "camcOrder", 1, "Jerry"); 88 | assertContains(result, "newOrder", 1, "Itchy"); 89 | } 90 | 91 | @Test 92 | public void shouldJoinOnSameItem() { 93 | parentCollection.insert(new BasicDBObject("_id", "firstOrder").append("item", id1)); 94 | parentCollection.insert(new BasicDBObject("_id", "newOrder").append("item", id1)); 95 | 96 | List result = lookupResults(createLookup("item", "_id", "lookedUpItem")); 97 | 98 | Assertions.assertThat(result).hasSize(2); 99 | assertContains(result, "firstOrder", 1, "Tom"); 100 | assertContains(result, "newOrder", 1, "Tom"); 101 | } 102 | 103 | private List lookupResults(List pipeline) { 104 | AggregationOutput output = parentCollection.aggregate(pipeline); 105 | 106 | List result = Lists.newArrayList(output.results()); 107 | System.out.println(result); 108 | return result; 109 | } 110 | 111 | private void assertContains(List result, String _id, int matchedItemsSize, String... itemNames) { 112 | for (DBObject res : result) { 113 | if (res.get("_id").equals(_id)) { 114 | List matchedItems = (List) res.get("lookedUpItem"); 115 | assertEquals(matchedItemsSize, matchedItems.size()); 116 | if (matchedItemsSize > 0) { 117 | for (int i = 0; i < itemNames.length; ++i) { 118 | assertEquals(itemNames[i], ((DBObject) matchedItems.get(i)).get("name")); 119 | } 120 | } 121 | return; 122 | } 123 | } 124 | fail("Expected value from lookup collection not found. Wanted _id '" + _id + "' with item names: " + itemNames); 125 | } 126 | 127 | private List createLookup(String localField, String foreignField, String as) { 128 | return Collections.singletonList(bo("$lookup", bo("from", CHILD) 129 | .append("localField", localField) 130 | .append("foreignField", foreignField) 131 | .append("as", as))); 132 | } 133 | 134 | private BasicDBObject bo(String key, Object val) { 135 | return new BasicDBObject(key, val); 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/FongoAggregateOutTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.github.fakemongo.junit.FongoRule; 4 | import static com.github.fakemongo.junit.FongoRule.randomName; 5 | import com.mongodb.AggregationOutput; 6 | import com.mongodb.DBCollection; 7 | import com.mongodb.DBObject; 8 | import com.mongodb.client.AggregateIterable; 9 | import com.mongodb.client.MongoCollection; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | import org.assertj.core.util.Lists; 14 | import org.bson.Document; 15 | import static org.junit.Assert.assertEquals; 16 | import org.junit.Rule; 17 | import org.junit.Test; 18 | import org.junit.rules.ExpectedException; 19 | import org.junit.rules.RuleChain; 20 | import org.junit.rules.TestRule; 21 | 22 | 23 | /** 24 | * User: gdepourtales 25 | * 2015/06/15 26 | */ 27 | public class FongoAggregateOutTest { 28 | 29 | public final FongoRule fongoRule = new FongoRule(false); 30 | 31 | public final ExpectedException exception = ExpectedException.none(); 32 | 33 | @Rule 34 | public TestRule rules = RuleChain.outerRule(exception).around(fongoRule); 35 | 36 | /** 37 | * See http://docs.mongodb.org/manual/reference/operator/aggregation/out/#pipe._S_out 38 | */ 39 | @Test 40 | public void testOut() { 41 | DBCollection coll = fongoRule.newCollection(); 42 | DBCollection secondCollection = fongoRule.newCollection(); 43 | String data = "[{ _id: 1, sec: \"dessert\", category: \"pie\", type: \"apple\" },\n" + 44 | "{ _id: 2, sec: \"dessert\", category: \"pie\", type: \"cherry\" },\n" + 45 | "{ _id: 3, sec: \"main\", category: \"pie\", type: \"shepherd's\" },\n" + 46 | "{ _id: 4, sec: \"main\", category: \"pie\", type: \"chicken pot\" }]"; 47 | fongoRule.insertJSON(coll, data); 48 | 49 | DBObject project = fongoRule.parseDBObject( 50 | "{ $out: \"" + secondCollection.getName() + "\"}" 51 | ); 52 | 53 | AggregationOutput output = coll.aggregate(Arrays.asList(project)); 54 | 55 | List resultAggregate = Lists.newArrayList(output.results()); 56 | 57 | assertThat(resultAggregate).isEqualTo(fongoRule.parse(data)); 58 | assertEquals(4, secondCollection.count()); 59 | assertEquals("apple", secondCollection.find(fongoRule.parseDBObject("{_id:1}")).next().get("type")); 60 | assertEquals("pie", secondCollection.find(fongoRule.parseDBObject("{_id:1}")).next().get("category")); 61 | assertEquals("chicken pot", secondCollection.find(fongoRule.parseDBObject("{_id:4}")).next().get("type")); 62 | } 63 | 64 | 65 | /** 66 | * See http://docs.mongodb.org/manual/reference/operator/aggregation/out/#pipe._S_out 67 | */ 68 | @Test 69 | public void testOutWithEmptyCollection() { 70 | DBCollection coll = fongoRule.newCollection(); 71 | DBCollection secondCollection = fongoRule.newCollection(); 72 | String data = "[]"; 73 | fongoRule.insertJSON(coll, data); 74 | 75 | DBObject project = fongoRule.parseDBObject( 76 | "{ $out: \"" + secondCollection.getName() + "\"}" 77 | ); 78 | 79 | AggregationOutput output = coll.aggregate(Arrays.asList(project)); 80 | List resultAggregate = Lists.newArrayList(output.results()); 81 | 82 | assertThat(resultAggregate).isEqualTo(fongoRule.parse(data)); 83 | assertEquals(0, secondCollection.count()); 84 | } 85 | 86 | /** 87 | * See http://docs.mongodb.org/manual/reference/operator/aggregation/out/#pipe._S_out 88 | */ 89 | @Test 90 | public void testOutWithNonExistentCollection() { 91 | DBCollection coll = fongoRule.newCollection(); 92 | String data = "[{ _id: 1, sec: \"dessert\", category: \"pie\", type: \"apple\" },\n" + 93 | "{ _id: 2, sec: \"dessert\", category: \"pie\", type: \"cherry\" },\n" + 94 | "{ _id: 3, sec: \"main\", category: \"pie\", type: \"shepherd's\" } ,\n" + 95 | "{ _id: 4, sec: \"main\", category: \"pie\", type: \"chicken pot\" }]"; 96 | fongoRule.insertJSON(coll, data); 97 | String newCollectionName = "new_collection"; 98 | 99 | DBObject project = fongoRule.parseDBObject( 100 | "{ $out: \"" + newCollectionName + "\"}" 101 | ); 102 | 103 | AggregationOutput output = coll.aggregate(Arrays.asList(project)); 104 | List resultAggregate = Lists.newArrayList(output.results()); 105 | 106 | assertThat(resultAggregate).isEqualTo(fongoRule.parse(data)); 107 | DBCollection newCollection = fongoRule.getDB().getCollection("new_collection"); 108 | assertEquals(4, newCollection.count()); 109 | assertEquals("apple", newCollection.find(fongoRule.parseDBObject("{_id:1}")).next().get("type")); 110 | assertEquals("pie", newCollection.find(fongoRule.parseDBObject("{_id:1}")).next().get("category")); 111 | assertEquals("chicken pot", newCollection.find(fongoRule.parseDBObject("{_id:4}")).next().get("type")); 112 | } 113 | 114 | /** 115 | * See http://docs.mongodb.org/manual/reference/operator/aggregation/out/#pipe._S_out 116 | */ 117 | @Test 118 | public void should_out_in_non_empty_collection() { 119 | MongoCollection coll = fongoRule.newMongoCollection(randomName("from")); 120 | MongoCollection secondCollection = fongoRule.newMongoCollection(randomName("out")); 121 | String data = "[{_id:3}, {_id:4}]"; 122 | fongoRule.insertJSON(coll, data); 123 | fongoRule.insertJSON(secondCollection, "[{_id:1}, {_id:2}]"); 124 | 125 | List aggregate = fongoRule.parseList("[{$project:{_id:1}}," + 126 | "{ $out: \"" + secondCollection.getNamespace().getCollectionName() + "\"}]" 127 | ); 128 | 129 | AggregateIterable output = coll.aggregate(aggregate); 130 | List resultAggregate = Lists.newArrayList(output); 131 | System.out.println(resultAggregate); 132 | 133 | assertThat(resultAggregate).isEqualTo(Arrays.asList(new Document("_id", 3), new Document("_id", 4))); 134 | assertThat(secondCollection.find().iterator()).containsExactly(new Document("_id", 3), new Document("_id", 4)); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/FongoAggregateZipTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.github.fakemongo.impl.Util; 4 | import com.github.fakemongo.junit.FongoRule; 5 | import com.google.common.collect.Lists; 6 | import com.mongodb.AggregationOutput; 7 | import com.mongodb.DBCollection; 8 | import com.mongodb.DBObject; 9 | import java.util.List; 10 | import org.junit.Assert; 11 | import static org.junit.Assert.assertEquals; 12 | import org.junit.Before; 13 | import org.junit.Rule; 14 | import org.junit.Test; 15 | import org.junit.rules.ExpectedException; 16 | import org.junit.rules.RuleChain; 17 | import org.junit.rules.TestRule; 18 | 19 | public class FongoAggregateZipTest { 20 | 21 | public final FongoRule fongoRule = new FongoRule(false); 22 | 23 | public final ExpectedException exception = ExpectedException.none(); 24 | 25 | @Rule 26 | public TestRule rules = RuleChain.outerRule(exception).around(fongoRule); 27 | 28 | private DBCollection collection; 29 | 30 | @Before 31 | public void setup() throws Exception { 32 | collection = fongoRule.insertFile(fongoRule.newCollection(), "/zips.json"); 33 | } 34 | 35 | // see http://stackoverflow.com/questions/11418985/mongodb-aggregation-framework-group-over-multiple-values 36 | @Test 37 | public void shouldHandleStatesWithPopulationsOver5Million() { 38 | List pipeline = fongoRule.parseList("[{ $group :\n" + 39 | " { _id : \"$state\",\n" + 40 | " totalPop : { $sum : \"$pop\" } } },\n" + 41 | "{ $match : {totalPop : { $gte : 5000000 } } },\n" + 42 | "{ $sort : {_id:1}}\n" + 43 | "]"); 44 | 45 | // Aggregate 46 | AggregationOutput output = collection.aggregate(pipeline.get(0), pipeline.get(1), pipeline.get(2)); 47 | // Assert 48 | // TODO WDEL 49 | // assertTrue(output.getCommandResult().ok()); 50 | // assertTrue(output.getCommandResult().containsField("result")); 51 | 52 | 53 | Iterable resultAggregate = output.results(); 54 | Assert.assertEquals(fongoRule.parseDBObject("[ { \"_id\" : \"MA\" , \"totalPop\" : 6016425} , " + 55 | "{ \"_id\" : \"NJ\" , \"totalPop\" : 7730188} , " + 56 | "{ \"_id\" : \"NY\" , \"totalPop\" : 12950936}]"), resultAggregate); 57 | } 58 | 59 | @Test 60 | public void shouldHandleLargestAndSmallestCitiesByState() { 61 | List pipeline = fongoRule.parseList("[ { $group:\n" + 62 | " { _id: { state: \"$state\", city: \"$city\" },\n" + 63 | " pop: { $sum: \"$pop\" } } },\n" + 64 | " { $sort: { pop: 1 } },\n" + 65 | " { $group:\n" + 66 | " { _id : \"$_id.state\",\n" + 67 | " biggestCity: { $last: \"$_id.city\" },\n" + 68 | " biggestPop: { $last: \"$pop\" },\n" + 69 | " smallestCity: { $first: \"$_id.city\" },\n" + 70 | " smallestPop: { $first: \"$pop\" } } },\n" + 71 | " { $project:\n" + 72 | " { _id: 0,\n" + 73 | " state: \"$_id\",\n" + 74 | " biggestCity: { name: \"$biggestCity\", pop: \"$biggestPop\" },\n" + 75 | " smallestCity: { name: \"$smallestCity\", pop: \"$smallestPop\" } } },\n" + 76 | " { $sort : { \"biggestCity.name\" : 1 } }\n" + 77 | "]"); 78 | 79 | 80 | // Aggregate 81 | AggregationOutput output = collection.aggregate(pipeline.get(0), pipeline.get(1), pipeline.get(2), pipeline.get(3), pipeline.get(4)); 82 | 83 | // Assert 84 | // TODO WDEL 85 | // assertTrue(output.getCommandResult().ok()); 86 | // assertTrue(output.getCommandResult().containsField("result")); 87 | 88 | List resultAggregate = Lists.newArrayList(output.results()); 89 | // TODO(twillouer) : $project { _id : 0 } must remove the id field but $sort add him... 90 | // Assert.assertEquals(fongoAsyncRule.parseList("" + 91 | // "[ { \"biggestCity\" : { \"name\" : \"BRIDGEPORT\" , \"pop\" : 141638} , \"smallestCity\" : { \"name\" : \"EAST KILLINGLY\" , \"pop\" : 25} , \"state\" : \"CT\"} , " + 92 | // "{ \"biggestCity\" : { \"name\" : \"BROOKLYN\" , \"pop\" : 2300504} , \"smallestCity\" : { \"name\" : \"NEW HYDE PARK\" , \"pop\" : 1} , \"state\" : \"NY\"} , " + 93 | // "{ \"biggestCity\" : { \"name\" : \"BURLINGTON\" , \"pop\" : 39127} , \"smallestCity\" : { \"name\" : \"UNIV OF VERMONT\" , \"pop\" : 0} , \"state\" : \"VT\"} , " + 94 | // "{ \"biggestCity\" : { \"name\" : \"CRANSTON\" , \"pop\" : 176404} , \"smallestCity\" : { \"name\" : \"CLAYVILLE\" , \"pop\" : 45} , \"state\" : \"RI\"} , " + 95 | // "{ \"biggestCity\" : { \"name\" : \"MANCHESTER\" , \"pop\" : 106452} , \"smallestCity\" : { \"name\" : \"WEST NOTTINGHAM\" , \"pop\" : 27} , \"state\" : \"NH\"} , " + 96 | // "{ \"biggestCity\" : { \"name\" : \"NEWARK\" , \"pop\" : 275572} , \"smallestCity\" : { \"name\" : \"IMLAYSTOWN\" , \"pop\" : 17} , \"state\" : \"NJ\"} , " + 97 | // "{ \"biggestCity\" : { \"name\" : \"PORTLAND\" , \"pop\" : 63268} , \"smallestCity\" : { \"name\" : \"BUSTINS ISLAND\" , \"pop\" : 0} , \"state\" : \"ME\"} , " + 98 | // "{ \"biggestCity\" : { \"name\" : \"WORCESTER\" , \"pop\" : 169856} , \"smallestCity\" : { \"name\" : \"BUCKLAND\" , \"pop\" : 16} , \"state\" : \"MA\"}]"), resultAggregate); 99 | assertEquals(8, resultAggregate.size()); 100 | assertEquals("BRIDGEPORT", Util.extractField(resultAggregate.get(0), "biggestCity.name")); 101 | assertEquals(141638, Util.extractField(resultAggregate.get(0), "biggestCity.pop")); 102 | assertEquals("EAST KILLINGLY", Util.extractField(resultAggregate.get(0), "smallestCity.name")); 103 | assertEquals(25, Util.extractField(resultAggregate.get(0), "smallestCity.pop")); 104 | DBObject last = resultAggregate.get(resultAggregate.size() - 1); 105 | assertEquals("WORCESTER", Util.extractField(last, "biggestCity.name")); 106 | assertEquals(169856, Util.extractField(last, "biggestCity.pop")); 107 | assertEquals("BUCKLAND", Util.extractField(last, "smallestCity.name")); 108 | assertEquals(16, Util.extractField(last, "smallestCity.pop")); 109 | } 110 | } -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/impl/index/IndexedListTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.index; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Random; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | public class IndexedListTest { 13 | private Random random; 14 | 15 | @Before 16 | public void setUp() throws Exception { 17 | random = new Random(); 18 | } 19 | 20 | @Test 21 | public void testAdd() throws Exception { 22 | int firstElement = random.nextInt(); 23 | int secondElement = random.nextInt(); 24 | int thirdElement = random.nextInt(); 25 | 26 | List innerList = new ArrayList(); 27 | innerList.add(firstElement); 28 | IndexedList list = new IndexedList(innerList); 29 | list.add(secondElement); 30 | list.add(thirdElement); 31 | 32 | assertEquals(firstElement, list.getElements().get(0).intValue()); 33 | assertEquals(secondElement, list.getElements().get(1).intValue()); 34 | assertEquals(thirdElement, list.getElements().get(2).intValue()); 35 | } 36 | 37 | @Test 38 | public void testSizeWithDuplicates() throws Exception { 39 | int firstElement = random.nextInt(); 40 | int secondElement = random.nextInt(); 41 | int thirdElement = random.nextInt(); 42 | 43 | List innerList = new ArrayList(); 44 | innerList.add(firstElement); 45 | IndexedList list = new IndexedList(innerList); 46 | list.add(secondElement); 47 | list.add(thirdElement); 48 | list.add(firstElement); 49 | list.add(secondElement); 50 | list.add(thirdElement); 51 | 52 | assertEquals(firstElement, list.getElements().get(0).intValue()); 53 | assertEquals(secondElement, list.getElements().get(1).intValue()); 54 | assertEquals(thirdElement, list.getElements().get(2).intValue()); 55 | 56 | assertEquals(6, list.size()); 57 | } 58 | 59 | @Test 60 | public void testContains() throws Exception { 61 | random = new Random(); 62 | 63 | int firstElement = 42; 64 | int secondElement = random.nextInt(); 65 | 66 | List innerList = new ArrayList(); 67 | innerList.add(firstElement); 68 | IndexedList list = new IndexedList(innerList); 69 | list.add(secondElement); 70 | 71 | assertTrue(list.contains(firstElement)); 72 | assertTrue(list.contains(secondElement)); 73 | } 74 | 75 | @Test 76 | public void testContainsWithoutSecondElement() throws Exception { 77 | int firstElement = 12; 78 | int secondElement = 13; 79 | 80 | List innerList = new ArrayList(); 81 | innerList.add(firstElement); 82 | IndexedList list = new IndexedList(innerList); 83 | 84 | assertTrue(list.contains(firstElement)); 85 | assertFalse(list.contains(secondElement)); 86 | } 87 | 88 | @Test 89 | public void testContainsSame() throws Exception { 90 | int firstElement = 12; 91 | int secondElement = 12; 92 | 93 | List innerList = new ArrayList(); 94 | innerList.add(firstElement); 95 | IndexedList list = new IndexedList(innerList); 96 | 97 | assertTrue(list.contains(firstElement)); 98 | assertTrue(list.contains(secondElement)); 99 | } 100 | 101 | @Test 102 | public void testRemove() throws Exception { 103 | random = new Random(); 104 | 105 | int firstElement = random.nextInt(); 106 | int secondElement = random.nextInt(); 107 | 108 | List innerList = new ArrayList(); 109 | innerList.add(firstElement); 110 | IndexedList list = new IndexedList(innerList); 111 | list.add(secondElement); 112 | 113 | assertTrue(list.contains(firstElement)); 114 | assertTrue(list.contains(secondElement)); 115 | 116 | list.remove(firstElement); 117 | 118 | assertFalse(list.contains(firstElement)); 119 | assertTrue(list.contains(secondElement)); 120 | 121 | list.remove(secondElement); 122 | 123 | assertFalse(list.contains(firstElement)); 124 | assertFalse(list.contains(secondElement)); 125 | 126 | assertEquals(0, list.size()); 127 | } 128 | 129 | @Test 130 | public void testRemoveSame() throws Exception { 131 | random = new Random(); 132 | 133 | int firstElement = 42; 134 | int secondElement = 42; 135 | 136 | List innerList = new ArrayList(); 137 | innerList.add(firstElement); 138 | IndexedList list = new IndexedList(innerList); 139 | list.add(secondElement); 140 | 141 | assertTrue(list.contains(firstElement)); 142 | assertTrue(list.contains(secondElement)); 143 | 144 | list.remove(firstElement); 145 | 146 | assertTrue(list.contains(firstElement)); 147 | assertTrue(list.contains(secondElement)); 148 | 149 | list.remove(secondElement); 150 | 151 | assertFalse(list.contains(firstElement)); 152 | assertFalse(list.contains(secondElement)); 153 | } 154 | 155 | @Test 156 | public void testRemoveNotExistedElement() throws Exception { 157 | random = new Random(); 158 | 159 | int firstElement = 42; 160 | int secondElement = 54; 161 | 162 | List innerList = new ArrayList(); 163 | innerList.add(firstElement); 164 | IndexedList list = new IndexedList(innerList); 165 | 166 | assertTrue(list.contains(firstElement)); 167 | assertFalse(list.contains(secondElement)); 168 | 169 | list.remove(secondElement); 170 | 171 | assertTrue(list.contains(firstElement)); 172 | assertFalse(list.contains(secondElement)); 173 | 174 | list.remove(firstElement); 175 | 176 | assertFalse(list.contains(firstElement)); 177 | assertFalse(list.contains(secondElement)); 178 | } 179 | } -------------------------------------------------------------------------------- /src/main/java/com/mongodb/MockMongoClient.java: -------------------------------------------------------------------------------- 1 | package com.mongodb; 2 | 3 | import com.github.fakemongo.Fongo; 4 | import com.github.fakemongo.FongoConnection; 5 | import com.mongodb.async.SingleResultCallback; 6 | import com.mongodb.client.MongoDatabase; 7 | import com.mongodb.connection.AsyncConnection; 8 | import com.mongodb.connection.BufferProvider; 9 | import com.mongodb.connection.Cluster; 10 | import com.mongodb.connection.ClusterConnectionMode; 11 | import com.mongodb.connection.ClusterDescription; 12 | import com.mongodb.connection.ClusterSettings; 13 | import com.mongodb.connection.ClusterType; 14 | import com.mongodb.connection.Connection; 15 | import com.mongodb.connection.Server; 16 | import com.mongodb.connection.ServerConnectionState; 17 | import com.mongodb.connection.ServerDescription; 18 | import com.mongodb.internal.connection.PowerOfTwoBufferPool; 19 | import com.mongodb.operation.OperationExecutor; 20 | import com.mongodb.selector.ServerSelector; 21 | import java.net.UnknownHostException; 22 | import java.util.Collection; 23 | import java.util.Collections; 24 | import java.util.List; 25 | import org.objenesis.ObjenesisHelper; 26 | import org.objenesis.ObjenesisStd; 27 | 28 | public class MockMongoClient extends MongoClient { 29 | 30 | private volatile BufferProvider bufferProvider; 31 | 32 | private Fongo fongo; 33 | private MongoOptions options; 34 | private ReadConcern readConcern; 35 | private MongoClientOptions clientOptions; 36 | 37 | public static MockMongoClient create(Fongo fongo) { 38 | // using objenesis here to prevent default constructor from spinning up background threads. 39 | // MockMongoClient client = new ObjenesisStd().getInstantiatorOf(MockMongoClient.class).newInstance(); 40 | MockMongoClient client = ObjenesisHelper.newInstance(MockMongoClient.class); 41 | 42 | MongoClientOptions clientOptions = MongoClientOptions.builder().codecRegistry(fongo.getCodecRegistry()).build(); 43 | client.clientOptions = clientOptions; 44 | client.options = new MongoOptions(clientOptions); 45 | client.fongo = fongo; 46 | client.setWriteConcern(clientOptions.getWriteConcern()); 47 | client.setReadPreference(clientOptions.getReadPreference()); 48 | client.readConcern = clientOptions.getReadConcern() == null ? ReadConcern.DEFAULT : clientOptions.getReadConcern(); 49 | return client; 50 | } 51 | 52 | public MockMongoClient() throws UnknownHostException { 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return fongo.toString(); 58 | } 59 | 60 | @Override 61 | public Collection getUsedDatabases() { 62 | return fongo.getUsedDatabases(); 63 | } 64 | 65 | @Override 66 | public List getDatabaseNames() { 67 | return fongo.getDatabaseNames(); 68 | } 69 | 70 | @Override 71 | public ReplicaSetStatus getReplicaSetStatus() { 72 | // return new ReplicaSetStatus(getCluster()); 73 | return null; 74 | } 75 | 76 | @Override 77 | public int getMaxBsonObjectSize() { 78 | return 16 * 1024 * 1024; 79 | } 80 | 81 | @Override 82 | public DB getDB(String dbname) { 83 | return this.fongo.getDB(dbname); 84 | } 85 | 86 | @Override 87 | public MongoDatabase getDatabase(final String databaseName) { 88 | return new FongoMongoDatabase(databaseName, this.fongo); 89 | } 90 | 91 | @Override 92 | public void dropDatabase(String dbName) { 93 | this.fongo.dropDatabase(dbName); 94 | } 95 | 96 | @Override 97 | public MongoOptions getMongoOptions() { 98 | return this.options; 99 | } 100 | 101 | @Override 102 | public MongoClientOptions getMongoClientOptions() { 103 | return clientOptions; 104 | } 105 | 106 | @Override 107 | public List getAllAddress() { 108 | return getServerAddressList(); 109 | } 110 | 111 | @Override 112 | public ServerAddress getAddress() { 113 | return fongo.getServerAddress(); 114 | } 115 | 116 | @Override 117 | public List getServerAddressList() { 118 | return Collections.singletonList(fongo.getServerAddress()); 119 | } 120 | 121 | private ServerDescription getServerDescription() { 122 | return ServerDescription.builder().address(fongo.getServerAddress()).state(ServerConnectionState.CONNECTED).version(fongo.getServerVersion()).build(); 123 | } 124 | 125 | @Override 126 | public Cluster getCluster() { 127 | return new Cluster() { 128 | @Override 129 | public ClusterSettings getSettings() { 130 | return ClusterSettings.builder().hosts(getServerAddressList()) 131 | .requiredReplicaSetName(options.getRequiredReplicaSetName()) 132 | // .serverSelectionTimeout(options.getServerSelectionTimeout(), MILLISECONDS) 133 | // .serverSelector(createServerSelector(options)) 134 | .description(options.getDescription()) 135 | .maxWaitQueueSize(10).build(); 136 | } 137 | 138 | @Override 139 | public ClusterDescription getDescription() { 140 | return new ClusterDescription(ClusterConnectionMode.SINGLE, ClusterType.STANDALONE, Collections.singletonList(getServerDescription())); 141 | } 142 | 143 | @Override 144 | public Server selectServer(ServerSelector serverSelector) { 145 | return new Server() { 146 | @Override 147 | public ServerDescription getDescription() { 148 | return new ObjenesisStd().getInstantiatorOf(ServerDescription.class).newInstance(); 149 | } 150 | 151 | @Override 152 | public Connection getConnection() { 153 | return new FongoConnection(fongo); 154 | } 155 | 156 | @Override 157 | public void getConnectionAsync(SingleResultCallback callback) { 158 | // TODO 159 | } 160 | 161 | }; 162 | } 163 | 164 | @Override 165 | public void selectServerAsync(ServerSelector serverSelector, SingleResultCallback callback) { 166 | 167 | } 168 | 169 | @Override 170 | public void close() { 171 | 172 | } 173 | 174 | @Override 175 | public boolean isClosed() { 176 | return false; 177 | } 178 | }; 179 | } 180 | 181 | OperationExecutor createOperationExecutor() { 182 | return fongo; 183 | } 184 | 185 | @Override 186 | public void close() { 187 | } 188 | 189 | @Override 190 | synchronized BufferProvider getBufferProvider() { 191 | if (bufferProvider == null) { 192 | bufferProvider = new PowerOfTwoBufferPool(); 193 | } 194 | return bufferProvider; 195 | } 196 | 197 | @Override 198 | public ReadConcern getReadConcern() { 199 | return readConcern; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/integration/jongo/FongoJongoTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.integration.jongo; 2 | 3 | import com.github.fakemongo.junit.FongoRule; 4 | import com.google.common.collect.Lists; 5 | import com.mongodb.AggregationOptions; 6 | import com.mongodb.DBCursor; 7 | import com.mongodb.WriteConcern; 8 | import java.util.Iterator; 9 | import org.assertj.core.api.Assertions; 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | import org.bson.types.ObjectId; 12 | import org.jongo.Jongo; 13 | import org.jongo.MongoCollection; 14 | import org.jongo.MongoCursor; 15 | import org.jongo.QueryModifier; 16 | import org.junit.Before; 17 | import org.junit.Rule; 18 | import org.junit.Test; 19 | 20 | public class FongoJongoTest { 21 | 22 | @Rule 23 | public FongoRule fongoRule = new FongoRule(false); 24 | 25 | private Jongo jongo; 26 | 27 | private MongoCollection collection; 28 | 29 | @Before 30 | public void setup() { 31 | this.jongo = new Jongo(fongoRule.getDB()); 32 | this.collection = jongo.getCollection("test").withWriteConcern(WriteConcern.UNACKNOWLEDGED); 33 | } 34 | 35 | @Test 36 | public void should_insert_neested_class() { 37 | // Given 38 | JongoItem jongoItem = new JongoItem(); 39 | jongoItem.setField("Hello World"); 40 | jongoItem.setId(new JongoItem.JongoItemId("one", "two")); 41 | 42 | // When 43 | this.collection.insert(jongoItem); 44 | JongoItem result = this.collection.findOne().as(JongoItem.class); 45 | 46 | // Then 47 | Assertions.assertThat(result).isEqualTo(jongoItem); 48 | } 49 | 50 | @Test 51 | public void should_save_neested_class() { 52 | // Given 53 | JongoItem jongoItem = new JongoItem(); 54 | jongoItem.setField("Hello World"); 55 | jongoItem.setId(new JongoItem.JongoItemId("one", "two")); 56 | 57 | // When 58 | this.collection.save(jongoItem); 59 | JongoItem result = this.collection.findOne().as(JongoItem.class); 60 | 61 | // Then 62 | Assertions.assertThat(result).isEqualTo(jongoItem); 63 | } 64 | 65 | @Test 66 | public void should_retrieve_neested_class() { 67 | // Given 68 | JongoItem jongoItem = new JongoItem(); 69 | jongoItem.setField("Hello World"); 70 | jongoItem.setId(new JongoItem.JongoItemId("one", "two")); 71 | this.collection.insert(jongoItem); 72 | 73 | // When 74 | JongoItem result = this.collection.findOne("{_id:#}", jongoItem.getId()).as(JongoItem.class); 75 | 76 | // Then 77 | Assertions.assertThat(result).isEqualTo(jongoItem); 78 | } 79 | 80 | @Test 81 | public void should_findAll_works() { 82 | // Given 83 | JongoItem jongoItem = new JongoItem(); 84 | jongoItem.setField("Hello World"); 85 | jongoItem.setId(new JongoItem.JongoItemId("one", "two")); 86 | this.collection.insert(jongoItem); 87 | 88 | // When 89 | final MongoCursor result = this.collection.find("{_id:#}", jongoItem.getId()).as(JongoItem.class); 90 | 91 | // Then 92 | Assertions.assertThat((Iterable) result).containsExactly(jongoItem); 93 | } 94 | 95 | @Test 96 | public void should_findAndModify_works() { 97 | // Given 98 | JongoItem jongoItem = new JongoItem(); 99 | jongoItem.setField("Hello World"); 100 | jongoItem.setId(new JongoItem.JongoItemId("one", "two")); 101 | this.collection.insert(jongoItem); 102 | 103 | // When 104 | final JongoItem result = this.collection.findAndModify("{_id:#}", jongoItem.getId()).with("{$set:{field:#}}", "newField").returnNew().as(JongoItem.class); 105 | 106 | // Then 107 | jongoItem.setField("newField"); 108 | Assertions.assertThat(result).isEqualTo(jongoItem); 109 | } 110 | 111 | @Test 112 | public void should_findAndModify_returnOld_works() { 113 | // Given 114 | JongoItem jongoItem = new JongoItem(); 115 | jongoItem.setField("Hello World"); 116 | jongoItem.setId(new JongoItem.JongoItemId("one", "two")); 117 | this.collection.insert(jongoItem); 118 | 119 | // When 120 | final JongoItem result = this.collection.findAndModify("{_id:#}", jongoItem.getId()).with("{$set:{field:#}}", "newField").as(JongoItem.class); 121 | 122 | // Then 123 | Assertions.assertThat(result).isEqualTo(jongoItem); 124 | } 125 | 126 | @Test 127 | public void should_update_upsert_works() { 128 | // Given 129 | JongoItem jongoItem = new JongoItem(); 130 | jongoItem.setField("Hello World"); 131 | jongoItem.setId(new JongoItem.JongoItemId("one", "two")); 132 | this.collection.insert(jongoItem); 133 | 134 | // When 135 | this.collection.update("{_id:#}", jongoItem.getId()).upsert().with(jongoItem); 136 | final JongoItem result = this.collection.findOne().as(JongoItem.class); 137 | 138 | // Then 139 | Assertions.assertThat(result).isEqualTo(jongoItem); 140 | } 141 | 142 | @Test 143 | public void should_canBindPrimitiveArrayParameter() { 144 | // Given 145 | collection.insert("{value:42, other:true}"); 146 | 147 | // When 148 | 149 | // Then 150 | assertThat(collection.count("{value:{$in:#}}", new int[]{42, 34})).isEqualTo(1); 151 | } 152 | 153 | 154 | @Test 155 | public void canUseListWithANullElement() throws Exception { 156 | 157 | collection.insert("{name:null}"); 158 | collection.insert("{name:'John'}"); 159 | 160 | long nb = collection.count("{name:{$in:#}}", Lists.newArrayList(1, null)); 161 | 162 | assertThat(nb).isEqualTo(1); 163 | } 164 | 165 | @Test 166 | public void canUseQueryModifier() throws Exception { 167 | /* given */ 168 | collection.save(new Friend(new ObjectId(), "John")); 169 | collection.save(new Friend(new ObjectId(), "Robert")); 170 | 171 | /* when */ 172 | Iterator friends = collection.find() 173 | .with(new QueryModifier() { 174 | public void modify(DBCursor cursor) { 175 | cursor.addSpecial("$maxScan", 1); 176 | } 177 | }) 178 | .as(Friend.class); 179 | 180 | /* then */ 181 | assertThat(friends.hasNext()).isTrue(); 182 | friends.next(); 183 | assertThat(friends.hasNext()).isFalse(); 184 | } 185 | 186 | @Test 187 | public void canAggregateWithDefaultOptions() throws Exception { 188 | collection.save(new Friend(new ObjectId(), "John")); 189 | collection.save(new Friend(new ObjectId(), "Robert")); 190 | 191 | AggregationOptions options = AggregationOptions.builder().build(); 192 | Iterable friends = collection.aggregate("{$match:{}}").options(options).as(Friend.class); 193 | 194 | assertThat(friends.iterator().hasNext()).isTrue(); 195 | for (Friend friend : friends) { 196 | assertThat(friend.getName()).isIn("John", "Robert"); 197 | } 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/impl/index/IndexTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.index; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.UUID; 6 | 7 | import org.junit.Test; 8 | 9 | import com.mongodb.BasicDBObject; 10 | import com.mongodb.DBObject; 11 | 12 | /** 13 | * Unit tests for {@link Index}. 14 | * 15 | * @author Nils Meder 16 | * 17 | */ 18 | public class IndexTest { 19 | 20 | // ------------------------------------------------------------------------------------------------- Private Constants 21 | 22 | private static final String INDEX_NAME = UUID.randomUUID().toString(); 23 | 24 | private static final boolean UNIQUE = true; 25 | 26 | private static final boolean SPARSE = false; 27 | 28 | private static final String ID_KEY = "_id"; 29 | 30 | private static final String ID = UUID.randomUUID().toString(); 31 | 32 | private static final DBObject DEFAULT_ID_KEY = new BasicDBObject(ID_KEY, 1); 33 | 34 | private static final String TOP_LEVEL_KEY = UUID.randomUUID().toString(); 35 | 36 | private static final String SECOND_LEVEL_KEY = UUID.randomUUID().toString(); 37 | 38 | private static final String SECOND_LEVEL_VALUE = UUID.randomUUID().toString(); 39 | 40 | private static final String THIRD_LEVEL_KEY = UUID.randomUUID().toString(); 41 | 42 | private static final String FOURTH_LEVEL_KEY = UUID.randomUUID().toString(); 43 | 44 | private static final String FIFTH_LEVEL_KEY = UUID.randomUUID().toString(); 45 | 46 | private static final String FIFTH_LEVEL_VALUE = UUID.randomUUID().toString(); 47 | 48 | // ---------------------------------------------------------------------------------------------------- Public Methods 49 | 50 | /** 51 | * Test {@link Index#embedded(DBObject)} with an expended {@link DBObject}. 52 | */ 53 | @Test 54 | public void testEmbeddedExpandedDBObject() { 55 | final DBObject sourceObject = new BasicDBObject(); 56 | sourceObject.put(ID_KEY, ID); 57 | sourceObject.put(TOP_LEVEL_KEY, new BasicDBObject(SECOND_LEVEL_KEY, SECOND_LEVEL_VALUE)); 58 | 59 | final Index iut = new Index(INDEX_NAME, DEFAULT_ID_KEY, UNIQUE, SPARSE); 60 | final DBObject resultObject = iut.embedded(sourceObject); 61 | 62 | assertThat(resultObject).isEqualTo(sourceObject); 63 | assertThat(resultObject.containsField(ID_KEY)).isTrue(); 64 | assertThat(resultObject.containsField(TOP_LEVEL_KEY)).isTrue(); 65 | 66 | final DBObject topLevelObject = (DBObject) resultObject.get(TOP_LEVEL_KEY); 67 | assertThat(topLevelObject.containsField(SECOND_LEVEL_KEY)).isTrue(); 68 | 69 | final String secondLevelValue = (String) topLevelObject.get(SECOND_LEVEL_KEY); 70 | assertThat(secondLevelValue).isEqualTo(SECOND_LEVEL_VALUE); 71 | } 72 | 73 | /** 74 | * Test {@link Index#embedded(DBObject)} with an flattened {@link DBObject}. 75 | */ 76 | @Test 77 | public void testEmbeddedFlattenedDBObject() { 78 | final DBObject sourceObject = new BasicDBObject(); 79 | sourceObject.put(ID_KEY, ID); 80 | sourceObject.put(TOP_LEVEL_KEY + "." + SECOND_LEVEL_KEY, SECOND_LEVEL_VALUE); 81 | 82 | final Index iut = new Index(INDEX_NAME, DEFAULT_ID_KEY, UNIQUE, SPARSE); 83 | final DBObject resultObject = iut.embedded(sourceObject); 84 | 85 | assertThat(resultObject).isEqualTo(sourceObject); 86 | assertThat(resultObject.containsField(ID_KEY)).isTrue(); 87 | assertThat(resultObject.containsField(TOP_LEVEL_KEY)).isTrue(); 88 | 89 | final DBObject topLevelObject = (DBObject) resultObject.get(TOP_LEVEL_KEY); 90 | assertThat(topLevelObject.containsField(SECOND_LEVEL_KEY)).isTrue(); 91 | 92 | final String secondLevelValue = (String) topLevelObject.get(SECOND_LEVEL_KEY); 93 | assertThat(secondLevelValue).isEqualTo(SECOND_LEVEL_VALUE); 94 | } 95 | 96 | /** 97 | * Test {@link Index#embedded(DBObject)} with an flattened {@link DBObject} 98 | * with five levels depth. 99 | */ 100 | @Test 101 | public void testEmbeddedFlattenedDBObjectWithFiveLevelsDepth() { 102 | final DBObject sourceObject = new BasicDBObject(); 103 | sourceObject.put(ID_KEY, ID); 104 | sourceObject.put(TOP_LEVEL_KEY + "." + SECOND_LEVEL_KEY + "." + THIRD_LEVEL_KEY + "." + FOURTH_LEVEL_KEY + "." 105 | + FIFTH_LEVEL_KEY, FIFTH_LEVEL_VALUE); 106 | 107 | final Index iut = new Index(INDEX_NAME, DEFAULT_ID_KEY, UNIQUE, SPARSE); 108 | final DBObject resultObject = iut.embedded(sourceObject); 109 | 110 | assertThat(resultObject).isEqualTo(sourceObject); 111 | assertThat(resultObject.containsField(ID_KEY)).isTrue(); 112 | assertThat(resultObject.containsField(TOP_LEVEL_KEY)).isTrue(); 113 | 114 | final DBObject topLevelObject = (DBObject) resultObject.get(TOP_LEVEL_KEY); 115 | assertThat(topLevelObject.containsField(SECOND_LEVEL_KEY)).isTrue(); 116 | 117 | final DBObject secondLevelObject = (DBObject) topLevelObject.get(SECOND_LEVEL_KEY); 118 | assertThat(secondLevelObject.containsField(THIRD_LEVEL_KEY)).isTrue(); 119 | 120 | final DBObject thirdLevelObject = (DBObject) secondLevelObject.get(THIRD_LEVEL_KEY); 121 | assertThat(thirdLevelObject.containsField(FOURTH_LEVEL_KEY)).isTrue(); 122 | 123 | final DBObject fourthLevelObject = (DBObject) thirdLevelObject.get(FOURTH_LEVEL_KEY); 124 | assertThat(fourthLevelObject.containsField(FIFTH_LEVEL_KEY)).isTrue(); 125 | 126 | final String fithLevelValue = (String) fourthLevelObject.get(FIFTH_LEVEL_KEY); 127 | assertThat(fithLevelValue).isEqualTo(FIFTH_LEVEL_VALUE); 128 | } 129 | 130 | /** 131 | * Test {@link Index#embedded(DBObject)} with an flattened {@link DBObject} 132 | * with two elements on the same level. 133 | */ 134 | @Test 135 | public void testEmbeddedFlattenedDBObjectWithTwoElementOnTheSameLevel() { 136 | final DBObject sourceObject = new BasicDBObject(); 137 | sourceObject.put(ID_KEY, ID); 138 | sourceObject.put(TOP_LEVEL_KEY + "." + SECOND_LEVEL_KEY, SECOND_LEVEL_VALUE); 139 | sourceObject.put(TOP_LEVEL_KEY + "." + FIFTH_LEVEL_KEY, FIFTH_LEVEL_VALUE); 140 | 141 | final Index iut = new Index(INDEX_NAME, DEFAULT_ID_KEY, UNIQUE, SPARSE); 142 | final DBObject resultObject = iut.embedded(sourceObject); 143 | 144 | assertThat(resultObject).isEqualTo(sourceObject); 145 | assertThat(resultObject.containsField(ID_KEY)).isTrue(); 146 | assertThat(resultObject.containsField(TOP_LEVEL_KEY)).isTrue(); 147 | 148 | final DBObject topLevelObject = (DBObject) resultObject.get(TOP_LEVEL_KEY); 149 | assertThat(topLevelObject.containsField(SECOND_LEVEL_KEY)).isTrue(); 150 | assertThat(topLevelObject.containsField(FIFTH_LEVEL_KEY)).isTrue(); 151 | 152 | final String secondLevelObject = (String) topLevelObject.get(SECOND_LEVEL_KEY); 153 | assertThat(secondLevelObject).isEqualTo(SECOND_LEVEL_VALUE); 154 | 155 | final String fithLevelValue = (String) topLevelObject.get(FIFTH_LEVEL_KEY); 156 | assertThat(fithLevelValue).isEqualTo(FIFTH_LEVEL_VALUE); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/com/github/fakemongo/Fongo.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo; 2 | 3 | import com.mongodb.DB; 4 | import com.mongodb.FongoDB; 5 | import com.mongodb.MockMongoClient; 6 | import com.mongodb.MongoClient; 7 | import com.mongodb.ReadConcern; 8 | import com.mongodb.ReadPreference; 9 | import com.mongodb.ServerAddress; 10 | import com.mongodb.WriteConcern; 11 | import com.mongodb.binding.ConnectionSource; 12 | import com.mongodb.binding.ReadBinding; 13 | import com.mongodb.binding.WriteBinding; 14 | import com.mongodb.client.MongoDatabase; 15 | import com.mongodb.connection.ServerVersion; 16 | import com.mongodb.operation.OperationExecutor; 17 | import com.mongodb.operation.ReadOperation; 18 | import com.mongodb.operation.WriteOperation; 19 | import java.net.InetSocketAddress; 20 | import java.util.ArrayList; 21 | import java.util.Collection; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.concurrent.ConcurrentHashMap; 25 | import org.bson.codecs.configuration.CodecRegistry; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | /** 30 | * Faked out version of com.mongodb.Mongo 31 | *

32 | * This class doesn't implement Mongo, but does provide the same basic interface 33 | *

34 | * Usage: 35 | *
 36 |  * {@code
 37 |  * Fongo fongo = new Fongo("test server");
 38 |  * com.mongodb.DB db = fongo.getDB("mydb");
 39 |  * // if you need an instance of com.mongodb.Mongo
 40 |  * com.mongodb.MongoClient mongo = fongo.getMongo();
 41 |  * }
 42 |  * 
43 | * 44 | * @author jon 45 | * @author twillouer 46 | */ 47 | public class Fongo implements OperationExecutor { 48 | private final static Logger LOG = LoggerFactory.getLogger(Fongo.class); 49 | 50 | public static final ServerVersion V3_2_SERVER_VERSION = new ServerVersion(3, 2); 51 | public static final ServerVersion V3_3_SERVER_VERSION = new ServerVersion(3, 3); 52 | public static final ServerVersion V3_SERVER_VERSION = new ServerVersion(3, 0); 53 | public static final ServerVersion OLD_SERVER_VERSION = new ServerVersion(0, 0); 54 | public static final ServerVersion DEFAULT_SERVER_VERSION = V3_2_SERVER_VERSION; 55 | 56 | private final Map dbMap = new ConcurrentHashMap(); 57 | private final ServerAddress serverAddress; 58 | private final MongoClient mongo; 59 | private final String name; 60 | private final ServerVersion serverVersion; 61 | private final CodecRegistry codecRegistry; 62 | 63 | /** 64 | * @param name Used only for a nice toString in case you have multiple instances 65 | */ 66 | public Fongo(final String name) { 67 | this(name, DEFAULT_SERVER_VERSION); 68 | } 69 | 70 | /** 71 | * @param name Used only for a nice toString in case you have multiple instances 72 | * @param serverVersion version of the server to use for fongo. 73 | */ 74 | public Fongo(final String name, final ServerVersion serverVersion) { 75 | this(name, serverVersion, MongoClient.getDefaultCodecRegistry()); 76 | } 77 | 78 | /** 79 | * @param name Used only for a nice toString in case you have multiple instances 80 | * @param serverVersion version of the server to use for fongo. 81 | * @param codecRegistry the codec registry used by fongo. 82 | */ 83 | public Fongo(final String name, final ServerVersion serverVersion, final CodecRegistry codecRegistry) { 84 | this.name = name; 85 | this.serverAddress = new ServerAddress(new InetSocketAddress(ServerAddress.defaultHost(), ServerAddress.defaultPort())); 86 | this.serverVersion = serverVersion; 87 | this.codecRegistry = codecRegistry; 88 | this.mongo = createMongo(); 89 | } 90 | 91 | /** 92 | * equivalent to getDB in driver 93 | * multiple calls to this method return the same DB instance 94 | * 95 | * @param dbname name of the db. 96 | * @return the DB associated to this name. 97 | */ 98 | public FongoDB getDB(String dbname) { 99 | synchronized (dbMap) { 100 | FongoDB fongoDb = dbMap.get(dbname); 101 | if (fongoDb == null) { 102 | fongoDb = new FongoDB(this, dbname); 103 | dbMap.put(dbname, fongoDb); 104 | } 105 | return fongoDb; 106 | } 107 | } 108 | 109 | public synchronized MongoDatabase getDatabase(final String databaseName) { 110 | return mongo.getDatabase(databaseName); 111 | } 112 | 113 | /** 114 | * Get databases that have been used 115 | * 116 | * @return database names. 117 | */ 118 | public Collection getUsedDatabases() { 119 | return new ArrayList(dbMap.values()); 120 | } 121 | 122 | /** 123 | * Get database names that have been used 124 | * 125 | * @return database names. 126 | */ 127 | public List getDatabaseNames() { 128 | return new ArrayList(dbMap.keySet()); 129 | } 130 | 131 | /** 132 | * Drop db and all data from memory 133 | * 134 | * @param dbName name of the database. 135 | */ 136 | public void dropDatabase(String dbName) { 137 | FongoDB db = dbMap.remove(dbName); 138 | if (db != null) { 139 | db.dropDatabase(); 140 | } 141 | } 142 | 143 | /** 144 | * This will always be localhost:27017 145 | * 146 | * @return the server address. 147 | */ 148 | public ServerAddress getServerAddress() { 149 | return serverAddress; 150 | } 151 | 152 | /** 153 | * A mocked out instance of com.mongodb.Mongo 154 | * All methods calls are intercepted and execute associated Fongo method 155 | * 156 | * @return the mongo client 157 | */ 158 | public MongoClient getMongo() { 159 | return this.mongo; 160 | } 161 | 162 | public WriteConcern getWriteConcern() { 163 | return mongo.getWriteConcern(); 164 | } 165 | 166 | public ReadConcern getReadConcern() { 167 | return mongo.getReadConcern(); 168 | } 169 | 170 | public CodecRegistry getCodecRegistry() { 171 | return codecRegistry; 172 | } 173 | 174 | private MongoClient createMongo() { 175 | return MockMongoClient.create(this); 176 | } 177 | 178 | public T execute(final ReadOperation operation, final ReadPreference readPreference) { 179 | return operation.execute(new ReadBinding() { 180 | @Override 181 | public ReadPreference getReadPreference() { 182 | return readPreference; 183 | } 184 | 185 | @Override 186 | public ConnectionSource getReadConnectionSource() { 187 | return new FongoConnectionSource(Fongo.this); 188 | } 189 | 190 | @Override 191 | public ReadBinding retain() { 192 | return this; 193 | } 194 | 195 | @Override 196 | public int getCount() { 197 | return 0; 198 | } 199 | 200 | @Override 201 | public void release() { 202 | 203 | } 204 | }); 205 | } 206 | 207 | public T execute(final WriteOperation operation) { 208 | return operation.execute(new WriteBinding() { 209 | @Override 210 | public ConnectionSource getWriteConnectionSource() { 211 | return new FongoConnectionSource(Fongo.this); 212 | } 213 | 214 | @Override 215 | public WriteBinding retain() { 216 | return this; 217 | } 218 | 219 | @Override 220 | public int getCount() { 221 | return 0; 222 | } 223 | 224 | @Override 225 | public void release() { 226 | 227 | } 228 | }); 229 | } 230 | 231 | 232 | @Override 233 | public String toString() { 234 | return "Fongo (" + this.name + ")"; 235 | } 236 | 237 | public ServerVersion getServerVersion() { 238 | return serverVersion; 239 | } 240 | 241 | } 242 | -------------------------------------------------------------------------------- /src/test/java/com/github/fakemongo/impl/aggregation/ReplaceRootTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fakemongo.impl.aggregation; 2 | 3 | import com.github.fakemongo.junit.FongoRule; 4 | import com.mongodb.AggregationOutput; 5 | import com.mongodb.BasicDBList; 6 | import com.mongodb.DBCollection; 7 | import com.mongodb.DBObject; 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.Iterator; 14 | import java.util.List; 15 | 16 | import static org.junit.Assert.assertNotNull; 17 | import static org.junit.Assert.assertTrue; 18 | 19 | /** 20 | * Created by rkolliva 21 | * 1/28/17. 22 | */ 23 | public class ReplaceRootTest { 24 | 25 | private static final String REPLACE_ROOT_ARRAY = "[{ \"_id\" : 1, \"name\" : \"Susan\",\n" + 26 | " \"phones\" : [ { \"cell\" : \"555-653-6527\" },\n" + 27 | " { \"home\" : \"555-965-2454\" } ] },\n" + 28 | "{ \"_id\" : 2, \"name\" : \"Mark\",\n" + 29 | " \"phones\" : [ { \"cell\" : \"555-445-8767\" },\n" + 30 | " { \"home\" : \"555-322-2774\" } ] }]"; 31 | 32 | private static final String REPLACE_ROOT_NEW_DOC = "[{ \"_id\" : 1, \"first_name\" : \"Gary\", " + 33 | "\"last_name\" : \"Sheffield\", \"city\" : \"New York\" },\n" + 34 | "{ \"_id\" : 2, \"first_name\" : \"Nancy\", " + 35 | "\"last_name\" : \"Walker\", \"city\" : \"Anaheim\" },\n" + 36 | "{ \"_id\" : 3, \"first_name\" : \"Peter\", " + 37 | "\"last_name\" : \"Sumner\", \"city\" : \"Toledo\" }]"; 38 | 39 | private static final String REPLACE_ROOT_EMBEDDED_DOC = "[{\n" + 40 | " \"_id\" : 1,\n" + 41 | " \"fruit\" : [ \"apples\", \"oranges\" ],\n" + 42 | " \"in_stock\" : { \"oranges\" : 20, \"apples\" : 60 },\n" + 43 | " \"on_order\" : { \"oranges\" : 35, \"apples\" : 75 }\n" + 44 | "},\n" + 45 | "{\n" + 46 | " \"_id\" : 2,\n" + 47 | " \"vegetables\" : [ \"beets\", \"yams\" ],\n" + 48 | " \"in_stock\" : { \"beets\" : 130, \"yams\" : 200 },\n" + 49 | " \"on_order\" : { \"beets\" : 90, \"yams\" : 145 }\n" + 50 | "}]"; 51 | 52 | 53 | @Rule 54 | public FongoRule fongoRule = new FongoRule(false); 55 | 56 | @Test 57 | public void mustReplaceRootWithEmbeddedDocument() throws Exception { 58 | DBCollection collection = fongoRule.newCollection(); 59 | fongoRule.insertJSON(collection, REPLACE_ROOT_EMBEDDED_DOC); 60 | DBObject bucket = fongoRule.parseDBObject("{$replaceRoot: { newRoot: \"$in_stock\" }}"); 61 | List pipeline = new ArrayList(); 62 | pipeline.add(bucket); 63 | AggregationOutput output = collection.aggregate(pipeline); 64 | Iterable result = output.results(); 65 | assertNotNull(result); 66 | Iterator iterator = result.iterator(); 67 | assertTrue(((List)result).size() == 2); 68 | while(iterator.hasNext()) { 69 | DBObject object = iterator.next(); 70 | if(object.containsField("oranges")) { 71 | assertTrue(((Integer)object.get("oranges")) == 20); 72 | } 73 | if(object.containsField("yams")) { 74 | assertTrue(((Integer)object.get("yams")) == 200); 75 | } 76 | if(object.containsField("beets")) { 77 | assertTrue(((Integer)object.get("beets")) == 130); 78 | } 79 | if(object.containsField("apples")) { 80 | assertTrue(((Integer)object.get("apples")) == 60); 81 | } 82 | 83 | } 84 | } 85 | 86 | @Test 87 | public void mustReplaceRootWithNewDocument() throws Exception { 88 | DBCollection collection = fongoRule.newCollection(); 89 | fongoRule.insertJSON(collection, REPLACE_ROOT_NEW_DOC); 90 | DBObject bucket = fongoRule.parseDBObject("{\n" + 91 | " $replaceRoot: {\n" + 92 | " newRoot: {\n" + 93 | " full_name: {\n" + 94 | " $concat : [ \"$first_name\", \" \", \"$last_name\" ]\n" + 95 | " }\n" + 96 | " }\n" + 97 | " }}"); 98 | List pipeline = new ArrayList(); 99 | pipeline.add(bucket); 100 | AggregationOutput output = collection.aggregate(pipeline); 101 | Iterable result = output.results(); 102 | assertNotNull(result); 103 | Iterator iterator = result.iterator(); 104 | assertTrue(((List)result).size() == 3); 105 | List expectedResults = Arrays.asList("Gary Sheffield", "Nancy Walker", "Peter Sumner"); 106 | while(iterator.hasNext()) { 107 | DBObject object = iterator.next(); 108 | assertTrue(object.containsField("full_name")); 109 | Object value = object.get("full_name"); 110 | assertNotNull(value); 111 | assertTrue(String.class.isAssignableFrom(value.getClass())); 112 | String valueStr = (String)value; 113 | assertTrue(expectedResults.indexOf(valueStr) != -1); 114 | } 115 | } 116 | 117 | @Test 118 | public void mustReplaceRootWithArray() throws Exception { 119 | DBCollection collection = fongoRule.newCollection(); 120 | fongoRule.insertJSON(collection, REPLACE_ROOT_ARRAY); 121 | DBObject aggQuery = fongoRule.parseDBObject("[{\n" + 122 | " $unwind: \"$phones\"\n" + 123 | " },\n" + 124 | " {\n" + 125 | " $match: { \"phones.cell\" : { $exists: true } }\n" + 126 | " },\n" + 127 | " {\n" + 128 | " $replaceRoot: { newRoot: \"$phones\"}\n" + 129 | " }]"); 130 | List pipeline = new ArrayList(); 131 | for (Object o : ((BasicDBList) aggQuery)) { 132 | pipeline.add((DBObject) o); 133 | } 134 | AggregationOutput output = collection.aggregate(pipeline); 135 | Iterable result = output.results(); 136 | assertNotNull(result); 137 | Iterator iterator = result.iterator(); 138 | assertTrue(((List)result).size() == 2); 139 | List expectedResults = Arrays.asList("555-653-6527", "555-445-8767"); 140 | while(iterator.hasNext()) { 141 | DBObject object = iterator.next(); 142 | assertTrue(object.containsField("cell")); 143 | Object value = object.get("cell"); 144 | assertNotNull(value); 145 | assertTrue(String.class.isAssignableFrom(value.getClass())); 146 | String valueStr = (String)value; 147 | assertTrue(expectedResults.indexOf(valueStr) != -1); 148 | } 149 | } 150 | 151 | } --------------------------------------------------------------------------------