Skip to content

Latest commit

 

History

History
375 lines (312 loc) · 11.6 KB

MongoDB.md

File metadata and controls

375 lines (312 loc) · 11.6 KB

MongoDb

Все хранится в JSON

GUI для MongoDb - MongoDb Compass. Но можно и в Idea (даже лучше)

docker run --name mongo-otus -d mongo:latest

ИЛИ полноценная:

docker run -d -p 27017:27017 --name some-mongo -e MONGO_INITDB_ROOT_USERNAME=mongoadmin -e MONGO_INITDB_ROOT_PASSWORD=bdung mongo:latest

default port: 27017

docker-compose config:

version: '3.7'

services:
  mongo:
    image: mongo:latest
    ports:
      - '27017:27017'
    environment:
      MONGO_INITDB_ROOT_USERNAME: mongoadmin
      MONGO_INITDB_ROOT_PASSWORD: bdung
    volumes:
      - mongo-data:/data/db
    restart: always

курсы по Mongo: basics, for java dev, aggregation, spring

Плюсы:

  1. Масштабируемая
  2. Высокопроизводительная
  3. Интерфейс на JS (!)
  4. Простая в пользовании
  5. Эффективно работает с большими данными
  6. Хранит документы (документооренитрованная)
  7. Никаких JOIN-ов

Аналогия с RGB (реляционные бд)
RGB - MongoDB
• Database = Database
• Table = Collection
• Row = Document
• Column = Field
• Join = Embedding & Linking + Lookup
• Foreign Key = Reference (ссылка на другой документ)

Document (MongoDB) – Row (SQL)
• Document - документ
• По сути дела – просто JSON
• Хранится в формате BSON
• Есть _id – аналог primary key (может сама создавать) • Поддерживает отношения с другим документами – по ссылке или embedded

Collection (MongoDB) – Table (SQL)
• Collection – коллекция
• Состоит из документов

Database (MongoDB) – Database (SQL)
• Database – база данных
• Состоит из коллекций
• Коллекция сама создаётся, если к ней обращаются

Типы данных в MongoDB:

  • Geo-Coordinates [45.123,47.232]
  • String
  • Number
  • massives
  • вложенные json-документы

В монге есть возможность орагизовывать связи между документами, но лучше ВСТРАИВАТЬ. И ничего страшного, что информация дублируется

Рекомендации для организации связей в MongoDB:

  • Внедряйте, если:
    • доминирует чтение, а не запись
    • повторяемость данных не проблема
    • изменения на одной из сторон происходят чаще
  • Ссылайтесь, если:
    • много связанных сущностей
    • измения часто случаются в обеих сущностях
  • Используйте смешанный подход для лучшей производительности

Зависимости

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
    <version>3.3.2</version>
</dependency>
        <dependency>
            <groupId>com.github.cloudyrock.mongock</groupId>
            <artifactId>mongock-spring-v5</artifactId>
            <version>${mongock.version}</version>
        </dependency>

        <dependency>
            <groupId>com.github.cloudyrock.mongock</groupId>
            <artifactId>mongodb-springdata-v3-driver</artifactId>
            <version>${mongock.version}</version>
        </dependency>

