Elasticsearch, MongoDB ile nasıl kullanılır?


152

MongoDB'deki Koleksiyonlar'ı endekslemek için Elasticsearch for MongoDB'yi yapılandırmayla ilgili birçok blog ve siteden geçtim, ancak hiçbiri basit değildi.

Lütfen elasticsearch'ü yüklemek için aşağıdakileri içermesi gereken adım adım bir süreci açıklayın:

  • yapılandırma
  • tarayıcıda çalıştır

Node.js'yi express.js ile kullanıyorum, bu yüzden lütfen buna göre yardım edin.


4
Not: Nehirler kullanımdan kaldırıldı
abdul qayyum

Yanıtlar:


287

Bu yanıt , MongoDB, Elasticsearch ve AngularJS ile işlevsel bir arama bileşeni oluşturma hakkındaki bu öğreticiyi izlemenizi sağlamak için yeterli olmalıdır .

Bir API'den alınan verilerle yönlü aramayı kullanmak istiyorsanız, Matthiasn'in BirdWatch Repo'su bakmak isteyebileceğiniz bir şeydir.

Böylece, MongoDB'yi yeni bir EC2 Ubuntu 14.04 örneğinde bir NodeJS, Express uygulamasında kullanmak üzere endekslemek için tek bir düğüm Elasticsearch "kümesi" ayarlayabileceksiniz.

Her şeyin güncel olduğundan emin olun.

sudo apt-get update

NodeJS'yi yükleyin.

sudo apt-get install nodejs
sudo apt-get install npm

MongoDB'yi yükleyin - Bu adımlar doğrudan MongoDB belgelerinden yapılır. Hangi sürümü rahat ederseniz seçin. Ben v2.4.9 ile yapışıyorum çünkü MongoDB-River sorunsuz bir şekilde desteklediği en son sürüm gibi görünüyor .

MongoDB genel GPG Anahtarını içe aktarın.

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10

Kaynak listenizi güncelleyin.

echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list

10gen paketini alın.

sudo apt-get install mongodb-10gen

Son sürümü istemiyorsanız sürümünüzü seçin. Ortamınızı bir Windows 7 veya 8 makinesine kuruyorsanız, hizmet olarak çalıştırırken bazı hatalar çalışana kadar v2.6'dan uzak durun.

apt-get install mongodb-10gen=2.4.9

Güncelleme sırasında MongoDB kurulumunuzun sürümünün engellenmesini önleyin.

echo "mongodb-10gen hold" | sudo dpkg --set-selections

MongoDB hizmetini başlatın.

sudo service mongodb start

Veritabanı dosyalarınız varsayılan olarak / var / lib / mongo ve günlük dosyalarınız / var / log / mongo olarak ayarlanmıştır.

Mongo kabuğundan bir veritabanı oluşturun ve kukla verileri ona aktarın.

mongo YOUR_DATABASE_NAME
db.createCollection(YOUR_COLLECTION_NAME)
for (var i = 1; i <= 25; i++) db.YOUR_COLLECTION_NAME.insert( { x : i } )

Şimdi bağımsız MongoDB çoğaltma kümesine dönüştürmek için .

İlk önce işlemi kapatın.

mongo YOUR_DATABASE_NAME
use admin
db.shutdownServer()

Şimdi MongoDB'yi bir hizmet olarak çalıştırıyoruz, bu nedenle mongod işlemini yeniden başlattığımızda komut satırı bağımsız değişkeninde "--replSet rs0" seçeneğini iletmiyoruz. Bunun yerine, mongod.conf dosyasına koyduk.

vi /etc/mongod.conf

Bu satırları ekleyin, db ve log yollarınız için subbing.

replSet=rs0
dbpath=YOUR_PATH_TO_DATA/DB
logpath=YOUR_PATH_TO_LOG/MONGO.LOG

Şimdi kopya kümesini başlatmak için mongo kabuğunu tekrar açın.

mongo DATABASE_NAME
config = { "_id" : "rs0", "members" : [ { "_id" : 0, "host" : "127.0.0.1:27017" } ] }
rs.initiate(config)
rs.slaveOk() // allows read operations to run on secondary members.

