Pokazywanie postów oznaczonych etykietą schema. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą schema. Pokaż wszystkie posty

niedziela, 17 marca 2013

[NoSQL] MongoDB + Node.js: mongoose

Interesującą konfiguracją dla aplikacji webowych jest Node.js jako serwer http udostępniający RESTowe API oraz MongoDB jako baza danych. Za takim pomysłem przemawia przede wszystkim format danych: klient przysyła JSONa, Node.js automatycznie parsuje go do obiektu JavaScript na którym można wykonać pewne operacje a później zapisać w bazie jako JSON znowu.

Klientem MongoDB w Node.js jest mongoose. Można go pobrać z menadżera pakietów poleceniem

npm install mongoose

Po dodaniu pakietu do serwera Node.js przy użyciu konstrukcji require, można rozpocząć pracę z MongoDB. Pakiet mongoose do CRUDowych operacji wykorzystuje obiekty typu Schema. Definiując taki obiekt podajemy jakiego typu pola znajdować się będą w naszych dokumentach. Dzięki temu, wstawiając dokument do bazy mamy pewność, że wstawione zostaną jedynie te pola, które zdefiniowaliśmy w schemacie. Mechanizm ten jest konfigurowalny poprzez opcję strict. Jeżeli ustawimy ją na false, to do bazy trafiać będą całe dokumenty. Jeden schemat mapowany będzie na jedną kolekcję w bazie przy zachowaniu zasad języka angielskiego. Na przykład schemat Car zmapowany zostanie do kolekcji cars. Poniżej prosta konfiguracja:
var mongoose = require('mongoose');
mongoose.connect('localhost','mongoosetest');

var schema = mongoose.Schema({ 
    make: 'string',
    enginePower : 'number'
 }, {strict: false});

var Car = mongoose.model('Car', schema);

Dodawanie dokumentów do bazy przy odbieraniu wiadomości typu POST:
app.post('/store/', function(req, resp) {
    var nm = req.body.name;
    var pw = req.body.enginePower;
    var car = new Car({ make: nm, enginePower: pw, day: "17-03" });
    car.save(function(err){
        if(err){
            resp.send({
                ok: 'sth went wrong'
            });
        }
        else{
            resp.send({
                ok: 'everything is fine'
            });
        }       
    });    
});

Analogicznie pobieranie danych wykonujemy także na obiekcie modelu;
app.get('/store/:key', function(req, resp) {
    Car.find({make: req.params.key}, function(err, docs){
        if(err)
            resp.send(501);
        else
            resp.send(docs);
    })
});

Funkcja find może przekazywać bardziej skomplikowane zapytania MongoDB, te same, które dostępne są w shellu bazy (program mongo.exe).

Z kolei aby usunąć obiekt, używamy funkcji remove wołanej również na modelu. Jako pierwszy parametr podaje się warunek.
app.del('/store/:key', function(req, resp) {
    Car.remove({make: req.params.key}, function(err, docs){
        if(err)
            resp.send(500);
        else
            resp.send(200);
    });
});

niedziela, 30 grudnia 2012

[SQL|ORM] Entity Framework : Stored Procedures

Procedury składowane to narzędzie, z którego bardzo często warto skorzystać. Przeniesienie niektórych obliczeń na serwer bazy danych może usprawnić działanie aplikacji i uporządkować ją z logicznego punktu widzenia. Ponieważ procedury "żyją" po stronie bazy danych, należy wiedzieć, jak wywoływać je z poziomu narzędzia ORM. Poniżej kilka praktycznych przykładów w EF:

Procedura zwracająca kolekcję encji

Dla tabeli Customer mamy przykładową procedurę składowaną:

CREATE PROCEDURE dbo.GetCustomers
(@Company VARCHAR(50), @ContactTitle VARCHAR(50))
AS
 BEGIN
  SELECT * FROM Customer
  WHERE (@Company IS null OR Company = @Company) AND
  (@ContactTitle is null or ContactTitle = @ContactTitle)
 END

Po dodaniu do EDM odpowiednich encji i procedury GetCustomers, przechodzimy do okna designera. Z menu dostępnego pod prawym przyciskiem myszy wybieramy opcję Add -> Function Import...
W menu należy podać nazwę procedury, oraz określić co ma zwracać ( w tym przypadku kolekcję encji typu Customer). To wszystko, teraz w kodzie możemy na obiekcie kontekstu wołać GetCustomers.

var allCustomers = context.GetCustomers("GoShopNow.com", "Sales Manager");
foreach (var customer in allCustomers)
{
    Console.WriteLine("\t{0}", customer.Name);
}

Procedura zwracająca wartości przez parametr

Przykładowa procedura przyjmująca jeden parametr wejściowy i dwa wyjściowe, zwracająca dodatkowo kolekcję encji.

CREATE PROCEDURE dbo.GetVehiclesWithRentals
(@date DATE,
@totalRentals INT OUTPUT,
@totalPayments DECIMAL(18,2) OUTPUT)
AS
 BEGIN
 SELECT @totalRentals = COUNT(*), @totalPayments = SUM(Payment)
 FROM dbo.Rental
 WHERE RentalDate = @date
 
 SELECT DISTINCT v.*
 FROM dbo.Vehicle AS v JOIN dbo.Rental AS r
 ON v.VehicleId = r.VehicleId 
END

Procedurę taką dodajemy w sposób analogiczny, jak poprzednio. Różnice pojawiają się w kodzie. Korzystamy z obiektów typu ObjectParameter

string reportDate = "2/2/2010";
var totalRentals = new ObjectParameter("totalRentals", typeof (int));
var totalPayments = new ObjectParameter("totalPayments", typeof(decimal));
var vehicles = context.GetVehiclesWIthRentals(DateTime.Parse(reportDate), 
    totalRentals, totalPayments);
foreach (var vehicle in vehicles)
{
    Console.WriteLine("{0} {1}, {2} year", vehicle.Manufacturer, vehicle.Model,
        vehicle.Year);
}
Console.WriteLine("Total Rentals: {0}", totalRentals.Value);
Console.WriteLine("Total Payments: {0}", totalPayments.Value);

Custom Functions

Custom Functions to funkcje definiowane na poziome storage model. Nie musimy zatem (być może nie mamy dostępu) tworzyć procedur na serwerze bazy danych, a możemy je dodać do SSDL. Zatem treść procedury dodajemy do schematu bazy <Schema></Schema> edytując plik.edmx jako XML. Na przykład można dodać taką procedurę:

<Function Name="MembersWithTheMostMessages" IsComposable="false">
  <CommandText>
    select m.*
    from dbo.Member m
    join
    (
    select distinct msg.MemberId
    from dbo.Message msg where DateSent = @datesent
    ) temp on m.MemberId = temp.MemberId
  </CommandText>
  <Parameter Name="datesent" Type="date" />
</Function>

Funkcję taką wykorzystuje się tam samo jak procedurę składowaną

var members = context.MemberWithTheMostMessages(today);