<!--Встроенная монга = H2 для Реляционных-->
        <dependency>
            <groupId>de.flapdoodle.embed</groupId>
            <artifactId>de.flapdoodle.embed.mongo</artifactId>
            <version>${flapdoodle.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>de.flapdoodle.embed</groupId>
            <artifactId>de.flapdoodle.embed.mongo.spring30x</artifactId>
            <version>${flapdoodle.version}</version>
        </dependency>

Query (MongoDb и аналог на MySQL)

  • gt - greater that
  • _id выдаст даже без явного указания
  • 1 - boolean true

Еще пример поиска:

// поиск по тегу (если есть одно из значений)
db.books.find({
    "tags": {
        $in:['earlang', 'haskell']
    }
})

Пример вставки

db.products.insertOne({
     _id: 10, item: "box", qty: 20
})

Найти по id:

db.books.find({
    _id: 10
})

Найти все:

db.books.find({})

Вставить несколько записей:

db.products.insertMany([
    { _id: 11, item: "box", qty: 10 }
    { _id: 12, item: "cox", qty: 20 }
    { _id: 13, item: "fox", qty: 30 }
]
)

Сортировки + limit

db.products.find().sort({ item: 1 }).limit(5)

Агрегация (фильтрация + группировка)

//поле cust_id переименовывается в _id
//группировка по _id
//проссумировать по полю amount с именем total
db.orders.aggregate( [
    { $match: { status: A } },
    { $group: { _id: "$cust_id", total: { $sum: "$amount" } } }
]

)

Агрегация2 (поиск сущности по id + получения списка связных сущностей (знания у учителя), а именно двух его полей)

    private final MongoTemplate mongoTemplate;

    @Override
    public List<Knowledge> getTeacherExperienceById(String teacherId) {
        val aggregation = newAggregation(
            //фильтрация по id
                match(Criteria.where("id").is(teacherId))
                //был учитель и список знаний. размножаем учителя по каждому знанию в списке
                , unwind("experience")
                //убираем все поля с помощью project, остается id (по дефолту). andExclude убирает id. И после добавляем уже нужные поля в иговоую выборку
                , project().andExclude("_id").and("experience.id").as("_id")
                        .and("experience.name").as("name")
        );
        return mongoTemplate.aggregate(aggregation, Teacher.class, Knowledge.class).getMappedResults();
    }

Конфиг MongoDb-spring

spring:
  data:  
    mongodb:
      authentication-database: admin #юзать, когда задан пользователь и пароль
      username: root
      password: root
      database: user_db
      port: 27017 #если по умолчанию, можно не указывать
      host: localhost

Аннотации MongoDB. ссылка-тык

  • @Document(collection = "someCollection") = @Entity
  • @Field = @Column
  • @Id
  • @DBRef - ссылка на другой документ
  • @Transient - не отражать поле

@EnableMongoRepositories - над основным классом

Репозиторий

public interface UserRepo extends MongoRepository<User, String> {}

Словарь MongoDB

@Query кастомная, как в JPA

//поиск по имени с выводом двух полей
@Query(value = "{'firstname' : :#{#firstname}}", fields="{ 'firstName' : 1, 'lastName' : 1}")
List<Person> findByPersonFirstName (@Param("firstname") String firstName);

MongoTemplate

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Alex"));
Update update = new Update();
update.set("name", "James");
mongoTemplate.updateFirst(query, update, User.class);

Mongock

примеры в git

Миграция для MongoDb - это Mongock

Зависимости

        <dependency>
            <groupId>com.github.cloudyrock.mongock</groupId>
            <artifactId>mongock-spring-v5</artifactId>
            <version>${mongock.version}</version>
        </dependency>

        <dependency>
            <groupId>com.github.cloudyrock.mongock</groupId>
            <artifactId>mongodb-springdata-v3-driver</artifactId>
            <version>${mongock.version}</version>
        </dependency>
@EnableMongock
@Configuration
public class MongockConfig{}

Конфиг для Mongock

mongock:
  runner-type: "ApplicationRunner" # default - исп. для тестов если со Spring Shell
  #runner-type: "InitializationBean" #для работы совместно с Spring Shell

  #откуда брать changelog-и - это папка с java-классом! 
  change-logs-scan-package: 
    - ru.otus.spring.mongock.changelog

Пример changelog-а для Mongock:

import com.github.cloudyrock.mongock.ChangeLog;
import com.github.cloudyrock.mongock.ChangeSet;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repostory.PersonRepository;

@ChangeLog
public class DatabaseChangelog {

    @ChangeSet(order = "001", id = "dropDb", author = "stvort", runAlways = true)
    public void dropDb(MongoDatabase db) {
        db.drop();
    }

    @ChangeSet(order = "002", id = "insertLermontov", author = "ydvorzhetskiy")
    public void insertLermontov(MongoDatabase db) {
        MongoCollection<Document> myCollection = db.getCollection("persons");
        var doc = new Document().append("name", "Lermontov");
        myCollection.insertOne(doc);
    }

    @ChangeSet(order = "003", id = "insertPushkin", author = "stvort")
    public void insertPushkin(PersonRepository repository) {
        repository.save(new Person("Pushkin"));
    }
}

Организация удаления связных сущностей в MongoDB как в реляционных БД

Пример 1

public class UserCascadeSaveMongoEventListener
 extends AbstractMongoEventListener<Object> {
 @Autowired
 private MongoOperations mongoOperations;

 //если ивент типа User, то достаем из юзера почту, и сохраняем ее отдельно
 @Override
 public void onBeforeConvert(BeforeConvertEvent<Object> event) {
 Object source = event.getSource();
 if ((source instanceOf User) && ((User) source).getEmailAddress() != null)) {
 mongoOperations.save(((User) source).getEmailAddress());
 }
 }
}

Пример 2

@Component
@RequiredArgsConstructor
public class MongoKnowledgeCascadeDeleteEventsListener extends AbstractMongoEventListener<Knowledge> {

    private final StudentRepository studentRepository;

    @Override
    public void onAfterDelete(AfterDeleteEvent<Knowledge> event) {
        super.onAfterDelete(event);
        val source = event.getSource();
        val id = source.get("_id").toString();
        studentRepository.removeExperienceArrayElementsById(id);
    }
}

Тесты
@DataMongoTest - над тестируемым классом

Дропнуть базу данных

db.dropDatabase()

Подключиться к БД из Idea

  • подключаться не к конкретной БД, а в целом к монге

Показать базы данных

show dbs;