Şimdi Elasticsearch'ü yükleyin. Ben sadece bu yardımcı Gist'i takip ediyorum .

Java'nın kurulu olduğundan emin olun.

sudo apt-get install openjdk-7-jre-headless -y

Mongo-River eklenti hatası v1.2.1'de düzeltilene kadar v1.1.x ile devam edin.

wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.1.1.deb
sudo dpkg -i elasticsearch-1.1.1.deb

curl -L http://github.com/elasticsearch/elasticsearch-servicewrapper/tarball/master | tar -xz
sudo mv *servicewrapper*/service /usr/local/share/elasticsearch/bin/
sudo rm -Rf *servicewrapper*
sudo /usr/local/share/elasticsearch/bin/service/elasticsearch install
sudo ln -s `readlink -f /usr/local/share/elasticsearch/bin/service/elasticsearch` /usr/local/bin/rcelasticsearch

Şimdilik yalnızca tek bir düğümde geliştiriyorsanız /etc/elasticsearch/elasticsearch.yml dosyasında aşağıdaki yapılandırma seçeneklerinin etkinleştirildiğinden emin olun:

cluster.name: "MY_CLUSTER_NAME"
node.local: true

Elasticsearch hizmetini başlatın.

sudo service elasticsearch start

Çalıştığını doğrulayın.

curl http://localhost:9200

Eğer böyle bir şey görürsen iyisin.

{
  "status" : 200,
  "name" : "Chi Demon",
  "version" : {
    "number" : "1.1.2",
    "build_hash" : "e511f7b28b77c4d99175905fac65bffbf4c80cf7",
    "build_timestamp" : "2014-05-22T12:27:39Z",
    "build_snapshot" : false,
    "lucene_version" : "4.7"
  },
  "tagline" : "You Know, for Search"
}

Şimdi MonsticsDB ile oynayabilmesi için Elasticsearch eklentilerini kurun.

bin/plugin --install com.github.richardwilly98.elasticsearch/elasticsearch-river-mongodb/1.6.0
bin/plugin --install elasticsearch/elasticsearch-mapper-attachments/1.6.0

Bu iki eklenti gerekli değildir, ancak sorguları test etmek ve dizinlerinizdeki değişiklikleri görselleştirmek için iyidir.

bin/plugin --install mobz/elasticsearch-head
bin/plugin --install lukas-vlcek/bigdesk

Elasticsearch'ü yeniden başlatın.

sudo service elasticsearch restart

Sonunda bir koleksiyonu MongoDB'den indeksleyin.

curl -XPUT localhost:9200/_river/DATABASE_NAME/_meta -d '{
  "type": "mongodb",
  "mongodb": {
    "servers": [
      { "host": "127.0.0.1", "port": 27017 }
    ],
    "db": "DATABASE_NAME",
    "collection": "ACTUAL_COLLECTION_NAME",
    "options": { "secondary_read_preference": true },
    "gridfs": false
  },
  "index": {
    "name": "ARBITRARY INDEX NAME",
    "type": "ARBITRARY TYPE NAME"
  }
}'

Dizininizin Elasticsearch'te olup olmadığını kontrol edin

curl -XGET http://localhost:9200/_aliases

Küme sağlığınızı kontrol edin.

curl -XGET 'http://localhost:9200/_cluster/health?pretty=true'

Atanmamış parçalarla muhtemelen sarıdır. Elasticsearch'e ne ile çalışmak istediğimizi söylemeliyiz.

curl -XPUT 'localhost:9200/_settings' -d '{ "index" : { "number_of_replicas" : 0 } }'

Küme sağlığını tekrar kontrol edin. Şimdi yeşil olmalı.

curl -XGET 'http://localhost:9200/_cluster/health?pretty=true'

Git oyna.


@ Duck5auce, express.js tarafından sonucun (elastik arama sonucu) nasıl elde edileceği ve tarayıcıda yeşim veya ejs şablonu kullanılarak örneğin app.get ('search = "google"', işlev (req) ,)} + {) res; ve harika cevap için teşekkür ederim
bibin david

