COUCHDB

<- Watch the car   Oliver Kurowski, @okurow
Who am I
 Oliver Kurowski
 Degree in Computer Science Beuth School of applied Sciences Berlin


 First Computer: Apple II 1983
 Since 2003: kw automotive GmbH


 Write articles in Magazines
 2012: Book „CouchDB mit PHP“ (www.CouchDBmitPHP.de)


 Twitter: @okurow
 Mail: oliver@thinkcouchdb.com




                                      Oliver Kurowski, @okurow
TRANSFORMATIONS

                  Oliver Kurowski, @okurow
Transformation Functions
Documents are JSON objects
Results of views are also JSON objects

CouchDB can transfrom those JSON Objects on server side.

Shows:           Transformation for documents
Lists:           Transformation for views

Shows and lists are performed during request, results are not stored (like views).




                                         Oliver Kurowski, @okurow
Shows:Transform Documents I




                Oliver Kurowski, @okurow
Shows:Transform Documents I




                Oliver Kurowski, @okurow
Shows:Transform Documents I




                Oliver Kurowski, @okurow
Shows:Transform Documents I




                Oliver Kurowski, @okurow
Shows:Transform Documents I




                Oliver Kurowski, @okurow
Shows:Transform Documents I
Data document (JSON):        Show function in a design doc:
 {                           “shows“: {
 “_id“: “1“,                  “toHTML“:“function(doc,req) {
 “make“: “Audi“,                 var outS=‘‘;
 “model“: “A3“,                  outS=‘<b>‘+doc.maker+‘ ‘+doc.model+‘</b><br>`;
 “year“: 2000,                   outS+=‘Year of Prod.: ‘+doc.year+‘<br>‘;
 “price“: 5.400                  outS+=‘Price: ‘+doc.price;
 }                              return outS; }“
                              }



Result of _show/toHTML/1 :
 Audi A3
 Year of Prod.: 2000
 Price: 5.400




                                   Oliver Kurowski, @okurow
Shows:Transform Documents II
Data document (JSON):
{
    “_id“: “1“,
    “author“: “Oliver Kurowski“,
    “headline“: “CouchDB is great“,
    “body“: “CouchDb is a great database for schemeless data“ ,
    “comments“: [ {“name“: “John“, “text“: “Yes, it really is“},
                    {“name“: “Jane“, “text“: “What about couchbase?“} ]
}

“shows“: {
  “headline“:“function(doc,req) {
     var comments=0;
     if (doc.comments) { comments=doc.comments.length };
     var outS=‘<b>‘ + doc.headline + ‘</b> (‘ + comments + ‘ comments)`;
     return outS; }“,
   “detail“:“function(doc,req) {
     var outS=‘<b>‘ + doc.headline + ‘</b><br>`;
     outS+= doc.body + ‘<br>`;
     if (doc.comments && doc.comments.length>0) {
       outS+= ‘comments:<br>`;
       for (var curComment in doc.comments) {
        outS+=doc.comments[curComment].name+‘:<br>`;
        outS+=doc.comments[curComment].text+‘<br>`;
      }
   }
   return outS; }“
}
                                                                    Oliver Kurowski, @okurow
Shows:Transform Documents II
Data document (JSON):
{
    “_id“: “1“,
    “author“: “Oliver Kurowski“,
    “headline“: “CouchDB is great“,
    “body“: “CouchDb is a great database for schemeless data“ ,
    “comments“: [ {“name“: “John“, “text“: “Yes, it really is“},
                    {“name“: “Jane“, “text“: “What about couchbase?“} ]
}

“shows“: {
  “headline“:“function(doc,req) {
     var comments=0;
     if (doc.comments) { comments=doc.comments.length };
     var outS=‘<b>‘ + doc.headline + ‘</b> (‘ + comments + ‘ comments)`;
     return outS; }“,
   “detail“:“function(doc,req) {
     var outS=‘<b>‘ + doc.headline + ‘</b><br>`;
     outS+= doc.body + ‘<br>`;
     if (doc.comments && doc.comments.length>0) {
       outS+= ‘comments:<br>`;
       for (var curComment in doc.comments) {
        outS+=doc.comments[curComment].name+‘:<br>`;
        outS+=doc.comments[curComment].text+‘<br>`;
      }
   }
   return outS; }“
}
                                                                    Oliver Kurowski, @okurow
Shows:Transform Documents II
Data document (JSON):
{
    “_id“: “1“,
    “author“: “Oliver Kurowski“,
    “headline“: “CouchDB is great“,
    “body“: “CouchDb is a great database for schemeless data“ ,
    “comments“: [ {“name“: “John“, “text“: “Yes, it really is“},
                    {“name“: “Jane“, “text“: “What about couchbase?“} ]
}

“shows“: {
  “headline“:“function(doc,req) {
     var comments=0;
     if (doc.comments) { comments=doc.comments.length };
     var outS=‘<b>‘ + doc.headline + ‘</b> (‘ + comments + ‘ comments)`;
     return outS; }“,
   “detail“:“function(doc,req) {
     var outS=‘<b>‘ + doc.headline + ‘</b><br>`;
     outS+= doc.body + ‘<br>`;
     if (doc.comments && doc.comments.length>0) {
       outS+= ‘comments:<br>`;
       for (var curComment in doc.comments) {
        outS+=doc.comments[curComment].name+‘:<br>`;
        outS+=doc.comments[curComment].text+‘<br>`;
      }
   }
   return outS; }“
}
                                                                    Oliver Kurowski, @okurow
Shows:Transform Documents II
Data document (JSON):
{
    “_id“: “1“,
    “author“: “Oliver Kurowski“,
    “headline“: “CouchDB is great“,
    “body“: “CouchDb is a great database for schemeless data.“ ,
    “comments“: [ {“name“: “John“, “text“: “Yes, it really is“},
                    {“name“: “Jane“, “text“: “What about couchbase?“} ]
}

“shows“: {
  “headline“:“function(doc,req) {                                              Result of _show/headline/1 :
     var comments=0;                                                             CouchDB is great (2 comments)
     if (doc.comments) { comments=doc.comments.length };
     var outS=‘<b>‘ + doc.headline + ‘</b> (‘ + comments + ‘ comments)`;
     return outS; }“,
   “detail“:“function(doc,req) {
     var outS=‘<b>‘ + doc.headline + ‘</b><br>`;
     outS+= doc.body + ‘<br>`;
     if (doc.comments && doc.comments.length>0) {
       outS+= ‘<hr>Comments:<br>`;
       for (var curComment in doc.comments) {
        outS+=doc.comments[curComment].name+‘:<br>`;
        outS+=doc.comments[curComment].text+‘<br>`;
      }
   }
   return outS; }“
}
                                                                    Oliver Kurowski, @okurow
Shows:Transform Documents II
Data document (JSON):
{
    “_id“: “1“,
    “author“: “Oliver Kurowski“,
    “headline“: “CouchDB is great“,
    “body“: “CouchDb is a great database for schemeless data.“ ,
    “comments“: [ {“name“: “John“, “text“: “Yes, it really is“},
                    {“name“: “Jane“, “text“: “What about couchbase?“} ]
}

“shows“: {
  “headline“:“function(doc,req) {                                              Result of _show/headline/1 :
     var comments=0;                                                             CouchDB is great (2 comments)
     if (doc.comments) { comments=doc.comments.length };
     var outS=‘<b>‘ + doc.headline + ‘</b> (‘ + comments + ‘ comments)`;
     return outS; }“,
   “detail“:“function(doc,req) {
     var outS=‘<b>‘ + doc.headline + ‘</b><br>`;
                                                                               Result of _show/detail/1 :
     outS+= doc.body + ‘<br>`;                                                   CouchDB is great
     if (doc.comments && doc.comments.length>0) {                                CouchDb is a great database for schemeless data.
       outS+= ‘<hr>Comments:<br>`;
       for (var curComment in doc.comments) {                                    Comments:
        outS+=doc.comments[curComment].name+‘:<br>`;                             John:
        outS+=doc.comments[curComment].text+‘<br>`;                              Yes, it really is
      }                                                                          Jane:
   }                                                                             What about couchbase?
   return outS; }“
}
                                                                    Oliver Kurowski, @okurow
The Request Object
Shows and lists can access the Request Object containing:
(the most interesting)

Info:           Informations about the Database
Id:             called document ID (null, if not given)
requested
_path:          The request in parts (divided by /) as Array
headers:        Header, sent from the client
userCTX:        Information about the user, usefull for access limitations!
query:          Parameters of the call

The field req.query allows the use of own parameters that can be used in a
show/list function.
F.e: _show/toHTML/1?showPrice=true

handled in the show function:   if(req.query.showPrice==true) { ….}

                                         Oliver Kurowski, @okurow
Shows:Talking Parrots
Data documents :                      Design document :
{                                     {
    “_id“: “lora“,                         “_id“: “_design/parrot“,
    “name“: “Lora LoL“,                    “shows{
    “owner“: “Emily“,                       "introduce": "function(doc,req) {
    “color“: [“red“ , “#ff0000“ ] ,            var outS='';
    “languages“: [ “english“ ]                 if(doc) {     // doc with id in database found
}                                                outS+='<font color='+doc.color[1]+'>'+doc.name
                                                       +' belongs to '+doc.owner+'</font>';
{                                                if(req.query.say) {
    “_id“: “polly“,                                if(doc.languages) {
    “name“: “Polly Poll“,                            outS+=' and says: '+req.query.say;
    “owner“: “Ernest“,                             }else{
    “color“: [“blue“ , “#0000ff“ ],                  outS+='tweet';
    “languages“: [ “english“ ]                     }
}                                                }
                                               } else { // no doc found
                                                 if(req.path[5]!=undefined) { // no id given
{                                                  outS+='I dont know '+req.path[5];
    “_id“: “agnus“,                              }else{
    “name“: “Agnus Angry“,                          outS+='which parrot?';
    “owner“: “Ernest“,                           }
    “color“: [“grey“ , “#cococo“ ]            }
}                                             return outS; }“
                                          }
                                      }



                                                  Oliver Kurowski, @okurow
Shows: Talking Parrots
http://localhost:5984/parrots/_design/parrot/_show/introduce/
->which parrot?

http://localhost:5984/parrots/_design/parrot/_show/introduce/polly
->Polly Poll belongs to Ernest

http://localhost:5984/parrots/_design/parrot/_show/introduce/pollx
->I dont know pollx

http://localhost:5984/parrots/_design/parrot/_show/introduce/lora?say=Hello
->Lora Loll belongs to Emily and says: Hello

http://localhost:5984/parrots/_design/parrot/_show/introduce/agnus?say=Hello
->Agnus Angry belongs to Ernest tweet




                                       Oliver Kurowski, @okurow
List:Transform view results I




                Oliver Kurowski, @okurow
List:Transform view results I




                Oliver Kurowski, @okurow
List:Transform view results I




                Oliver Kurowski, @okurow
List:Transform view results I




                Oliver Kurowski, @okurow
List:Transform view results I




                Oliver Kurowski, @okurow
List:Transform view results I
Result of a View byPrice:                          A simple list function:
{"total_rows":5,"offset":0,                         “lists“: {
"rows":[                                              “asLI “:“function(head,req) {
{"id":"1","key":5.400,"value":“Audi-A3-                 start({'code':200,'headers':{'Content-Type':'text/html'}});
2000“}, {"id":"2","key":9.000,"value":“VW-Golf-         while(row=getRow()) {
2008“}, {"id":"3","key":12.000,"value":“VW-Polo-          send(‘<li>‘+row.value+‘ :‘ +row.key+‘</li>‘);
2010“}, {"id":"4","key":15.000,"value":“VW-Golf-        }
2009“}, {"id":"5","key":16.000,"value":“Audi-A4-      }“
2009“} ]}                                           }


result from _list/asLi/byPrice :
 •   Audi-A3-2000 : 5.400
 •   VW-Golf-2008 : 9.000
 •   VW-Polo-2010 : 12.000
 •   VW-Golf-2009 : 15.000
 •   Audi-A4-2009 : 16.000


- All arguments from a view can also be used in a list call
(startkey, endkey, skip, limit etc)

- Like Shows, you have access to the request Object with all ist possibilities.
                                                              Oliver Kurowski, @okurow
List:Transform view results II
Different headers can be sent
“lists“: {
  “asXML “:“function(head,req) {
    start({'code':200,'headers':{'Content-Type':'application/xml'}});
    var xmlS=‘<cars>‘;
    while(row=getRow()) {
      xmlS+=‘<car id=‘ + row.id + ‘>‘+row.value+‘ :‘ +row.key+‘</car>‘);
    }
    xmlS+=‘</cars>‘;
   return xmlS;
  }“
}


result from _list/asXML/byPrice :
<cars>
<car id=‘1‘>Audi-A3-2000 : 5.400</car>
<car id=‘2‘>VW-Golf-2008 : 9.000</car>
<car id=‘3‘ >VW-Polo-2010 : 12.000</car>
<car id=‘4‘>VW-Golf-2009 : 15.000</car>
<car id=‘5‘>Audi-A4-2009 : 16.000</car>
</cars>




                                                                       Oliver Kurowski, @okurow
http://apache.couchdb.org

Irc: #couchdb




Q (&A?)

                            Oliver Kurowski, @okurow

Couchdb List and Show Introduction

  • 1.
    COUCHDB <- Watch thecar Oliver Kurowski, @okurow
  • 2.
    Who am I Oliver Kurowski  Degree in Computer Science Beuth School of applied Sciences Berlin  First Computer: Apple II 1983  Since 2003: kw automotive GmbH  Write articles in Magazines  2012: Book „CouchDB mit PHP“ (www.CouchDBmitPHP.de)  Twitter: @okurow  Mail: oliver@thinkcouchdb.com Oliver Kurowski, @okurow
  • 3.
    TRANSFORMATIONS Oliver Kurowski, @okurow
  • 4.
    Transformation Functions Documents areJSON objects Results of views are also JSON objects CouchDB can transfrom those JSON Objects on server side. Shows: Transformation for documents Lists: Transformation for views Shows and lists are performed during request, results are not stored (like views). Oliver Kurowski, @okurow
  • 5.
    Shows:Transform Documents I Oliver Kurowski, @okurow
  • 6.
    Shows:Transform Documents I Oliver Kurowski, @okurow
  • 7.
    Shows:Transform Documents I Oliver Kurowski, @okurow
  • 8.
    Shows:Transform Documents I Oliver Kurowski, @okurow
  • 9.
    Shows:Transform Documents I Oliver Kurowski, @okurow
  • 10.
    Shows:Transform Documents I Datadocument (JSON): Show function in a design doc: { “shows“: { “_id“: “1“, “toHTML“:“function(doc,req) { “make“: “Audi“, var outS=‘‘; “model“: “A3“, outS=‘<b>‘+doc.maker+‘ ‘+doc.model+‘</b><br>`; “year“: 2000, outS+=‘Year of Prod.: ‘+doc.year+‘<br>‘; “price“: 5.400 outS+=‘Price: ‘+doc.price; } return outS; }“ } Result of _show/toHTML/1 : Audi A3 Year of Prod.: 2000 Price: 5.400 Oliver Kurowski, @okurow
  • 11.
    Shows:Transform Documents II Datadocument (JSON): { “_id“: “1“, “author“: “Oliver Kurowski“, “headline“: “CouchDB is great“, “body“: “CouchDb is a great database for schemeless data“ , “comments“: [ {“name“: “John“, “text“: “Yes, it really is“}, {“name“: “Jane“, “text“: “What about couchbase?“} ] } “shows“: { “headline“:“function(doc,req) { var comments=0; if (doc.comments) { comments=doc.comments.length }; var outS=‘<b>‘ + doc.headline + ‘</b> (‘ + comments + ‘ comments)`; return outS; }“, “detail“:“function(doc,req) { var outS=‘<b>‘ + doc.headline + ‘</b><br>`; outS+= doc.body + ‘<br>`; if (doc.comments && doc.comments.length>0) { outS+= ‘comments:<br>`; for (var curComment in doc.comments) { outS+=doc.comments[curComment].name+‘:<br>`; outS+=doc.comments[curComment].text+‘<br>`; } } return outS; }“ } Oliver Kurowski, @okurow
  • 12.
    Shows:Transform Documents II Datadocument (JSON): { “_id“: “1“, “author“: “Oliver Kurowski“, “headline“: “CouchDB is great“, “body“: “CouchDb is a great database for schemeless data“ , “comments“: [ {“name“: “John“, “text“: “Yes, it really is“}, {“name“: “Jane“, “text“: “What about couchbase?“} ] } “shows“: { “headline“:“function(doc,req) { var comments=0; if (doc.comments) { comments=doc.comments.length }; var outS=‘<b>‘ + doc.headline + ‘</b> (‘ + comments + ‘ comments)`; return outS; }“, “detail“:“function(doc,req) { var outS=‘<b>‘ + doc.headline + ‘</b><br>`; outS+= doc.body + ‘<br>`; if (doc.comments && doc.comments.length>0) { outS+= ‘comments:<br>`; for (var curComment in doc.comments) { outS+=doc.comments[curComment].name+‘:<br>`; outS+=doc.comments[curComment].text+‘<br>`; } } return outS; }“ } Oliver Kurowski, @okurow
  • 13.
    Shows:Transform Documents II Datadocument (JSON): { “_id“: “1“, “author“: “Oliver Kurowski“, “headline“: “CouchDB is great“, “body“: “CouchDb is a great database for schemeless data“ , “comments“: [ {“name“: “John“, “text“: “Yes, it really is“}, {“name“: “Jane“, “text“: “What about couchbase?“} ] } “shows“: { “headline“:“function(doc,req) { var comments=0; if (doc.comments) { comments=doc.comments.length }; var outS=‘<b>‘ + doc.headline + ‘</b> (‘ + comments + ‘ comments)`; return outS; }“, “detail“:“function(doc,req) { var outS=‘<b>‘ + doc.headline + ‘</b><br>`; outS+= doc.body + ‘<br>`; if (doc.comments && doc.comments.length>0) { outS+= ‘comments:<br>`; for (var curComment in doc.comments) { outS+=doc.comments[curComment].name+‘:<br>`; outS+=doc.comments[curComment].text+‘<br>`; } } return outS; }“ } Oliver Kurowski, @okurow
  • 14.
    Shows:Transform Documents II Datadocument (JSON): { “_id“: “1“, “author“: “Oliver Kurowski“, “headline“: “CouchDB is great“, “body“: “CouchDb is a great database for schemeless data.“ , “comments“: [ {“name“: “John“, “text“: “Yes, it really is“}, {“name“: “Jane“, “text“: “What about couchbase?“} ] } “shows“: { “headline“:“function(doc,req) { Result of _show/headline/1 : var comments=0; CouchDB is great (2 comments) if (doc.comments) { comments=doc.comments.length }; var outS=‘<b>‘ + doc.headline + ‘</b> (‘ + comments + ‘ comments)`; return outS; }“, “detail“:“function(doc,req) { var outS=‘<b>‘ + doc.headline + ‘</b><br>`; outS+= doc.body + ‘<br>`; if (doc.comments && doc.comments.length>0) { outS+= ‘<hr>Comments:<br>`; for (var curComment in doc.comments) { outS+=doc.comments[curComment].name+‘:<br>`; outS+=doc.comments[curComment].text+‘<br>`; } } return outS; }“ } Oliver Kurowski, @okurow
  • 15.
    Shows:Transform Documents II Datadocument (JSON): { “_id“: “1“, “author“: “Oliver Kurowski“, “headline“: “CouchDB is great“, “body“: “CouchDb is a great database for schemeless data.“ , “comments“: [ {“name“: “John“, “text“: “Yes, it really is“}, {“name“: “Jane“, “text“: “What about couchbase?“} ] } “shows“: { “headline“:“function(doc,req) { Result of _show/headline/1 : var comments=0; CouchDB is great (2 comments) if (doc.comments) { comments=doc.comments.length }; var outS=‘<b>‘ + doc.headline + ‘</b> (‘ + comments + ‘ comments)`; return outS; }“, “detail“:“function(doc,req) { var outS=‘<b>‘ + doc.headline + ‘</b><br>`; Result of _show/detail/1 : outS+= doc.body + ‘<br>`; CouchDB is great if (doc.comments && doc.comments.length>0) { CouchDb is a great database for schemeless data. outS+= ‘<hr>Comments:<br>`; for (var curComment in doc.comments) { Comments: outS+=doc.comments[curComment].name+‘:<br>`; John: outS+=doc.comments[curComment].text+‘<br>`; Yes, it really is } Jane: } What about couchbase? return outS; }“ } Oliver Kurowski, @okurow
  • 16.
    The Request Object Showsand lists can access the Request Object containing: (the most interesting) Info: Informations about the Database Id: called document ID (null, if not given) requested _path: The request in parts (divided by /) as Array headers: Header, sent from the client userCTX: Information about the user, usefull for access limitations! query: Parameters of the call The field req.query allows the use of own parameters that can be used in a show/list function. F.e: _show/toHTML/1?showPrice=true handled in the show function: if(req.query.showPrice==true) { ….} Oliver Kurowski, @okurow
  • 17.
    Shows:Talking Parrots Data documents: Design document : { { “_id“: “lora“, “_id“: “_design/parrot“, “name“: “Lora LoL“, “shows{ “owner“: “Emily“, "introduce": "function(doc,req) { “color“: [“red“ , “#ff0000“ ] , var outS=''; “languages“: [ “english“ ] if(doc) { // doc with id in database found } outS+='<font color='+doc.color[1]+'>'+doc.name +' belongs to '+doc.owner+'</font>'; { if(req.query.say) { “_id“: “polly“, if(doc.languages) { “name“: “Polly Poll“, outS+=' and says: '+req.query.say; “owner“: “Ernest“, }else{ “color“: [“blue“ , “#0000ff“ ], outS+='tweet'; “languages“: [ “english“ ] } } } } else { // no doc found if(req.path[5]!=undefined) { // no id given { outS+='I dont know '+req.path[5]; “_id“: “agnus“, }else{ “name“: “Agnus Angry“, outS+='which parrot?'; “owner“: “Ernest“, } “color“: [“grey“ , “#cococo“ ] } } return outS; }“ } } Oliver Kurowski, @okurow
  • 18.
    Shows: Talking Parrots http://localhost:5984/parrots/_design/parrot/_show/introduce/ ->whichparrot? http://localhost:5984/parrots/_design/parrot/_show/introduce/polly ->Polly Poll belongs to Ernest http://localhost:5984/parrots/_design/parrot/_show/introduce/pollx ->I dont know pollx http://localhost:5984/parrots/_design/parrot/_show/introduce/lora?say=Hello ->Lora Loll belongs to Emily and says: Hello http://localhost:5984/parrots/_design/parrot/_show/introduce/agnus?say=Hello ->Agnus Angry belongs to Ernest tweet Oliver Kurowski, @okurow
  • 19.
    List:Transform view resultsI Oliver Kurowski, @okurow
  • 20.
    List:Transform view resultsI Oliver Kurowski, @okurow
  • 21.
    List:Transform view resultsI Oliver Kurowski, @okurow
  • 22.
    List:Transform view resultsI Oliver Kurowski, @okurow
  • 23.
    List:Transform view resultsI Oliver Kurowski, @okurow
  • 24.
    List:Transform view resultsI Result of a View byPrice: A simple list function: {"total_rows":5,"offset":0, “lists“: { "rows":[ “asLI “:“function(head,req) { {"id":"1","key":5.400,"value":“Audi-A3- start({'code':200,'headers':{'Content-Type':'text/html'}}); 2000“}, {"id":"2","key":9.000,"value":“VW-Golf- while(row=getRow()) { 2008“}, {"id":"3","key":12.000,"value":“VW-Polo- send(‘<li>‘+row.value+‘ :‘ +row.key+‘</li>‘); 2010“}, {"id":"4","key":15.000,"value":“VW-Golf- } 2009“}, {"id":"5","key":16.000,"value":“Audi-A4- }“ 2009“} ]} } result from _list/asLi/byPrice : • Audi-A3-2000 : 5.400 • VW-Golf-2008 : 9.000 • VW-Polo-2010 : 12.000 • VW-Golf-2009 : 15.000 • Audi-A4-2009 : 16.000 - All arguments from a view can also be used in a list call (startkey, endkey, skip, limit etc) - Like Shows, you have access to the request Object with all ist possibilities. Oliver Kurowski, @okurow
  • 25.
    List:Transform view resultsII Different headers can be sent “lists“: { “asXML “:“function(head,req) { start({'code':200,'headers':{'Content-Type':'application/xml'}}); var xmlS=‘<cars>‘; while(row=getRow()) { xmlS+=‘<car id=‘ + row.id + ‘>‘+row.value+‘ :‘ +row.key+‘</car>‘); } xmlS+=‘</cars>‘; return xmlS; }“ } result from _list/asXML/byPrice : <cars> <car id=‘1‘>Audi-A3-2000 : 5.400</car> <car id=‘2‘>VW-Golf-2008 : 9.000</car> <car id=‘3‘ >VW-Polo-2010 : 12.000</car> <car id=‘4‘>VW-Golf-2009 : 15.000</car> <car id=‘5‘>Audi-A4-2009 : 16.000</car> </cars> Oliver Kurowski, @okurow
  • 26.