@bibindavid Bu kaynağı kontrol ederdim. Filtrelenmiş sorguları diğer iki özel modül aracılığıyla ilettiğiniz bir sunucu tarafı ES istemci modülü oluşturarak size yol gösterir. Verilerin oluşturulması hala istemcide ele alınır, ancak bu iyi bir başlangıç ​​noktası olmalıdır. sahan.me/posts/dabbling-in-elasticsearch-part-2-with-nodejs Github burada yer Repo: github.com/sahan/sahan.github.io/tree/master/resources/...
Donald Gary

U pls bana hangisinin daha iyi mongoosastic olacağını veya mongose ​​ve elasticsearch modülünü farklı kullanarak söyleyebilir misin ??
Sudhanshu Gaur

7
Onun duck5auce mükemmel cevap beri bir yıl oldu. İnsanların artık bir MongoDB kümesini Elastik Arama ile gerçek zamanlı olarak senkronize etmek için 10jen [mongo konektör] [1] kullandığını düşünün. MongoDB oplogunu takip eder. [1]: github.com/10gen-labs/mongo-connector/wiki/…
Andrew Betts

8
@ duck5auce Lütfen bu yanıtı güncelleyin, güncel değil. Nehir kullanımdan kaldırıldı
tsturzl

35

Nehrin kullanılması, operasyonunuz ölçeklendiğinde sorunlar gösterebilir. River yoğun operasyonda bir ton hafıza kullanacak. Kendi elasticsearch modellerinizi uygulamanızı öneririm, ya da mongoose kullanıyorsanız, elasticsearch modellerinizi doğrudan bunun üzerine oluşturabilir veya bunu sizin için esas olarak yapan mongoosastic kullanabilirsiniz .

Mongodb Nehri'nin bir başka dezavantajı, mongodb 2.4.x dalı ve Elastik Arama 0.90.x kullanarak sıkışıp kalmanızdır. Çok güzel özellikleri kaçırdığınızı görmeye başlayacaksınız ve mongodb nehri projesi, istikrarlı kalacak kadar hızlı bir şekilde kullanılabilir bir ürün üretmiyor. Bu Mongodb Nehri'nin kesinlikle üretime geçeceğim bir şey olmadığını söyledi. Değerinden daha fazla sorun yarattı. Ağır yük altında rasgele yazma bırakacak, çok fazla bellek tüketecek ve bunu sınırlayacak bir ayar yok. Buna ek olarak, nehir gerçek zamanlı olarak güncellenmez, mongodb'dan oplogları okur ve bu da benim deneyimimde güncellemeleri 5 dakika kadar geciktirebilir.

Son zamanlarda projemizin büyük bir bölümünü yeniden yazmak zorunda kaldık, çünkü bu elastik aramada bir şeyler ters gittiğinde haftalık bir olay. Hatta River'dan uzaklaşmak için elinden gelenin en iyisini yaptığını kabul eden bir Dev Ops danışmanı kiralamak için gitmiştik.

GÜNCELLEME: Elasticsearch-mongodb-river artık ES v1.4.0 ve mongodb v2.6.x'i destekliyor. Bununla birlikte, bu eklenti senkronize etmek için mongodb'un oploglarını okumaya çalışacağından, ağır ekleme / güncelleme işlemlerinde performans sorunlarıyla karşılaşmaya devam edersiniz. Kilit (veya mandal yerine) kilidinin açılmasından bu yana çok fazla işlem varsa, elasticsearch sunucunuzda son derece yüksek bellek kullanımı fark edeceksiniz. Büyük bir operasyon yapmayı planlıyorsanız, nehir iyi bir seçenek değildir. Elastik Arama geliştiricileri, nehir kullanmak yerine kendi diliniz için istemci kitaplığını kullanarak API'leriyle doğrudan iletişim kurarak kendi dizinlerinizi yönetmenizi önerir. Bu gerçekten nehrin amacı değil. Twitter nehri nehrin nasıl kullanılması gerektiğine harika bir örnektir. Aslında dış kaynaklardan veri elde etmenin harika bir yolu,

Ayrıca mongodb nehrinin, üçüncü bir parti tarafından sürdürülen Elastik Arama Organizasyonu tarafından korunmadığı için versiyonda geride kaldığını düşünün. Geliştirme, v1.0'ın yayınlanmasından sonra uzun bir süre v0.90 dalında kaldı ve v1.0 için bir sürüm yayınlandığında, elasticsearch v1.3.0'ı yayınlayana kadar kararlı değildi. Mongodb versiyonları da geride kalıyor. Her birinin daha sonraki bir sürümüne geçmek istediğinizde, özellikle de bu kadar ağır bir gelişme altında Elastik Arama ile, yolda çok beklenen özelliklerle kendinizi sıkı bir noktada bulabilirsiniz. Ürünümüzün temel bir parçası olan arama işlevselliğimizi sürekli olarak geliştirmeye oldukça önem verdiğimizden, en son Esnek Arama'yı takip etmek çok önemli olmuştur.

Sonuçta, kendiniz yaparsanız daha iyi bir ürün elde edersiniz. O kadar zor değil. Kodunuzda yönetmek için sadece başka bir veritabanı ve büyük bir yeniden düzenleme yapmadan mevcut modellerinize kolayca bırakılabilir.


Yayın ve yazar 2 koleksiyonda olduğundan ve referansla ve referansla bağlantı
kurduğundan,


Bu katılırsanız / nasıl açıklıyor veri ilişki elastic.co/guide/en/elasticsearch/guide/current/...
tsturzl

1
Elasticsearch, ilişkisel olandan ziyade bir belge depolama DB'sidir. Elastik aramadaki verileri ilişkilendirmek imkansız değildir, ancak denormalizasyonun gerçekleşmesi daha olasıdır, ancak ek mantıkla yönetilebilir (eklentiler vardır). Yukarıdaki bağlantıda durum olarak verileri ilişkilendirmenin en yaygın yolu, ilgili belgede bir kimlik başvurusu depolamaktır. Bu kimliği ayarlanan bir alanda sakladığınızdan emin olun not_analyzed, aksi takdirde analiz edilen alanların tokenleştirilmesi için bunu yapmakta zorlanırsınız.
tsturzl

4

Mongo konektörünü faydalı buldum. Bu form Mongo Labs (MongoDB Inc.) 'dir ve şimdi Elasticsearch 2.x ile kullanılabilir

Elastik 2.x doktor yöneticisi: https://github.com/mongodb-labs/elastic2-doc-manager

mongo-connector, bir MongoDB kümesinden Solr, Elasticsearch veya başka bir MongoDB kümesi gibi bir veya daha fazla hedef sisteme bir boru hattı oluşturur. MongoDB'deki verileri hedefe senkronize eder, sonra MongoDB oplogunu takip ederek MongoDB'deki işlemlere gerçek zamanlı olarak devam eder. Python 2.6, 2.7 ve 3.3+ ile test edilmiştir. Ayrıntılı belgeler wiki'de mevcuttur.

https://github.com/mongodb-labs/mongo-connector https://github.com/mongodb-labs/mongo-connector/wiki/Usage%20with%20ElasticSearch


4

Neredeyse gerçek zamanlı senkronizasyon ve genel çözüm elde etmek istediğinizde River iyi bir çözümdür.

MongoDB'de zaten verileriniz varsa ve "tek seferlik" gibi Elasticsearch'e kolayca göndermek istiyorsanız, paketimi Node.js'de deneyebilirsiniz https://github.com/itemsapi/elasticbulk .

Akışları destekleyen her şeyden (yani MongoDB, PostgreSQL, MySQL, JSON dosyaları, vb.) Veri aktarabilmeniz için Node.js akışlarını kullanıyor.

Elasticsearch'ten MongoDB'ye örnek:

Paketleri yükleyin:

npm install elasticbulk
npm install mongoose
npm install bluebird

Komut dosyası oluşturun, yani script.js:

const elasticbulk = require('elasticbulk');
const mongoose = require('mongoose');
const Promise = require('bluebird');
mongoose.connect('mongodb://localhost/your_database_name', {
  useMongoClient: true
});

mongoose.Promise = Promise;

var Page = mongoose.model('Page', new mongoose.Schema({
  title: String,
  categories: Array
}), 'your_collection_name');

// stream query 
var stream = Page.find({
}, {title: 1, _id: 0, categories: 1}).limit(1500000).skip(0).batchSize(500).stream();

elasticbulk.import(stream, {
  index: 'my_index_name',
  type: 'my_type_name',
  host: 'localhost:9200',
})
.then(function(res) {
  console.log('Importing finished');
})

Verilerinizi gönderin:

node script.js

Son derece hızlı değil ama milyonlarca kayıt için çalışıyor (akışlar sayesinde).


3

Mongodb 3.0'da bunu nasıl yapacağınız. Bu güzel blogu kullandım

  1. Mongodb'u yükleyin.
  2. Veri dizinleri oluşturun:
$ mkdir RANDOM_PATH/node1
$ mkdir RANDOM_PATH/node2> 
$ mkdir RANDOM_PATH/node3
  1. Mongod örneklerini başlatma
$ mongod --replSet test --port 27021 --dbpath node1
$ mongod --replSet test --port 27022 --dbpath node2
$ mongod --replSet test --port 27023 --dbpath node3
  1. Çoğaltma Kümesini yapılandırın:
$ mongo
config = {_id: 'test', members: [ {_id: 0, host: 'localhost:27021'}, {_id: 1, host: 'localhost:27022'}]};    
rs.initiate(config);
  1. Elasticsearch Kurulumu:
a. Download and unzip the [latest Elasticsearch][2] distribution

b. Run bin/elasticsearch to start the es server.

c. Run curl -XGET http://localhost:9200/ to confirm it is working.
  1. MongoDB Nehri'nin kurulması ve yapılandırılması:

$ bin / plugin - install com.github.richardwilly98.elasticsearch / elasticsearch-river-mongodb

$ bin / plugin - elasticsearch / elasticsearch-mapper-eklerini yükle

  1. “Nehir” ve İndeksi yaratın:

curl -XPUT ' http: // localhost: 8080 / _river / mongodb / _meta ' -d '{"tür": "mongodb", "mongodb": {"db": "mydb", "koleksiyon": "foo" }, "index": {"name": "name", "type": "random"}} '

  1. Tarayıcıda test et:

    http: // localhost: 9200 / _search q = ev


6
Elastik Arama nehir eklentilerini kullanımdan kaldırmıştır. Bu kesinlikle bir arama dizinini korumanın en iyi yolu değildir.
tsturzl

3

Burada MongoDB verilerinizi Elasticsearch'e taşımak için iyi bir seçenek buldum. Mongodb'u gerçek zamanlı olarak elasticsearch ile senkronize eden bir go daemon. Bu Monstache. Mevcut olduğu yer: Monstache

İlk ayar kümesinin altında yapılandırmak ve kullanmak için.

Aşama 1:

C:\Program Files\MongoDB\Server\4.0\bin>mongod --smallfiles --oplogSize 50 --replSet test

Adım 2 :

C:\Program Files\MongoDB\Server\4.0\bin>mongo

C:\Program Files\MongoDB\Server\4.0\bin>mongo
MongoDB shell version v4.0.2
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 4.0.2
Server has startup warnings:
2019-01-18T16:56:44.931+0530 I CONTROL  [initandlisten]
2019-01-18T16:56:44.931+0530 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2019-01-18T16:56:44.931+0530 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2019-01-18T16:56:44.931+0530 I CONTROL  [initandlisten]
2019-01-18T16:56:44.931+0530 I CONTROL  [initandlisten] ** WARNING: This server is bound to localhost.
2019-01-18T16:56:44.931+0530 I CONTROL  [initandlisten] **          Remote systems will be unable to connect to this server.
2019-01-18T16:56:44.931+0530 I CONTROL  [initandlisten] **          Start the server with --bind_ip <address> to specify which IP
2019-01-18T16:56:44.931+0530 I CONTROL  [initandlisten] **          addresses it should serve responses from, or with --bind_ip_all to
2019-01-18T16:56:44.931+0530 I CONTROL  [initandlisten] **          bind to all interfaces. If this behavior is desired, start the
2019-01-18T16:56:44.931+0530 I CONTROL  [initandlisten] **          server with --bind_ip 127.0.0.1 to disable this warning.
2019-01-18T16:56:44.931+0530 I CONTROL  [initandlisten]
MongoDB Enterprise test:PRIMARY>

3. Adım: Çoğaltmayı doğrulayın.

MongoDB Enterprise test:PRIMARY> rs.status();
{
        "set" : "test",
        "date" : ISODate("2019-01-18T11:39:00.380Z"),
        "myState" : 1,
        "term" : NumberLong(2),
        "syncingTo" : "",
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "heartbeatIntervalMillis" : NumberLong(2000),
        "optimes" : {
                "lastCommittedOpTime" : {
                        "ts" : Timestamp(1547811537, 1),
                        "t" : NumberLong(2)
                },
                "readConcernMajorityOpTime" : {
                        "ts" : Timestamp(1547811537, 1),
                        "t" : NumberLong(2)
                },
                "appliedOpTime" : {
                        "ts" : Timestamp(1547811537, 1),
                        "t" : NumberLong(2)
                },
                "durableOpTime" : {
                        "ts" : Timestamp(1547811537, 1),
                        "t" : NumberLong(2)
                }
        },
        "lastStableCheckpointTimestamp" : Timestamp(1547811517, 1),
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 736,
                        "optime" : {
                                "ts" : Timestamp(1547811537, 1),
                                "t" : NumberLong(2)
                        },
                        "optimeDate" : ISODate("2019-01-18T11:38:57Z"),
                        "syncingTo" : "",
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "",
                        "electionTime" : Timestamp(1547810805, 1),
                        "electionDate" : ISODate("2019-01-18T11:26:45Z"),
                        "configVersion" : 1,
                        "self" : true,
                        "lastHeartbeatMessage" : ""
                }
        ],
        "ok" : 1,
        "operationTime" : Timestamp(1547811537, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1547811537, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}
MongoDB Enterprise test:PRIMARY>

Adım 4. " https://github.com/rwynn/monstache/releases " dosyasını indirin . İndirme işlemini açın ve PATH değişkeninizi platformunuzun klasörün yolunu içerecek şekilde ayarlayın. "monstache -v" Cmd'ye gidin ve # 4.13.1 yazın. Monstache, yapılandırması için TOML biçimini kullanır. Dosyayı config.toml adlı taşıma için yapılandırın

Adım 5.

Benim config.toml ->

mongo-url = "mongodb://127.0.0.1:27017/?replicaSet=test"
elasticsearch-urls = ["http://localhost:9200"]

direct-read-namespaces = [ "admin.users" ]

gzip = true
stats = true
index-stats = true

elasticsearch-max-conns = 4
elasticsearch-max-seconds = 5
elasticsearch-max-bytes = 8000000 

dropped-collections = false
dropped-databases = false

resume = true
resume-write-unsafe = true
resume-name = "default"
index-files = false
file-highlighting = false
verbose = true
exit-after-direct-reads = false

index-as-update=true
index-oplog-time=true

6. Adım

D:\15-1-19>monstache -f config.toml

Monstache Koşu ...

Taşınan Verileri Elasticsearch'te Onayla

Mongo'da Kayıt Ekle

Monstache Etkinliği yakaladı ve verileri elasticsearch'e taşıdı


3

Mongo konektörü artık ölü göründüğünden, şirketim Elasticsearch'e çıktı vermek için Mongo değişiklik akışlarını kullanmak için bir araç oluşturmaya karar verdi.

İlk sonuçlarımız umut vericidir. Https://github.com/electionsexperts/mongo-stream adresinden kontrol edebilirsiniz . Hala geliştirilme aşamasındayız ve öneri veya katkıları memnuniyetle karşılarız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.